From 399d1ed3e15077e277164880caff15d9649143ed Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Tue, 27 Jan 2026 21:06:36 +0100 Subject: [PATCH 1/6] feat(redirections): ast commands now have a field for the list of redirections + redirections implemented in parser --- src/parser/grammar_basic.c | 10 ++++++---- src/utils/ast/ast_command.c | 5 ++++- src/utils/ast/ast_command.h | 4 +++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index d4a740f..f3e63b6 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -171,6 +171,7 @@ struct ast *parse_command(struct lexer_context *ctx) struct ast *parse_simple_command(struct lexer_context *ctx) { struct list *command_elements = NULL; + struct list *redirections = NULL; // list of redirection ASTs // WORD struct token *token = POP_TOKEN(); @@ -204,15 +205,15 @@ struct ast *parse_simple_command(struct lexer_context *ctx) } else if (ast_is_redir(element)) { - // TODO - perror("NOT IMPLEMENTED"); - return NULL; + // append redirections to the list of redirections + redirections = list_append(redirections, element); } else { perror("Internal error: unexpected return value from parse_element " "in parse_simple_command"); list_deep_destroy(command_elements); + list_deep_destroy(redirections); return NULL; } @@ -221,10 +222,11 @@ struct ast *parse_simple_command(struct lexer_context *ctx) } // Result - struct ast *result = ast_create_command(command_elements); + struct ast *result = ast_create_command(command_elements, redirections); if (result == NULL) { list_deep_destroy(command_elements); + list_deep_destroy(redirections); return NULL; } return result; diff --git a/src/utils/ast/ast_command.c b/src/utils/ast/ast_command.c index 14904ae..c4fe3e6 100644 --- a/src/utils/ast/ast_command.c +++ b/src/utils/ast/ast_command.c @@ -5,13 +5,15 @@ #include "../lists/lists.h" -struct ast *ast_create_command(struct list *command) +struct ast *ast_create_command(struct list *command, + struct list *redirections) { struct ast_command *command_data = malloc(sizeof(struct ast_command)); if (!command_data) return NULL; command_data->command = command; + command_data->redirections = redirections; return ast_create(AST_CMD, command_data); } @@ -33,5 +35,6 @@ void ast_free_command(struct ast_command *command_data) if (command_data == NULL) return; list_deep_destroy(command_data->command); + ast_list_deep_destroy(command_data->redirections); free(command_data); } diff --git a/src/utils/ast/ast_command.h b/src/utils/ast/ast_command.h index cf6b913..835bf5d 100644 --- a/src/utils/ast/ast_command.h +++ b/src/utils/ast/ast_command.h @@ -7,6 +7,7 @@ struct ast_command { struct list *command; // A list of words (char*) + struct ast_list *redirections; // A list of ASTs, all ast_redir }; /** @@ -23,7 +24,8 @@ struct ast_command *ast_get_command(struct ast *node); /** * Creates a new AST node representing a command. */ -struct ast *ast_create_command(struct list *command); +struct ast *ast_create_command(struct list *command, + struct list *redirections); /* * @brief: frees the given ast_command and sets the pointer to NULL. From 9a0f9bc6f1965ac683dd9b8208643517b8b4a0a9 Mon Sep 17 00:00:00 2001 From: matteo Date: Wed, 28 Jan 2026 11:34:29 +0100 Subject: [PATCH 2/6] fix: redirections types --- src/execution/execution_helpers.c | 2 +- src/parser/grammar_advanced.c | 11 ++++++++++- src/utils/ast/ast_redir.h | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/execution/execution_helpers.c b/src/execution/execution_helpers.c index 8041a3c..25dd4a7 100644 --- a/src/execution/execution_helpers.c +++ b/src/execution/execution_helpers.c @@ -121,7 +121,7 @@ static int get_fd_target(const struct ast_redir *redir) if (redir->io_number != -1) return redir->io_number; if (redir->type == AST_REDIR_TYPE_LESS - || redir->type == AST_REDIR_TYPE_DLESS + || redir->type == AST_REDIR_TYPE_LESSGREAT || redir->type == AST_REDIR_TYPE_LESSAND) return 0; return 1; diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c index 3850564..8c4b68c 100644 --- a/src/parser/grammar_advanced.c +++ b/src/parser/grammar_advanced.c @@ -16,7 +16,16 @@ static enum ast_redir_type redir_tok_to_ast_type(enum token_type tok_type) return AST_REDIR_TYPE_LESS; case TOKEN_REDIR_RIGHT: return AST_REDIR_TYPE_GREAT; - // TODO finish this + case TOKEN_REDIR_DOUBLE_RIGHT: + return AST_REDIR_TYPE_DGREAT; + case TOKEN_REDIR_LEFT_RIGHT: + return AST_REDIR_TYPE_LESSGREAT; + case TOKEN_REDIR_LEFT_AMP: + return AST_REDIR_TYPE_LESSAND; + case TOKEN_REDIR_RIGHT_AMP: + return AST_REDIR_TYPE_GREATAND; + case TOKEN_REDIR_RIGHT_PIPE: + return AST_REDIR_TYPE_CLOBBER; default: return AST_REDIR_TYPE_NULL; } diff --git a/src/utils/ast/ast_redir.h b/src/utils/ast/ast_redir.h index eb95942..fdea88c 100644 --- a/src/utils/ast/ast_redir.h +++ b/src/utils/ast/ast_redir.h @@ -8,7 +8,7 @@ enum ast_redir_type AST_REDIR_TYPE_NULL, AST_REDIR_TYPE_LESS, // < AST_REDIR_TYPE_GREAT, // > - AST_REDIR_TYPE_DLESS, // << + AST_REDIR_TYPE_LESSGREAT, // <> AST_REDIR_TYPE_DGREAT, // >> AST_REDIR_TYPE_LESSAND, // <& AST_REDIR_TYPE_GREATAND, // >& From b657d65664a27f55c23d58d984fcae5892afcfb8 Mon Sep 17 00:00:00 2001 From: matteo Date: Wed, 28 Jan 2026 12:11:40 +0100 Subject: [PATCH 3/6] feat(parser): parse_command version with prefixes and elements --- src/parser/grammar_basic.c | 69 +++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index f3e63b6..932d7f5 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -153,11 +153,11 @@ struct ast *parse_command(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); - if (token->type == TOKEN_WORD) + if (is_first(*token, RULE_SIMPLE_COMMAND)) { return parse_simple_command(ctx); } - else if (token->type == TOKEN_IF) + else if (is_first(*token, RULE_SHELL_COMMAND)) { return parse_shell_command(ctx); } @@ -168,23 +168,54 @@ struct ast *parse_command(struct lexer_context *ctx) } } +/* @brief: frees command_elements and redirections lists + * @return: NULL + */ +static err_simple_command(struct list *command_elements, + struct list *redirections) +{ + list_deep_destroy(command_elements); + list_deep_destroy(redirections); + return NULL; +} + struct ast *parse_simple_command(struct lexer_context *ctx) { struct list *command_elements = NULL; struct list *redirections = NULL; // list of redirection ASTs - // WORD - struct token *token = POP_TOKEN(); - if (token->type != TOKEN_WORD) + bool has_prefix = false; + struct token *token = PEEK_TOKEN(); + if (is_first(*token, RULE_PREFIX)) + { + has_prefix = true; + while (is_first(*token, RULE_PREFIX)) + { + struct ast *redir = parse_prefix(ctx); + if (redir == NULL) + { + return err_simple_command(command_elements, redirections); + } + redirections = list_append(redirections, redir); + token = PEEK_TOKEN(); + } + } + + if (token->type != TOKEN_WORD && !has_prefix) { perror("Expected a command but got a different token type"); - return NULL; + return err_simple_command(command_elements, redirections); } - char *command = strdup(token->data); - command_elements = list_append(command_elements, command); - - token = PEEK_TOKEN(); + + // we can have only prefixes askip + if (token->type == TOKEN_WORD) + { + char *command = strdup(token->data); + command_elements = list_append(command_elements, command); + POP_TOKEN(); + token = PEEK_TOKEN(); + } // Eventual elements while (is_first(*token, RULE_ELEMENT)) { @@ -192,16 +223,20 @@ struct ast *parse_simple_command(struct lexer_context *ctx) struct ast *element = parse_element(ctx); if (element == NULL) { - list_deep_destroy(command_elements); - return NULL; + return err_simple_command(command_elements, redirections); } // Get element type if (ast_is_word(element)) { struct ast_word *element_word = ast_get_word(element); + + // TODO test this fix for the memory leaks + char *word = strdup(element_word->word); + ast_free(element_word); command_elements = - list_append(command_elements, element_word->word); + list_append(command_elements, word); + // end of fix } else if (ast_is_redir(element)) { @@ -212,9 +247,7 @@ struct ast *parse_simple_command(struct lexer_context *ctx) { perror("Internal error: unexpected return value from parse_element " "in parse_simple_command"); - list_deep_destroy(command_elements); - list_deep_destroy(redirections); - return NULL; + return err_simple_command(command_elements, redirections); } // Forward @@ -225,9 +258,7 @@ struct ast *parse_simple_command(struct lexer_context *ctx) struct ast *result = ast_create_command(command_elements, redirections); if (result == NULL) { - list_deep_destroy(command_elements); - list_deep_destroy(redirections); - return NULL; + return err_simple_command(command_elements, redirections); } return result; } From 97e4b6c0f3203ac7700ea3cae7efe49e226c8119 Mon Sep 17 00:00:00 2001 From: matteo Date: Wed, 28 Jan 2026 12:21:57 +0100 Subject: [PATCH 4/6] style(parser): refactor parse_command --- src/parser/grammar_basic.c | 94 ++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index 932d7f5..ab4194c 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -152,14 +152,15 @@ struct ast *parse_pipeline(struct lexer_context *ctx) struct ast *parse_command(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); + struct ast *result = NULL; if (is_first(*token, RULE_SIMPLE_COMMAND)) { - return parse_simple_command(ctx); + result = parse_simple_command(ctx); } else if (is_first(*token, RULE_SHELL_COMMAND)) { - return parse_shell_command(ctx); + parse_shell_command(ctx); } else { @@ -201,57 +202,62 @@ struct ast *parse_simple_command(struct lexer_context *ctx) } } - if (token->type != TOKEN_WORD && !has_prefix) + if (token->type != TOKEN_WORD) { - perror("Expected a command but got a different token type"); - return err_simple_command(command_elements, redirections); - } - - // we can have only prefixes askip - if (token->type == TOKEN_WORD) - { - char *command = strdup(token->data); - command_elements = list_append(command_elements, command); - - POP_TOKEN(); - token = PEEK_TOKEN(); - } - // Eventual elements - while (is_first(*token, RULE_ELEMENT)) - { - // Get element - struct ast *element = parse_element(ctx); - if (element == NULL) + if (!has_prefix) { + perror("Expected a command but got a different token type"); return err_simple_command(command_elements, redirections); } - - // Get element type - if (ast_is_word(element)) + // else : only prefixes + } + else + { + if (token->type == TOKEN_WORD) { - struct ast_word *element_word = ast_get_word(element); + char *command = strdup(token->data); + command_elements = list_append(command_elements, command); - // TODO test this fix for the memory leaks - char *word = strdup(element_word->word); - ast_free(element_word); - command_elements = - list_append(command_elements, word); - // end of fix + POP_TOKEN(); + token = PEEK_TOKEN(); } - else if (ast_is_redir(element)) + // Eventual elements + while (is_first(*token, RULE_ELEMENT)) { - // append redirections to the list of redirections - redirections = list_append(redirections, element); - } - else - { - perror("Internal error: unexpected return value from parse_element " - "in parse_simple_command"); - return err_simple_command(command_elements, redirections); - } + // Get element + struct ast *element = parse_element(ctx); + if (element == NULL) + { + return err_simple_command(command_elements, redirections); + } - // Forward - token = PEEK_TOKEN(); + // Get element type + if (ast_is_word(element)) + { + struct ast_word *element_word = ast_get_word(element); + + // TODO test this fix for the memory leaks + char *word = strdup(element_word->word); + ast_free(element_word); + command_elements = list_append(command_elements, word); + // end of fix + } + else if (ast_is_redir(element)) + { + // append redirections to the list of redirections + redirections = list_append(redirections, element); + } + else + { + perror("Internal error: unexpected return value from " + "parse_element " + "in parse_simple_command"); + return err_simple_command(command_elements, redirections); + } + + // Forward + token = PEEK_TOKEN(); + } } // Result From 0db50e28de94d5dfdfffc169898d07e3a337bf97 Mon Sep 17 00:00:00 2001 From: matteo Date: Wed, 28 Jan 2026 16:26:17 +0100 Subject: [PATCH 5/6] fix(parser): small typos --- src/parser/grammar_basic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index ab4194c..fb1b12d 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -172,7 +172,7 @@ struct ast *parse_command(struct lexer_context *ctx) /* @brief: frees command_elements and redirections lists * @return: NULL */ -static err_simple_command(struct list *command_elements, +static void *err_simple_command(struct list *command_elements, struct list *redirections) { list_deep_destroy(command_elements); @@ -238,7 +238,7 @@ struct ast *parse_simple_command(struct lexer_context *ctx) // TODO test this fix for the memory leaks char *word = strdup(element_word->word); - ast_free(element_word); + ast_free(&element); command_elements = list_append(command_elements, word); // end of fix } From c40e5c2d0f7ee9e8ebaa76e504453b1d3b7ebba6 Mon Sep 17 00:00:00 2001 From: matteo Date: Wed, 28 Jan 2026 19:16:48 +0100 Subject: [PATCH 6/6] feat(parser): helper static function --- src/parser/grammar_basic.c | 42 +++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index fb1b12d..17e006f 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -169,7 +169,7 @@ struct ast *parse_command(struct lexer_context *ctx) } } -/* @brief: frees command_elements and redirections lists +/* @brief: frees command_elements and redirections lists (helper func) * @return: NULL */ static void *err_simple_command(struct list *command_elements, @@ -293,6 +293,17 @@ struct ast *parse_shell_command(struct lexer_context *ctx) return parse_if_rule(ctx); } +/* @brief: frees all the arguments. (helper func) + * @return: NULL. + */ +static void *err_if_rule(struct ast **cond, struct ast **then_clause, struct ast **else_clause) +{ + ast_free(cond); + ast_free(then_clause); + ast_free(else_clause); + return NULL; +} + struct ast *parse_if_rule(struct lexer_context *ctx) { // If keyword @@ -311,38 +322,34 @@ struct ast *parse_if_rule(struct lexer_context *ctx) token = POP_TOKEN(); if (token->type != TOKEN_THEN) { - ast_free(&condition_content); perror("Expected the 'then' keyword but token has different type"); - return NULL; + return err_if_rule(&condition_content, NULL, NULL); } // Then content struct ast *then_content = parse_compound_list(ctx); if (then_content == NULL) { - ast_free(&condition_content); - ast_free(&then_content); - return NULL; + return err_if_rule(&condition_content, &then_content, NULL); } + struct ast *else_content = NULL; // Eventual else/elif clause(s) - struct ast *else_content = parse_else_clause(ctx); - if (else_content == NULL) + if (is_first(*token, RULE_ELSE_CLAUSE)) { - ast_free(&condition_content); - ast_free(&then_content); - return NULL; + else_content = parse_else_clause(ctx); + if (else_content == NULL) + { + return err_if_rule(&condition_content, &then_content, NULL); + } } // Fi keyword token = POP_TOKEN(); if (token->type != TOKEN_FI) { - ast_free(&condition_content); - ast_free(&then_content); - ast_free(&else_content); perror("Expected the 'fi' keyword but token has different type"); - return NULL; + return err_if_rule(&condition_content, &then_content, &else_content); } // Result @@ -350,11 +357,8 @@ struct ast *parse_if_rule(struct lexer_context *ctx) ast_create_if(condition_content, then_content, else_content); if (result == NULL) { - ast_free(&condition_content); - ast_free(&then_content); - ast_free(&else_content); perror("Internal error: could not create a new AST (AST_IF)"); - return NULL; + return err_if_rule(&condition_content, &then_content, &else_content); } return result;