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 | 1087 |
1 files changed, 908 insertions, 179 deletions
diff --git a/js/scripting-lang/baba-yaga-c/src/parser.c b/js/scripting-lang/baba-yaga-c/src/parser.c index 68a4870..6c94913 100644 --- a/js/scripting-lang/baba-yaga-c/src/parser.c +++ b/js/scripting-lang/baba-yaga-c/src/parser.c @@ -60,7 +60,9 @@ typedef enum { TOKEN_FUNCTION_REF, TOKEN_IO_IN, TOKEN_IO_OUT, - TOKEN_IO_ASSERT + TOKEN_IO_ASSERT, + TOKEN_IO_EMIT, + TOKEN_IO_LISTEN } TokenType; typedef struct { @@ -333,6 +335,7 @@ static ASTNode* ast_when_expr_node(ASTNode* test, ASTNode** patterns, node->data.when_expr.patterns = patterns; node->data.when_expr.pattern_count = pattern_count; + return node; } @@ -587,7 +590,10 @@ 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) @@ -604,25 +610,25 @@ static ASTNode* parser_parse_primary(Parser* parser) { switch (token->type) { case TOKEN_NUMBER: { - printf("DEBUG: parser_parse_primary consuming number: %g\n", token->literal.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: { - printf("DEBUG: parser_parse_primary consuming string: %s\n", token->lexeme); + 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: { - printf("DEBUG: parser_parse_primary consuming boolean: %s\n", token->literal.boolean ? "true" : "false"); + 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: { - printf("DEBUG: parser_parse_primary consuming identifier: %s\n", token->lexeme); + DEBUG_TRACE("parser_parse_primary consuming identifier: %s", token->lexeme); parser_advance(parser); /* Special handling for wildcard pattern */ if (strcmp(token->lexeme, "_") == 0) { @@ -633,8 +639,10 @@ static ASTNode* parser_parse_primary(Parser* parser) { } case TOKEN_IO_IN: case TOKEN_IO_OUT: - case TOKEN_IO_ASSERT: { - printf("DEBUG: parser_parse_primary consuming io operation: %s\n", token->lexeme); + 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 ".." */ @@ -665,18 +673,55 @@ static ASTNode* parser_parse_primary(Parser* parser) { 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: { - printf("DEBUG: parser_parse_primary consuming function ref: %s\n", token->lexeme); + 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) { - printf("DEBUG: parser_parse_primary consuming '('\n"); + DEBUG_TRACE("parser_parse_primary consuming '('"); parser_advance(parser); /* consume '(' */ /* Parse the expression inside parentheses */ @@ -702,6 +747,7 @@ static ASTNode* parser_parse_primary(Parser* parser) { } /* 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 && @@ -724,6 +770,11 @@ static ASTNode* parser_parse_primary(Parser* parser) { 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; @@ -757,7 +808,7 @@ static ASTNode* parser_parse_primary(Parser* parser) { } /* Parse argument */ - ASTNode* arg = parser_parse_primary(parser); + ASTNode* arg = parser_parse_postfix(parser); if (arg == NULL) { /* Cleanup on error */ for (int i = 0; i < arg_count; i++) { @@ -805,7 +856,7 @@ static ASTNode* parser_parse_primary(Parser* parser) { return func_node; } case TOKEN_LPAREN: { - printf("DEBUG: parser_parse_primary consuming '('\n"); + DEBUG_TRACE("parser_parse_primary consuming '('"); parser_advance(parser); /* consume '(' */ ASTNode* expr = parser_parse_expression(parser); if (expr == NULL) { @@ -819,19 +870,327 @@ static ASTNode* parser_parse_primary(Parser* parser) { 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: { - printf("DEBUG: parser_parse_primary consuming unary minus\n"); + DEBUG_TRACE("parser_parse_primary consuming unary minus"); parser_advance(parser); /* consume '-' */ - ASTNode* operand = parser_parse_primary(parser); + 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: { - printf("DEBUG: parser_parse_primary consuming 'not'\n"); + DEBUG_TRACE("parser_parse_primary consuming 'not'"); parser_advance(parser); /* consume 'not' */ - ASTNode* operand = parser_parse_primary(parser); + ASTNode* operand = parser_parse_postfix(parser); if (operand == NULL) { return NULL; } @@ -859,14 +1218,14 @@ static ASTNode* parser_parse_primary(Parser* parser) { * @return Parsed expression node */ static ASTNode* parser_parse_power(Parser* parser) { - ASTNode* left = parser_parse_primary(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_primary(parser); + ASTNode* right = parser_parse_postfix(parser); if (right == NULL) { ast_destroy_node(left); return NULL; @@ -1025,9 +1384,13 @@ static ASTNode* parser_parse_logical(Parser* parser) { } /* Handle logical operators */ - while (parser_check(parser, TOKEN_KEYWORD_AND) || - parser_check(parser, TOKEN_KEYWORD_OR) || - parser_check(parser, TOKEN_KEYWORD_XOR)) { + 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) { @@ -1036,11 +1399,17 @@ static ASTNode* parser_parse_logical(Parser* parser) { } const char* operator_name; - switch (op->type) { - case TOKEN_KEYWORD_AND: operator_name = "and"; break; - case TOKEN_KEYWORD_OR: operator_name = "or"; break; - case TOKEN_KEYWORD_XOR: operator_name = "xor"; break; - default: operator_name = "unknown"; break; + 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); @@ -1053,16 +1422,43 @@ static ASTNode* parser_parse_logical(Parser* parser) { 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 || @@ -1084,9 +1480,14 @@ static ASTNode* parser_parse_logical(Parser* parser) { 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 || @@ -1102,16 +1503,17 @@ static ASTNode* parser_parse_logical(Parser* parser) { Token* next_next_token = parser->tokens[parser->current + 1]; if (next_next_token && next_next_token->type == TOKEN_KEYWORD_THEN) { is_pattern_boundary = true; - printf("DEBUG: Found pattern boundary: %s followed by 'then'\n", next_token->lexeme); + DEBUG_TRACE("Found pattern boundary: %s followed by 'then'", next_token->lexeme); } } } - printf("DEBUG: Function application check: can_be_arg=%d, should_not_trigger=%d, is_pattern_boundary=%d\n", + 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; } @@ -1128,6 +1530,7 @@ static ASTNode* parser_parse_logical(Parser* parser) { 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 || @@ -1167,7 +1570,7 @@ static ASTNode* parser_parse_logical(Parser* parser) { Token* next_next_token = parser->tokens[parser->current + 1]; if (next_next_token && next_next_token->type == TOKEN_KEYWORD_THEN) { is_pattern_boundary = true; - printf("DEBUG: Inner loop found pattern boundary: %s followed by 'then'\n", arg_token->lexeme); + DEBUG_TRACE("Inner loop found pattern boundary: %s followed by 'then'", arg_token->lexeme); } } } @@ -1258,137 +1661,95 @@ static ASTNode* parser_parse_composition(Parser* parser) { } */ + + /** - * @brief Parse function application (juxtaposition) - * - * @param parser Parser instance - * @return Parsed expression node - */ -/** - * @brief Parse function application (juxtaposition) + * @brief Parse postfix operations (table access, function calls, etc.) * * @param parser Parser instance * @return Parsed expression node */ -static ASTNode* parser_parse_application(Parser* parser) { - ASTNode* left = parser_parse_logical(parser); +static ASTNode* parser_parse_postfix(Parser* parser) { + ASTNode* left = parser_parse_primary(parser); if (left == NULL) { return NULL; } - /* Function application is left-associative */ - while (!parser_is_at_end(parser) && - (parser_peek(parser)->type == TOKEN_IDENTIFIER || - parser_peek(parser)->type == TOKEN_FUNCTION_REF || - parser_peek(parser)->type == TOKEN_NUMBER || - parser_peek(parser)->type == TOKEN_STRING || - parser_peek(parser)->type == TOKEN_LPAREN || - parser_peek(parser)->type == TOKEN_LBRACE || - parser_peek(parser)->type == TOKEN_OP_UNARY_MINUS || - parser_peek(parser)->type == TOKEN_KEYWORD_NOT) && - parser_peek(parser)->type != TOKEN_OP_PLUS && - parser_peek(parser)->type != TOKEN_OP_MINUS && - parser_peek(parser)->type != TOKEN_OP_MULTIPLY && - parser_peek(parser)->type != TOKEN_OP_DIVIDE && - parser_peek(parser)->type != TOKEN_OP_MODULO && - parser_peek(parser)->type != TOKEN_OP_POWER && - parser_peek(parser)->type != TOKEN_OP_EQUALS && - parser_peek(parser)->type != TOKEN_OP_NOT_EQUALS && - parser_peek(parser)->type != TOKEN_OP_LESS && - parser_peek(parser)->type != TOKEN_OP_LESS_EQUAL && - parser_peek(parser)->type != TOKEN_OP_GREATER && - parser_peek(parser)->type != TOKEN_OP_GREATER_EQUAL && - parser_peek(parser)->type != TOKEN_KEYWORD_AND && - parser_peek(parser)->type != TOKEN_KEYWORD_OR && - parser_peek(parser)->type != TOKEN_KEYWORD_XOR && - parser_peek(parser)->type != TOKEN_KEYWORD_WHEN && - parser_peek(parser)->type != TOKEN_KEYWORD_IS && - parser_peek(parser)->type != TOKEN_KEYWORD_THEN && - parser_peek(parser)->type != TOKEN_RPAREN && - parser_peek(parser)->type != TOKEN_RBRACE && - parser_peek(parser)->type != TOKEN_RBRACKET && - parser_peek(parser)->type != TOKEN_SEMICOLON && - parser_peek(parser)->type != TOKEN_COMMA && - parser_peek(parser)->type != TOKEN_EOF) { - - /* Collect all arguments for this function call */ - ASTNode** args = NULL; - int arg_count = 0; + while (!parser_is_at_end(parser)) { + Token* token = parser_peek(parser); + if (token == NULL) { + break; + } - while (!parser_is_at_end(parser) && - (parser_peek(parser)->type == TOKEN_IDENTIFIER || - parser_peek(parser)->type == TOKEN_FUNCTION_REF || - parser_peek(parser)->type == TOKEN_NUMBER || - parser_peek(parser)->type == TOKEN_STRING || - parser_peek(parser)->type == TOKEN_LPAREN || - parser_peek(parser)->type == TOKEN_LBRACE || - parser_peek(parser)->type == TOKEN_OP_UNARY_MINUS || - parser_peek(parser)->type == TOKEN_KEYWORD_NOT) && - parser_peek(parser)->type != TOKEN_OP_PLUS && - parser_peek(parser)->type != TOKEN_OP_MINUS && - parser_peek(parser)->type != TOKEN_OP_MULTIPLY && - parser_peek(parser)->type != TOKEN_OP_DIVIDE && - parser_peek(parser)->type != TOKEN_OP_MODULO && - parser_peek(parser)->type != TOKEN_OP_POWER && - parser_peek(parser)->type != TOKEN_OP_EQUALS && - parser_peek(parser)->type != TOKEN_OP_NOT_EQUALS && - parser_peek(parser)->type != TOKEN_OP_LESS && - parser_peek(parser)->type != TOKEN_OP_LESS_EQUAL && - parser_peek(parser)->type != TOKEN_OP_GREATER && - parser_peek(parser)->type != TOKEN_OP_GREATER_EQUAL && - parser_peek(parser)->type != TOKEN_KEYWORD_AND && - parser_peek(parser)->type != TOKEN_KEYWORD_OR && - parser_peek(parser)->type != TOKEN_KEYWORD_XOR && - parser_peek(parser)->type != TOKEN_KEYWORD_WHEN && - parser_peek(parser)->type != TOKEN_KEYWORD_IS && - parser_peek(parser)->type != TOKEN_KEYWORD_THEN && - parser_peek(parser)->type != TOKEN_RPAREN && - parser_peek(parser)->type != TOKEN_RBRACE && - parser_peek(parser)->type != TOKEN_RBRACKET && - parser_peek(parser)->type != TOKEN_SEMICOLON && - parser_peek(parser)->type != TOKEN_COMMA && - parser_peek(parser)->type != TOKEN_EOF) { + switch (token->type) { + case TOKEN_DOT: { + /* Table property access: table.property */ + parser_advance(parser); /* consume '.' */ - ASTNode* arg = parser_parse_logical(parser); - if (arg == NULL) { - /* Cleanup on error */ - for (int i = 0; i < arg_count; i++) { - ast_destroy_node(args[i]); - } - free(args); + Token* property = parser_consume(parser, TOKEN_IDENTIFIER, "Expected property name after '.'"); + if (property == NULL) { 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); + ASTNode* key = ast_literal_node(baba_yaga_value_string(property->lexeme), property->line, property->column); + if (key == NULL) { ast_destroy_node(left); return NULL; } - args = new_args; - args[arg_count++] = arg; + + 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; } - - /* 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]); + 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; } - free(args); - 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; } - - left = new_left; } return left; @@ -1401,7 +1762,7 @@ static ASTNode* parser_parse_application(Parser* parser) { * @return Parsed expression node */ static ASTNode* parser_parse_expression(Parser* parser) { - return parser_parse_application(parser); + return parser_parse_logical(parser); } /* ============================================================================ @@ -1429,6 +1790,8 @@ static ASTNode* parser_parse_variable_decl(Parser* parser) { return NULL; } + + ASTNode* node = malloc(sizeof(ASTNode)); if (node == NULL) { ast_destroy_node(value); @@ -1441,6 +1804,7 @@ static ASTNode* parser_parse_variable_decl(Parser* parser) { node->data.variable_decl.name = strdup(name->lexeme); node->data.variable_decl.value = value; + return node; } @@ -1521,6 +1885,73 @@ static ASTNode* parser_parse_function_def(Parser* parser) { } /** + * @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 @@ -1992,6 +2423,61 @@ void* baba_yaga_ast_get_when_pattern_result(void* node) { 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; @@ -2073,9 +2559,100 @@ void baba_yaga_print_ast(void* node, int indent) { * @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; - ASTNode* test = parser_parse_expression(parser); + + + + /* 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; } @@ -2110,6 +2687,7 @@ static ASTNode* parser_parse_when_expression(Parser* parser) { } // 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; } @@ -2133,19 +2711,19 @@ static bool parser_is_next_pattern(Parser* parser) { // Parse a result expression for a when pattern, stopping at pattern boundaries static ASTNode* parser_parse_when_result_expression(Parser* parser) { - printf("DEBUG: parser_parse_when_result_expression start at token %d\n", parser->current); + 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) { - printf("DEBUG: Before parsing result, token type=%d, lexeme='%s'\n", + 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)) { - printf("DEBUG: Detected next pattern, returning empty result\n"); + DEBUG_TRACE("Detected next pattern, returning empty result"); return ast_literal_node(baba_yaga_value_string(""), parser_peek(parser)->line, parser_peek(parser)->column); } @@ -2159,68 +2737,219 @@ static ASTNode* parser_parse_when_result_expression(Parser* parser) { // Show current token after parsing Token* after_token = parser_peek(parser); if (after_token) { - printf("DEBUG: After parsing result, token type=%d, lexeme='%s'\n", + DEBUG_TRACE("After parsing result, token type=%d, lexeme='%s'", after_token->type, after_token->lexeme ? after_token->lexeme : "NULL"); } - printf("DEBUG: parser_parse_when_result_expression end at token %d\n", parser->current); + DEBUG_TRACE("parser_parse_when_result_expression end at token %d", parser->current); return result; } static ASTNode* parser_parse_when_pattern(Parser* parser) { - printf("DEBUG: parser_parse_when_pattern start\n"); + 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) { - printf("DEBUG: Current token type=%d, lexeme='%s'\n", current_token->type, current_token->lexeme ? current_token->lexeme : "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; } - /* Check if this is a wildcard pattern (_) */ ASTNode* pattern_test; - if (current_token && current_token->type == TOKEN_IDENTIFIER && - current_token->lexeme && strcmp(current_token->lexeme, "_") == 0) { - /* Special handling for wildcard pattern */ - printf("DEBUG: Found wildcard pattern\n"); + 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("__WILDCARD__"), + pattern_test = ast_literal_node(baba_yaga_value_string("_"), current_token->line, current_token->column); /* Consume the _ token */ parser_advance(parser); - printf("DEBUG: Consumed _ token, current token type=%d, lexeme='%s'\n", + 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 */ - pattern_test = parser_parse_expression(parser); + /* 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) { - printf("DEBUG: Failed to parse pattern test expression\n"); + DEBUG_TRACE("Failed to parse pattern test expression"); return NULL; } - printf("DEBUG: Parsed pattern test expression\n"); - } - - /* Consume 'then' keyword */ - Token* then_token = parser_consume(parser, TOKEN_KEYWORD_THEN, "Expected 'then' after pattern"); - if (then_token == NULL) { - printf("DEBUG: Failed to consume 'then' token\n"); - ast_destroy_node(pattern_test); - return NULL; - } - printf("DEBUG: Consumed 'then' token\n"); - - /* Parse result expression (bounded) */ - ASTNode* result = parser_parse_when_result_expression(parser); - if (result == NULL) { - ast_destroy_node(pattern_test); - return NULL; + DEBUG_TRACE("Parsed pattern test expression"); } - printf("DEBUG: Parsed result expression\n"); - printf("DEBUG: parser_parse_when_pattern success\n"); + DEBUG_TRACE("parser_parse_when_pattern success"); - /* Create when pattern node */ - return ast_when_pattern_node(pattern_test, result, - then_token->line, then_token->column); + /* Create when pattern node - only the pattern test, result will be added by caller */ + return pattern_test; } /* Helper function to get node type name */ |