diff options
Diffstat (limited to 'js/scripting-lang/baba-yaga-c/src/parser.c')
-rw-r--r-- | js/scripting-lang/baba-yaga-c/src/parser.c | 2973 |
1 files changed, 0 insertions, 2973 deletions
diff --git a/js/scripting-lang/baba-yaga-c/src/parser.c b/js/scripting-lang/baba-yaga-c/src/parser.c deleted file mode 100644 index 6c94913..0000000 --- a/js/scripting-lang/baba-yaga-c/src/parser.c +++ /dev/null @@ -1,2973 +0,0 @@ -/** - * @file parser.c - * @brief Parser implementation for Baba Yaga - * @author eli_oat - * @version 0.0.1 - * @date 2025 - * - * This file implements the parser for the Baba Yaga language. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include "baba_yaga.h" - -/* ============================================================================ - * Token Types (from lexer.c) - * ============================================================================ */ - -typedef enum { - TOKEN_EOF, - TOKEN_NUMBER, - TOKEN_STRING, - TOKEN_BOOLEAN, - TOKEN_IDENTIFIER, - TOKEN_KEYWORD_WHEN, - TOKEN_KEYWORD_IS, - TOKEN_KEYWORD_THEN, - TOKEN_KEYWORD_AND, - TOKEN_KEYWORD_OR, - TOKEN_KEYWORD_XOR, - TOKEN_KEYWORD_NOT, - TOKEN_KEYWORD_VIA, - TOKEN_OP_PLUS, - TOKEN_OP_MINUS, - TOKEN_OP_UNARY_MINUS, - TOKEN_OP_MULTIPLY, - TOKEN_OP_DIVIDE, - TOKEN_OP_MODULO, - TOKEN_OP_POWER, - TOKEN_OP_EQUALS, - TOKEN_OP_NOT_EQUALS, - TOKEN_OP_LESS, - TOKEN_OP_LESS_EQUAL, - TOKEN_OP_GREATER, - TOKEN_OP_GREATER_EQUAL, - TOKEN_LPAREN, - TOKEN_RPAREN, - TOKEN_LBRACE, - TOKEN_RBRACE, - TOKEN_LBRACKET, - TOKEN_RBRACKET, - TOKEN_COMMA, - TOKEN_COLON, - TOKEN_SEMICOLON, - TOKEN_ARROW, - TOKEN_DOT, - TOKEN_FUNCTION_REF, - TOKEN_IO_IN, - TOKEN_IO_OUT, - TOKEN_IO_ASSERT, - TOKEN_IO_EMIT, - TOKEN_IO_LISTEN -} TokenType; - -typedef struct { - TokenType type; - char* lexeme; - int line; - int column; - union { - double number; - bool boolean; - } literal; -} Token; - -/* ============================================================================ - * AST Node Types - * ============================================================================ */ - -/* NodeType enum is now defined in baba_yaga.h */ - -/* ============================================================================ - * AST Node Structure - * ============================================================================ */ - -struct ASTNode { - NodeType type; - int line; - int column; - union { - Value literal; - char* identifier; - struct { - struct ASTNode* left; - struct ASTNode* right; - char* operator; - } binary; - struct { - struct ASTNode* operand; - char* operator; - } unary; - struct { - struct ASTNode* function; - struct ASTNode** arguments; - int arg_count; - } function_call; - struct { - char* name; - struct ASTNode** parameters; - int param_count; - struct ASTNode* body; - } function_def; - struct { - char* name; - struct ASTNode* value; - } variable_decl; - struct { - struct ASTNode* test; - struct ASTNode** patterns; - int pattern_count; - } when_expr; - struct { - struct ASTNode* test; - struct ASTNode* result; - } when_pattern; - struct { - struct ASTNode** elements; - int element_count; - } table; - struct { - struct ASTNode* object; - struct ASTNode* key; - } table_access; - struct { - char* operation; - struct ASTNode* argument; - } io_operation; - struct { - struct ASTNode** statements; - int statement_count; - } sequence; - } data; -}; - -/* ============================================================================ - * Parser Structure - * ============================================================================ */ - -typedef struct { - Token** tokens; - int token_count; - int current; - bool has_error; - char* error_message; -} Parser; - -/* ============================================================================ - * AST Node Management - * ============================================================================ */ - -/** - * @brief Create a literal node - * - * @param value Literal value - * @param line Line number - * @param column Column number - * @return New literal node - */ -static ASTNode* ast_literal_node(Value value, int line, int column) { - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - return NULL; - } - - node->type = NODE_LITERAL; - node->line = line; - node->column = column; - node->data.literal = value; - - return node; -} - -/** - * @brief Create an identifier node - * - * @param identifier Identifier name - * @param line Line number - * @param column Column number - * @return New identifier node - */ -static ASTNode* ast_identifier_node(const char* identifier, int line, int column) { - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - return NULL; - } - - node->type = NODE_IDENTIFIER; - node->line = line; - node->column = column; - node->data.identifier = strdup(identifier); - - return node; -} - -/** - * @brief Create a function call node - * - * @param function Function expression - * @param arguments Array of argument expressions - * @param arg_count Number of arguments - * @param line Line number - * @param column Column number - * @return New function call node - */ -static ASTNode* ast_function_call_node(ASTNode* function, ASTNode** arguments, - int arg_count, int line, int column) { - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - return NULL; - } - - node->type = NODE_FUNCTION_CALL; - node->line = line; - node->column = column; - node->data.function_call.function = function; - node->data.function_call.arguments = arguments; - node->data.function_call.arg_count = arg_count; - - return node; -} - -/** - * @brief Create a binary operator node - * - * @param left Left operand - * @param right Right operand - * @param operator Operator name - * @param line Line number - * @param column Column number - * @return New binary operator node - */ -static ASTNode* ast_binary_op_node(ASTNode* left, ASTNode* right, - const char* operator, int line, int column) { - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - return NULL; - } - - node->type = NODE_BINARY_OP; - node->line = line; - node->column = column; - node->data.binary.left = left; - node->data.binary.right = right; - node->data.binary.operator = strdup(operator); - - return node; -} - -/** - * @brief Create a unary operator node (translated to function call) - * - * @param operand Operand expression - * @param operator Operator name - * @param line Line number - * @param column Column number - * @return New function call node representing the operator - */ -static ASTNode* ast_unary_op_node(ASTNode* operand, const char* operator, - int line, int column) { - /* Create simple function call: operator(operand) */ - ASTNode* operator_node = ast_identifier_node(operator, line, column); - if (operator_node == NULL) { - return NULL; - } - - ASTNode** args = malloc(1 * sizeof(ASTNode*)); - if (args == NULL) { - free(operator_node); - return NULL; - } - args[0] = operand; - - return ast_function_call_node(operator_node, args, 1, line, column); -} - -/** - * @brief Create a sequence node - * - * @param statements Array of statement nodes - * @param statement_count Number of statements - * @param line Line number - * @param column Column number - * @return New sequence node - */ -static ASTNode* ast_sequence_node(ASTNode** statements, int statement_count, - int line, int column) { - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - return NULL; - } - - node->type = NODE_SEQUENCE; - node->line = line; - node->column = column; - node->data.sequence.statements = statements; - node->data.sequence.statement_count = statement_count; - - return node; -} - -/** - * @brief Create a when expression node - * - * @param test Test expression - * @param patterns Array of pattern nodes - * @param pattern_count Number of patterns - * @param line Line number - * @param column Column number - * @return New when expression node - */ -static ASTNode* ast_when_expr_node(ASTNode* test, ASTNode** patterns, - int pattern_count, int line, int column) { - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - return NULL; - } - - node->type = NODE_WHEN_EXPR; - node->line = line; - node->column = column; - node->data.when_expr.test = test; - node->data.when_expr.patterns = patterns; - node->data.when_expr.pattern_count = pattern_count; - - - return node; -} - -/** - * @brief Create a when pattern node - * - * @param test Pattern test expression - * @param result Result expression - * @param line Line number - * @param column Column number - * @return New when pattern node - */ -static ASTNode* ast_when_pattern_node(ASTNode* test, ASTNode* result, - int line, int column) { - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - return NULL; - } - - node->type = NODE_WHEN_PATTERN; - node->line = line; - node->column = column; - node->data.when_pattern.test = test; - node->data.when_pattern.result = result; - - return node; -} - -/** - * @brief Destroy an AST node - * - * @param node Node to destroy - */ -static void ast_destroy_node(ASTNode* node) { - if (node == NULL) { - return; - } - - switch (node->type) { - case NODE_IDENTIFIER: - free(node->data.identifier); - break; - case NODE_FUNCTION_CALL: - for (int i = 0; i < node->data.function_call.arg_count; i++) { - ast_destroy_node(node->data.function_call.arguments[i]); - } - free(node->data.function_call.arguments); - ast_destroy_node(node->data.function_call.function); - break; - case NODE_FUNCTION_DEF: - for (int i = 0; i < node->data.function_def.param_count; i++) { - ast_destroy_node(node->data.function_def.parameters[i]); - } - free(node->data.function_def.parameters); - free(node->data.function_def.name); - ast_destroy_node(node->data.function_def.body); - break; - case NODE_VARIABLE_DECL: - free(node->data.variable_decl.name); - ast_destroy_node(node->data.variable_decl.value); - break; - case NODE_WHEN_EXPR: - ast_destroy_node(node->data.when_expr.test); - for (int i = 0; i < node->data.when_expr.pattern_count; i++) { - ast_destroy_node(node->data.when_expr.patterns[i]); - } - free(node->data.when_expr.patterns); - break; - case NODE_WHEN_PATTERN: - ast_destroy_node(node->data.when_pattern.test); - ast_destroy_node(node->data.when_pattern.result); - break; - case NODE_TABLE: - for (int i = 0; i < node->data.table.element_count; i++) { - ast_destroy_node(node->data.table.elements[i]); - } - free(node->data.table.elements); - break; - case NODE_TABLE_ACCESS: - ast_destroy_node(node->data.table_access.object); - ast_destroy_node(node->data.table_access.key); - break; - case NODE_IO_OPERATION: - free(node->data.io_operation.operation); - ast_destroy_node(node->data.io_operation.argument); - break; - case NODE_SEQUENCE: - for (int i = 0; i < node->data.sequence.statement_count; i++) { - ast_destroy_node(node->data.sequence.statements[i]); - } - free(node->data.sequence.statements); - break; - default: - /* No cleanup needed for other types */ - break; - } - - free(node); -} - -/* ============================================================================ - * Parser Functions - * ============================================================================ */ - -/** - * @brief Create a new parser - * - * @param tokens Array of tokens - * @param token_count Number of tokens - * @return New parser instance, or NULL on failure - */ -static Parser* parser_create(Token** tokens, int token_count) { - Parser* parser = malloc(sizeof(Parser)); - if (parser == NULL) { - return NULL; - } - - parser->tokens = tokens; - parser->token_count = token_count; - parser->current = 0; - parser->has_error = false; - parser->error_message = NULL; - - return parser; -} - -/** - * @brief Destroy a parser - * - * @param parser Parser to destroy - */ -static void parser_destroy(Parser* parser) { - if (parser == NULL) { - return; - } - - if (parser->error_message != NULL) { - free(parser->error_message); - } - - free(parser); -} - -/** - * @brief Set parser error - * - * @param parser Parser instance - * @param message Error message - */ -static void parser_set_error(Parser* parser, const char* message) { - if (parser == NULL) { - return; - } - - parser->has_error = true; - if (parser->error_message != NULL) { - free(parser->error_message); - } - parser->error_message = strdup(message); -} - -/** - * @brief Check if we're at the end of tokens - * - * @param parser Parser instance - * @return true if at end, false otherwise - */ -static bool parser_is_at_end(const Parser* parser) { - return parser->current >= parser->token_count; -} - -/** - * @brief Peek at current token - * - * @param parser Parser instance - * @return Current token, or NULL if at end - */ -static Token* parser_peek(const Parser* parser) { - if (parser_is_at_end(parser)) { - return NULL; - } - return parser->tokens[parser->current]; -} - -/** - * @brief Peek at next token - * - * @param parser Parser instance - * @return Next token, or NULL if at end - */ -static Token* parser_peek_next(const Parser* parser) { - if (parser->current + 1 >= parser->token_count) { - return NULL; - } - return parser->tokens[parser->current + 1]; -} - -/** - * @brief Advance to next token - * - * @param parser Parser instance - * @return Token that was advanced over - */ -static Token* parser_advance(Parser* parser) { - if (parser_is_at_end(parser)) { - return NULL; - } - return parser->tokens[parser->current++]; -} - -/** - * @brief Check if current token matches expected type - * - * @param parser Parser instance - * @param type Expected token type - * @return true if matches, false otherwise - */ -static bool parser_check(const Parser* parser, TokenType type) { - if (parser_is_at_end(parser)) { - return false; - } - return parser->tokens[parser->current]->type == type; -} - -/** - * @brief Consume token of expected type - * - * @param parser Parser instance - * @param type Expected token type - * @param error_message Error message if type doesn't match - * @return Consumed token, or NULL on error - */ -static Token* parser_consume(Parser* parser, TokenType type, const char* error_message) { - if (parser_check(parser, type)) { - return parser_advance(parser); - } - - parser_set_error(parser, error_message); - return NULL; -} - -/* ============================================================================ - * Expression Parsing (Operator Precedence) - * ============================================================================ */ - -/* Forward declarations */ -static ASTNode* parser_parse_expression(Parser* parser); -static ASTNode* parser_parse_logical(Parser* parser); -/* static ASTNode* parser_parse_composition(Parser* parser); */ -/* static ASTNode* parser_parse_application(Parser* parser); */ -static ASTNode* parser_parse_statement(Parser* parser); -static ASTNode* parser_parse_when_expression(Parser* parser); -static ASTNode* parser_parse_when_pattern(Parser* parser); -static ASTNode* parser_parse_when_result_expression(Parser* parser); -static ASTNode* parser_parse_postfix(Parser* parser); -static const char* node_type_name(NodeType type); -static ASTNode* parser_parse_function_def(Parser* parser); -static ASTNode* parser_parse_embedded_arrow_function(Parser* parser); - -/** - * @brief Parse primary expression (literals, identifiers, parentheses) - * - * @param parser Parser instance - * @return Parsed expression node - */ -static ASTNode* parser_parse_primary(Parser* parser) { - Token* token = parser_peek(parser); - if (token == NULL) { - parser_set_error(parser, "Unexpected end of input"); - return NULL; - } - - switch (token->type) { - case TOKEN_NUMBER: { - DEBUG_TRACE("parser_parse_primary consuming number: %g", token->literal.number); - parser_advance(parser); - return ast_literal_node(baba_yaga_value_number(token->literal.number), - token->line, token->column); - } - case TOKEN_STRING: { - DEBUG_TRACE("parser_parse_primary consuming string: %s", token->lexeme); - parser_advance(parser); - return ast_literal_node(baba_yaga_value_string(token->lexeme), - token->line, token->column); - } - case TOKEN_BOOLEAN: { - DEBUG_TRACE("parser_parse_primary consuming boolean: %s", token->literal.boolean ? "true" : "false"); - parser_advance(parser); - return ast_literal_node(baba_yaga_value_boolean(token->literal.boolean), - token->line, token->column); - } - case TOKEN_IDENTIFIER: { - DEBUG_TRACE("parser_parse_primary consuming identifier: %s", token->lexeme); - parser_advance(parser); - /* Special handling for wildcard pattern */ - if (strcmp(token->lexeme, "_") == 0) { - /* Create a special wildcard literal */ - return ast_literal_node(baba_yaga_value_string("_"), token->line, token->column); - } - return ast_identifier_node(token->lexeme, token->line, token->column); - } - case TOKEN_IO_IN: - case TOKEN_IO_OUT: - case TOKEN_IO_ASSERT: - case TOKEN_IO_EMIT: - case TOKEN_IO_LISTEN: { - DEBUG_TRACE("parser_parse_primary consuming io operation: %s", token->lexeme); - parser_advance(parser); - /* IO operations are treated as function calls - strip the ".." prefix */ - const char* func_name = token->lexeme + 2; /* Skip ".." */ - - /* For ..assert, parse the entire expression as a single argument */ - if (strcmp(func_name, "assert") == 0) { - /* Parse the assertion expression */ - ASTNode* assertion_expr = parser_parse_expression(parser); - if (assertion_expr == NULL) { - return NULL; - } - - /* Create function call with the assertion expression as argument */ - ASTNode** args = malloc(1 * sizeof(ASTNode*)); - if (args == NULL) { - ast_destroy_node(assertion_expr); - return NULL; - } - args[0] = assertion_expr; - - ASTNode* func_node = ast_identifier_node(func_name, token->line, token->column); - if (func_node == NULL) { - free(args); - ast_destroy_node(assertion_expr); - return NULL; - } - - return ast_function_call_node(func_node, args, 1, token->line, token->column); - } - - /* For ..emit, parse the entire expression as a single argument */ - if (strcmp(func_name, "emit") == 0) { - /* Parse the expression */ - ASTNode* expr = parser_parse_expression(parser); - if (expr == NULL) { - return NULL; - } - - /* Create function call with the expression as argument */ - ASTNode** args = malloc(1 * sizeof(ASTNode*)); - if (args == NULL) { - ast_destroy_node(expr); - return NULL; - } - args[0] = expr; - - ASTNode* func_node = ast_identifier_node(func_name, token->line, token->column); - if (func_node == NULL) { - free(args); - ast_destroy_node(expr); - return NULL; - } - - return ast_function_call_node(func_node, args, 1, token->line, token->column); - } - - /* For ..listen, create a function call with no arguments */ - if (strcmp(func_name, "listen") == 0) { - ASTNode* func_node = ast_identifier_node(func_name, token->line, token->column); - if (func_node == NULL) { - return NULL; - } - - return ast_function_call_node(func_node, NULL, 0, token->line, token->column); - } - - return ast_identifier_node(func_name, token->line, token->column); - } - case TOKEN_KEYWORD_WHEN: { - - return parser_parse_when_expression(parser); - } - case TOKEN_FUNCTION_REF: { - DEBUG_TRACE("parser_parse_primary consuming function ref: %s", token->lexeme); - parser_advance(parser); - - /* Check if this is @(expression) syntax */ - if (!parser_is_at_end(parser) && parser_peek(parser)->type == TOKEN_LPAREN) { - DEBUG_TRACE("parser_parse_primary consuming '('"); - parser_advance(parser); /* consume '(' */ - - /* Parse the expression inside parentheses */ - ASTNode* expr = parser_parse_expression(parser); - if (expr == NULL) { - return NULL; - } - - /* Expect closing parenthesis */ - if (!parser_consume(parser, TOKEN_RPAREN, "Expected ')' after expression")) { - ast_destroy_node(expr); - return NULL; - } - - /* Return the expression as-is (it will be evaluated when used as an argument) */ - return expr; - } - - /* Handle @function_name syntax */ - ASTNode* func_node = ast_identifier_node(token->lexeme, token->line, token->column); - if (func_node == NULL) { - return NULL; - } - - /* Check if this function reference is followed by arguments */ - /* Only treat as function call if it's at the top level (not in an argument position) */ - if (!parser_is_at_end(parser)) { - Token* next_token = parser_peek(parser); - if (next_token != NULL && - next_token->type != TOKEN_OP_PLUS && - next_token->type != TOKEN_OP_MINUS && - next_token->type != TOKEN_OP_MULTIPLY && - next_token->type != TOKEN_OP_DIVIDE && - next_token->type != TOKEN_OP_MODULO && - next_token->type != TOKEN_OP_POWER && - next_token->type != TOKEN_OP_EQUALS && - next_token->type != TOKEN_OP_NOT_EQUALS && - next_token->type != TOKEN_OP_LESS && - next_token->type != TOKEN_OP_LESS_EQUAL && - next_token->type != TOKEN_OP_GREATER && - next_token->type != TOKEN_OP_GREATER_EQUAL && - next_token->type != TOKEN_RPAREN && - next_token->type != TOKEN_RBRACE && - next_token->type != TOKEN_RBRACKET && - next_token->type != TOKEN_SEMICOLON && - next_token->type != TOKEN_COMMA && - next_token->type != TOKEN_EOF) { - - /* For now, always treat function references as values, not function calls */ - /* This allows them to be passed as arguments to other functions */ - DEBUG_TRACE("parser_parse_primary: treating function reference as value"); - return func_node; - - /* Parse arguments for this function call */ - ASTNode** args = NULL; - int arg_count = 0; - - while (!parser_is_at_end(parser)) { - Token* arg_token = parser_peek(parser); - if (arg_token == NULL) { - break; - } - - /* Stop if we hit an operator or delimiter */ - if (arg_token->type == TOKEN_OP_PLUS || - arg_token->type == TOKEN_OP_MINUS || - arg_token->type == TOKEN_OP_MULTIPLY || - arg_token->type == TOKEN_OP_DIVIDE || - arg_token->type == TOKEN_OP_MODULO || - arg_token->type == TOKEN_OP_POWER || - arg_token->type == TOKEN_OP_EQUALS || - arg_token->type == TOKEN_OP_NOT_EQUALS || - arg_token->type == TOKEN_OP_LESS || - arg_token->type == TOKEN_OP_LESS_EQUAL || - arg_token->type == TOKEN_OP_GREATER || - arg_token->type == TOKEN_OP_GREATER_EQUAL || - arg_token->type == TOKEN_RPAREN || - arg_token->type == TOKEN_RBRACE || - arg_token->type == TOKEN_RBRACKET || - arg_token->type == TOKEN_SEMICOLON || - arg_token->type == TOKEN_COMMA || - arg_token->type == TOKEN_EOF) { - break; - } - - /* Parse argument */ - ASTNode* arg = parser_parse_postfix(parser); - if (arg == NULL) { - /* Cleanup on error */ - for (int i = 0; i < arg_count; i++) { - ast_destroy_node(args[i]); - } - free(args); - ast_destroy_node(func_node); - return NULL; - } - - /* Add to arguments array */ - ASTNode** new_args = realloc(args, (arg_count + 1) * sizeof(ASTNode*)); - if (new_args == NULL) { - /* Cleanup on error */ - for (int i = 0; i < arg_count; i++) { - ast_destroy_node(args[i]); - } - free(args); - ast_destroy_node(arg); - ast_destroy_node(func_node); - return NULL; - } - args = new_args; - args[arg_count] = arg; - arg_count++; - } - - /* Create function call with the arguments */ - if (arg_count > 0) { - ASTNode* func_call = ast_function_call_node(func_node, args, arg_count, func_node->line, func_node->column); - if (func_call == NULL) { - /* Cleanup on error */ - for (int i = 0; i < arg_count; i++) { - ast_destroy_node(args[i]); - } - free(args); - ast_destroy_node(func_node); - return NULL; - } - return func_call; - } - } - } - - return func_node; - } - case TOKEN_LPAREN: { - DEBUG_TRACE("parser_parse_primary consuming '('"); - parser_advance(parser); /* consume '(' */ - ASTNode* expr = parser_parse_expression(parser); - if (expr == NULL) { - return NULL; - } - - if (!parser_consume(parser, TOKEN_RPAREN, "Expected ')' after expression")) { - ast_destroy_node(expr); - return NULL; - } - - return expr; - } - case TOKEN_LBRACE: { - DEBUG_TRACE("parser_parse_primary consuming table literal '{'"); - parser_advance(parser); /* consume '{' */ - - ASTNode** elements = NULL; - int element_count = 0; - int capacity = 10; - - /* Allocate initial space for elements */ - elements = malloc(capacity * sizeof(ASTNode*)); - if (elements == NULL) { - return NULL; - } - - /* Parse table entries */ - while (!parser_is_at_end(parser) && parser_peek(parser)->type != TOKEN_RBRACE) { - ASTNode* value = NULL; - - /* Check if this is a key-value pair (any token: value) */ - - /* Check if this is a key-value pair */ - bool is_key_value_pair = false; - - if (parser_peek(parser)->type == TOKEN_LPAREN) { - /* For expression keys, we need to look ahead to find the colon */ - int look_ahead = parser->current; - int paren_count = 0; - bool found_colon = false; - - while (look_ahead < parser->token_count) { - Token* token = parser->tokens[look_ahead]; - if (token->type == TOKEN_LPAREN) { - paren_count++; - } else if (token->type == TOKEN_RPAREN) { - paren_count--; - if (paren_count == 0) { - /* We've found the closing parenthesis, check if next is colon */ - if (look_ahead + 1 < parser->token_count && - parser->tokens[look_ahead + 1]->type == TOKEN_COLON) { - found_colon = true; - } - break; - } - } else if (token->type == TOKEN_COMMA || token->type == TOKEN_RBRACE) { - /* Stop looking if we hit table boundaries */ - break; - } - look_ahead++; - } - is_key_value_pair = found_colon; - } else { - /* For literal keys, check if next token is colon */ - is_key_value_pair = (parser_peek(parser)->type == TOKEN_IDENTIFIER || - parser_peek(parser)->type == TOKEN_NUMBER || - parser_peek(parser)->type == TOKEN_BOOLEAN || - parser_peek(parser)->type == TOKEN_STRING) && - !parser_is_at_end(parser) && - parser_peek_next(parser)->type == TOKEN_COLON; - } - - if (is_key_value_pair) { - - /* Parse key-value pair */ - ASTNode* key_node = NULL; - Token* key_token = NULL; - - if (parser_peek(parser)->type == TOKEN_LPAREN) { - /* Parse expression key */ - key_node = parser_parse_expression(parser); - if (key_node == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - return NULL; - } - /* Create a dummy token for line/column info */ - key_token = parser_peek(parser); - if (key_token == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - ast_destroy_node(key_node); - return NULL; - } - } else { - /* Parse literal key */ - key_token = parser_advance(parser); /* Consume the key token */ - if (key_token == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - return NULL; - } - } - - /* Consume colon */ - if (!parser_consume(parser, TOKEN_COLON, "Expected ':' after table key")) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - return NULL; - } - - /* Check if this is an arrow function by looking ahead */ - bool is_arrow_function = false; - int look_ahead = parser->current; - int identifier_count = 0; - - /* Look ahead to see if we have identifiers followed by '->' */ - while (look_ahead < parser->token_count) { - Token* token = parser->tokens[look_ahead]; - if (token->type == TOKEN_ARROW) { - /* If we have at least one identifier before '->', it's an arrow function */ - if (identifier_count > 0) { - is_arrow_function = true; - } - break; - } - if (token->type == TOKEN_IDENTIFIER) { - identifier_count++; - } else if (token->type == TOKEN_COMMA || token->type == TOKEN_RBRACE) { - /* Stop looking if we hit table boundaries */ - break; - } else { - /* If we hit anything else, it's not an arrow function */ - identifier_count = 0; - break; - } - look_ahead++; - } - - /* Parse the value */ - if (is_arrow_function) { - /* Parse as embedded arrow function */ - value = parser_parse_embedded_arrow_function(parser); - } else { - /* Parse as general expression */ - value = parser_parse_expression(parser); - } - if (value == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - return NULL; - } - - /* For now, we'll store key-value pairs as function calls to a special "table_entry" function */ - /* This allows us to represent both key-value pairs and array-like entries uniformly */ - ASTNode** entry_args = malloc(2 * sizeof(ASTNode*)); - if (entry_args == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - ast_destroy_node(value); - return NULL; - } - - /* Create key value based on token type or expression */ - ASTNode* key_arg = NULL; - if (key_node != NULL) { - /* Expression key - use the parsed AST node */ - key_arg = key_node; - } else { - /* Literal key - create literal value from token */ - Value key_value; - if (key_token->type == TOKEN_IDENTIFIER) { - key_value = baba_yaga_value_string(key_token->lexeme); - } else if (key_token->type == TOKEN_NUMBER) { - key_value = baba_yaga_value_number(key_token->literal.number); - } else if (key_token->type == TOKEN_BOOLEAN) { - key_value = baba_yaga_value_boolean(key_token->literal.boolean); - } else if (key_token->type == TOKEN_STRING) { - key_value = baba_yaga_value_string(key_token->lexeme); - } else { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - free(entry_args); - ast_destroy_node(value); - return NULL; - } - key_arg = ast_literal_node(key_value, key_token->line, key_token->column); - } - - entry_args[0] = key_arg; - entry_args[1] = value; - - ASTNode* table_entry_node = ast_identifier_node("table_entry", key_token->line, key_token->column); - if (table_entry_node == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - free(entry_args); - ast_destroy_node(value); - if (key_node != NULL) { - ast_destroy_node(key_node); - } - return NULL; - } - - ASTNode* entry_node = ast_function_call_node(table_entry_node, entry_args, 2, key_token->line, key_token->column); - if (entry_node == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - free(entry_args); - ast_destroy_node(table_entry_node); - ast_destroy_node(value); - if (key_node != NULL) { - ast_destroy_node(key_node); - } - return NULL; - } - - value = entry_node; - } else { - /* Parse array-like entry (just a value) */ - value = parser_parse_expression(parser); - if (value == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - return NULL; - } - } - - /* Check if we need more space */ - if (element_count >= capacity) { - capacity *= 2; - ASTNode** new_elements = realloc(elements, capacity * sizeof(ASTNode*)); - if (new_elements == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - ast_destroy_node(value); - return NULL; - } - elements = new_elements; - } - - elements[element_count++] = value; - - /* Check for comma separator */ - if (!parser_is_at_end(parser) && parser_peek(parser)->type == TOKEN_COMMA) { - parser_advance(parser); /* consume ',' */ - } else if (!parser_is_at_end(parser) && parser_peek(parser)->type != TOKEN_RBRACE) { - /* No comma but not end of table - this is an error */ - parser_set_error(parser, "Expected ',' or '}' in table literal"); - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - return NULL; - } - } - - /* Expect closing brace */ - if (!parser_consume(parser, TOKEN_RBRACE, "Expected '}' after table literal")) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - return NULL; - } - - /* Create table node */ - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - /* Cleanup on error */ - for (int i = 0; i < element_count; i++) { - ast_destroy_node(elements[i]); - } - free(elements); - return NULL; - } - - node->type = NODE_TABLE; - node->line = token->line; - node->column = token->column; - node->data.table.elements = elements; - node->data.table.element_count = element_count; - - return node; - } - case TOKEN_OP_UNARY_MINUS: { - DEBUG_TRACE("parser_parse_primary consuming unary minus"); - parser_advance(parser); /* consume '-' */ - ASTNode* operand = parser_parse_postfix(parser); - if (operand == NULL) { - return NULL; - } - return ast_unary_op_node(operand, "negate", token->line, token->column); - } - case TOKEN_KEYWORD_NOT: { - DEBUG_TRACE("parser_parse_primary consuming 'not'"); - parser_advance(parser); /* consume 'not' */ - ASTNode* operand = parser_parse_postfix(parser); - if (operand == NULL) { - return NULL; - } - return ast_unary_op_node(operand, "not", token->line, token->column); - } - default: - parser_set_error(parser, "Unexpected token in expression"); - return NULL; - } -} - -/** - * @brief Parse function call expression - * - * @param parser Parser instance - * @return Parsed expression node - */ -/* TODO: Re-implement function call parsing at application level */ -/* TODO: Re-implement function call parsing at application level */ - -/** - * @brief Parse power expression (^) - * - * @param parser Parser instance - * @return Parsed expression node - */ -static ASTNode* parser_parse_power(Parser* parser) { - ASTNode* left = parser_parse_postfix(parser); - if (left == NULL) { - return NULL; - } - - while (parser_check(parser, TOKEN_OP_POWER)) { - Token* op = parser_advance(parser); - ASTNode* right = parser_parse_postfix(parser); - if (right == NULL) { - ast_destroy_node(left); - return NULL; - } - - ASTNode* new_left = ast_binary_op_node(left, right, "pow", op->line, op->column); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(right); - return NULL; - } - - left = new_left; - } - - return left; -} - -/** - * @brief Parse multiplicative expression (*, /, %) - * - * @param parser Parser instance - * @return Parsed expression node - */ -static ASTNode* parser_parse_multiplicative(Parser* parser) { - ASTNode* left = parser_parse_power(parser); - if (left == NULL) { - return NULL; - } - - while (parser_check(parser, TOKEN_OP_MULTIPLY) || - parser_check(parser, TOKEN_OP_DIVIDE) || - parser_check(parser, TOKEN_OP_MODULO)) { - Token* op = parser_advance(parser); - ASTNode* right = parser_parse_power(parser); - if (right == NULL) { - ast_destroy_node(left); - return NULL; - } - - const char* operator_name; - switch (op->type) { - case TOKEN_OP_MULTIPLY: operator_name = "multiply"; break; - case TOKEN_OP_DIVIDE: operator_name = "divide"; break; - case TOKEN_OP_MODULO: operator_name = "modulo"; break; - default: operator_name = "unknown"; break; - } - - ASTNode* new_left = ast_binary_op_node(left, right, operator_name, op->line, op->column); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(right); - return NULL; - } - - left = new_left; - } - - return left; -} - -/** - * @brief Parse additive expression (+, -) - * - * @param parser Parser instance - * @return Parsed expression node - */ -static ASTNode* parser_parse_additive(Parser* parser) { - ASTNode* left = parser_parse_multiplicative(parser); - if (left == NULL) { - return NULL; - } - - while (parser_check(parser, TOKEN_OP_PLUS) || parser_check(parser, TOKEN_OP_MINUS)) { - Token* op = parser_advance(parser); - ASTNode* right = parser_parse_multiplicative(parser); - if (right == NULL) { - ast_destroy_node(left); - return NULL; - } - - const char* operator_name = (op->type == TOKEN_OP_PLUS) ? "add" : "subtract"; - - ASTNode* new_left = ast_binary_op_node(left, right, operator_name, op->line, op->column); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(right); - return NULL; - } - - left = new_left; - } - - return left; -} - -/** - * @brief Parse comparison expression (=, !=, <, <=, >, >=) - * - * @param parser Parser instance - * @return Parsed expression node - */ -static ASTNode* parser_parse_comparison(Parser* parser) { - ASTNode* left = parser_parse_additive(parser); - if (left == NULL) { - return NULL; - } - - while (parser_check(parser, TOKEN_OP_EQUALS) || - parser_check(parser, TOKEN_OP_NOT_EQUALS) || - parser_check(parser, TOKEN_OP_LESS) || - parser_check(parser, TOKEN_OP_LESS_EQUAL) || - parser_check(parser, TOKEN_OP_GREATER) || - parser_check(parser, TOKEN_OP_GREATER_EQUAL)) { - Token* op = parser_advance(parser); - ASTNode* right = parser_parse_additive(parser); - if (right == NULL) { - ast_destroy_node(left); - return NULL; - } - - const char* operator_name; - switch (op->type) { - case TOKEN_OP_EQUALS: operator_name = "equals"; break; - case TOKEN_OP_NOT_EQUALS: operator_name = "not_equals"; break; - case TOKEN_OP_LESS: operator_name = "less"; break; - case TOKEN_OP_LESS_EQUAL: operator_name = "less_equal"; break; - case TOKEN_OP_GREATER: operator_name = "greater"; break; - case TOKEN_OP_GREATER_EQUAL: operator_name = "greater_equal"; break; - default: operator_name = "unknown"; break; - } - - ASTNode* new_left = ast_binary_op_node(left, right, operator_name, op->line, op->column); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(right); - return NULL; - } - - left = new_left; - } - - return left; -} - -/** - * @brief Parse logical expression (and, or, xor) - * - * @param parser Parser instance - * @return Parsed expression node - */ -static ASTNode* parser_parse_logical(Parser* parser) { - ASTNode* left = parser_parse_comparison(parser); - if (left == NULL) { - return NULL; - } - - /* Handle logical operators */ - while ((parser_check(parser, TOKEN_KEYWORD_AND) || - parser_check(parser, TOKEN_KEYWORD_OR) || - parser_check(parser, TOKEN_KEYWORD_XOR)) || - (parser_check(parser, TOKEN_IDENTIFIER) && - (strcmp(parser_peek(parser)->lexeme, "and") == 0 || - strcmp(parser_peek(parser)->lexeme, "or") == 0 || - strcmp(parser_peek(parser)->lexeme, "xor") == 0))) { - Token* op = parser_advance(parser); - ASTNode* right = parser_parse_comparison(parser); - if (right == NULL) { - ast_destroy_node(left); - return NULL; - } - - const char* operator_name; - if (op->type == TOKEN_KEYWORD_AND || - (op->type == TOKEN_IDENTIFIER && strcmp(op->lexeme, "and") == 0)) { - operator_name = "and"; - } else if (op->type == TOKEN_KEYWORD_OR || - (op->type == TOKEN_IDENTIFIER && strcmp(op->lexeme, "or") == 0)) { - operator_name = "or"; - } else if (op->type == TOKEN_KEYWORD_XOR || - (op->type == TOKEN_IDENTIFIER && strcmp(op->lexeme, "xor") == 0)) { - operator_name = "xor"; - } else { - operator_name = "unknown"; - } - - ASTNode* new_left = ast_binary_op_node(left, right, operator_name, op->line, op->column); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(right); - return NULL; - } - - left = new_left; - } - - /* Handle via operator (function composition) - right-associative */ - while (parser_check(parser, TOKEN_KEYWORD_VIA)) { - Token* op = parser_advance(parser); - ASTNode* right = parser_parse_logical(parser); /* Right-associative: recurse */ - if (right == NULL) { - ast_destroy_node(left); - return NULL; - } - - ASTNode* new_left = ast_binary_op_node(left, right, "via", op->line, op->column); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(right); - return NULL; - } - - left = new_left; - } - - /* Handle function application */ - /* Skip function application if the left node is a when expression */ - if (left->type == NODE_WHEN_EXPR) { - return left; - } - - while (!parser_is_at_end(parser)) { - Token* next_token = parser_peek(parser); - if (next_token == NULL) break; - - - - /* Check if this token can be a function argument */ - bool can_be_arg = (next_token->type == TOKEN_IDENTIFIER || - next_token->type == TOKEN_FUNCTION_REF || - next_token->type == TOKEN_NUMBER || - next_token->type == TOKEN_STRING || - next_token->type == TOKEN_BOOLEAN || - next_token->type == TOKEN_LPAREN || - next_token->type == TOKEN_LBRACE || - next_token->type == TOKEN_OP_UNARY_MINUS || - next_token->type == TOKEN_KEYWORD_NOT); - - /* Check if this token should not trigger function application */ - bool should_not_trigger = (next_token->type == TOKEN_OP_PLUS || - next_token->type == TOKEN_OP_MINUS || - next_token->type == TOKEN_OP_MULTIPLY || - next_token->type == TOKEN_OP_DIVIDE || - next_token->type == TOKEN_OP_MODULO || - next_token->type == TOKEN_OP_POWER || - next_token->type == TOKEN_OP_EQUALS || - next_token->type == TOKEN_OP_NOT_EQUALS || - next_token->type == TOKEN_OP_LESS || - next_token->type == TOKEN_OP_LESS_EQUAL || - next_token->type == TOKEN_OP_GREATER || - next_token->type == TOKEN_OP_GREATER_EQUAL || - next_token->type == TOKEN_KEYWORD_AND || - next_token->type == TOKEN_KEYWORD_OR || - next_token->type == TOKEN_KEYWORD_XOR || - (next_token->type == TOKEN_IDENTIFIER && - (strcmp(next_token->lexeme, "and") == 0 || - strcmp(next_token->lexeme, "or") == 0 || - strcmp(next_token->lexeme, "xor") == 0)) || - next_token->type == TOKEN_KEYWORD_WHEN || - next_token->type == TOKEN_KEYWORD_IS || - next_token->type == TOKEN_KEYWORD_THEN || - next_token->type == TOKEN_KEYWORD_VIA || - next_token->type == TOKEN_RPAREN || - next_token->type == TOKEN_RBRACE || - next_token->type == TOKEN_RBRACKET || - next_token->type == TOKEN_SEMICOLON || - next_token->type == TOKEN_COMMA || - next_token->type == TOKEN_EOF); - - /* Check if this is a pattern boundary (identifier followed by 'then') */ - bool is_pattern_boundary = false; - if (next_token->type == TOKEN_IDENTIFIER) { - /* Look ahead to see if the next token is 'then' */ - if (parser->current + 1 < parser->token_count) { - Token* next_next_token = parser->tokens[parser->current + 1]; - if (next_next_token && next_next_token->type == TOKEN_KEYWORD_THEN) { - is_pattern_boundary = true; - DEBUG_TRACE("Found pattern boundary: %s followed by 'then'", next_token->lexeme); - } - } - } - - DEBUG_TRACE("Function application check: can_be_arg=%d, should_not_trigger=%d, is_pattern_boundary=%d", - can_be_arg, should_not_trigger, is_pattern_boundary); - - /* Only proceed with function application if it can be an arg and shouldn't trigger */ - if (!can_be_arg || should_not_trigger || is_pattern_boundary) { - - break; - } - - /* Collect all arguments for this function call */ - ASTNode** args = NULL; - int arg_count = 0; - - while (!parser_is_at_end(parser)) { - Token* arg_token = parser_peek(parser); - if (arg_token == NULL) break; - - /* Check if this token can be a function argument */ - bool can_be_arg = (arg_token->type == TOKEN_IDENTIFIER || - arg_token->type == TOKEN_FUNCTION_REF || - arg_token->type == TOKEN_NUMBER || - arg_token->type == TOKEN_STRING || - arg_token->type == TOKEN_BOOLEAN || - arg_token->type == TOKEN_LPAREN || - arg_token->type == TOKEN_LBRACE || - arg_token->type == TOKEN_OP_UNARY_MINUS || - arg_token->type == TOKEN_KEYWORD_NOT); - - /* Check if this token should not trigger function application */ - bool should_not_trigger = (arg_token->type == TOKEN_OP_PLUS || - arg_token->type == TOKEN_OP_MINUS || - arg_token->type == TOKEN_OP_MULTIPLY || - arg_token->type == TOKEN_OP_DIVIDE || - arg_token->type == TOKEN_OP_MODULO || - arg_token->type == TOKEN_OP_POWER || - arg_token->type == TOKEN_OP_EQUALS || - arg_token->type == TOKEN_OP_NOT_EQUALS || - arg_token->type == TOKEN_OP_LESS || - arg_token->type == TOKEN_OP_LESS_EQUAL || - arg_token->type == TOKEN_OP_GREATER || - arg_token->type == TOKEN_OP_GREATER_EQUAL || - arg_token->type == TOKEN_KEYWORD_AND || - arg_token->type == TOKEN_KEYWORD_OR || - arg_token->type == TOKEN_KEYWORD_XOR || - arg_token->type == TOKEN_KEYWORD_WHEN || - arg_token->type == TOKEN_KEYWORD_IS || - arg_token->type == TOKEN_KEYWORD_THEN || - arg_token->type == TOKEN_RPAREN || - arg_token->type == TOKEN_RBRACE || - arg_token->type == TOKEN_RBRACKET || - arg_token->type == TOKEN_SEMICOLON || - arg_token->type == TOKEN_COMMA || - arg_token->type == TOKEN_EOF); - - /* Check if this is a pattern boundary (identifier followed by 'then') */ - bool is_pattern_boundary = false; - if (arg_token->type == TOKEN_IDENTIFIER) { - /* Look ahead to see if the next token is 'then' */ - if (parser->current + 1 < parser->token_count) { - Token* next_next_token = parser->tokens[parser->current + 1]; - if (next_next_token && next_next_token->type == TOKEN_KEYWORD_THEN) { - is_pattern_boundary = true; - DEBUG_TRACE("Inner loop found pattern boundary: %s followed by 'then'", arg_token->lexeme); - } - } - } - - /* Stop if it can't be an arg, should not trigger, or is a pattern boundary */ - if (!can_be_arg || should_not_trigger || is_pattern_boundary) { - break; - } - - ASTNode* arg = parser_parse_comparison(parser); - if (arg == NULL) { - /* Cleanup on error */ - for (int i = 0; i < arg_count; i++) { - ast_destroy_node(args[i]); - } - free(args); - ast_destroy_node(left); - return NULL; - } - - /* Add to arguments array */ - ASTNode** new_args = realloc(args, (arg_count + 1) * sizeof(ASTNode*)); - if (new_args == NULL) { - /* Cleanup on error */ - for (int i = 0; i < arg_count; i++) { - ast_destroy_node(args[i]); - } - free(args); - ast_destroy_node(arg); - ast_destroy_node(left); - return NULL; - } - args = new_args; - args[arg_count++] = arg; - } - - /* Create function call with all arguments */ - ASTNode* new_left = ast_function_call_node(left, args, arg_count, left->line, left->column); - if (new_left == NULL) { - /* Cleanup on error */ - for (int i = 0; i < arg_count; i++) { - ast_destroy_node(args[i]); - } - free(args); - ast_destroy_node(left); - return NULL; - } - - left = new_left; - } - - return left; -} - -/** - * @brief Parse function composition (via) - * - * @param parser Parser instance - * @return Parsed expression node - */ -/* TODO: Re-implement composition parsing */ -/* -static ASTNode* parser_parse_composition(Parser* parser) { - ASTNode* left = parser_parse_application(parser); - if (left == NULL) { - return NULL; - } - - while (parser_check(parser, TOKEN_KEYWORD_VIA)) { - Token* op = parser_advance(parser); - ASTNode* right = parser_parse_logical(parser); - if (right == NULL) { - ast_destroy_node(left); - return NULL; - } - - ASTNode* new_left = ast_binary_op_node(left, right, "compose", op->line, op->column); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(right); - return NULL; - } - - left = new_left; - } - - return left; -} -*/ - - - -/** - * @brief Parse postfix operations (table access, function calls, etc.) - * - * @param parser Parser instance - * @return Parsed expression node - */ -static ASTNode* parser_parse_postfix(Parser* parser) { - ASTNode* left = parser_parse_primary(parser); - if (left == NULL) { - return NULL; - } - - while (!parser_is_at_end(parser)) { - Token* token = parser_peek(parser); - if (token == NULL) { - break; - } - - switch (token->type) { - case TOKEN_DOT: { - /* Table property access: table.property */ - parser_advance(parser); /* consume '.' */ - - Token* property = parser_consume(parser, TOKEN_IDENTIFIER, "Expected property name after '.'"); - if (property == NULL) { - ast_destroy_node(left); - return NULL; - } - - ASTNode* key = ast_literal_node(baba_yaga_value_string(property->lexeme), property->line, property->column); - if (key == NULL) { - ast_destroy_node(left); - return NULL; - } - - ASTNode* new_left = malloc(sizeof(ASTNode)); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(key); - return NULL; - } - - new_left->type = NODE_TABLE_ACCESS; - new_left->line = left->line; - new_left->column = left->column; - new_left->data.table_access.object = left; - new_left->data.table_access.key = key; - - left = new_left; - break; - } - case TOKEN_LBRACKET: { - /* Table bracket access: table[key] */ - parser_advance(parser); /* consume '[' */ - - ASTNode* key = parser_parse_expression(parser); - if (key == NULL) { - ast_destroy_node(left); - return NULL; - } - - if (!parser_consume(parser, TOKEN_RBRACKET, "Expected ']' after table key")) { - ast_destroy_node(left); - ast_destroy_node(key); - return NULL; - } - - ASTNode* new_left = malloc(sizeof(ASTNode)); - if (new_left == NULL) { - ast_destroy_node(left); - ast_destroy_node(key); - return NULL; - } - - new_left->type = NODE_TABLE_ACCESS; - new_left->line = left->line; - new_left->column = left->column; - new_left->data.table_access.object = left; - new_left->data.table_access.key = key; - - left = new_left; - break; - } - default: - /* No more postfix operations */ - return left; - } - } - - return left; -} - -/** - * @brief Parse expression (entry point) - * - * @param parser Parser instance - * @return Parsed expression node - */ -static ASTNode* parser_parse_expression(Parser* parser) { - return parser_parse_logical(parser); -} - -/* ============================================================================ - * Statement Parsing - * ============================================================================ */ - -/** - * @brief Parse variable declaration - * - * @param parser Parser instance - * @return Parsed variable declaration node - */ -static ASTNode* parser_parse_variable_decl(Parser* parser) { - Token* name = parser_consume(parser, TOKEN_IDENTIFIER, "Expected variable name"); - if (name == NULL) { - return NULL; - } - - if (!parser_consume(parser, TOKEN_COLON, "Expected ':' after variable name")) { - return NULL; - } - - ASTNode* value = parser_parse_expression(parser); - if (value == NULL) { - return NULL; - } - - - - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - ast_destroy_node(value); - return NULL; - } - - node->type = NODE_VARIABLE_DECL; - node->line = name->line; - node->column = name->column; - node->data.variable_decl.name = strdup(name->lexeme); - node->data.variable_decl.value = value; - - - return node; -} - -/** - * @brief Parse function definition - * - * @param parser Parser instance - * @return Parsed function definition node - */ -static ASTNode* parser_parse_function_def(Parser* parser) { - Token* name = parser_consume(parser, TOKEN_IDENTIFIER, "Expected function name"); - if (name == NULL) { - return NULL; - } - - if (!parser_consume(parser, TOKEN_COLON, "Expected ':' after function name")) { - return NULL; - } - - /* Parse parameters */ - ASTNode** parameters = NULL; - int param_count = 0; - - while (!parser_is_at_end(parser) && - parser_peek(parser)->type == TOKEN_IDENTIFIER) { - Token* param = parser_advance(parser); - - ASTNode** new_params = realloc(parameters, (param_count + 1) * sizeof(ASTNode*)); - if (new_params == NULL) { - for (int i = 0; i < param_count; i++) { - ast_destroy_node(parameters[i]); - } - free(parameters); - return NULL; - } - parameters = new_params; - - parameters[param_count] = ast_identifier_node(param->lexeme, param->line, param->column); - param_count++; - } - - if (!parser_consume(parser, TOKEN_ARROW, "Expected '->' after parameters")) { - for (int i = 0; i < param_count; i++) { - ast_destroy_node(parameters[i]); - } - free(parameters); - return NULL; - } - - ASTNode* body = parser_parse_expression(parser); - if (body == NULL) { - for (int i = 0; i < param_count; i++) { - ast_destroy_node(parameters[i]); - } - free(parameters); - return NULL; - } - - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - for (int i = 0; i < param_count; i++) { - ast_destroy_node(parameters[i]); - } - free(parameters); - ast_destroy_node(body); - return NULL; - } - - node->type = NODE_FUNCTION_DEF; - node->line = name->line; - node->column = name->column; - node->data.function_def.name = strdup(name->lexeme); - node->data.function_def.parameters = parameters; - node->data.function_def.param_count = param_count; - node->data.function_def.body = body; - - return node; -} - -/** - * @brief Parse embedded arrow function (params -> body) without function name - * - * @param parser Parser instance - * @return Parsed function definition node - */ -static ASTNode* parser_parse_embedded_arrow_function(Parser* parser) { - /* Parse parameters */ - ASTNode** parameters = NULL; - int param_count = 0; - - while (!parser_is_at_end(parser) && - parser_peek(parser)->type == TOKEN_IDENTIFIER) { - Token* param = parser_advance(parser); - - ASTNode** new_params = realloc(parameters, (param_count + 1) * sizeof(ASTNode*)); - if (new_params == NULL) { - for (int i = 0; i < param_count; i++) { - ast_destroy_node(parameters[i]); - } - free(parameters); - return NULL; - } - parameters = new_params; - - parameters[param_count] = ast_identifier_node(param->lexeme, param->line, param->column); - param_count++; - } - - if (!parser_consume(parser, TOKEN_ARROW, "Expected '->' after parameters")) { - for (int i = 0; i < param_count; i++) { - ast_destroy_node(parameters[i]); - } - free(parameters); - return NULL; - } - - ASTNode* body = parser_parse_expression(parser); - if (body == NULL) { - for (int i = 0; i < param_count; i++) { - ast_destroy_node(parameters[i]); - } - free(parameters); - return NULL; - } - - ASTNode* node = malloc(sizeof(ASTNode)); - if (node == NULL) { - for (int i = 0; i < param_count; i++) { - ast_destroy_node(parameters[i]); - } - free(parameters); - ast_destroy_node(body); - return NULL; - } - - node->type = NODE_FUNCTION_DEF; - node->line = parser_peek(parser)->line; - node->column = parser_peek(parser)->column; - node->data.function_def.name = strdup(""); /* Empty name for embedded functions */ - node->data.function_def.parameters = parameters; - node->data.function_def.param_count = param_count; - node->data.function_def.body = body; - - return node; -} - -/** - * @brief Parse multiple statements separated by semicolons - * - * @param parser Parser instance - * @return Parsed sequence node or single statement node - */ -static ASTNode* parser_parse_statements(Parser* parser) { - if (parser_is_at_end(parser)) { - return NULL; - } - - /* Parse first statement */ - ASTNode* first_statement = parser_parse_statement(parser); - if (first_statement == NULL) { - return NULL; - } - - /* Check if there are more statements (semicolon-separated) */ - if (parser_is_at_end(parser)) { - return first_statement; /* Single statement */ - } - - Token* next_token = parser_peek(parser); - if (next_token->type != TOKEN_SEMICOLON) { - return first_statement; /* Single statement */ - } - - /* We have multiple statements, collect them */ - ASTNode** statements = malloc(10 * sizeof(ASTNode*)); /* Start with space for 10 */ - if (statements == NULL) { - ast_destroy_node(first_statement); - return NULL; - } - - int statement_count = 0; - int capacity = 10; - - /* Add first statement */ - statements[statement_count++] = first_statement; - - /* Parse remaining statements */ - while (!parser_is_at_end(parser) && - parser_peek(parser)->type == TOKEN_SEMICOLON) { - - /* Consume semicolon */ - parser_consume(parser, TOKEN_SEMICOLON, "Expected semicolon"); - - /* Skip any whitespace after semicolon */ - /* Comments are already skipped by the lexer */ - - if (parser_is_at_end(parser)) { - break; /* Trailing semicolon */ - } - - /* Parse next statement */ - ASTNode* next_statement = parser_parse_statement(parser); - if (next_statement == NULL) { - /* Error parsing statement, but continue with what we have */ - break; - } - - /* Expand array if needed */ - if (statement_count >= capacity) { - capacity *= 2; - ASTNode** new_statements = realloc(statements, capacity * sizeof(ASTNode*)); - if (new_statements == NULL) { - /* Cleanup and return what we have */ - for (int i = 0; i < statement_count; i++) { - ast_destroy_node(statements[i]); - } - free(statements); - return NULL; - } - statements = new_statements; - } - - statements[statement_count++] = next_statement; - } - - /* If we only have one statement, return it directly */ - if (statement_count == 1) { - ASTNode* result = statements[0]; - free(statements); - return result; - } - - /* Create sequence node */ - return ast_sequence_node(statements, statement_count, - first_statement->line, first_statement->column); -} - -/** - * @brief Parse statement - * - * @param parser Parser instance - * @return Parsed statement node - */ -static ASTNode* parser_parse_statement(Parser* parser) { - if (parser_is_at_end(parser)) { - return NULL; - } - - Token* token = parser_peek(parser); - - /* Check for variable declaration */ - if (token->type == TOKEN_IDENTIFIER && - parser_peek_next(parser) != NULL && - parser_peek_next(parser)->type == TOKEN_COLON) { - - /* Look ahead to see if it's a function definition */ - int save_current = parser->current; - parser->current += 2; /* skip identifier and colon */ - - bool is_function = false; - while (!parser_is_at_end(parser) && - parser_peek(parser)->type == TOKEN_IDENTIFIER) { - parser->current++; - } - - if (!parser_is_at_end(parser) && - parser_peek(parser)->type == TOKEN_ARROW) { - is_function = true; - } - - parser->current = save_current; - - if (is_function) { - return parser_parse_function_def(parser); - } else { - return parser_parse_variable_decl(parser); - } - } - - - - /* Default to expression */ - return parser_parse_expression(parser); -} - -/* ============================================================================ - * Public Parser API - * ============================================================================ */ - -/** - * @brief Parse source code into AST - * - * @param tokens Array of tokens - * @param token_count Number of tokens - * @return Root AST node, or NULL on error - */ -void* baba_yaga_parse(void** tokens, size_t token_count) { - if (tokens == NULL || token_count == 0) { - return NULL; - } - - Parser* parser = parser_create((Token**)tokens, (int)token_count); - if (parser == NULL) { - return NULL; - } - - ASTNode* result = parser_parse_statements(parser); - - if (parser->has_error) { - fprintf(stderr, "Parse error: %s\n", parser->error_message); - if (result != NULL) { - ast_destroy_node(result); - result = NULL; - } - } - - parser_destroy(parser); - return (void*)result; -} - -/** - * @brief Destroy AST - * - * @param node Root AST node - */ -void baba_yaga_destroy_ast(void* node) { - ast_destroy_node((ASTNode*)node); -} - -/** - * @brief Print AST for debugging - * - * @param node Root AST node - * @param indent Initial indentation level - */ -/* ============================================================================ - * AST Accessor Functions - * ============================================================================ */ - -NodeType baba_yaga_ast_get_type(void* node) { - if (node == NULL) { - return NODE_LITERAL; /* Default fallback */ - } - ASTNode* ast_node = (ASTNode*)node; - return ast_node->type; -} - -Value baba_yaga_ast_get_literal(void* node) { - if (node == NULL) { - return baba_yaga_value_nil(); - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_LITERAL) { - return baba_yaga_value_copy(&ast_node->data.literal); - } - return baba_yaga_value_nil(); -} - -const char* baba_yaga_ast_get_identifier(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_IDENTIFIER) { - return ast_node->data.identifier; - } - return NULL; -} - -void* baba_yaga_ast_get_function_call_func(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_FUNCTION_CALL) { - return ast_node->data.function_call.function; - } - return NULL; -} - -int baba_yaga_ast_get_function_call_arg_count(void* node) { - if (node == NULL) { - return 0; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_FUNCTION_CALL) { - return ast_node->data.function_call.arg_count; - } - return 0; -} - -void* baba_yaga_ast_get_function_call_arg(void* node, int index) { - if (node == NULL || index < 0) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_FUNCTION_CALL && - index < ast_node->data.function_call.arg_count) { - return ast_node->data.function_call.arguments[index]; - } - return NULL; -} - -void* baba_yaga_ast_get_binary_op_left(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_BINARY_OP) { - return ast_node->data.binary.left; - } - return NULL; -} - -void* baba_yaga_ast_get_binary_op_right(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_BINARY_OP) { - return ast_node->data.binary.right; - } - return NULL; -} - -const char* baba_yaga_ast_get_binary_op_operator(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_BINARY_OP) { - return ast_node->data.binary.operator; - } - return NULL; -} - -void* baba_yaga_ast_get_unary_op_operand(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_UNARY_OP) { - return ast_node->data.unary.operand; - } - return NULL; -} - -const char* baba_yaga_ast_get_unary_op_operator(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_UNARY_OP) { - return ast_node->data.unary.operator; - } - return NULL; -} - -const char* baba_yaga_ast_get_function_def_name(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_FUNCTION_DEF) { - return ast_node->data.function_def.name; - } - return NULL; -} - -int baba_yaga_ast_get_function_def_param_count(void* node) { - if (node == NULL) { - return 0; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_FUNCTION_DEF) { - return ast_node->data.function_def.param_count; - } - return 0; -} - -void* baba_yaga_ast_get_function_def_param(void* node, int index) { - if (node == NULL || index < 0) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_FUNCTION_DEF) { - if (index < ast_node->data.function_def.param_count) { - return ast_node->data.function_def.parameters[index]; - } - } - return NULL; -} - -void* baba_yaga_ast_get_function_def_body(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_FUNCTION_DEF) { - return ast_node->data.function_def.body; - } - return NULL; -} - -const char* baba_yaga_ast_get_variable_decl_name(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_VARIABLE_DECL) { - return ast_node->data.variable_decl.name; - } - return NULL; -} - -void* baba_yaga_ast_get_variable_decl_value(void* node) { - if (node == NULL) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_VARIABLE_DECL) { - return ast_node->data.variable_decl.value; - } - return NULL; -} - -int baba_yaga_ast_get_sequence_statement_count(void* node) { - if (node == NULL) { - return 0; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_SEQUENCE) { - return ast_node->data.sequence.statement_count; - } - return 0; -} - -void* baba_yaga_ast_get_sequence_statement(void* node, int index) { - if (node == NULL || index < 0) { - return NULL; - } - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type == NODE_SEQUENCE) { - if (index < ast_node->data.sequence.statement_count) { - return ast_node->data.sequence.statements[index]; - } - } - return NULL; -} - -void* baba_yaga_ast_get_when_expr_test(void* node) { - if (node == NULL) { - return NULL; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_WHEN_EXPR) { - return NULL; - } - - return ast_node->data.when_expr.test; -} - -int baba_yaga_ast_get_when_expr_pattern_count(void* node) { - if (node == NULL) { - return 0; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_WHEN_EXPR) { - return 0; - } - - return ast_node->data.when_expr.pattern_count; -} - -void* baba_yaga_ast_get_when_expr_pattern(void* node, int index) { - if (node == NULL) { - return NULL; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_WHEN_EXPR) { - return NULL; - } - - if (index >= 0 && index < ast_node->data.when_expr.pattern_count) { - return ast_node->data.when_expr.patterns[index]; - } - return NULL; -} - -void* baba_yaga_ast_get_when_pattern_test(void* node) { - if (node == NULL) { - return NULL; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_WHEN_PATTERN) { - return NULL; - } - - return ast_node->data.when_pattern.test; -} - -void* baba_yaga_ast_get_when_pattern_result(void* node) { - if (node == NULL) { - return NULL; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_WHEN_PATTERN) { - return NULL; - } - - return ast_node->data.when_pattern.result; -} - -int baba_yaga_ast_get_table_element_count(void* node) { - if (node == NULL) { - return 0; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_TABLE) { - return 0; - } - - return ast_node->data.table.element_count; -} - -void* baba_yaga_ast_get_table_element(void* node, int index) { - if (node == NULL) { - return NULL; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_TABLE) { - return NULL; - } - - if (index >= 0 && index < ast_node->data.table.element_count) { - return ast_node->data.table.elements[index]; - } - return NULL; -} - -void* baba_yaga_ast_get_table_access_object(void* node) { - if (node == NULL) { - return NULL; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_TABLE_ACCESS) { - return NULL; - } - - return ast_node->data.table_access.object; -} - -void* baba_yaga_ast_get_table_access_key(void* node) { - if (node == NULL) { - return NULL; - } - - ASTNode* ast_node = (ASTNode*)node; - if (ast_node->type != NODE_TABLE_ACCESS) { - return NULL; - } - - return ast_node->data.table_access.key; -} - -void baba_yaga_print_ast(void* node, int indent) { - if (node == NULL) { - return; - } - - ASTNode* ast_node = (ASTNode*)node; - - /* Print indentation */ - for (int i = 0; i < indent; i++) { - printf(" "); - } - - /* Print node type */ - printf("%s", node_type_name(ast_node->type)); - - /* Print node-specific information */ - switch (ast_node->type) { - case NODE_LITERAL: - if (ast_node->data.literal.type == VAL_NUMBER) { - printf(": %g", ast_node->data.literal.data.number); - } else if (ast_node->data.literal.type == VAL_STRING) { - printf(": \"%s\"", ast_node->data.literal.data.string); - } else if (ast_node->data.literal.type == VAL_BOOLEAN) { - printf(": %s", ast_node->data.literal.data.boolean ? "true" : "false"); - } - break; - case NODE_IDENTIFIER: - printf(": %s", ast_node->data.identifier); - break; - case NODE_FUNCTION_CALL: - printf(" (args: %d)", ast_node->data.function_call.arg_count); - break; - case NODE_FUNCTION_DEF: - printf(": %s (params: %d)", ast_node->data.function_def.name, ast_node->data.function_def.param_count); - break; - case NODE_VARIABLE_DECL: - printf(": %s", ast_node->data.variable_decl.name); - break; - case NODE_SEQUENCE: - printf(" (statements: %d)", ast_node->data.sequence.statement_count); - break; - default: - break; - } - - printf(" (line %d, col %d)\n", ast_node->line, ast_node->column); - - /* Print children */ - switch (ast_node->type) { - case NODE_FUNCTION_CALL: - baba_yaga_print_ast(ast_node->data.function_call.function, indent + 1); - for (int i = 0; i < ast_node->data.function_call.arg_count; i++) { - baba_yaga_print_ast(ast_node->data.function_call.arguments[i], indent + 1); - } - break; - case NODE_FUNCTION_DEF: - for (int i = 0; i < ast_node->data.function_def.param_count; i++) { - baba_yaga_print_ast(ast_node->data.function_def.parameters[i], indent + 1); - } - baba_yaga_print_ast(ast_node->data.function_def.body, indent + 1); - break; - case NODE_VARIABLE_DECL: - baba_yaga_print_ast(ast_node->data.variable_decl.value, indent + 1); - break; - case NODE_SEQUENCE: - for (int i = 0; i < ast_node->data.sequence.statement_count; i++) { - baba_yaga_print_ast(ast_node->data.sequence.statements[i], indent + 1); - } - break; - default: - break; - } -} - -/** - * @brief Parse when expression - * - * @param parser Parser instance - * @return Parsed when expression node - */ -static ASTNode* parser_parse_when_expression(Parser* parser) { - DEBUG_DEBUG("Parsing WHEN expression at token %d", parser->current); - Token* when_token = parser_consume(parser, TOKEN_KEYWORD_WHEN, "Expected 'when'"); - if (!when_token) return NULL; - - - - /* Check if this is a multi-parameter pattern by looking ahead for multiple identifiers */ - bool is_multi_param = false; - int look_ahead = parser->current; - int identifier_count = 0; - - /* Count consecutive identifiers or expressions before 'is' */ - while (look_ahead < parser->token_count) { - Token* token = parser->tokens[look_ahead]; - if (token->type == TOKEN_KEYWORD_IS) { - break; - } - if (token->type == TOKEN_IDENTIFIER) { - identifier_count++; - } else if (token->type == TOKEN_LPAREN) { - /* Expression in parentheses - count as one parameter */ - identifier_count++; - /* Skip to closing parenthesis */ - int paren_count = 1; - look_ahead++; - while (look_ahead < parser->token_count && paren_count > 0) { - Token* next_token = parser->tokens[look_ahead]; - if (next_token->type == TOKEN_LPAREN) { - paren_count++; - } else if (next_token->type == TOKEN_RPAREN) { - paren_count--; - } - look_ahead++; - } - /* Continue from the position after the closing parenthesis */ - continue; - } else { - /* If we hit anything other than an identifier or expression, it's not multi-parameter */ - identifier_count = 0; - break; - } - look_ahead++; - } - - /* If we have multiple identifiers followed by 'is', it's multi-parameter */ - if (identifier_count > 1) { - is_multi_param = true; - } - - ASTNode* test; - if (is_multi_param) { - /* Parse as sequence of identifiers or expressions */ - ASTNode** identifiers = malloc(identifier_count * sizeof(ASTNode*)); - if (!identifiers) return NULL; - - for (int i = 0; i < identifier_count; i++) { - Token* current_token = parser_peek(parser); - if (current_token->type == TOKEN_LPAREN) { - /* Expression in parentheses - parse the expression */ - identifiers[i] = parser_parse_expression(parser); - if (identifiers[i] == NULL) { - /* Cleanup on error */ - for (int j = 0; j < i; j++) { - ast_destroy_node(identifiers[j]); - } - free(identifiers); - return NULL; - } - } else { - /* Identifier - parse as identifier */ - Token* id_token = parser_advance(parser); - identifiers[i] = ast_identifier_node(id_token->lexeme, id_token->line, id_token->column); - } - } - - /* Create a sequence node for the identifiers */ - test = ast_sequence_node(identifiers, identifier_count, when_token->line, when_token->column); - - /* Ensure we're positioned at the 'is' token */ - if (parser->current < parser->token_count && - parser->tokens[parser->current]->type != TOKEN_KEYWORD_IS) { - /* We're not at the 'is' token - find it */ - for (int j = parser->current; j < parser->token_count; j++) { - if (parser->tokens[j]->type == TOKEN_KEYWORD_IS) { - parser->current = j; - break; - } - } - } - } else { - /* Parse as single expression */ - test = parser_parse_expression(parser); - } - - if (!test) return NULL; - Token* is_token = parser_consume(parser, TOKEN_KEYWORD_IS, "Expected 'is' after test expression"); - if (!is_token) { ast_destroy_node(test); return NULL; } - - // Prepare flat array of NODE_WHEN_PATTERN nodes - ASTNode** patterns = NULL; - int pattern_count = 0, pattern_cap = 4; - patterns = malloc(pattern_cap * sizeof(ASTNode*)); - - while (!parser_is_at_end(parser) && parser_peek(parser)->type != TOKEN_SEMICOLON) { - // Parse pattern - ASTNode* pattern = parser_parse_when_pattern(parser); - if (!pattern) break; - // Expect 'then' - Token* then_token = parser_consume(parser, TOKEN_KEYWORD_THEN, "Expected 'then' after pattern in when case"); - if (!then_token) { ast_destroy_node(pattern); break; } - // Parse result (single expression) - ASTNode* result = parser_parse_when_result_expression(parser); - if (!result) { ast_destroy_node(pattern); break; } - // Create NODE_WHEN_PATTERN node - ASTNode* case_node = ast_when_pattern_node(pattern, result, when_token->line, when_token->column); - if (pattern_count >= pattern_cap) { - pattern_cap *= 2; - patterns = realloc(patterns, pattern_cap * sizeof(ASTNode*)); - } - patterns[pattern_count++] = case_node; - // If next token is a valid pattern start, continue loop; else break - Token* next = parser_peek(parser); - if (!next || next->type == TOKEN_SEMICOLON) break; - int is_wildcard = (next->type == TOKEN_IDENTIFIER && next->lexeme && strcmp(next->lexeme, "_") == 0); - if (!(is_wildcard || next->type == TOKEN_IDENTIFIER || next->type == TOKEN_NUMBER || next->type == TOKEN_STRING)) break; - } - // Build AST node for when expression - ASTNode* when_node = ast_when_expr_node(test, patterns, pattern_count, when_token->line, when_token->column); - - return when_node; -} - -/** - * @brief Parse when pattern - * - * @param parser Parser instance - * @return Parsed when pattern node - */ -// Helper: look ahead to see if the next two tokens are a pattern start followed by 'then' -static bool parser_is_next_pattern(Parser* parser) { - if (parser_is_at_end(parser)) return false; - Token* t1 = parser_peek(parser); - if (!t1) return false; - if (t1->type != TOKEN_IDENTIFIER && t1->type != TOKEN_NUMBER && t1->type != TOKEN_STRING) return false; - // Look ahead one more - if (parser->current + 1 >= parser->token_count) return false; - Token* t2 = parser->tokens[parser->current + 1]; - return t2 && t2->type == TOKEN_KEYWORD_THEN; -} - -// Parse a result expression for a when pattern, stopping at pattern boundaries -static ASTNode* parser_parse_when_result_expression(Parser* parser) { - DEBUG_TRACE("parser_parse_when_result_expression start at token %d", parser->current); - - // Show current token before parsing - Token* before_token = parser_peek(parser); - if (before_token) { - DEBUG_TRACE("Before parsing result, token type=%d, lexeme='%s'", - before_token->type, before_token->lexeme ? before_token->lexeme : "NULL"); - } - - // Check if the next token is a pattern start followed by 'then' - // If so, return an empty result expression - if (parser_is_next_pattern(parser)) { - DEBUG_TRACE("Detected next pattern, returning empty result"); - return ast_literal_node(baba_yaga_value_string(""), parser_peek(parser)->line, parser_peek(parser)->column); - } - - // Parse a single expression using a bounded parser - // Stop when we hit a pattern boundary or statement terminator - ASTNode* result = parser_parse_primary(parser); - if (result == NULL) { - return NULL; - } - - // Show current token after parsing - Token* after_token = parser_peek(parser); - if (after_token) { - DEBUG_TRACE("After parsing result, token type=%d, lexeme='%s'", - after_token->type, after_token->lexeme ? after_token->lexeme : "NULL"); - } - - DEBUG_TRACE("parser_parse_when_result_expression end at token %d", parser->current); - return result; -} - -static ASTNode* parser_parse_when_pattern(Parser* parser) { - DEBUG_DEBUG("Parsing WHEN pattern at token %d", parser->current); - DEBUG_TRACE("parser_parse_when_pattern start"); - - /* Show current token */ - Token* current_token = parser_peek(parser); - if (current_token != NULL) { - DEBUG_TRACE("Current token type=%d, lexeme='%s'", current_token->type, current_token->lexeme ? current_token->lexeme : "NULL"); - } - - /* Check if this is a multi-parameter pattern by looking ahead for multiple literals */ - bool is_multi_param = false; - int look_ahead = parser->current; - int literal_count = 0; - - /* Count consecutive literals or expressions before 'then' */ - DEBUG_DEBUG("Multi-parameter detection: starting at token %d", look_ahead); - while (look_ahead < parser->token_count) { - Token* token = parser->tokens[look_ahead]; - if (token->type == TOKEN_KEYWORD_THEN) { - break; - } - if (token->type == TOKEN_IDENTIFIER || - token->type == TOKEN_NUMBER || - token->type == TOKEN_STRING || - (token->type == TOKEN_IDENTIFIER && token->lexeme && strcmp(token->lexeme, "_") == 0)) { - literal_count++; - } else if (token->type == TOKEN_LPAREN) { - /* Expression in parentheses - count as one pattern */ - DEBUG_DEBUG("Multi-parameter detection: found TOKEN_LPAREN at token %d", look_ahead); - literal_count++; - /* Skip to closing parenthesis */ - int paren_count = 1; - look_ahead++; - while (look_ahead < parser->token_count && paren_count > 0) { - Token* next_token = parser->tokens[look_ahead]; - if (next_token->type == TOKEN_LPAREN) { - paren_count++; - } else if (next_token->type == TOKEN_RPAREN) { - paren_count--; - } - look_ahead++; - } - DEBUG_DEBUG("Multi-parameter detection: finished expression, literal_count=%d, look_ahead=%d", literal_count, look_ahead); - /* Continue from the position after the closing parenthesis */ - continue; - } else if (token->type == TOKEN_OP_EQUALS || - token->type == TOKEN_OP_NOT_EQUALS || - token->type == TOKEN_OP_LESS || - token->type == TOKEN_OP_LESS_EQUAL || - token->type == TOKEN_OP_GREATER || - token->type == TOKEN_OP_GREATER_EQUAL) { - /* If we hit a comparison operator, it's not multi-parameter */ - literal_count = 0; - break; - } else if (token->type == TOKEN_SEMICOLON) { - /* If we hit a semicolon, stop looking */ - break; - } else { - /* If we hit anything other than a literal or expression, it's not multi-parameter */ - literal_count = 0; - break; - } - look_ahead++; - } - - /* If we have multiple literals followed by 'then', it's multi-parameter */ - DEBUG_DEBUG("Multi-parameter detection: final literal_count=%d, is_multi_param=%s", literal_count, literal_count > 1 ? "true" : "false"); - if (literal_count > 1) { - is_multi_param = true; - } - - ASTNode* pattern_test; - if (is_multi_param) { - /* Parse as sequence of literals */ - ASTNode** literals = malloc(literal_count * sizeof(ASTNode*)); - if (!literals) return NULL; - - for (int i = 0; i < literal_count; i++) { - Token* current_token = parser_peek(parser); - if (current_token->type == TOKEN_LPAREN) { - /* Expression pattern - parse the expression */ - literals[i] = parser_parse_expression(parser); - if (literals[i] == NULL) { - /* Cleanup on error */ - for (int j = 0; j < i; j++) { - ast_destroy_node(literals[j]); - } - free(literals); - return NULL; - } - } else { - /* Literal pattern */ - Token* lit_token = parser_advance(parser); - if (lit_token->type == TOKEN_IDENTIFIER && lit_token->lexeme && strcmp(lit_token->lexeme, "_") == 0) { - /* Wildcard pattern - treat as literal in multi-parameter context */ - literals[i] = ast_literal_node(baba_yaga_value_string("_"), lit_token->line, lit_token->column); - } else if (lit_token->type == TOKEN_IDENTIFIER) { - /* Identifier pattern */ - literals[i] = ast_identifier_node(lit_token->lexeme, lit_token->line, lit_token->column); - } else if (lit_token->type == TOKEN_NUMBER) { - /* Number pattern */ - literals[i] = ast_literal_node(baba_yaga_value_number(lit_token->literal.number), lit_token->line, lit_token->column); - } else if (lit_token->type == TOKEN_STRING) { - /* String pattern */ - literals[i] = ast_literal_node(baba_yaga_value_string(lit_token->lexeme), lit_token->line, lit_token->column); - } else { - /* Cleanup on error */ - for (int j = 0; j < i; j++) { - ast_destroy_node(literals[j]); - } - free(literals); - return NULL; - } - } - } - - /* Create a sequence node for the literals */ - pattern_test = ast_sequence_node(literals, literal_count, parser_peek(parser)->line, parser_peek(parser)->column); - } else if (current_token && current_token->type == TOKEN_LBRACE) { - /* Table pattern: { status: "placeholder" } */ - DEBUG_TRACE("Found table pattern"); - /* Parse as table literal */ - pattern_test = parser_parse_primary(parser); - if (pattern_test == NULL) { - DEBUG_TRACE("Failed to parse table pattern"); - return NULL; - } - DEBUG_TRACE("Successfully parsed table pattern"); - } else if (current_token && current_token->type == TOKEN_IDENTIFIER && - current_token->lexeme && strcmp(current_token->lexeme, "_") == 0) { - /* Special handling for single wildcard pattern */ - DEBUG_TRACE("Found wildcard pattern"); - /* Create a special wildcard literal */ - pattern_test = ast_literal_node(baba_yaga_value_string("_"), - current_token->line, current_token->column); - /* Consume the _ token */ - parser_advance(parser); - DEBUG_TRACE("Consumed _ token, current token type=%d, lexeme='%s'", - parser_peek(parser)->type, parser_peek(parser)->lexeme ? parser_peek(parser)->lexeme : "NULL"); - } else { - /* Parse pattern test expression - stop at 'then' */ - /* Check if this is a comparison expression by looking ahead */ - bool is_comparison = false; - int look_ahead = parser->current; - - /* Look ahead to see if there's a comparison operator */ - while (look_ahead < parser->token_count) { - Token* token = parser->tokens[look_ahead]; - if (token->type == TOKEN_KEYWORD_THEN) { - break; /* Found 'then', stop looking */ - } - if (token->type == TOKEN_OP_EQUALS || - token->type == TOKEN_OP_NOT_EQUALS || - token->type == TOKEN_OP_LESS || - token->type == TOKEN_OP_LESS_EQUAL || - token->type == TOKEN_OP_GREATER || - token->type == TOKEN_OP_GREATER_EQUAL) { - is_comparison = true; - break; - } - look_ahead++; - } - - if (is_comparison) { - /* Parse as comparison expression but stop at 'then' */ - /* Find the 'then' token position */ - int then_pos = -1; - for (int i = parser->current; i < parser->token_count; i++) { - if (parser->tokens[i]->type == TOKEN_KEYWORD_THEN) { - then_pos = i; - break; - } - } - - if (then_pos == -1) { - DEBUG_TRACE("No 'then' token found after comparison pattern"); - return NULL; - } - - /* Temporarily limit parsing to stop at 'then' */ - int original_token_count = parser->token_count; - parser->token_count = then_pos; - - /* Parse the comparison expression */ - pattern_test = parser_parse_comparison(parser); - - /* Restore parser state */ - parser->token_count = original_token_count; - } else { - /* Parse as simple expression */ - pattern_test = parser_parse_primary(parser); - } - - if (pattern_test == NULL) { - DEBUG_TRACE("Failed to parse pattern test expression"); - return NULL; - } - DEBUG_TRACE("Parsed pattern test expression"); - } - - DEBUG_TRACE("parser_parse_when_pattern success"); - - /* Create when pattern node - only the pattern test, result will be added by caller */ - return pattern_test; -} - -/* Helper function to get node type name */ -static const char* node_type_name(NodeType type) { - switch (type) { - case NODE_LITERAL: return "LITERAL"; - case NODE_IDENTIFIER: return "IDENTIFIER"; - case NODE_BINARY_OP: return "BINARY_OP"; - case NODE_UNARY_OP: return "UNARY_OP"; - case NODE_FUNCTION_CALL: return "FUNCTION_CALL"; - case NODE_FUNCTION_DEF: return "FUNCTION_DEF"; - case NODE_VARIABLE_DECL: return "VARIABLE_DECL"; - case NODE_WHEN_EXPR: return "WHEN_EXPR"; - case NODE_WHEN_PATTERN: return "WHEN_PATTERN"; - case NODE_TABLE: return "TABLE"; - case NODE_TABLE_ACCESS: return "TABLE_ACCESS"; - case NODE_IO_OPERATION: return "IO_OPERATION"; - case NODE_SEQUENCE: return "SEQUENCE"; - default: return "UNKNOWN"; - } -} |