#define _POSIX_C_SOURCE 200809L #include "grammar_basic.h" #include #include #include #include "../utils/lists/lists.h" #include "grammar.h" #include "grammar_advanced.h" // === Static functions static enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) { switch (tok_type) { case TOKEN_AND: return AST_AND_OR_TYPE_AND; case TOKEN_OR: return AST_AND_OR_TYPE_OR; default: fprintf(stderr, "and_or impossible to init, wrong token type"); return AST_AND_OR_NULL; } } /* @brief: frees all the arguments. (helper func) * @return: NULL. */ static void *err_if_rule(struct ast **cond, struct ast **then_clause, struct ast **else_clause) { ast_free(cond); ast_free(then_clause); ast_free(else_clause); return NULL; } /* @brief: frees command_elements and redirections lists (helper func) * @return: NULL */ static void *err_s_com(struct list *command_elements, struct list *redirections, struct list *assignments) { list_deep_destroy(command_elements); list_deep_destroy(redirections); list_deep_destroy(assignments); return NULL; } /* @brief: used when export keyword is found, and expects an assignment after. * @return: an ast_assignment with the field [global] set to true. */ static struct ast *parse_export(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); if (token->type != TOKEN_EXPORT) { fprintf(stderr, "expected the export keyword in parse_export"); return NULL; } // export POP_TOKEN(); token = PEEK_TOKEN(); if (token->type != TOKEN_ASSIGNMENT_WORD) { fprintf(stderr, "in parser: export must be followed by 'x=y'"); return NULL; } // assignment POP_TOKEN(); return ast_create_assignment(token->data, true); } // === Functions struct ast *parse_list(struct lexer_context *ctx) { struct list *result_list = NULL; struct ast *current_node = NULL; struct token *token = PEEK_TOKEN(); // and_or current_node = parse_and_or(ctx); if (current_node == NULL) return NULL; result_list = list_append(result_list, current_node); // Following and_or commands token = PEEK_TOKEN(); while (token->type == TOKEN_SEMICOLON) { // Forward POP_TOKEN(); token = PEEK_TOKEN(); // TODO seems a little akward (not fully compliant with the grammar) // but it's time consuming to rewrite to only cover edge cases. // So it'll probably stay like that for now 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(); } } return ast_create_list(result_list); } 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(); while (token->type == TOKEN_AND || token->type == TOKEN_OR) { // Build AST (left part) enum ast_and_or_type type = and_or_tok_to_ast(token->type); struct ast *left = result; POP_TOKEN(); token = PEEK_TOKEN(); // Skip newlines while (token->type == TOKEN_NEWLINE) { token = POP_TOKEN(); token = PEEK_TOKEN(); } // Right part struct ast *right = parse_pipeline(ctx); if (right == NULL) { ast_free(&left); return NULL; } token = PEEK_TOKEN(); result = ast_create_and_or(left, right, type); if (result == NULL) { ast_free(&left); ast_free(&right); return NULL; } } return result; } struct ast *parse_pipeline(struct lexer_context *ctx) { bool negation = false; struct token *token = PEEK_TOKEN(); // Eventual '!' if (token->type == TOKEN_NEGATION) { negation = true; POP_TOKEN(); token = PEEK_TOKEN(); } // command rule struct ast *left = parse_command(ctx); token = PEEK_TOKEN(); if (negation) { left = ast_create_neg(negation, left); } // Pipes while (token->type == TOKEN_PIPE) { POP_TOKEN(); token = PEEK_TOKEN(); // skip newlines while (token->type == TOKEN_NEWLINE) { POP_TOKEN(); token = PEEK_TOKEN(); } // command rule struct ast *right = parse_command(ctx); token = PEEK_TOKEN(); // Create AST left = ast_create_pipe(left, right); } return left; } struct ast *parse_command(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); struct ast *result = NULL; if (is_first(*token, RULE_SIMPLE_COMMAND)) { result = parse_simple_command(ctx); } else if (is_first(*token, RULE_SHELL_COMMAND)) { result = parse_shell_command(ctx); } // WARNING funcdec seems to require a LL(2) parser else if (is_first(*token, RULE_FUNCDEC)) { result = parse_funcdec(ctx); } else { perror("Syntax error: unexpected token"); return NULL; } return result; } struct ast *parse_simple_command(struct lexer_context *ctx) { struct list *command_elements = NULL; struct list *redirections = NULL; // list of redirection ASTs struct list *assignments = NULL; bool has_prefix = false; struct token *token = PEEK_TOKEN(); if (is_first(*token, RULE_PREFIX)) { has_prefix = true; while (is_first(*token, RULE_PREFIX)) { struct ast *prefix = parse_prefix(ctx); if (prefix == NULL) { return err_s_com(command_elements, redirections, assignments); } if (prefix->type == AST_ASSIGNMENT) { assignments = list_append(assignments, prefix); } else if (prefix->type == AST_REDIR) { redirections = list_append(redirections, prefix); } token = PEEK_TOKEN(); } } if (token->type != TOKEN_WORD) { if (!has_prefix && token->type != TOKEN_EXPORT) { perror("Expected a command but got a different token type"); return err_s_com(command_elements, redirections, assignments); } if (token->type == TOKEN_EXPORT) { struct ast *assignment_export = parse_export(ctx); if (assignment_export == NULL) return err_s_com(command_elements, redirections, assignments); assignments = list_append(assignments, assignment_export); } } else // TOKEN WORD { char *command = strdup(token->data); command_elements = list_append(command_elements, command); POP_TOKEN(); } token = PEEK_TOKEN(); // Eventual elements while (is_first(*token, RULE_ELEMENT)) { // Get element struct ast *element = parse_element(ctx); if (element == NULL) { return err_s_com(command_elements, redirections, assignments); } // Get element type if (ast_is_word(element)) { // Extract word struct ast_word *element_word = ast_get_word(element); char *word = element_word->word; element_word->word = NULL; // Prevents word to be freed ast_free(&element); command_elements = list_append(command_elements, word); } else if (ast_is_redir(element)) { // append redirections to the list of redirections redirections = list_append(redirections, element); } else { perror("Internal error: unexpected return value from " "parse_element in parse_simple_command"); return err_s_com(command_elements, redirections, assignments); } // Forward token = PEEK_TOKEN(); } struct ast *result = ast_create_command(command_elements, redirections, assignments); if (result == NULL) { return err_s_com(command_elements, redirections, assignments); } return result; } struct ast *parse_element(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); if (token->type == TOKEN_WORD || token->type == TOKEN_ASSIGNMENT_WORD) { POP_TOKEN(); struct ast *result = ast_create_word(token->data); if (result == NULL) { perror("Internal error: could not create ast node (is your memory " "full ?)"); return NULL; } return result; } else if (token->type == TOKEN_IONUMBER || is_token_redir(token)) { return parse_redirection(ctx); } else if (token->type == TOKEN_EXPORT) { return parse_export(ctx); } else { perror("Syntax error: unexpected token at parse_element"); return NULL; } } struct ast *parse_shell_command(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); struct ast *result = NULL; // Grouping // '(' or '{' if (token->type == TOKEN_LEFT_BRACKET || token->type == TOKEN_LEFT_PAREN) { POP_TOKEN(); result = parse_compound_list(ctx); if (result == NULL) return NULL; // ')' or '}' token = PEEK_TOKEN(); if (token->type == TOKEN_LEFT_BRACKET || token->type == TOKEN_LEFT_PAREN) { ast_free(&result); perror("Syntax error: bracket/parenthesis mismatch"); return NULL; } POP_TOKEN(); return result; } else if (is_first(*token, RULE_IF)) { return parse_if_rule(ctx); } else if (is_first(*token, RULE_WHILE)) { return parse_while(ctx); } else if (is_first(*token, RULE_UNTIL)) { return parse_until(ctx); } // TODO for and case else { perror("Syntax error: unexpected token in parse_shell_command"); return NULL; } } struct ast *parse_if_rule(struct lexer_context *ctx) { // If keyword struct token *token = POP_TOKEN(); if (token->type != TOKEN_IF) { perror("Internal error: expected a if rule but token has different " "type"); return NULL; } // Condition content struct ast *condition_content = parse_compound_list(ctx); if (condition_content == NULL) return NULL; token = PEEK_TOKEN(); // Then keyword if (token->type != TOKEN_THEN) { perror("Syntax error: Expected the 'then' keyword but token has " "different type"); return err_if_rule(&condition_content, NULL, NULL); } POP_TOKEN(); // Then content struct ast *then_content = parse_compound_list(ctx); if (then_content == NULL) { return err_if_rule(&condition_content, &then_content, NULL); } token = PEEK_TOKEN(); struct ast *else_content = NULL; // Eventual else/elif clause(s) if (is_first(*token, RULE_ELSE_CLAUSE)) { else_content = parse_else_clause(ctx); if (else_content == NULL) { return err_if_rule(&condition_content, &then_content, NULL); } token = PEEK_TOKEN(); } // Fi keyword if (token->type != TOKEN_FI) { perror("Expected the 'fi' keyword but token has different type"); return err_if_rule(&condition_content, &then_content, &else_content); } POP_TOKEN(); // Result struct ast *result = ast_create_if(condition_content, then_content, else_content); if (result == NULL) { perror("Internal error: could not create a new AST (AST_IF)"); return err_if_rule(&condition_content, &then_content, &else_content); } return result; } struct ast *parse_compound_list(struct lexer_context *ctx) { struct list *result_list = NULL; // ast* list struct ast *current_cmd = NULL; struct token *token = PEEK_TOKEN(); // Skip newlines while (token->type == TOKEN_NEWLINE) { POP_TOKEN(); 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); token = PEEK_TOKEN(); // Following commands while (token->type == TOKEN_SEMICOLON || token->type == TOKEN_NEWLINE) { POP_TOKEN(); token = PEEK_TOKEN(); // Skip newlines while (token->type == TOKEN_NEWLINE) { POP_TOKEN(); token = PEEK_TOKEN(); } // 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(); } } // Eventual semicolon if (token->type == TOKEN_SEMICOLON) { POP_TOKEN(); token = PEEK_TOKEN(); } // Skip newlines while (token->type == TOKEN_NEWLINE) { POP_TOKEN(); token = PEEK_TOKEN(); } struct ast *result = ast_create_list(result_list); return result; } struct ast *parse_else_clause(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); // Eventual elif content while (token->type == TOKEN_ELIF) { // Condition token = POP_TOKEN(); struct ast *condition = parse_compound_list(ctx); // Then keyword token = POP_TOKEN(); if (token->type != TOKEN_THEN) { perror("Expected the 'then' keyword but got a different token " "type"); return NULL; } struct ast *then_content = parse_compound_list(ctx); if (then_content == NULL) { ast_free(&condition); return NULL; } token = PEEK_TOKEN(); // Eventual else clause (recursive) struct ast *else_content = NULL; if (token->type == TOKEN_ELSE || token->type == TOKEN_ELIF) { else_content = parse_else_clause(ctx); if (else_content == NULL) { ast_free(&then_content); ast_free(&condition); return NULL; } } else { else_content = ast_create_void(); } return ast_create_if(condition, then_content, else_content); } // Eventual else content struct ast *result = NULL; if (token->type == TOKEN_ELSE) { token = POP_TOKEN(); result = parse_compound_list(ctx); if (result == NULL) return NULL; } else { result = ast_create_void(); } return result; }