summaryrefslogtreecommitdiff
path: root/42sh/src/parser/parser_commands.c
diff options
context:
space:
mode:
authorMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
committerMartial Simon <msimon_fr@hotmail.com>2025-09-15 01:07:58 +0200
commit967be9e750221ab2ab783f95df79bb26d290a45e (patch)
tree6802900a5e975f9f68b169f0f503f040056d6952 /42sh/src/parser/parser_commands.c
add: added projectsHEADmain
Diffstat (limited to '42sh/src/parser/parser_commands.c')
-rw-r--r--42sh/src/parser/parser_commands.c393
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
+ }
+}