// === Includes #include "grammar.h" #include #include #include "../lexer/lexer.h" #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->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; } /* @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_AMP); add_first(rule, TOKEN_REDIR_RIGHT_PIPE); } // === Functions bool grammar_init(void) { // Initialize the firsts map bool success = init_firsts_map(); if (success != true) return false; // Populate the firsts map add_first(RULE_INPUT, TOKEN_WORD); add_first(RULE_INPUT, TOKEN_IF); add_first(RULE_COMMAND, TOKEN_NEGATION); 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_COMMAND, TOKEN_NEGATION); add_first(RULE_AND_OR, TOKEN_WORD); add_first(RULE_AND_OR, TOKEN_IF); add_first(RULE_COMMAND, TOKEN_NEGATION); add_first(RULE_PIPELINE, TOKEN_WORD); add_first(RULE_PIPELINE, TOKEN_IF); add_first(RULE_COMMAND, TOKEN_NEGATION); 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_redir(RULE_ELEMENT); add_first_redir(RULE_REDIRECTION); add_first_redir(RULE_PREFIX); 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) { 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) { 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; }