diff options
Diffstat (limited to '42sh/src/parser/parser_commands.c')
| -rw-r--r-- | 42sh/src/parser/parser_commands.c | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/42sh/src/parser/parser_commands.c b/42sh/src/parser/parser_commands.c new file mode 100644 index 0000000..3321773 --- /dev/null +++ b/42sh/src/parser/parser_commands.c @@ -0,0 +1,393 @@ +#include <err.h> + +#include "parser_functions.h" +#include "parser_utils.h" + +static struct ast *cons_redirs(struct lexer *lexer, enum parser_state *state, + struct ast *root) +{ + struct token next = lexer_peek(lexer); + + while (TST_REDIR) + { + // The redir constructions are not consistents between the two + // grammars that can create redirs + // This is normal as theses redirs don't serve the same purposes + // This redir here is meant to redir a chuncky command + // While the one on the prefix grammar is meant for redirs mixed + // with assignments of variables + struct ast *redir = parse_redirection(lexer, state); + next = lexer_peek(lexer); + if (error_check(root, state, next)) + { + return NULL; + } + set_left(redir, root); + root = redir; + } + + return root; +} + +struct ast *parse_command(struct lexer *lexer, enum parser_state *state) +{ + struct token next = lexer_peek(lexer); + + if (error_check(NULL, state, next)) + { + return NULL; + } + + // Case where we are in a loop + if (next.type == TOKEN_IF || next.type == TOKEN_WHILE + || next.type == TOKEN_UNTIL || next.type == TOKEN_FOR + || next.type == TOKEN_CURLY_LEFT || next.type == TOKEN_PAR_LEFT) + { + struct ast *loops = parse_shell_command(lexer, state); + next = lexer_peek(lexer); + + if (error_check(loops, state, next)) + { + return NULL; + } + + return cons_redirs(lexer, state, loops); + } + // Case where we're sure we have a prefix + else if (next.type == TOKEN_ASS_WORD || TST_REDIR) + { + return parse_simple_command(lexer, state); + } + // Ambiguous case where a WORD can be used in simple_command or in fundec + // We know that parse_simple will crash if something after the WORD is + // a parenthesis. So we just save the word in case and call simple command + else + { + next = lexer_peek(lexer); + if (next.type != TOKEN_WORD) + { + QUICK_CLEANUP + } + + struct string *cpy = + string_create((next.value) ? next.value->data : NULL); + if (cpy == NULL) + { + QUICK_CLEANUP + } + struct ast *temp = parse_simple_command(lexer, state); + + next = lexer_peek(lexer); + + // I have the feeling that this can be problematic in the case where + // we have a function definiton and a subshell + if (next.type == TOKEN_PAR_LEFT) + { + ast_free(temp); + temp = NULL; + } + + if (temp != NULL) + { + string_free(cpy); + return temp; + } + else + { + // Just like it never happened... + *state = OK; + + if (next.type == TOKEN_WORD) + { + string_free(cpy); + temp = parse_funcdec(lexer, state, NULL); + } + else + { + temp = parse_funcdec(lexer, state, cpy); + } + + next = lexer_peek(lexer); + if (error_check(temp, state, next)) + { + return NULL; + } + + return cons_redirs(lexer, state, temp); + } + } +} + +struct ast *parse_shell_command(struct lexer *lexer, enum parser_state *state) +{ + struct token next = lexer_peek(lexer); + switch (next.type) + { + case TOKEN_IF: + return parse_rule_if(lexer, state); + case TOKEN_WHILE: + return parse_rule_while(lexer, state); + case TOKEN_UNTIL: + return parse_rule_until(lexer, state); + case TOKEN_FOR: + return parse_rule_for(lexer, state); + case TOKEN_CURLY_LEFT: + lexer_pop(lexer); + struct ast *lst = parse_compound_list(lexer, state); + next = lexer_peek(lexer); + + if (error_check(lst, state, next)) + { + return NULL; + } + + if (next.type != TOKEN_CURLY_RIGHT) + { + cleanup(lst, state); + return NULL; + } + + lexer_pop(lexer); + return lst; + case TOKEN_PAR_LEFT: + lexer_pop(lexer); + struct ast *subshell = ast_create(AST_SUBSHELL); + + if (subshell == NULL) + { + QUICK_CLEANUP + } + + struct ast *body = parse_compound_list(lexer, state); + next = lexer_peek(lexer); + set_left(subshell, body); + + if (error_check(subshell, state, next)) + { + return NULL; + } + + if (next.type != TOKEN_PAR_RIGHT) + { + cleanup(subshell, state); + return NULL; + } + + lexer_pop(lexer); + return subshell; + default: + // This case should not happen but just in case + for pedantic warnings + cleanup(NULL, state); + return NULL; + } +} + +struct ast *parse_simple_command(struct lexer *lexer, enum parser_state *state) +{ + struct token next = lexer_peek(lexer); + struct ast *root = NULL; + struct ast *up = NULL; + + // Should be enough to guard against the case where we have no prefix/WORDs + if (next.type != TOKEN_ASS_WORD && next.type != TOKEN_IONUMBER + && next.type != TOKEN_REDIR && next.type != TOKEN_WORD) + { + // Honnestly, it's quite useful and also puts me below 40 lines + QUICK_CLEANUP + } + struct ast *lst = ast_create(AST_LIST); + + if (lst == NULL) + { + QUICK_CLEANUP + } + + size_t i = 0; + + while (next.type == TOKEN_ASS_WORD || TST_REDIR) + { + if (next.type == TOKEN_ASS_WORD) + { + struct ast *var = parse_prefix(lexer, state); + next = lexer_peek(lexer); + if (error_check(var, state, next)) + { + return NULL; + } + + set_i(lst, var, i); + i++; + } + else + { + struct ast *redir = parse_prefix(lexer, state); + next = lexer_peek(lexer); + if (error_check(redir, state, next)) + { + return NULL; + } + + if (root == NULL || up == NULL) + { + root = redir; + up = redir; + set_left(up, lst); + } + else + { + set_left(up, redir); + set_left(redir, lst); + up = redir; + } + } + + next = lexer_peek(lexer); + } + + struct ast *son = parse_element(lexer, state, lst, up); + if (root == NULL) + { + root = son; + } + next = lexer_peek(lexer); + + if (error_check(root, state, next)) + { + // You never know if it was only variable assignment + // If so, this would never be attached to root + // cleanup(lst, state); // this one may not be necessary ~ + return NULL; + } + + return (root == NULL) ? lst : root; +} + +// This function takes care of the reallocation and all the checks that +// comes with calling realloc. (Mainly freeing stuff and exiting) +static struct string **_realloc_ast_command(struct ast *ast, size_t *capacity) +{ + struct ast_command *ast_c = ((struct ast_command *)ast); + size_t i = 0; + while (i < *capacity && ast_c->args[i]) + { + i++; + } + struct string **p = + realloc(ast_c->args, (i + CMDSIZE) * sizeof(struct string *)); + if (!p) + { + errx(EXIT_FAILURE, "Realloc failed."); + } + ast_c->args = p; + *capacity = i + CMDSIZE; + memset(ast_c->args + i, 0, (CMDSIZE) * sizeof(struct string *)); + return p; +} + +// Does not EXACTLY follows the grammar but it's close enough +// Here is the pattern it produces: +// element = { (WORD | redirection) }; +// Plus, it directly changes the tree instead of returning an ast to be +// attached to the parent node +struct ast *parse_element(struct lexer *lexer, enum parser_state *state, + struct ast *lst, struct ast *up) +{ + struct ast *root = up; + struct token next = lexer_peek(lexer); + struct ast *base = ast_create(AST_COMMAND); + int child_filled = 0; + if (base == NULL) + { + QUICK_CLEANUP; + } + + struct string **space = calloc(11, sizeof(struct string *)); + if (space == NULL) + { + cleanup(base, state); + return NULL; + } + + size_t capacity = CMDSIZE; + size_t i = 0; + + ((struct ast_command *)base)->args = space; + + while (ISWORD(next.type) || TST_REDIR) + { + if (!(ISWORD(next.type))) + { + struct ast *redir = parse_redirection(lexer, state); + // Because state is at ERROR state, I'm sure the 2 will be executed + if (error_check(root, state, next) && error_check(lst, state, next)) + { + return NULL; + } + if (root == NULL) + { + root = redir; + up = redir; + } + set_left(up, redir); + set_left(redir, lst); + up = redir; + } + else + { + if (!child_filled) + { + child_filled = 1; + } + if (i == capacity) + { + space = _realloc_ast_command(base, &capacity); + } + + litteral_reserved_word(&next); + space[i] = next.value; + i++; + lexer_pop(lexer); + } + next = lexer_peek(lexer); + } + + // In case we created the child for nothing and he wasn't filled + if (!child_filled) + { + ast_free(base); + } + else + { + size_t lst_end = list_length(lst); + set_i(lst, base, lst_end); + } + + // No need to reassign the space variable to the ast here because the worst + // that can happens is if it has been moved becasue of realloc() and this + // case has been dealt with + + return (root == NULL) ? lst : root; +} + +struct ast *parse_prefix(struct lexer *lexer, enum parser_state *state) +{ + struct token next = lexer_peek(lexer); + if (next.type == TOKEN_ASS_WORD) + { + struct ast *assign = assign_setup(next); + lexer_pop(lexer); + if (assign == NULL) + { + QUICK_CLEANUP + } + return assign; + } + else if (TST_REDIR) + { + return parse_redirection(lexer, state); + } + else + { + QUICK_CLEANUP + } +} |
