// === Includes #include "grammar.h" #include #include #include "grammar_basic.h" // === Static variables // rule-indexed array containing firsts static struct firsts_list *firsts_map = NULL; // === Static functions /* @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, enum token_type 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] == token) return true; } // Append item->list_length++; item->tokens = realloc(item->tokens, (item->list_length) * sizeof(enum token_type)); } else { // Create entry item->list_length = 1; item->tokens = calloc(1, sizeof(enum token_type)); } // Check for alloc error if (item->tokens == NULL) { item->list_length = 0; return false; } // Fill item->tokens[item->list_length - 1] = token; return true; } /* @brief Add a list of tokens to a rule's firsts (in firsts_map) * * @arg rule the rule to which add a first * @arg tokens_list the list of tokens to add to the rule's firsts * @return true on success, false on error */ static bool add_firsts(enum rule rule, struct firsts_list *tokens_list) { for (size_t i = 0; i < tokens_list->list_length; i++) { bool res = add_first(rule, tokens_list->tokens[i]); if (!res) return false; } 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) { perror("Internal error: couldn't create the firsts_map (is your memory " "full ?)"); return false; } return true; } // === Functions int grammar_init(void) { // Initialize the firsts map bool success = init_firsts_map(); if (success != true) return false; // Populate the firsts map // TODO CHECK ORDER // If add_first(RULE_IF, TOKEN_IF); // Else clause add_first(RULE_ELSE_CLAUSE, TOKEN_ELSE); add_first(RULE_ELSE_CLAUSE, TOKEN_ELIF); // For add_first(RULE_FOR, TOKEN_FOR); // While add_first(RULE_WHILE, TOKEN_WHILE); // Until add_first(RULE_WHILE, TOKEN_UNTIL); // Case add_first(RULE_CASE, TOKEN_CASE); // Case item 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)); // Redirection add_first(RULE_REDIRECTION, TOKEN_IONUMBER); add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT); add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT); add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_RIGHT); add_first(RULE_REDIRECTION, TOKEN_REDIR_DOUBLE_RIGHT); add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_AMP); add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_AMP); add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_PIPE); // %RIP Matteo 30/01/2026 // 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); add_firsts(RULE_PREFIX, first(RULE_REDIRECTION)); // Shell command add_firsts(RULE_SHELL_COMMAND, first(RULE_IF)); // Simple command add_firsts(RULE_SIMPLE_COMMAND, first(RULE_PREFIX)); add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD); // Funcdec add_first(RULE_FUNCDEC, TOKEN_WORD); // Command add_firsts(RULE_COMMAND, first(RULE_SIMPLE_COMMAND)); add_firsts(RULE_COMMAND, first(RULE_SHELL_COMMAND)); 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)); return true; } void grammar_close(void) { // 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 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"); 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) { struct token *token = PEEK_TOKEN(); if (token->type == TOKEN_EOF) { POP_TOKEN(); return ast_create_end(); } if (token->type == TOKEN_NEWLINE) { POP_TOKEN(); return ast_create_list(NULL); } struct ast *ast = parse_list(ctx); token = PEEK_TOKEN(); if (ast == NULL) { if (token != NULL && token->type == TOKEN_EOF) { POP_TOKEN(); } return NULL; } if (token->type == TOKEN_NEWLINE || token->type == TOKEN_EOF) { POP_TOKEN(); return ast; } perror("Syntax error: expected newline or EOF after list"); ast_free(&ast); return NULL; }