diff --git a/src/main.c b/src/main.c index f1d0c00..e548360 100644 --- a/src/main.c +++ b/src/main.c @@ -31,6 +31,9 @@ static int main_loop(struct lexer_context *ctx, struct args_options *options, struct hash_map *vars) { int return_code = SUCCESS; + // init parser + int parser_init(); + // Retrieve and build first AST struct ast *command_ast = get_ast(ctx); @@ -61,6 +64,7 @@ static int main_loop(struct lexer_context *ctx, struct args_options *options, return err_input(&vars); ast_free(&command_ast); + parser_close(); return return_code; } 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.c b/src/parser/grammar.c new file mode 100644 index 0000000..02098e0 --- /dev/null +++ b/src/parser/grammar.c @@ -0,0 +1,206 @@ +// === 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->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_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_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_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; +} diff --git a/src/parser/grammar.h b/src/parser/grammar.h new file mode 100644 index 0000000..5721ee7 --- /dev/null +++ b/src/parser/grammar.h @@ -0,0 +1,93 @@ +#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 = 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, + RULE_ELEMENT, + RULE_REDIRECTION, + RULE_PREFIX, + NUMBER_OF_RULES +}; + +struct firsts_list { + enum token_type* tokens; // Heap allocated array + size_t list_length; +}; + +// === 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 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' + * | 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..aac5742 --- /dev/null +++ b/src/parser/grammar_advanced.c @@ -0,0 +1,45 @@ +#include "grammar_advanced.h" + +#include +#include +#include + +#include "grammar_basic.h" + +struct ast *parse_redirection(struct lexer_context *ctx) +{ + struct token *token = PEEK_TOKEN(); + int io_number = -1; + if (token->type == TOKEN_IONUMBER) + { + io_number = atoi(token->data); + POP_TOKEN(); + token = PEEK_TOKEN(); + } + + if (token->type != TOKEN_REDIRECTION) + { + puts("Syntax error: expected a redirection token but got something " + "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) +{ + return parse_redirection(ctx); +} diff --git a/src/parser/grammar_advanced.h b/src/parser/grammar_advanced.h new file mode 100644 index 0000000..72b5a43 --- /dev/null +++ b/src/parser/grammar_advanced.h @@ -0,0 +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/parsing_utils.c b/src/parser/grammar_basic.c similarity index 71% rename from src/parser/parsing_utils.c rename to src/parser/grammar_basic.c index 2daeb3d..a0e0868 100644 --- a/src/parser/parsing_utils.c +++ b/src/parser/grammar_basic.c @@ -1,19 +1,15 @@ -#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" +#include "../utils/lists/lists.h" +#include "grammar.h" +#include "grammar_advanced.h" // === 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) { @@ -27,53 +23,8 @@ enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) } } -/* 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); -} - struct ast *parse_list(struct lexer_context *ctx) { struct list *result_list = NULL; @@ -92,20 +43,20 @@ 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); - // } token = PEEK_TOKEN(); + if (is_first(*token, RULE_AND_OR)) + { + 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(); + } } - // result_list = list_append(result_list, current_node); return ast_create_list(result_list); } @@ -113,33 +64,41 @@ 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 + // 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; @@ -164,53 +123,92 @@ 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; } } 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); - while (token->type == TOKEN_WORD) + token = PEEK_TOKEN(); + + // Eventual elements + while (is_first(*token, RULE_ELEMENT)) { - 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 + 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) + { + token = POP_TOKEN(); + return ast_create_word(token->data); + } + 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); @@ -257,6 +255,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx) return NULL; } + // Fi keyword token = POP_TOKEN(); if (token->type != TOKEN_FI) { @@ -267,6 +266,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) @@ -294,7 +294,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; @@ -314,12 +314,15 @@ struct ast *parse_compound_list(struct lexer_context *ctx) token = PEEK_TOKEN(); } - // and_or - current_cmd = parse_and_or(ctx); - if (current_cmd == NULL) - return NULL; - result_list = list_append(result_list, current_cmd); - + // And/or + 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(); } @@ -352,7 +355,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 new file mode 100644 index 0000000..f1c2f77 --- /dev/null +++ b/src/parser/grammar_basic.h @@ -0,0 +1,112 @@ +#ifndef GRAMMAR_BASIC_H +#define GRAMMAR_BASIC_H + +#include "../lexer/lexer.h" +#include "../utils/ast/ast.h" + +// === Functions + +/* + * @brief parses a list of [and_or] rules separated by semicolons and that + * 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); + +/* + * @brief Parses a simple command rule or a shell command rule depending on + * the first token. + * @note + * TOKEN_WORD => simple_command + * TOKEN_IF => shell_command + * + * @code command = simple_command + * | shell_command + * ; + * @first first(simple_command), first(shell_command) + */ +struct ast *parse_command(struct lexer_context *ctx); + +/* + * @brief Parses a simple list of words (command and arguments) + * ending by a separator + * + * @code simple_command = WORD { element } ; + * + * @first WORD + */ +struct ast *parse_simple_command(struct lexer_context *ctx); + +/* + * @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 ; + * + * @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); + +/* + * @brief parses commands inside if/else clauses and returns the corresponding + * AST list + * + * @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); + +/* + * @brief + * + * @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); + +#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..a79497b 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -1,9 +1,29 @@ #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. diff --git a/src/parser/parsing_utils.h b/src/parser/parsing_utils.h deleted file mode 100644 index 89eb4bf..0000000 --- a/src/parser/parsing_utils.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef PARSING_UTILS_H -#define PARSING_UTILS_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); - -/* @brief: parses a list of [and_or] rules separated by semicolons and that - * ends by a newline - * - * @code list = and_or { ';' and_or } [ ';' ] ; - */ -struct ast *parse_list(struct lexer_context *ctx); - -/* @brief Only parses a pipeline rule for the moment - * - * @code and_or = pipeline ; - */ -struct ast *parse_and_or(struct lexer_context *ctx); - -/* @brief Only parses a command rule for the moment - * - * @code pipeline = command ; - */ -struct ast *parse_pipeline(struct lexer_context *ctx); - -/* @brief Parses a simple command rule or a shell command rule depending on - * the first token. - * @note - * TOKEN_WORD => simple_command - * TOKEN_IF => shell_command - * - * @code command = simple_command - * | shell_command - * ; - */ -struct ast *parse_command(struct lexer_context *ctx); - -/* @brief Parses a simple list of words (command and arguments) - * ending by a separator - * - * @code simple_command = WORD { element } ; - */ -struct ast *parse_simple_command(struct lexer_context *ctx); - -/* @brief Only parses if rules for the moment - * - * @code shell_command = 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' ; - */ -struct ast *parse_if_rule(struct lexer_context *ctx); - -/* @brief parses commands inside if/else clauses and returns the corresponding - * AST list - * - * @code compound_list = {'\n'} and_or { ( ';' | '\n' ) {'\n'} and_or } [';'] - * {'\n'} ; - */ -struct ast *parse_compound_list(struct lexer_context *ctx); - -/* @brief - * - * @code else_clause = 'else' compound_list - * | 'elif' compound_list 'then' compound_list [else_clause] - * ; - */ -struct ast *parse_else_clause(struct lexer_context *ctx); - -#endif /* ! PARSING_UTILS_H */ diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c index ae042ee..b4afb9c 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)) 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 */