#include #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 } }