From f31fca4204c5036b843687bc7091b46bada06494 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 30 Jan 2026 23:43:49 +0100 Subject: [PATCH] feat: full while/until loops support, important bug fixes and more tests --- src/execution/execution.c | 6 +-- src/execution/execution_helpers.c | 14 ++++-- src/execution/execution_helpers.h | 1 + src/parser/grammar.c | 84 +++++++++++++++++++++---------- src/utils/ast/ast.c | 6 +++ tests/functional/run-tests.sh | 11 ++-- 6 files changed, 85 insertions(+), 37 deletions(-) diff --git a/src/execution/execution.c b/src/execution/execution.c index cf751f7..46bbfcb 100644 --- a/src/execution/execution.c +++ b/src/execution/execution.c @@ -1,17 +1,13 @@ #define _POSIX_C_SOURCE 200809L #include "execution.h" -#include #include #include -#include -#include #include #include #include #include "../expansion/expansion.h" -#include "../utils/ast/ast.h" #include "../utils/hash_map/hash_map.h" // Refactored: delegates to helpers in execution_helpers.c @@ -40,6 +36,8 @@ int execution(struct ast *ast, struct hash_map *vars) return exec_ast_list(ast_get_list(ast), vars); case AST_AND_OR: return exec_ast_and_or(ast_get_and_or(ast), vars); + case AST_LOOP: + return exec_ast_loop(ast_get_loop(ast), vars); default: return 127; } diff --git a/src/execution/execution_helpers.c b/src/execution/execution_helpers.c index 9a72c10..e65c0d1 100644 --- a/src/execution/execution_helpers.c +++ b/src/execution/execution_helpers.c @@ -1,7 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include "execution_helpers.h" -#include #include #include #include @@ -9,8 +8,6 @@ #include #include -#include "../expansion/expansion.h" -#include "../utils/ast/ast.h" #include "../utils/hash_map/hash_map.h" #include "../utils/lists/lists.h" #include "../utils/vars/vars.h" @@ -275,6 +272,17 @@ void unset_all_redir(struct list *redir_list) } } +int exec_ast_loop(struct ast_loop *loop_node, struct hash_map *vars) +{ + int res = 0; + while (execution(loop_node->condition, vars) == 0) + { + res = execution(loop_node->body, vars); + } + + return res; +} + // --- Builtins --- static int builtin_echo(char **argv) diff --git a/src/execution/execution_helpers.h b/src/execution/execution_helpers.h index ebf6858..1716179 100644 --- a/src/execution/execution_helpers.h +++ b/src/execution/execution_helpers.h @@ -8,6 +8,7 @@ int exec_ast_command(struct ast_command *command, struct hash_map *vars); int exec_ast_if(struct ast_if *if_node, struct hash_map *vars); int exec_ast_list(struct ast_list *list_node, struct hash_map *vars); int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars); +int exec_ast_loop(struct ast_loop *loop_node, struct hash_map *vars); void unset_all_redir(struct list *redir_list); #endif // EXECUTION_HELPERS_H diff --git a/src/parser/grammar.c b/src/parser/grammar.c index 0680a79..e2a4887 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -101,17 +101,13 @@ static void add_first_redir(void) add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_PIPE); } -// === Functions - -int grammar_init(void) +// Adds only direct tokens to rules firsts into the firsts map +static void add_firsts_tokens(void) { - // Initialize the firsts map - bool success = init_firsts_map(); - if (success != true) - return false; - - // Populate the firsts map - // TODO CHECK ORDER + // Redirection + add_first_redir(); + // %RIP Matteo 30/01/2026 + // %RAX Guillem 30/01/2026 hehe // If add_first(RULE_IF, TOKEN_IF); @@ -127,7 +123,7 @@ int grammar_init(void) add_first(RULE_WHILE, TOKEN_WHILE); // Until - add_first(RULE_WHILE, TOKEN_UNTIL); + add_first(RULE_UNTIL, TOKEN_UNTIL); // Case add_first(RULE_CASE, TOKEN_CASE); @@ -136,33 +132,57 @@ int grammar_init(void) add_first(RULE_CASE_ITEM, TOKEN_LEFT_PAREN); add_first(RULE_CASE_ITEM, TOKEN_WORD); - // Case clause - add_firsts(RULE_CASE_CLAUSE, first(RULE_CASE_ITEM)); + // Shell command + add_first(RULE_SHELL_COMMAND, TOKEN_LEFT_BRACKET); + add_first(RULE_SHELL_COMMAND, TOKEN_LEFT_PAREN); - // Redirection - add_first_redir(); - // %RIP Matteo 30/01/2026 - // %RAX Guillem 30/01/2026 hehe + // Simple command + add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD); + add_first(RULE_SIMPLE_COMMAND, TOKEN_EXPORT); // Element add_first(RULE_ELEMENT, TOKEN_WORD); add_first(RULE_ELEMENT, TOKEN_ASSIGNMENT_WORD); - add_firsts(RULE_ELEMENT, first(RULE_REDIRECTION)); // Prefix add_first(RULE_PREFIX, TOKEN_ASSIGNMENT_WORD); + + // Pipeline + add_first(RULE_PIPELINE, TOKEN_WORD); + + // Compound list + add_first(RULE_COMPOUND_LIST, TOKEN_NEWLINE); + + // Input + add_first(RULE_INPUT, TOKEN_NEWLINE); + add_first(RULE_INPUT, TOKEN_EOF); + + // Funcdec + add_first(RULE_FUNCDEC, TOKEN_WORD); +} + +// Adds only firsts that depend on other rules to the firsts map +// WARNING order matters +static void add_firsts_rec(void) +{ + // Case clause + add_firsts(RULE_CASE_CLAUSE, first(RULE_CASE_ITEM)); + + // Element + add_firsts(RULE_ELEMENT, first(RULE_REDIRECTION)); + + // Prefix add_firsts(RULE_PREFIX, first(RULE_REDIRECTION)); // Shell command add_firsts(RULE_SHELL_COMMAND, first(RULE_IF)); + add_firsts(RULE_SHELL_COMMAND, first(RULE_FOR)); + add_firsts(RULE_SHELL_COMMAND, first(RULE_WHILE)); + add_firsts(RULE_SHELL_COMMAND, first(RULE_UNTIL)); + add_firsts(RULE_SHELL_COMMAND, first(RULE_CASE)); // Simple command add_firsts(RULE_SIMPLE_COMMAND, first(RULE_PREFIX)); - add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD); - add_first(RULE_SIMPLE_COMMAND, TOKEN_EXPORT); - - // Funcdec - add_first(RULE_FUNCDEC, TOKEN_WORD); // Command add_firsts(RULE_COMMAND, first(RULE_SIMPLE_COMMAND)); @@ -170,23 +190,33 @@ int grammar_init(void) add_firsts(RULE_COMMAND, first(RULE_FUNCDEC)); // Pipeline - add_first(RULE_PIPELINE, TOKEN_WORD); add_firsts(RULE_PIPELINE, first(RULE_COMMAND)); // And Or add_firsts(RULE_AND_OR, first(RULE_PIPELINE)); // Compound list - add_first(RULE_COMPOUND_LIST, TOKEN_NEWLINE); add_firsts(RULE_COMPOUND_LIST, first(RULE_AND_OR)); // List add_firsts(RULE_LIST, first(RULE_AND_OR)); // Input - add_first(RULE_INPUT, TOKEN_NEWLINE); - add_first(RULE_INPUT, TOKEN_EOF); add_firsts(RULE_INPUT, first(RULE_LIST)); +} + +// === Functions + +int grammar_init(void) +{ + // Initialize the firsts map + bool success = init_firsts_map(); + if (success != true) + return false; + + // Populate the firsts map + add_firsts_tokens(); + add_firsts_rec(); return true; } diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c index 95a2f15..5d64f7d 100644 --- a/src/utils/ast/ast.c +++ b/src/utils/ast/ast.c @@ -42,6 +42,12 @@ void ast_free(struct ast **node) case AST_ASSIGNMENT: ast_free_assignment(ast_get_assignment(*node)); break; + case AST_NEG: + ast_free_neg(ast_get_neg(*node)); + break; + case AST_LOOP: + ast_free_loop(ast_get_loop(*node)); + break; case AST_VOID: case AST_END: break; diff --git a/tests/functional/run-tests.sh b/tests/functional/run-tests.sh index 5119aad..42654da 100755 --- a/tests/functional/run-tests.sh +++ b/tests/functional/run-tests.sh @@ -377,9 +377,14 @@ test_str "If with negation" "if ! false; then echo Yes; fi" test_str "If faut aller niquer sa mere" "if false; ! false; then echo Embrasse moi; fi" -echo -e "\n$BBlue=== For/While ===$Color_Off" -test_str "While loop" "i=0; while [ \$i -lt 3 ]; do echo \$i; i=\$((i+1)); done" -test_str "Until loop" "i=0; until [ \$i -ge 3 ]; do echo \$i; i=\$((i+1)); done" +echo -e "\n$BBlue=== Loops ===$Color_Off" +test_str "While false" "while false; do false; done" +test_str "While(false) true" "while false; do true; done" +test_str "Until(true) false" "until true; do false; done" +test_str "Until true" "until true; do true; done" +# test_str "While var" "a=2; while [ \$a -eq 2 ]; do \$a=3; done" +test_str "While arithmetic" "i=0; while [ \$i -lt 3 ]; do echo \$i; i=\$((i+1)); done" +test_str "Until arithmetic" "i=0; until [ \$i -ge 3 ]; do echo \$i; i=\$((i+1)); done" test_str "While break" "while true; do echo break; break; done" test_str "While continue" "i=0; while [ \$i -lt 3 ]; do i=\$((i+1)); if [ \$i -eq 2 ]; then continue; fi; echo \$i; done" test_str "For loop basic" "for i in a b c; do echo \$i; done"