From 8a5c58974200553c2830b594b34d9c47aa8eee2e Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Tue, 27 Jan 2026 19:56:33 +0100 Subject: [PATCH] feat(parser): redirections --- src/lexer/lexer_utils.h | 2 +- src/parser/grammar.c | 4 +- src/parser/grammar.h | 12 +++-- src/parser/grammar_advanced.c | 28 +++++----- src/parser/grammar_advanced.h | 4 +- src/parser/grammar_basic.c | 12 +++-- src/parser/parser.c | 6 +-- src/utils/ast/ast.c | 2 +- src/utils/ast/ast.h | 4 +- src/utils/ast/ast_base.h | 2 +- src/utils/ast/ast_neg.h | 6 +-- src/utils/ast/ast_pipe.h | 8 +-- src/utils/ast/ast_redir.c | 4 +- src/utils/ast/ast_redir.h | 3 +- src/utils/ast/ast_word.c | 3 +- src/utils/ast/ast_word.h | 2 +- tests/unit/io_backend/io_backend.c | 84 ++++++++++-------------------- 17 files changed, 78 insertions(+), 108 deletions(-) diff --git a/src/lexer/lexer_utils.h b/src/lexer/lexer_utils.h index 6b26c74..c6c26c9 100644 --- a/src/lexer/lexer_utils.h +++ b/src/lexer/lexer_utils.h @@ -54,7 +54,7 @@ enum token_type // TODO merge into one and use the data field // (Too difficult to handle in the parser because of firsts) // TOKEN_REDIRECTION - // + // // Redirections TOKEN_REDIR_LEFT, TOKEN_REDIR_RIGHT, diff --git a/src/parser/grammar.c b/src/parser/grammar.c index 5b3d78a..83f2266 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -66,7 +66,7 @@ static bool init_firsts_map(void) if (firsts_map == NULL) { perror("Internal error: couldn't create the firsts_map (is your memory " - "full ?)"); + "full ?)"); return false; } @@ -161,7 +161,7 @@ struct firsts_list *first(enum rule rule) if (firsts_map == NULL || firsts_map[rule].tokens == NULL) { perror("Internal error: attempted to get the firsts of a rule without " - "properly initializing the firsts map"); + "properly initializing the firsts map"); return NULL; } diff --git a/src/parser/grammar.h b/src/parser/grammar.h index 9538848..3c77747 100644 --- a/src/parser/grammar.h +++ b/src/parser/grammar.h @@ -11,7 +11,7 @@ peek_token(ctx); \ if (token == NULL) \ { \ - perror("Internal error: cannot get the following token"); \ + perror("Internal error: cannot get the following token"); \ return NULL; \ } @@ -19,13 +19,14 @@ pop_token(ctx); \ if (token == NULL) \ { \ - perror("Internal error: cannot get the following token"); \ + perror("Internal error: cannot get the following token"); \ return NULL; \ } // === Structures -enum rule { +enum rule +{ RULE_NULL = 0, RULE_INPUT, RULE_LIST, @@ -43,8 +44,9 @@ enum rule { NUMBER_OF_RULES }; -struct firsts_list { - enum token_type* tokens; // Heap allocated array +struct firsts_list +{ + enum token_type *tokens; // Heap allocated array size_t list_length; }; diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c index faa2f5e..3850564 100644 --- a/src/parser/grammar_advanced.c +++ b/src/parser/grammar_advanced.c @@ -1,3 +1,5 @@ +#define _POSIX_C_SOURCE 200809L + #include "grammar_advanced.h" #include @@ -8,23 +10,20 @@ static enum ast_redir_type redir_tok_to_ast_type(enum token_type tok_type) { - switch(tok_type) + switch (tok_type) { - case TOKEN_REDIR_LEFT: - return AST_REDIR_TYPE_LESS; - case TOKEN_REDIR_RIGHT: - return AST_REDIR_TYPE_GREAT; - // TODO finish this - default: - return AST_REDIR_TYPE_NULL; + case TOKEN_REDIR_LEFT: + return AST_REDIR_TYPE_LESS; + case TOKEN_REDIR_RIGHT: + return AST_REDIR_TYPE_GREAT; + // TODO finish this + default: + return AST_REDIR_TYPE_NULL; } } struct ast *parse_redirection(struct lexer_context *ctx) { - (void)ctx; - return NULL; - /* struct token *token = PEEK_TOKEN(); int io_number = -1; if (token->type == TOKEN_IONUMBER) @@ -37,10 +36,9 @@ struct ast *parse_redirection(struct lexer_context *ctx) if (!is_token_redir(token)) { perror("Syntax error: expected a redirection token but got something " - "else"); + "else"); return NULL; } - // char *redir_op = strdup(token->data); enum ast_redir_type redir_type = redir_tok_to_ast_type(token->type); POP_TOKEN(); @@ -49,14 +47,12 @@ struct ast *parse_redirection(struct lexer_context *ctx) if (token->type != TOKEN_WORD) { perror("Syntax error: expected a word after redirection"); - // free(redir_op); return NULL; } char *target = strdup(token->data); POP_TOKEN(); - return ast_create_redir(io_number, redir_type, target); - */ + return ast_create_redir(target, io_number, redir_type); } struct ast *parse_prefix(struct lexer_context *ctx) diff --git a/src/parser/grammar_advanced.h b/src/parser/grammar_advanced.h index 72b5a43..f3c27ae 100644 --- a/src/parser/grammar_advanced.h +++ b/src/parser/grammar_advanced.h @@ -8,7 +8,8 @@ /* * @brief parses a redirection rule * - * @code redirection = [IONUMBER] ( '>' | '<' | '>>' | '>&' | '<&' | '>|' | '<>' ) WORD ; + * @code redirection = [IONUMBER] ( '>' | '<' | '>>' | '>&' | '<&' | '>|' | '<>' + * ) WORD ; * * @first TOKEN_IONUMBER, TOKEN_REDIRECTION */ @@ -23,5 +24,4 @@ struct ast *parse_redirection(struct lexer_context *ctx); */ struct ast *parse_prefix(struct lexer_context *ctx); - #endif /* ! GRAMMAR_ADVANCED_H */ diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index 25d29d1..d4a740f 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -1,13 +1,14 @@ -#include #define _POSIX_C_SOURCE 200809L +#include "grammar_basic.h" + +#include #include #include #include "../utils/lists/lists.h" #include "grammar.h" #include "grammar_advanced.h" -#include "grammar_basic.h" // === Static functions @@ -210,7 +211,7 @@ struct ast *parse_simple_command(struct lexer_context *ctx) else { perror("Internal error: unexpected return value from parse_element " - "in parse_simple_command"); + "in parse_simple_command"); list_deep_destroy(command_elements); return NULL; } @@ -260,7 +261,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx) if (token->type != TOKEN_IF) { perror("Internal error: expected a if rule but token has different " - "type"); + "type"); return NULL; } @@ -398,7 +399,8 @@ struct ast *parse_else_clause(struct lexer_context *ctx) token = POP_TOKEN(); if (token->type != TOKEN_THEN) { - perror("Expected the 'then' keyword but got a different token type"); + perror( + "Expected the 'then' keyword but got a different token type"); return NULL; } diff --git a/src/parser/parser.c b/src/parser/parser.c index e5c5f94..c2b8aaf 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -40,19 +40,19 @@ struct ast *get_ast(struct lexer_context *ctx) if (ctx == NULL) { perror("Internal error: called parser with no lexer context (NULL " - "pointer). Aborting."); + "pointer). Aborting."); return NULL; } if (state == PARSER_STATE_NOT_INITIALIZED) { perror("Internal error: attempted to call parser without initializing " - "it. Aborting."); + "it. Aborting."); return NULL; } if (state == PARSER_STATE_CLOSED) { perror("Internal error: attempted to call parser after closing it. " - "Aborting."); + "Aborting."); return NULL; } diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c index a099473..16db64e 100644 --- a/src/utils/ast/ast.c +++ b/src/utils/ast/ast.c @@ -46,7 +46,7 @@ void ast_free(struct ast **node) default: perror("WARNING: Internal error: failed to free an AST node (Unknown " - "type)"); + "type)"); return; } diff --git a/src/utils/ast/ast.h b/src/utils/ast/ast.h index 334820b..3964968 100644 --- a/src/utils/ast/ast.h +++ b/src/utils/ast/ast.h @@ -7,11 +7,11 @@ #include "ast_end.h" #include "ast_if.h" #include "ast_list.h" +#include "ast_neg.h" +#include "ast_pipe.h" #include "ast_redir.h" #include "ast_void.h" #include "ast_word.h" -#include "ast_pipe.h" -#include "ast_neg.h" /** * Prints the Graphviz DOT representation of the given AST to stdout. diff --git a/src/utils/ast/ast_base.h b/src/utils/ast/ast_base.h index 127dad7..f81f2f0 100644 --- a/src/utils/ast/ast_base.h +++ b/src/utils/ast/ast_base.h @@ -1,8 +1,8 @@ #ifndef AST_BASE_H #define AST_BASE_H -#include #include +#include enum ast_type { diff --git a/src/utils/ast/ast_neg.h b/src/utils/ast/ast_neg.h index 8c2d4e8..738c246 100644 --- a/src/utils/ast/ast_neg.h +++ b/src/utils/ast/ast_neg.h @@ -6,12 +6,12 @@ struct ast_neg { bool negation; // True negates the child's output - struct ast* child; + struct ast *child; }; bool ast_is_neg(struct ast *node); struct ast_neg *ast_get_neg(struct ast *node); -struct ast *ast_create_neg(bool negation, struct ast* child); -void ast_free_neg(struct ast_neg* node); +struct ast *ast_create_neg(bool negation, struct ast *child); +void ast_free_neg(struct ast_neg *node); #endif /* ! AST_NEG_H */ diff --git a/src/utils/ast/ast_pipe.h b/src/utils/ast/ast_pipe.h index c5e1e39..930cb2c 100644 --- a/src/utils/ast/ast_pipe.h +++ b/src/utils/ast/ast_pipe.h @@ -5,14 +5,14 @@ struct ast_pipe { - struct ast* left; - struct ast* right; + struct ast *left; + struct ast *right; // Output of left will be redirected to right stdin }; bool ast_is_pipe(struct ast *node); struct ast_pipe *ast_get_pipe(struct ast *node); -struct ast *ast_create_pipe(struct ast* left, struct ast* right); -void ast_free_pipe(struct ast_pipe* node); +struct ast *ast_create_pipe(struct ast *left, struct ast *right); +void ast_free_pipe(struct ast_pipe *node); #endif /* ! AST_PIPE_H */ diff --git a/src/utils/ast/ast_redir.c b/src/utils/ast/ast_redir.c index 8f0b1e9..d1dcedb 100644 --- a/src/utils/ast/ast_redir.c +++ b/src/utils/ast/ast_redir.c @@ -14,13 +14,12 @@ struct ast_redir *ast_get_redir(struct ast *node) return NULL; } -struct ast *ast_create_redir(struct ast *child, char *filename, int io_number, +struct ast *ast_create_redir(char *filename, int io_number, enum ast_redir_type type) { struct ast_redir *redir = malloc(sizeof(struct ast_redir)); if (!redir) return NULL; - redir->child = child; redir->filename = filename; // Takes ownership? Usually yes in simple ASTs, or dup. Let's // assume pointer copy for now, but user must manage memory. @@ -34,7 +33,6 @@ void ast_free_redir(struct ast_redir *redir) { if (!redir) return; - ast_free(&redir->child); free(redir->filename); free(redir); } diff --git a/src/utils/ast/ast_redir.h b/src/utils/ast/ast_redir.h index 4dae912..eb95942 100644 --- a/src/utils/ast/ast_redir.h +++ b/src/utils/ast/ast_redir.h @@ -17,7 +17,6 @@ enum ast_redir_type struct ast_redir { - struct ast *child; char *filename; int io_number; // The FD being redirected (default -1 if not specified, // implies 0 or 1 based on type) @@ -26,7 +25,7 @@ struct ast_redir bool ast_is_redir(struct ast *node); struct ast_redir *ast_get_redir(struct ast *node); -struct ast *ast_create_redir(struct ast *child, char *filename, int io_number, +struct ast *ast_create_redir(char *filename, int io_number, enum ast_redir_type type); void ast_free_redir(struct ast_redir *redir); diff --git a/src/utils/ast/ast_word.c b/src/utils/ast/ast_word.c index 957952c..6870c50 100644 --- a/src/utils/ast/ast_word.c +++ b/src/utils/ast/ast_word.c @@ -1,8 +1,8 @@ #define _POSIX_C_SOURCE 200809L #include "ast_word.h" -#include #include +#include #include #include @@ -17,6 +17,7 @@ struct ast *ast_create_word(char *word) struct ast *res = ast_create(AST_WORD, ast_node); if (res == NULL) { + free(ast_node->word); free(ast_node); return NULL; } diff --git a/src/utils/ast/ast_word.h b/src/utils/ast/ast_word.h index 571f869..a049003 100644 --- a/src/utils/ast/ast_word.h +++ b/src/utils/ast/ast_word.h @@ -23,7 +23,7 @@ struct ast_word *ast_get_word(struct ast *node); /** * Creates a new AST node representing a command. */ -struct ast *ast_create_word(char* word); +struct ast *ast_create_word(char *word); /* * @brief: frees the given ast_command and sets the pointer to NULL. diff --git a/tests/unit/io_backend/io_backend.c b/tests/unit/io_backend/io_backend.c index 1fa9dfa..4449a57 100644 --- a/tests/unit/io_backend/io_backend.c +++ b/tests/unit/io_backend/io_backend.c @@ -9,27 +9,19 @@ TestSuite(IO_Backend); Test(IO_Backend, init_null) { - struct iob_context ctx = - { - .mode = IOB_MODE_NULL, - .args = NULL - }; + struct iob_context ctx = { .mode = IOB_MODE_NULL, .args = NULL }; int actual = iob_init(&ctx); -int expected = IOB_ERROR_BAD_ARG; -cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + int expected = IOB_ERROR_BAD_ARG; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); } Test(IO_Backend, init_stdin) { - struct iob_context ctx = - { - .mode = IOB_MODE_STDIN, - .args = NULL - }; + struct iob_context ctx = { .mode = IOB_MODE_STDIN, .args = NULL }; int actual = iob_init(&ctx); -int expected = 0; -cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); -iob_close(); + int expected = 0; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + iob_close(); } // WARNING: this one could fail because of iob_close in the previous test @@ -37,82 +29,62 @@ iob_close(); Test(IO_Backend, init_script) { char *script_name = "script.tmp"; - struct iob_context ctx = { - .mode = IOB_MODE_SCRIPT, - .args = script_name - }; + struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = script_name }; // Create file FILE *f = fopen(script_name, "w"); fclose(f); int actual = iob_init(&ctx); -int expected = 0; -cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); -iob_close(); -remove(script_name); + int expected = 0; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + iob_close(); + remove(script_name); } Test(IO_Backend, init_script_not_a_file) { char *script_name = "not_a_file.tmp"; - struct iob_context ctx = { - .mode = IOB_MODE_SCRIPT, - .args = script_name - }; + struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = script_name }; int actual = iob_init(&ctx); -int expected = IOB_ERROR_CANNOT_OPEN_FILE; -cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + int expected = IOB_ERROR_CANNOT_OPEN_FILE; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); } Test(IO_Backend, init_script_null) { - struct iob_context ctx = - { - .mode = IOB_MODE_SCRIPT, - .args = NULL - }; + struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = NULL }; int actual = iob_init(&ctx); -int expected = IOB_ERROR_CANNOT_OPEN_FILE; -cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + int expected = IOB_ERROR_CANNOT_OPEN_FILE; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); } Test(IO_Backend, init_cmd) { char *cmd = "iamacommand --yesido"; - struct iob_context ctx = { - .mode = IOB_MODE_CMD, - .args = cmd - }; + struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = cmd }; int actual = iob_init(&ctx); -int expected = 0; -cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); -iob_close(); + int expected = 0; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + iob_close(); } Test(IO_Backend, init_cmd_null) { - struct iob_context ctx = - { - .mode = IOB_MODE_CMD, - .args = NULL - }; + struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = NULL }; int actual = iob_init(&ctx); -int expected = IOB_ERROR_BAD_ARG; -cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + int expected = IOB_ERROR_BAD_ARG; + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); } Test(IO_Backend, init_already_init) { char *cmd = "iamacommand --yesido"; - struct iob_context ctx = { - .mode = IOB_MODE_CMD, - .args = cmd - }; + struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = cmd }; iob_init(&ctx); int actual = iob_init(&ctx); int expected = IOB_ERROR_MODULE_ALREADY_INITIALIZED; -cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); -iob_close(); + cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); + iob_close(); } Test(IO_Backend, close_not_init)