diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index e06bec0..8c5b93e 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -4,6 +4,12 @@ #include #include "lexer_utils.h" + +/* + * @brief returns true if token is a redir type, false otherwise + */ +bool is_token_redir(struct token *token); + /* * @brief: returns the next (newly allocated) token without consuming it. * if end of input is reached, returns a token of type TOKEN_EOF. diff --git a/src/lexer/lexer_utils.c b/src/lexer/lexer_utils.c index 833b274..9883bf7 100644 --- a/src/lexer/lexer_utils.c +++ b/src/lexer/lexer_utils.c @@ -178,6 +178,25 @@ static bool is_end_of_line(char c) return c == EOF || c == '\n'; } +// === Functions + +bool is_token_redir(struct token *token) +{ + switch (token->type) + { + case TOKEN_REDIR_LEFT: + case TOKEN_REDIR_RIGHT: + case TOKEN_REDIR_LEFT_RIGHT: + case TOKEN_REDIR_DOUBLE_RIGHT: + case TOKEN_REDIR_LEFT_AMP: + case TOKEN_REDIR_RIGHT_AMP: + case TOKEN_REDIR_RIGHT_PIPE: + return true; + default: + return false; + } +} + bool is_special_char(char *stream, ssize_t i) { char c = stream[i]; diff --git a/src/lexer/lexer_utils.h b/src/lexer/lexer_utils.h index 6173485..29d444d 100644 --- a/src/lexer/lexer_utils.h +++ b/src/lexer/lexer_utils.h @@ -31,16 +31,13 @@ enum lexing_mode enum token_type { - // Special characters + // Blanks TOKEN_NULL = 0, TOKEN_EOF, TOKEN_WORD, TOKEN_NEWLINE, - // WARNING: quote and double quote should never be used inside a token. - TOKEN_QUOTE, - TOKEN_DOUBLE_QUOTE, - + // SPecial characters TOKEN_GRAVE, TOKEN_SEMICOLON, TOKEN_COMMENT, @@ -51,8 +48,14 @@ enum token_type TOKEN_RIGHT_PAREN, TOKEN_LEFT_BRACKET, TOKEN_RIGHT_BRACKET, + TOKEN_PIPE, + TOKEN_NEGATION, // TODO handle - // redirections + // TODO merge into one and use the data field + // (Too difficult to handle in the parser because of firsts) + // TOKEN_REDIRECTION + // + // Redirections TOKEN_REDIR_LEFT, TOKEN_REDIR_RIGHT, TOKEN_REDIR_LEFT_RIGHT, @@ -62,7 +65,6 @@ enum token_type TOKEN_REDIR_RIGHT_PIPE, TOKEN_IONUMBER, - TOKEN_PIPE, // Keywords TOKEN_IF, diff --git a/src/parser/grammar.c b/src/parser/grammar.c index c1457eb..6a192db 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -4,8 +4,8 @@ #include #include -#include "grammar_basic.h" #include "../lexer/lexer.h" +#include "grammar_basic.h" // === Static variables @@ -34,8 +34,8 @@ static bool add_first(enum rule rule, enum token_type token) // Append item->list_length++; - item->tokens = realloc( - item->tokens, (item->list_length) * sizeof(enum token_type)); + item->tokens = realloc(item->tokens, + (item->list_length) * sizeof(enum token_type)); } else { @@ -91,24 +91,6 @@ static void add_first_redir(enum rule rule) // === Functions -bool is_token_redir(struct token *token) -{ - switch (token->type) - { - case TOKEN_REDIR_LEFT: - case TOKEN_REDIR_RIGHT: - case TOKEN_REDIR_LEFT_RIGHT: - case TOKEN_REDIR_DOUBLE_RIGHT: - case TOKEN_REDIR_LEFT_AMP: - case TOKEN_REDIR_RIGHT_AMP: - case TOKEN_REDIR_RIGHT_PIPE: - return true; - default: - return false; - } -} - - bool grammar_init(void) { // Initialize the firsts map @@ -119,17 +101,21 @@ bool grammar_init(void) // Populate the firsts map add_first(RULE_INPUT, TOKEN_WORD); add_first(RULE_INPUT, TOKEN_IF); + add_first(RULE_COMMAND, TOKEN_NEGATION); 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_COMMAND, TOKEN_NEGATION); add_first(RULE_AND_OR, TOKEN_WORD); add_first(RULE_AND_OR, TOKEN_IF); + add_first(RULE_COMMAND, TOKEN_NEGATION); add_first(RULE_PIPELINE, TOKEN_WORD); add_first(RULE_PIPELINE, TOKEN_IF); + add_first(RULE_COMMAND, TOKEN_NEGATION); add_first(RULE_COMMAND, TOKEN_WORD); add_first(RULE_COMMAND, TOKEN_IF); diff --git a/src/parser/grammar.h b/src/parser/grammar.h index 3faf39d..9538848 100644 --- a/src/parser/grammar.h +++ b/src/parser/grammar.h @@ -50,10 +50,6 @@ struct firsts_list { // === Functions -/* @brief: returns true if token has a type redirection (not pipe or IONUMBER). - */ -bool is_token_redir(struct token *token); - /* * @brief Initializes the grammar submodule * @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index d99ba6d..0b3b7c6 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -1,13 +1,13 @@ +#include #define _POSIX_C_SOURCE 200809L -#include "grammar_basic.h" - #include #include #include "../utils/lists/lists.h" #include "grammar.h" #include "grammar_advanced.h" +#include "grammar_basic.h" // === Static functions @@ -108,7 +108,42 @@ struct ast *parse_and_or(struct lexer_context *ctx) struct ast *parse_pipeline(struct lexer_context *ctx) { - return parse_command(ctx); + bool negation = false; + struct token *token = PEEK_TOKEN(); + + // Eventual '!' + if (token->type == TOKEN_NEGATION) + { + negation = true; + POP_TOKEN(); + token = PEEK_TOKEN(); + } + + // TODO handle negation (new AST type) + + struct ast *left = parse_command(ctx); + + token = PEEK_TOKEN(); + while (token->type == TOKEN_PIPE) + { + POP_TOKEN(); + + // skip newlines + token = PEEK_TOKEN(); + while (token->type == TOKEN_NEWLINE) + { + POP_TOKEN(); + token = PEEK_TOKEN(); + } + + struct ast *right = parse_command(ctx); + + // Create AST + left = ast_create_pipe(left, right); + token = PEEK_TOKEN(); + } + + return left; } struct ast *parse_command(struct lexer_context *ctx) @@ -125,7 +160,7 @@ struct ast *parse_command(struct lexer_context *ctx) } else { - puts("Syntax error: expected command"); + puts("Syntax error: unexpected token"); return NULL; } } @@ -324,7 +359,7 @@ struct ast *parse_compound_list(struct lexer_context *ctx) return NULL; result_list = list_append(result_list, current_cmd); } - + token = PEEK_TOKEN(); } diff --git a/src/parser/grammar_basic.h b/src/parser/grammar_basic.h index f1c2f77..5ef6a56 100644 --- a/src/parser/grammar_basic.h +++ b/src/parser/grammar_basic.h @@ -28,9 +28,9 @@ struct ast *parse_and_or(struct lexer_context *ctx); /* * @brief Only parses a command rule for the moment * - * @code pipeline = command ; + * @code pipeline = ['!'] command { '|' {'\n'} command } ; * - * @first first(command) + * @first '!', first(command) */ struct ast *parse_pipeline(struct lexer_context *ctx); diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c index b4afb9c..a507a33 100644 --- a/src/utils/ast/ast.c +++ b/src/utils/ast/ast.c @@ -2,7 +2,6 @@ #define _POSIX_C_SOURCE 200809L #include "ast.h" -#include #include #include #include @@ -11,18 +10,45 @@ void ast_free(struct ast **node) { if (node == NULL || *node == NULL) + { + puts( + "WARNING: Internal error: failed to free AST node (NULL argument)"); return; - // ast void does not need to be freed. - if (ast_is_if(*node)) + } + + switch ((*node)->type) + { + case AST_IF: ast_free_if(ast_get_if(*node)); - else if (ast_is_command(*node)) + break; + case AST_CMD: ast_free_command(ast_get_command(*node)); - else if (ast_is_list(*node)) + break; + case AST_LIST: ast_free_list(ast_get_list(*node)); - else if (ast_is_and_or(*node)) + break; + case AST_AND_OR: ast_free_and_or(ast_get_and_or(*node)); - else if (ast_is_redir(*node)) + break; + case AST_REDIR: ast_free_redir(ast_get_redir(*node)); + break; + case AST_PIPE: + ast_free_pipe(ast_get_pipe(*node)); + break; + case AST_WORD: + ast_free_word(ast_get_word(*node)); + break; + + case AST_VOID: + case AST_END: + break; + + default: + puts("WARNING: Internal error: failed to free an AST node (Unknown " + "type)"); + return; + } free(*node); *node = NULL; @@ -40,6 +66,7 @@ struct ast *ast_create(enum ast_type type, void *data) return node; } +// TODO handle new types (AST_WORD, AST_PIPE, etc.) static void ast_print_dot_recursive(struct ast *node, FILE *out) { if (!node) diff --git a/src/utils/ast/ast.h b/src/utils/ast/ast.h index fc35666..cbb5efc 100644 --- a/src/utils/ast/ast.h +++ b/src/utils/ast/ast.h @@ -10,6 +10,7 @@ #include "ast_redir.h" #include "ast_void.h" #include "ast_word.h" +#include "ast_pipe.h" /** * Prints the Graphviz DOT representation of the given AST to stdout. diff --git a/src/utils/ast/ast_and_or.h b/src/utils/ast/ast_and_or.h index 344cfbf..4813592 100644 --- a/src/utils/ast/ast_and_or.h +++ b/src/utils/ast/ast_and_or.h @@ -1,8 +1,6 @@ #ifndef AST_AND_OR_H #define AST_AND_OR_H -#include - #include "ast_base.h" enum ast_and_or_type diff --git a/src/utils/ast/ast_base.h b/src/utils/ast/ast_base.h index 4ae00ec..341b380 100644 --- a/src/utils/ast/ast_base.h +++ b/src/utils/ast/ast_base.h @@ -2,6 +2,7 @@ #define AST_BASE_H #include +#include enum ast_type { @@ -12,7 +13,9 @@ enum ast_type AST_REDIR, AST_VOID, AST_CMD, - AST_WORD + AST_WORD, + AST_PIPE, + AST_NEG }; struct ast diff --git a/src/utils/ast/ast_command.c b/src/utils/ast/ast_command.c index e52787e..2b6bbf6 100644 --- a/src/utils/ast/ast_command.c +++ b/src/utils/ast/ast_command.c @@ -1,6 +1,5 @@ #include "ast_command.h" -#include #include #include @@ -19,15 +18,14 @@ struct ast *ast_create_command(struct list *command) struct ast_command *ast_get_command(struct ast *node) { - assert(node != NULL); - assert(node->type == AST_CMD); + if (node == NULL || node->type == AST_CMD) + return NULL; return (struct ast_command *)node->data; } bool ast_is_command(struct ast *node) { - assert(node != NULL); - return node->type == AST_CMD; + return node != NULL && node->type == AST_CMD; } void ast_free_command(struct ast_command *command_data) diff --git a/src/utils/ast/ast_command.h b/src/utils/ast/ast_command.h index 089225f..cf6b913 100644 --- a/src/utils/ast/ast_command.h +++ b/src/utils/ast/ast_command.h @@ -1,8 +1,6 @@ #ifndef AST_COMMAND_H #define AST_COMMAND_H -#include - #include "../lists/lists.h" #include "ast_base.h" diff --git a/src/utils/ast/ast_end.c b/src/utils/ast/ast_end.c index fbde72f..0d314bc 100644 --- a/src/utils/ast/ast_end.c +++ b/src/utils/ast/ast_end.c @@ -1,13 +1,11 @@ #include "ast_end.h" -#include #include #include bool ast_is_end(struct ast *node) { - assert(node != NULL); - return node->type == AST_END; + return node != NULL && node->type == AST_END; } struct ast *ast_create_end(void) diff --git a/src/utils/ast/ast_end.h b/src/utils/ast/ast_end.h index d846cad..55b5322 100644 --- a/src/utils/ast/ast_end.h +++ b/src/utils/ast/ast_end.h @@ -1,8 +1,6 @@ #ifndef AST_END_H #define AST_END_H -#include - #include "../lists/lists.h" #include "ast_base.h" diff --git a/src/utils/ast/ast_if.c b/src/utils/ast/ast_if.c index cff0320..1402ff6 100644 --- a/src/utils/ast/ast_if.c +++ b/src/utils/ast/ast_if.c @@ -1,6 +1,5 @@ #include "ast_if.h" -#include #include #include @@ -20,15 +19,14 @@ struct ast *ast_create_if(struct ast *condition, struct ast *then_clause, struct ast_if *ast_get_if(struct ast *node) { - assert(node != NULL); - assert(node->type == AST_IF); - return (struct ast_if *)node->data; + if (node == NULL || node->type == AST_IF) + return NULL; + return node->data; } bool ast_is_if(struct ast *node) { - assert(node != NULL); - return node->type == AST_IF; + return node != NULL && node->type == AST_IF; } void ast_free_if(struct ast_if *if_data) diff --git a/src/utils/ast/ast_if.h b/src/utils/ast/ast_if.h index f54a795..f1842bd 100644 --- a/src/utils/ast/ast_if.h +++ b/src/utils/ast/ast_if.h @@ -1,8 +1,6 @@ #ifndef AST_IF_H #define AST_IF_H -#include - #include "ast_base.h" struct ast_if diff --git a/src/utils/ast/ast_list.c b/src/utils/ast/ast_list.c index 2b96a23..cb4aaa6 100644 --- a/src/utils/ast/ast_list.c +++ b/src/utils/ast/ast_list.c @@ -1,6 +1,4 @@ -#include - -#include "ast.h" +#include "ast_list.h" struct ast *ast_create_list(struct list *list) { @@ -15,13 +13,14 @@ struct ast *ast_create_list(struct list *list) struct ast_list *ast_get_list(struct ast *node) { - assert(node != NULL); - return (struct ast_list *)node->data; + if (node == NULL) + return NULL; + return node->data; } bool ast_is_list(struct ast *node) { - return node->type == AST_LIST; + return node != NULL && node->type == AST_LIST; } void ast_free_list(struct ast_list *ast_list) diff --git a/src/utils/ast/ast_list.h b/src/utils/ast/ast_list.h index ee98ea5..21b24fb 100644 --- a/src/utils/ast/ast_list.h +++ b/src/utils/ast/ast_list.h @@ -1,8 +1,6 @@ #ifndef AST_LIST_H #define AST_LIST_H -#include - #include "../lists/lists.h" #include "ast_base.h" diff --git a/src/utils/ast/ast_neg.c b/src/utils/ast/ast_neg.c new file mode 100644 index 0000000..f5817fc --- /dev/null +++ b/src/utils/ast/ast_neg.c @@ -0,0 +1,32 @@ +#include "ast_neg.h" + +#include + +bool ast_is_neg(struct ast *node) +{ + return node != NULL && node->type == AST_REDIR; +} + +struct ast_neg *ast_get_neg(struct ast *node) +{ + if (ast_is_neg(node)) + return node->data; + return NULL; +} + +struct ast *ast_create_neg(bool negation, struct ast *child) +{ + struct ast_neg *node = malloc(sizeof(struct ast_neg)); + if (!node) + return NULL; + + return ast_create(AST_NEG, node); +} + +void ast_free_neg(struct ast_neg *node) +{ + if (!node) + return; + ast_free(&node->child); + free(node); +} diff --git a/src/utils/ast/ast_neg.h b/src/utils/ast/ast_neg.h new file mode 100644 index 0000000..8c2d4e8 --- /dev/null +++ b/src/utils/ast/ast_neg.h @@ -0,0 +1,17 @@ +#ifndef AST_NEG_H +#define AST_NEG_H + +#include "ast_base.h" + +struct ast_neg +{ + bool negation; // True negates the child's output + struct ast* child; +}; + +bool ast_is_neg(struct ast *node); +struct ast_neg *ast_get_neg(struct ast *node); +struct ast *ast_create_neg(bool negation, struct ast* child); +void ast_free_neg(struct ast_neg* node); + +#endif /* ! AST_NEG_H */ diff --git a/src/utils/ast/ast_pipe.c b/src/utils/ast/ast_pipe.c new file mode 100644 index 0000000..92754cf --- /dev/null +++ b/src/utils/ast/ast_pipe.c @@ -0,0 +1,35 @@ +#include "ast_pipe.h" + +#include + +bool ast_is_pipe(struct ast *node) +{ + return node != NULL && node->type == AST_REDIR; +} + +struct ast_pipe *ast_get_pipe(struct ast *node) +{ + if (ast_is_pipe(node)) + return node->data; + return NULL; +} + +struct ast *ast_create_pipe(struct ast *left, struct ast *right) +{ + struct ast_pipe *node = malloc(sizeof(struct ast_pipe)); + if (!node) + return NULL; + node->left = left; + node->right = right; + + return ast_create(AST_PIPE, node); +} + +void ast_free_pipe(struct ast_pipe *node) +{ + if (!node) + return; + ast_free(&node->left); + ast_free(&node->right); + free(node); +} diff --git a/src/utils/ast/ast_pipe.h b/src/utils/ast/ast_pipe.h new file mode 100644 index 0000000..c5e1e39 --- /dev/null +++ b/src/utils/ast/ast_pipe.h @@ -0,0 +1,18 @@ +#ifndef AST_PIPE_H +#define AST_PIPE_H + +#include "ast_base.h" + +struct ast_pipe +{ + struct ast* left; + struct ast* right; + // Output of left will be redirected to right stdin +}; + +bool ast_is_pipe(struct ast *node); +struct ast_pipe *ast_get_pipe(struct ast *node); +struct ast *ast_create_pipe(struct ast* left, struct ast* right); +void ast_free_pipe(struct ast_pipe* node); + +#endif /* ! AST_PIPE_H */ diff --git a/src/utils/ast/ast_redir.h b/src/utils/ast/ast_redir.h index 4b2eb63..4c1008a 100644 --- a/src/utils/ast/ast_redir.h +++ b/src/utils/ast/ast_redir.h @@ -1,8 +1,6 @@ #ifndef AST_REDIR_H #define AST_REDIR_H -#include - #include "ast_base.h" enum ast_redir_type diff --git a/src/utils/ast/ast_void.c b/src/utils/ast/ast_void.c index 213413b..e7d2dee 100644 --- a/src/utils/ast/ast_void.c +++ b/src/utils/ast/ast_void.c @@ -1,13 +1,11 @@ #include "ast_void.h" -#include #include #include bool ast_is_void(struct ast *node) { - assert(node != NULL); - return node->type == AST_VOID; + return node != NULL && node->type == AST_VOID; } struct ast *ast_create_void(void) diff --git a/src/utils/ast/ast_void.h b/src/utils/ast/ast_void.h index ecbdcf8..05a5933 100644 --- a/src/utils/ast/ast_void.h +++ b/src/utils/ast/ast_void.h @@ -1,8 +1,6 @@ #ifndef AST_VOID_H #define AST_VOID_H -#include - #include "../lists/lists.h" #include "ast_base.h" diff --git a/src/utils/ast/ast_word.h b/src/utils/ast/ast_word.h index d5e0e1d..571f869 100644 --- a/src/utils/ast/ast_word.h +++ b/src/utils/ast/ast_word.h @@ -1,8 +1,6 @@ #ifndef AST_WORD_H #define AST_WORD_H -#include - #include "ast_base.h" struct ast_word