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);