From 96ac2fea77cbf010e849716c60f0ea5407dfa068 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 23 Jan 2026 17:01:26 +0100 Subject: [PATCH 1/8] feat: yet another new parser architecture --- src/main.c | 4 + src/parser/grammar.c | 41 ++++++++++ src/parser/grammar.h | 65 +++++++++++++++ src/parser/grammar_advanced.c | 0 src/parser/grammar_advanced.h | 4 + .../{parsing_utils.c => grammar_basic.c} | 79 +++---------------- .../{parsing_utils.h => grammar_basic.h} | 35 ++------ src/parser/parser.c | 63 ++++++++------- src/parser/parser.h | 19 +++++ 9 files changed, 182 insertions(+), 128 deletions(-) create mode 100644 src/parser/grammar.c create mode 100644 src/parser/grammar.h create mode 100644 src/parser/grammar_advanced.c create mode 100644 src/parser/grammar_advanced.h rename src/parser/{parsing_utils.c => grammar_basic.c} (82%) rename src/parser/{parsing_utils.h => grammar_basic.h} (59%) diff --git a/src/main.c b/src/main.c index a03cfa9..7ef833c 100644 --- a/src/main.c +++ b/src/main.c @@ -73,6 +73,9 @@ int main(int argc, char **argv) // init lexer context struct lexer_context *ctx = calloc(1, sizeof(struct lexer_context)); + // init parser + int parser_init(); + // Retrieve and build first AST struct ast *command_ast = get_ast(ctx); @@ -107,6 +110,7 @@ int main(int argc, char **argv) return ERR_INPUT_PROCESSING; ast_free(&command_ast); + parser_close(); return return_code; } diff --git a/src/parser/grammar.c b/src/parser/grammar.c new file mode 100644 index 0000000..15ee342 --- /dev/null +++ b/src/parser/grammar.c @@ -0,0 +1,41 @@ +#define _POSIX_C_SOURCE 200809L + +// === Includes +#include "grammar.h" + +#include "../utils/hash_map/hash_map.h" +#include "grammar_basic.h" + +// === Static variables + +static struct hash_map *firsts_map = NULL; + +// === Static functions +static enum token_type first(enum rule r) +{ + // TODO + return TOKEN_NULL; +} + +// === Functions + +bool grammar_init(void) +{ + // Create firsts hashmap + // TODO + + // Populate the hashmap + // TODO + + return true; +} + +void grammar_close(void) +{ + // TODO free hashmap +} + +struct ast *parse_input(struct lexer_context *ctx) +{ + return parse_list(ctx); +} diff --git a/src/parser/grammar.h b/src/parser/grammar.h new file mode 100644 index 0000000..fe31849 --- /dev/null +++ b/src/parser/grammar.h @@ -0,0 +1,65 @@ +#ifndef GRAMMAR_H +#define GRAMMAR_H + +#include + +#include "../lexer/lexer.h" + +// === Macros + +#define PEEK_TOKEN() \ + peek_token(ctx); \ + if (token == NULL) \ + { \ + puts("Internal error: cannot get the following token"); \ + return NULL; \ + } + +#define POP_TOKEN() \ + pop_token(ctx); \ + if (token == NULL) \ + { \ + puts("Internal error: cannot get the following token"); \ + return NULL; \ + } + +// === Structures + +enum rule { + RULE_NULL, + RULE_INPUT, + RULE_LIST, + RULE_AND_OR, + RULE_PIPELINE, + RULE_COMMAND, + RULE_SIMPLE_COMMAND, + RULE_SHELL_COMMAND, + RULE_IF, + RULE_COMPOUND_LIST, + RULE_ELSE_CLAUSE +}; + +// === Functions + +/* @brief Initializes the grammar submodule + * @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error + * @warning Do not use outside the parser + */ + bool grammar_init(void); + +/* @brief Closes the grammar submodule + * @warning Do not use outside the parser + */ + void grammar_close(void); + +/* @brief Acts as the entry point of the parser, calls parse_list + * + * @code input = list '\n' + * | list EOF + * | '\n' + * | EOF + * ; + */ +struct ast *parse_input(struct lexer_context *ctx); + +#endif /* ! GRAMMAR_H */ diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c new file mode 100644 index 0000000..e69de29 diff --git a/src/parser/grammar_advanced.h b/src/parser/grammar_advanced.h new file mode 100644 index 0000000..8773655 --- /dev/null +++ b/src/parser/grammar_advanced.h @@ -0,0 +1,4 @@ +#ifndef GRAMMAR_ADVANCED_H +#define GRAMMAR_ADVANCED_H + +#endif /* ! GRAMMAR_ADVANCED_H */ diff --git a/src/parser/parsing_utils.c b/src/parser/grammar_basic.c similarity index 82% rename from src/parser/parsing_utils.c rename to src/parser/grammar_basic.c index 3be4f05..dcb1f1b 100644 --- a/src/parser/parsing_utils.c +++ b/src/parser/grammar_basic.c @@ -1,64 +1,11 @@ -#define _POSIX_C_SOURCE 200809L +#include "grammar_basic.h" -// === Includes -#include "parsing_utils.h" - -#include -#include #include #include #include "../lexer/lexer.h" -#include "../utils/ast/ast.h" - -// === Static functions - -/* Returns true if c is a command terminator, false otherwise -static bool isterminator(struct token *token) -{ - if (token == NULL) - return false; - - switch (token->type) - { - case TOKEN_NEWLINE: - case TOKEN_SEMICOLON: - case TOKEN_EOF: - return true; - default: - return false; - } -} - - */ - -/* @brief: returns true if token is an end of list indicator. - * @warning: not used - */ - -/* -static bool is_end_of_list(struct token *token) -{ - if (token == NULL) - return false; - - switch (token->type) - { - case TOKEN_NEWLINE: - case TOKEN_EOF: - return true; - default: - return false; - } -} -*/ - -// === Functions - -struct ast *parse_input(struct lexer_context *ctx) -{ - return parse_list(ctx); -} +#include "../utils/lists/lists.h" +#include "grammar.h" struct ast *parse_list(struct lexer_context *ctx) { @@ -78,20 +25,16 @@ struct ast *parse_list(struct lexer_context *ctx) while (token->type == TOKEN_SEMICOLON) { token = POP_TOKEN(); - // if (!isterminator(token)) // Follow(list) - // { - current_node = parse_and_or(ctx); - if (current_node == NULL) - { - // TODO free list - // There must be a function for that - return NULL; - } - result_list = list_append(result_list, current_node); - // } + current_node = parse_and_or(ctx); + if (current_node == NULL) + { + // TODO free list + // There must be a function for that + return NULL; + } + result_list = list_append(result_list, current_node); token = PEEK_TOKEN(); } - // result_list = list_append(result_list, current_node); return ast_create_list(result_list); } diff --git a/src/parser/parsing_utils.h b/src/parser/grammar_basic.h similarity index 59% rename from src/parser/parsing_utils.h rename to src/parser/grammar_basic.h index 89eb4bf..0990e1b 100644 --- a/src/parser/parsing_utils.h +++ b/src/parser/grammar_basic.h @@ -1,35 +1,10 @@ -#ifndef PARSING_UTILS_H -#define PARSING_UTILS_H +#ifndef GRAMMAR_BASIC_H +#define GRAMMAR_BASIC_H +#include "../utils/ast/ast.h" #include "../lexer/lexer.h" -// === Macros - -#define PEEK_TOKEN() \ - peek_token(ctx); \ - if (token == NULL) \ - { \ - puts("Internal error: cannot get the following token"); \ - return NULL; \ - } - -#define POP_TOKEN() \ - pop_token(ctx); \ - if (token == NULL) \ - { \ - puts("Internal error: cannot get the following token"); \ - return NULL; \ - } - -/* @brief Acts as the entry point of the parser, calls parse_list - * - * @code input = list '\n' - * | list EOF - * | '\n' - * | EOF - * ; - */ -struct ast *parse_input(struct lexer_context *ctx); +// === Functions /* @brief: parses a list of [and_or] rules separated by semicolons and that * ends by a newline @@ -97,4 +72,4 @@ struct ast *parse_compound_list(struct lexer_context *ctx); */ struct ast *parse_else_clause(struct lexer_context *ctx); -#endif /* ! PARSING_UTILS_H */ +#endif /* ! GRAMMAR_BASIC_H */ diff --git a/src/parser/parser.c b/src/parser/parser.c index dbe3f72..b86c696 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -1,49 +1,52 @@ #include "parser.h" -#include -#include #include -#include -#include -#include "../lexer/lexer.h" -#include "../parser/parsing_utils.h" -#include "../utils/lists/lists.h" +#include "grammar.h" -// === Static functions -// ... +// === Static variables + +static enum parser_state state = PARSER_STATE_NOT_INITIALIZED; // === Functions +bool parser_init(void) +{ + if (state == PARSER_STATE_READY) + { + puts("Internal error: tried to initialize the parser module twice."); + return NULL; + } + int success = grammar_init(); + if (success == false) + return false; + + state = PARSER_STATE_READY; + return true; +} + struct ast *get_ast(struct lexer_context *ctx) { - struct token *token = PEEK_TOKEN(); - struct ast *res; - - if (token->type == TOKEN_EOF) + if (ctx == NULL) { - token = pop_token(ctx); - return ast_create_end(); + puts("Internal error: called parser with no lexer context (NULL " + "pointer). Aborting."); + return NULL; } - else if (token->type == TOKEN_NEWLINE) + if (state == PARSER_STATE_NOT_INITIALIZED) { - token = pop_token(ctx); - return ast_create_void(); + puts("Internal error: attempted to call parser without initializing " + "it. Aborting."); + return NULL; } - else // TOKEN WORD + if (state == PARSER_STATE_CLOSED) { - res = parse_list(ctx); + puts("Internal error: attempted to call parser after closing it. " + "Aborting."); + return NULL; } - /* - if (token == NULL) - { - puts("Internal error: cannot get the following token"); - puts("Hint: EOF might be missing"); - return NULL; - } - */ - return res; + return parse_input(ctx); } // TODO @@ -51,4 +54,4 @@ struct ast *get_ast_str(char *command) { (void)command; return NULL; -} \ No newline at end of file +} diff --git a/src/parser/parser.h b/src/parser/parser.h index 1837e75..b51d2f5 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -1,9 +1,28 @@ #ifndef PARSER_H #define PARSER_H +#include + #include "../lexer/lexer.h" #include "../utils/ast/ast.h" +enum parser_state { + PARSER_STATE_NOT_INITIALIZED = 0, + PARSER_STATE_READY, + PARSER_STATE_CLOSED +}; + +/* @brief Initializes the parser module + * @warning parser needs to be closed after use with parser_close() + * + * @return Returns false on error and true on success + */ +bool parser_init(void); + +/* @brief Closes the parser module after use + */ +void parser_close(void); + /* @brief Builds the AST representation of the next command to execute. * * @return Returns the AST representation of the next command to execute. From 32e182bd50ccbb9e9ed4170964684fac46d81adf Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Fri, 23 Jan 2026 17:33:15 +0100 Subject: [PATCH 2/8] fix: random fixes --- src/parser/grammar_basic.c | 26 +++++++++++++++++--------- src/parser/grammar_basic.h | 2 +- src/utils/ast/ast.c | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index b90cf68..b7f74c0 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -9,7 +9,7 @@ // === Static functions -enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) +static enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) { switch (tok_type) { @@ -47,7 +47,7 @@ struct ast *parse_list(struct lexer_context *ctx) if (current_node == NULL) { // TODO free list - // There must be a function for that + // There must be a function for that return NULL; } result_list = list_append(result_list, current_node); @@ -64,29 +64,35 @@ struct ast *parse_and_or(struct lexer_context *ctx) if (token->type == TOKEN_AND || token->type == TOKEN_OR) { - // set left part + // Set left part struct ast *left = result; // eat and_or token token = POP_TOKEN(); - // set type + // Set type enum ast_and_or_type type = and_or_tok_to_ast(token->type); token = PEEK_TOKEN(); - // skip newlines + // Skip newlines while (token->type == TOKEN_NEWLINE) { token = POP_TOKEN(); token = PEEK_TOKEN(); } - // right part + // Right part struct ast *right = parse_pipeline(ctx); result = ast_create_and_or(left, right, type); + if (result == NULL) + { + ast_free(&left); + ast_free(&right); + return NULL; + } } return result; @@ -204,6 +210,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx) return NULL; } + // Fi keyword token = POP_TOKEN(); if (token->type != TOKEN_FI) { @@ -214,6 +221,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx) return NULL; } + // Result struct ast *result = ast_create_if(condition_content, then_content, else_content); if (result == NULL) @@ -241,7 +249,7 @@ struct ast *parse_compound_list(struct lexer_context *ctx) token = PEEK_TOKEN(); } - // and_or + // And/or current_cmd = parse_and_or(ctx); if (current_cmd == NULL) return NULL; @@ -261,7 +269,7 @@ struct ast *parse_compound_list(struct lexer_context *ctx) token = PEEK_TOKEN(); } - // and_or + // And/or current_cmd = parse_and_or(ctx); if (current_cmd == NULL) return NULL; @@ -299,7 +307,7 @@ struct ast *parse_else_clause(struct lexer_context *ctx) token = POP_TOKEN(); struct ast *condition = parse_compound_list(ctx); - // 'then' + // Then keyword token = POP_TOKEN(); if (token->type != TOKEN_THEN) { diff --git a/src/parser/grammar_basic.h b/src/parser/grammar_basic.h index 0990e1b..840f05d 100644 --- a/src/parser/grammar_basic.h +++ b/src/parser/grammar_basic.h @@ -15,7 +15,7 @@ struct ast *parse_list(struct lexer_context *ctx); /* @brief Only parses a pipeline rule for the moment * - * @code and_or = pipeline ; + * @code and_or = pipeline { ( '&&' | '||' ) {'\n'} pipeline } ; */ struct ast *parse_and_or(struct lexer_context *ctx); diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c index e336a73..75e1e7f 100644 --- a/src/utils/ast/ast.c +++ b/src/utils/ast/ast.c @@ -10,7 +10,7 @@ void ast_free(struct ast **node) { - if (*node == NULL) + if (node == NULL || *node == NULL) return; // ast void does not need to be freed. if (ast_is_if(*node)) From 9f22aa59b67e6346b38480e4c7c7c569f37eb4ec Mon Sep 17 00:00:00 2001 From: matteo Date: Fri, 23 Jan 2026 19:43:54 +0100 Subject: [PATCH 3/8] fix(parser): building + clang format -- UNSTABLE --- src/parser/Makefile.am | 4 +++- src/parser/grammar.h | 29 +++++++++++++++-------------- src/parser/grammar_basic.h | 2 +- src/parser/parser.h | 3 ++- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/parser/Makefile.am b/src/parser/Makefile.am index 3b7b6f5..642414b 100644 --- a/src/parser/Makefile.am +++ b/src/parser/Makefile.am @@ -2,7 +2,9 @@ lib_LIBRARIES = libparser.a libparser_a_SOURCES = \ parser.c \ - parsing_utils.c + grammar.c \ + grammar_basic.c \ + grammar_advanced.c libparser_a_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/parser/grammar.h b/src/parser/grammar.h index fe31849..cd9e888 100644 --- a/src/parser/grammar.h +++ b/src/parser/grammar.h @@ -25,18 +25,19 @@ // === Structures -enum rule { - RULE_NULL, - RULE_INPUT, - RULE_LIST, - RULE_AND_OR, - RULE_PIPELINE, - RULE_COMMAND, - RULE_SIMPLE_COMMAND, - RULE_SHELL_COMMAND, - RULE_IF, - RULE_COMPOUND_LIST, - RULE_ELSE_CLAUSE +enum rule +{ + RULE_NULL, + RULE_INPUT, + RULE_LIST, + RULE_AND_OR, + RULE_PIPELINE, + RULE_COMMAND, + RULE_SIMPLE_COMMAND, + RULE_SHELL_COMMAND, + RULE_IF, + RULE_COMPOUND_LIST, + RULE_ELSE_CLAUSE }; // === Functions @@ -45,12 +46,12 @@ enum rule { * @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error * @warning Do not use outside the parser */ - bool grammar_init(void); +bool grammar_init(void); /* @brief Closes the grammar submodule * @warning Do not use outside the parser */ - void grammar_close(void); +void grammar_close(void); /* @brief Acts as the entry point of the parser, calls parse_list * diff --git a/src/parser/grammar_basic.h b/src/parser/grammar_basic.h index 840f05d..7d9803a 100644 --- a/src/parser/grammar_basic.h +++ b/src/parser/grammar_basic.h @@ -1,8 +1,8 @@ #ifndef GRAMMAR_BASIC_H #define GRAMMAR_BASIC_H -#include "../utils/ast/ast.h" #include "../lexer/lexer.h" +#include "../utils/ast/ast.h" // === Functions diff --git a/src/parser/parser.h b/src/parser/parser.h index b51d2f5..a79497b 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -6,7 +6,8 @@ #include "../lexer/lexer.h" #include "../utils/ast/ast.h" -enum parser_state { +enum parser_state +{ PARSER_STATE_NOT_INITIALIZED = 0, PARSER_STATE_READY, PARSER_STATE_CLOSED From da1a73c264b682df98e01c13d04912c0cc06e4d2 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Sat, 24 Jan 2026 13:06:39 +0100 Subject: [PATCH 4/8] feat: made the firsts system for parser (not yet populated) --- src/parser/grammar.c | 106 +++++++++++++++++++++++++++++++++---- src/parser/grammar.h | 28 ++++++---- src/parser/grammar_basic.h | 17 ++++++ 3 files changed, 129 insertions(+), 22 deletions(-) diff --git a/src/parser/grammar.c b/src/parser/grammar.c index 15ee342..3fbb5fd 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -1,30 +1,105 @@ -#define _POSIX_C_SOURCE 200809L - // === Includes #include "grammar.h" -#include "../utils/hash_map/hash_map.h" +#include +#include + #include "grammar_basic.h" // === Static variables -static struct hash_map *firsts_map = NULL; +// rule-indexed array containing firsts +static struct firsts_list *firsts_map = NULL; // === Static functions -static enum token_type first(enum rule r) + +/* @brief get the first accepted tokens of a rule + * + * @arg r the rule + * @return the accepted tokens as a firsts_list struct + */ +static struct firsts_list *first(enum rule r) { - // TODO - return TOKEN_NULL; + if (firsts_map == NULL || firsts_map[r].tokens == NULL) + { + puts("Internal error: attempted to get the firsts of a rule without " + "properly initializing the firsts map"); + return NULL; + } + + return &firsts_map[r]; +} + +/* @brief Add a token to a rule's firsts (in firsts_map) + * + * @arg rule the rule to which add a first + * @arg token the token to add to the rule's firsts + * @return true on success, false on error + */ +static bool add_first(enum rule rule, struct token token) +{ + struct firsts_list *item = &firsts_map[rule]; + if (item->tokens != NULL) + { + // Check for duplicates + for (size_t i = 0; i < item->list_length; i++) + { + if (item->tokens[i].type == token.type) + return true; + } + + // Append + item->list_length++; + item->tokens = realloc( + item->tokens, (item->list_length) * sizeof(struct firsts_list)); + } + else + { + // Create entry + item->tokens = + calloc(item->list_length + 1, sizeof(struct firsts_list)); + } + + // Check for alloc error + if (item->tokens == NULL) + { + item->list_length = 0; + return false; + } + + // Fill + item->list_length++; + item->tokens[item->list_length - 1] = token; + + return true; +} + +/* @brief initializes the firsts_map static variable (does not populate it) + * @return true on success, false on error + */ +static bool init_firsts_map(void) +{ + firsts_map = calloc(NUMBER_OF_RULES, sizeof(struct firsts_list)); + if (firsts_map == NULL) + { + puts("Internal error: couldn't create the firsts_map (is your memory " + "full ?)"); + return false; + } + + return true; } // === Functions bool grammar_init(void) { - // Create firsts hashmap - // TODO + // Initialize the firsts map + bool success = init_firsts_map(); + if (success != true) + return false; - // Populate the hashmap + // Populate the firsts map // TODO return true; @@ -32,7 +107,16 @@ bool grammar_init(void) void grammar_close(void) { - // TODO free hashmap + // Deep free firsts map + for (int i = 0; i < NUMBER_OF_RULES; i++) + { + if (firsts_map[i].tokens != NULL) + { + free(firsts_map[i].tokens); + } + } + free(firsts_map); + firsts_map = NULL; } struct ast *parse_input(struct lexer_context *ctx) diff --git a/src/parser/grammar.h b/src/parser/grammar.h index fe31849..d2c1551 100644 --- a/src/parser/grammar.h +++ b/src/parser/grammar.h @@ -26,17 +26,23 @@ // === Structures enum rule { - RULE_NULL, - RULE_INPUT, - RULE_LIST, - RULE_AND_OR, - RULE_PIPELINE, - RULE_COMMAND, - RULE_SIMPLE_COMMAND, - RULE_SHELL_COMMAND, - RULE_IF, - RULE_COMPOUND_LIST, - RULE_ELSE_CLAUSE + RULE_NULL = 0, + RULE_INPUT, + RULE_LIST, + RULE_AND_OR, + RULE_PIPELINE, + RULE_COMMAND, + RULE_SIMPLE_COMMAND, + RULE_SHELL_COMMAND, + RULE_IF, + RULE_COMPOUND_LIST, + RULE_ELSE_CLAUSE, + NUMBER_OF_RULES +}; + +struct firsts_list { + struct token* tokens; // Heap allocated array + size_t list_length; }; // === Functions diff --git a/src/parser/grammar_basic.h b/src/parser/grammar_basic.h index 840f05d..ba3c817 100644 --- a/src/parser/grammar_basic.h +++ b/src/parser/grammar_basic.h @@ -10,18 +10,24 @@ * ends by a newline * * @code list = and_or { ';' and_or } [ ';' ] ; + * + * @first first(and_or) */ struct ast *parse_list(struct lexer_context *ctx); /* @brief Only parses a pipeline rule for the moment * * @code and_or = pipeline { ( '&&' | '||' ) {'\n'} pipeline } ; + * + * @first first(pipeline) */ struct ast *parse_and_or(struct lexer_context *ctx); /* @brief Only parses a command rule for the moment * * @code pipeline = command ; + * + * @first first(command) */ struct ast *parse_pipeline(struct lexer_context *ctx); @@ -34,6 +40,7 @@ struct ast *parse_pipeline(struct lexer_context *ctx); * @code command = simple_command * | shell_command * ; + * @first first(simple_command), first(shell_command) */ struct ast *parse_command(struct lexer_context *ctx); @@ -41,18 +48,24 @@ struct ast *parse_command(struct lexer_context *ctx); * ending by a separator * * @code simple_command = WORD { element } ; + * + * @first WORD */ struct ast *parse_simple_command(struct lexer_context *ctx); /* @brief Only parses if rules for the moment * * @code shell_command = if_rule ; + * + * @first first(if_rule) */ struct ast *parse_shell_command(struct lexer_context *ctx); /* @brief Parses a if rule (condition, then-clause, elif-clause, else-clause) * * @code if_rule = 'if' compound_list 'then' compound_list [else_clause] 'fi' ; + * + * @first TOKEN_IF */ struct ast *parse_if_rule(struct lexer_context *ctx); @@ -61,6 +74,8 @@ struct ast *parse_if_rule(struct lexer_context *ctx); * * @code compound_list = {'\n'} and_or { ( ';' | '\n' ) {'\n'} and_or } [';'] * {'\n'} ; + * + * @first TOKEN_NEWLINE, first(and_or) */ struct ast *parse_compound_list(struct lexer_context *ctx); @@ -69,6 +84,8 @@ struct ast *parse_compound_list(struct lexer_context *ctx); * @code else_clause = 'else' compound_list * | 'elif' compound_list 'then' compound_list [else_clause] * ; + * + * @first TOKEN_ELSE, TOKEN_ELIF */ struct ast *parse_else_clause(struct lexer_context *ctx); From 32f56beb6bff65750700c14410901d3dd003d9ef Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Sat, 24 Jan 2026 15:34:10 +0100 Subject: [PATCH 5/8] feat: redirection rules --- src/parser/grammar_advanced.c | 28 ++++++++++++++ src/parser/grammar_advanced.h | 23 ++++++++++++ src/parser/grammar_basic.c | 70 +++++++++++++++++++++++++++-------- src/parser/grammar_basic.h | 38 ++++++++++++++----- src/utils/ast/ast.h | 1 + src/utils/ast/ast_base.h | 3 +- src/utils/ast/ast_word.c | 45 ++++++++++++++++++++++ src/utils/ast/ast_word.h | 35 ++++++++++++++++++ 8 files changed, 218 insertions(+), 25 deletions(-) create mode 100644 src/utils/ast/ast_word.c create mode 100644 src/utils/ast/ast_word.h diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c index e69de29..0f29b9b 100644 --- a/src/parser/grammar_advanced.c +++ b/src/parser/grammar_advanced.c @@ -0,0 +1,28 @@ +#include "grammar_advanced.h" + +#include + +#include "grammar_basic.h" + +struct ast *parse_redirection(struct lexer_context *ctx) +{ + struct token *token = PEEK_TOKEN(); + if (token->type == TOKEN_IONUMBER) + { + // TODO + POP_TOKEN(); + token = PEEK_TOKEN(); + } + + if (token->type != TOKEN_REDIRECTION) + { + puts("Syntax error: expected a redirection token but got something " + "else"); + return NULL; + } +} + +struct ast *parse_prefix(struct lexer_context *ctx) +{ + return parse_redirection(ctx); +} diff --git a/src/parser/grammar_advanced.h b/src/parser/grammar_advanced.h index 8773655..72b5a43 100644 --- a/src/parser/grammar_advanced.h +++ b/src/parser/grammar_advanced.h @@ -1,4 +1,27 @@ #ifndef GRAMMAR_ADVANCED_H #define GRAMMAR_ADVANCED_H +#include "grammar.h" + +// === Functions + +/* + * @brief parses a redirection rule + * + * @code redirection = [IONUMBER] ( '>' | '<' | '>>' | '>&' | '<&' | '>|' | '<>' ) WORD ; + * + * @first TOKEN_IONUMBER, TOKEN_REDIRECTION + */ +struct ast *parse_redirection(struct lexer_context *ctx); + +/* + * @brief parses a prefix rule + * + * @code prefix = redirection ; + * + * @first first(redirection) + */ +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 b7f74c0..efa105d 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -3,9 +3,9 @@ #include #include -#include "../lexer/lexer.h" #include "../utils/lists/lists.h" #include "grammar.h" +#include "grammar_advanced.h" // === Static functions @@ -124,46 +124,86 @@ struct ast *parse_command(struct lexer_context *ctx) struct ast *parse_simple_command(struct lexer_context *ctx) { struct list *command_elements = NULL; - struct token *token = PEEK_TOKEN(); + + // WORD + struct token *token = POP_TOKEN(); if (token->type != TOKEN_WORD) { puts("Expected a command but got a different token type"); return NULL; } + char *command = strdup(token->data); + command_elements = list_append(command_elements, command); + token = PEEK_TOKEN(); + + // Eventual elements while (token->type == TOKEN_WORD) { - token = pop_token(ctx); - if (token == NULL) + // Get element + struct ast *element = parse_element(ctx); + if (element == NULL) { - // TODO free + list_deep_destroy(command_elements); return NULL; } - char *word = strdup(token->data); - if (word == NULL) + + // Get element type + if (ast_is_word(element)) { - // TODO free - puts("Internal error: Couldn't copy token content (is memory full " - "?)"); + struct ast_word *element_word = ast_get_word(element); + command_elements = + list_append(command_elements, element_word->word); + } + else if (ast_is_redir(element)) + { + // TODO + puts("NOT IMPLEMENTED"); return NULL; } - command_elements = list_append(command_elements, word); - token = peek_token(ctx); - if (token == NULL) + else { - // TODO free + puts("Internal error: unexpected return value from parse_element " + "in parse_simple_command"); + list_deep_destroy(command_elements); return NULL; } + + // Forward + POP_TOKEN(); + token = PEEK_TOKEN(); } + // Result struct ast *result = ast_create_command(command_elements); if (result == NULL) { - // TODO free + list_deep_destroy(command_elements); + return NULL; } return result; } +struct ast *parse_element(struct lexer_context *ctx) +{ + struct token *token = PEEK_TOKEN(); + if (token->type == TOKEN_WORD) + { + // TODO + puts("NOT IMPLEMENTED"); + return NULL; + } + else if (token->type == TOKEN_IONUMBER || token->type == TOKEN_REDIRECTION) + { + return parse_redirection(ctx); + } + else + { + puts("Syntax error: unexpected token at parse_element"); + return NULL; + } +} + struct ast *parse_shell_command(struct lexer_context *ctx) { return parse_if_rule(ctx); diff --git a/src/parser/grammar_basic.h b/src/parser/grammar_basic.h index cb49172..f1c2f77 100644 --- a/src/parser/grammar_basic.h +++ b/src/parser/grammar_basic.h @@ -6,7 +6,8 @@ // === Functions -/* @brief: parses a list of [and_or] rules separated by semicolons and that +/* + * @brief parses a list of [and_or] rules separated by semicolons and that * ends by a newline * * @code list = and_or { ';' and_or } [ ';' ] ; @@ -15,7 +16,8 @@ */ struct ast *parse_list(struct lexer_context *ctx); -/* @brief Only parses a pipeline rule for the moment +/* + * @brief Only parses a pipeline rule for the moment * * @code and_or = pipeline { ( '&&' | '||' ) {'\n'} pipeline } ; * @@ -23,7 +25,8 @@ struct ast *parse_list(struct lexer_context *ctx); */ struct ast *parse_and_or(struct lexer_context *ctx); -/* @brief Only parses a command rule for the moment +/* + * @brief Only parses a command rule for the moment * * @code pipeline = command ; * @@ -31,7 +34,8 @@ struct ast *parse_and_or(struct lexer_context *ctx); */ struct ast *parse_pipeline(struct lexer_context *ctx); -/* @brief Parses a simple command rule or a shell command rule depending on +/* + * @brief Parses a simple command rule or a shell command rule depending on * the first token. * @note * TOKEN_WORD => simple_command @@ -44,7 +48,8 @@ struct ast *parse_pipeline(struct lexer_context *ctx); */ struct ast *parse_command(struct lexer_context *ctx); -/* @brief Parses a simple list of words (command and arguments) +/* + * @brief Parses a simple list of words (command and arguments) * ending by a separator * * @code simple_command = WORD { element } ; @@ -53,7 +58,19 @@ struct ast *parse_command(struct lexer_context *ctx); */ struct ast *parse_simple_command(struct lexer_context *ctx); -/* @brief Only parses if rules for the moment +/* + * @brief Parses an element rule + * + * @code element = WORD + * | redirection + * ; + * + * @first WORD, first(redirection) + */ +struct ast *parse_element(struct lexer_context *ctx); + +/* + * @brief Only parses if rules for the moment * * @code shell_command = if_rule ; * @@ -61,7 +78,8 @@ struct ast *parse_simple_command(struct lexer_context *ctx); */ struct ast *parse_shell_command(struct lexer_context *ctx); -/* @brief Parses a if rule (condition, then-clause, elif-clause, else-clause) +/* + * @brief Parses a if rule (condition, then-clause, elif-clause, else-clause) * * @code if_rule = 'if' compound_list 'then' compound_list [else_clause] 'fi' ; * @@ -69,7 +87,8 @@ struct ast *parse_shell_command(struct lexer_context *ctx); */ struct ast *parse_if_rule(struct lexer_context *ctx); -/* @brief parses commands inside if/else clauses and returns the corresponding +/* + * @brief parses commands inside if/else clauses and returns the corresponding * AST list * * @code compound_list = {'\n'} and_or { ( ';' | '\n' ) {'\n'} and_or } [';'] @@ -79,7 +98,8 @@ struct ast *parse_if_rule(struct lexer_context *ctx); */ struct ast *parse_compound_list(struct lexer_context *ctx); -/* @brief +/* + * @brief * * @code else_clause = 'else' compound_list * | 'elif' compound_list 'then' compound_list [else_clause] diff --git a/src/utils/ast/ast.h b/src/utils/ast/ast.h index a98d88b..fc35666 100644 --- a/src/utils/ast/ast.h +++ b/src/utils/ast/ast.h @@ -9,6 +9,7 @@ #include "ast_list.h" #include "ast_redir.h" #include "ast_void.h" +#include "ast_word.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 e09b25d..4ae00ec 100644 --- a/src/utils/ast/ast_base.h +++ b/src/utils/ast/ast_base.h @@ -11,7 +11,8 @@ enum ast_type AST_AND_OR, AST_REDIR, AST_VOID, - AST_CMD + AST_CMD, + AST_WORD }; struct ast diff --git a/src/utils/ast/ast_word.c b/src/utils/ast/ast_word.c new file mode 100644 index 0000000..d798c77 --- /dev/null +++ b/src/utils/ast/ast_word.c @@ -0,0 +1,45 @@ +#include "ast_word.h" + +#include +#include +#include + +struct ast *ast_create_word(char *word) +{ + struct ast_word *ast_node = malloc(sizeof(struct ast_word)); + if (ast_node == NULL) + return NULL; + + ast_node->type = AST_WORD; + ast_node->word = strdup(word); + struct ast *res = ast_create(AST_WORD, ast_node); + if (res == NULL) + { + free(ast_node); + return NULL; + } + + return res; +} + +struct ast_word *ast_get_word(struct ast *node) +{ + if (node == NULL || node->type != AST_WORD) + return NULL; + + return node->data; +} + +bool ast_is_word(struct ast *node) +{ + return node && node->type == AST_WORD; +} + +void ast_free_word(struct ast_word *ast_node) +{ + if (ast_node == NULL) + return; + + free(ast_node->word); + free(ast_node); +} diff --git a/src/utils/ast/ast_word.h b/src/utils/ast/ast_word.h new file mode 100644 index 0000000..d5e0e1d --- /dev/null +++ b/src/utils/ast/ast_word.h @@ -0,0 +1,35 @@ +#ifndef AST_WORD_H +#define AST_WORD_H + +#include + +#include "ast_base.h" + +struct ast_word +{ + enum ast_type type; + char *word; +}; + +/** + * Checks if the given AST node is a command. + */ +bool ast_is_word(struct ast *node); + +/** + * Retrieves the command data from the given AST node. + * Assumes that the node is of type AST_CMD. + */ +struct ast_word *ast_get_word(struct ast *node); + +/** + * Creates a new AST node representing a command. + */ +struct ast *ast_create_word(char* word); + +/* + * @brief: frees the given ast_command and sets the pointer to NULL. + */ +void ast_free_word(struct ast_word *ast_node); + +#endif /* ! AST_WORD_H */ From 3ee4a0b9ca90117a4613ab79e38527b77351a4cb Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Sat, 24 Jan 2026 16:13:16 +0100 Subject: [PATCH 6/8] feat: finished the new firsts system and began supporting redirections --- src/parser/grammar.c | 91 ++++++++++++++++++++++++++--------- src/parser/grammar.h | 28 +++++++++-- src/parser/grammar_advanced.c | 22 +++++++++ src/parser/grammar_basic.c | 37 +++++++------- 4 files changed, 134 insertions(+), 44 deletions(-) diff --git a/src/parser/grammar.c b/src/parser/grammar.c index 3fbb5fd..982a744 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -13,30 +13,13 @@ static struct firsts_list *firsts_map = NULL; // === Static functions -/* @brief get the first accepted tokens of a rule - * - * @arg r the rule - * @return the accepted tokens as a firsts_list struct - */ -static struct firsts_list *first(enum rule r) -{ - if (firsts_map == NULL || firsts_map[r].tokens == NULL) - { - puts("Internal error: attempted to get the firsts of a rule without " - "properly initializing the firsts map"); - return NULL; - } - - return &firsts_map[r]; -} - /* @brief Add a token to a rule's firsts (in firsts_map) * * @arg rule the rule to which add a first * @arg token the token to add to the rule's firsts * @return true on success, false on error */ -static bool add_first(enum rule rule, struct token token) +static bool add_first(enum rule rule, enum token_type token) { struct firsts_list *item = &firsts_map[rule]; if (item->tokens != NULL) @@ -44,20 +27,20 @@ static bool add_first(enum rule rule, struct token token) // Check for duplicates for (size_t i = 0; i < item->list_length; i++) { - if (item->tokens[i].type == token.type) + if (item->tokens[i] == token) return true; } // Append item->list_length++; item->tokens = realloc( - item->tokens, (item->list_length) * sizeof(struct firsts_list)); + item->tokens, (item->list_length) * sizeof(enum token_type)); } else { // Create entry - item->tokens = - calloc(item->list_length + 1, sizeof(struct firsts_list)); + item->list_length = 1; + item->tokens = calloc(1, sizeof(enum token_type)); } // Check for alloc error @@ -100,7 +83,45 @@ bool grammar_init(void) return false; // Populate the firsts map - // TODO + add_first(RULE_INPUT, TOKEN_WORD); + add_first(RULE_INPUT, TOKEN_IF); + add_first(RULE_INPUT, TOKEN_NEWLINE); + add_first(RULE_INPUT, TOKEN_EOF); + + add_first(RULE_LIST, TOKEN_WORD); + add_first(RULE_LIST, TOKEN_IF); + + add_first(RULE_AND_OR, TOKEN_WORD); + add_first(RULE_AND_OR, TOKEN_IF); + + add_first(RULE_PIPELINE, TOKEN_WORD); + add_first(RULE_PIPELINE, TOKEN_IF); + + add_first(RULE_COMMAND, TOKEN_WORD); + add_first(RULE_COMMAND, TOKEN_IF); + + add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD); + + add_first(RULE_SHELL_COMMAND, TOKEN_IF); + + add_first(RULE_IF, TOKEN_IF); + + add_first(RULE_COMPOUND_LIST, TOKEN_NEWLINE); + add_first(RULE_COMPOUND_LIST, TOKEN_WORD); + add_first(RULE_COMPOUND_LIST, TOKEN_IF); + + add_first(RULE_ELSE_CLAUSE, TOKEN_ELSE); + add_first(RULE_ELSE_CLAUSE, TOKEN_ELIF); + + add_first(RULE_ELEMENT, TOKEN_WORD); + add_first(RULE_ELEMENT, TOKEN_IONUMBER); + add_first(RULE_ELEMENT, TOKEN_REDIRECTION); + + add_first(RULE_REDIRECTION, TOKEN_IONUMBER); + add_first(RULE_REDIRECTION, TOKEN_REDIRECTION); + + add_first(RULE_PREFIX, TOKEN_IONUMBER); + add_first(RULE_PREFIX, TOKEN_REDIRECTION); return true; } @@ -119,6 +140,30 @@ void grammar_close(void) firsts_map = NULL; } +struct firsts_list *first(enum rule rule) +{ + if (firsts_map == NULL || firsts_map[rule].tokens == NULL) + { + puts("Internal error: attempted to get the firsts of a rule without " + "properly initializing the firsts map"); + return NULL; + } + + return &firsts_map[rule]; +} + +bool is_first(struct token token, enum rule rule) +{ + struct firsts_list *firsts = &firsts_map[rule]; + for (size_t i = 0; i < firsts->list_length; i++) + { + if (firsts->tokens[i] == token.type) + return true; + } + + return false; +} + struct ast *parse_input(struct lexer_context *ctx) { return parse_list(ctx); diff --git a/src/parser/grammar.h b/src/parser/grammar.h index a5ec6e2..5721ee7 100644 --- a/src/parser/grammar.h +++ b/src/parser/grammar.h @@ -37,27 +37,49 @@ enum rule { RULE_IF, RULE_COMPOUND_LIST, RULE_ELSE_CLAUSE, + RULE_ELEMENT, + RULE_REDIRECTION, + RULE_PREFIX, NUMBER_OF_RULES }; struct firsts_list { - struct token* tokens; // Heap allocated array + enum token_type* tokens; // Heap allocated array size_t list_length; }; // === Functions -/* @brief Initializes the grammar submodule +/* + * @brief Initializes the grammar submodule * @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error * @warning Do not use outside the parser */ bool grammar_init(void); -/* @brief Closes the grammar submodule +/* + * @brief Closes the grammar submodule * @warning Do not use outside the parser */ void grammar_close(void); +/* + * @brief get the first accepted tokens of a rule + * + * @arg r the rule + * @return the accepted tokens as a firsts_list struct + */ +struct firsts_list *first(enum rule r); + +/* + * @brief tells is token belong to the firsts of a specific rule + * + * @arg token + * @arg rule + * @return true if token belongs to rule's firsts, false otherwise + */ +bool is_first(struct token token, enum rule rule); + /* @brief Acts as the entry point of the parser, calls parse_list * * @code input = list '\n' diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c index 0f29b9b..1608d5c 100644 --- a/src/parser/grammar_advanced.c +++ b/src/parser/grammar_advanced.c @@ -1,6 +1,8 @@ #include "grammar_advanced.h" #include +#include +#include #include "grammar_basic.h" @@ -10,6 +12,12 @@ struct ast *parse_redirection(struct lexer_context *ctx) if (token->type == TOKEN_IONUMBER) { // TODO + } + + int io_number = -1; + if (token->type == TOKEN_IONUMBER) + { + io_number = atoi(token->data); POP_TOKEN(); token = PEEK_TOKEN(); } @@ -20,6 +28,20 @@ struct ast *parse_redirection(struct lexer_context *ctx) "else"); return NULL; } + char *redir_op = strdup(token->data); + POP_TOKEN(); + + token = PEEK_TOKEN(); + if (token->type != TOKEN_WORD) + { + puts("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_op, target); } struct ast *parse_prefix(struct lexer_context *ctx) diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index efa105d..78b3b3d 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -43,15 +43,15 @@ struct ast *parse_list(struct lexer_context *ctx) while (token->type == TOKEN_SEMICOLON) { token = POP_TOKEN(); - current_node = parse_and_or(ctx); - if (current_node == NULL) - { - // TODO free list - // There must be a function for that - return NULL; - } - result_list = list_append(result_list, current_node); token = PEEK_TOKEN(); + if (is_first(*token, RULE_AND_OR)) + { + current_node = parse_and_or(ctx); + if (current_node == NULL) + return NULL; + result_list = list_append(result_list, current_node); + token = PEEK_TOKEN(); + } } return ast_create_list(result_list); @@ -138,7 +138,7 @@ struct ast *parse_simple_command(struct lexer_context *ctx) token = PEEK_TOKEN(); // Eventual elements - while (token->type == TOKEN_WORD) + while (is_first(*token, RULE_ELEMENT)) { // Get element struct ast *element = parse_element(ctx); @@ -170,7 +170,6 @@ struct ast *parse_simple_command(struct lexer_context *ctx) } // Forward - POP_TOKEN(); token = PEEK_TOKEN(); } @@ -189,9 +188,8 @@ struct ast *parse_element(struct lexer_context *ctx) struct token *token = PEEK_TOKEN(); if (token->type == TOKEN_WORD) { - // TODO - puts("NOT IMPLEMENTED"); - return NULL; + token = POP_TOKEN(); + return ast_create_word(token->data); } else if (token->type == TOKEN_IONUMBER || token->type == TOKEN_REDIRECTION) { @@ -310,11 +308,14 @@ struct ast *parse_compound_list(struct lexer_context *ctx) } // And/or - current_cmd = parse_and_or(ctx); - if (current_cmd == NULL) - return NULL; - result_list = list_append(result_list, current_cmd); - + if (is_first(*token, RULE_AND_OR)) + { + current_cmd = parse_and_or(ctx); + if (current_cmd == NULL) + return NULL; + result_list = list_append(result_list, current_cmd); + } + token = PEEK_TOKEN(); } From 787b1aed35ef083ab2da5d260c001f89d92de190 Mon Sep 17 00:00:00 2001 From: "Gu://em_" Date: Sat, 24 Jan 2026 16:48:21 +0100 Subject: [PATCH 7/8] fix: random fixes --- src/parser/grammar.c | 27 ++++++++++++++++++++++++++- src/parser/grammar_advanced.c | 5 ----- src/parser/grammar_basic.c | 11 +++++++++-- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/parser/grammar.c b/src/parser/grammar.c index 982a744..be26dfc 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -166,5 +166,30 @@ bool is_first(struct token token, enum rule rule) struct ast *parse_input(struct lexer_context *ctx) { - return parse_list(ctx); + struct token *token = PEEK_TOKEN(); + + if (token->type == TOKEN_EOF) + return ast_create_list(NULL); + + if (token->type == TOKEN_NEWLINE) + { + POP_TOKEN(); + return ast_create_list(NULL); + } + + struct ast *ast = parse_list(ctx); + if (ast == NULL) + return NULL; + + token = PEEK_TOKEN(); + if (token->type == TOKEN_NEWLINE || token->type == TOKEN_EOF) + { + if (token->type == TOKEN_NEWLINE) + POP_TOKEN(); + return ast; + } + + puts("Syntax error: expected newline or EOF after list"); + ast_free(&ast); + return NULL; } diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c index 1608d5c..aac5742 100644 --- a/src/parser/grammar_advanced.c +++ b/src/parser/grammar_advanced.c @@ -9,11 +9,6 @@ struct ast *parse_redirection(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); - if (token->type == TOKEN_IONUMBER) - { - // TODO - } - int io_number = -1; if (token->type == TOKEN_IONUMBER) { diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index 78b3b3d..a0e0868 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -48,7 +48,11 @@ struct ast *parse_list(struct lexer_context *ctx) { current_node = parse_and_or(ctx); if (current_node == NULL) + { + struct ast *tmp = ast_create_list(result_list); + ast_free(&tmp); return NULL; + } result_list = list_append(result_list, current_node); token = PEEK_TOKEN(); } @@ -60,9 +64,11 @@ struct ast *parse_list(struct lexer_context *ctx) struct ast *parse_and_or(struct lexer_context *ctx) { struct ast *result = parse_pipeline(ctx); + if (result == NULL) + return NULL; struct token *token = PEEK_TOKEN(); - if (token->type == TOKEN_AND || token->type == TOKEN_OR) + while (token->type == TOKEN_AND || token->type == TOKEN_OR) { // Set left part @@ -117,7 +123,8 @@ struct ast *parse_command(struct lexer_context *ctx) } else { - return ast_create_void(); // TODO not sure what to do + puts("Syntax error: expected command"); + return NULL; } } From 23f681516254350a281ace91fe478d71d23704a3 Mon Sep 17 00:00:00 2001 From: Matteo Flebus Date: Mon, 26 Jan 2026 18:35:48 +0100 Subject: [PATCH 8/8] feat(parser): redirections types handled in grammar init --- src/parser/grammar.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/parser/grammar.c b/src/parser/grammar.c index be26dfc..02098e0 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -73,6 +73,20 @@ static bool init_firsts_map(void) return true; } +/* @brief: add all the redirection token_types to the first of [rule]. + * this also contains IONUMBER + */ +static void add_first_redir(enum rule rule) +{ + add_first(rule, TOKEN_IONUMBER); + add_first(rule, TOKEN_REDIR_LEFT); + add_first(rule, TOKEN_REDIR_RIGHT); + add_first(rule, TOKEN_REDIR_LEFT_RIGHT); + add_first(rule, TOKEN_REDIR_DOUBLE_RIGHT); + add_first(rule, TOKEN_REDIR_LEFT_AMP); + add_first(rule, TOKEN_REDIR_RIGHT_PIPE); +} + // === Functions bool grammar_init(void) @@ -114,14 +128,11 @@ bool grammar_init(void) add_first(RULE_ELSE_CLAUSE, TOKEN_ELIF); add_first(RULE_ELEMENT, TOKEN_WORD); - add_first(RULE_ELEMENT, TOKEN_IONUMBER); - add_first(RULE_ELEMENT, TOKEN_REDIRECTION); + add_first_redir(RULE_ELEMENT); - add_first(RULE_REDIRECTION, TOKEN_IONUMBER); - add_first(RULE_REDIRECTION, TOKEN_REDIRECTION); + add_first_redir(RULE_REDIRECTION); - add_first(RULE_PREFIX, TOKEN_IONUMBER); - add_first(RULE_PREFIX, TOKEN_REDIRECTION); + add_first_redir(RULE_PREFIX); return true; }