diff --git a/README.md b/README.md index 0b91b92..d3686bf 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,11 @@ # 42sh - A POSIX shell with a bad name -42sh is a project aiming to implement a POSIX-compliant shell written in C with only the standard library. -Source de is fully documented with the doxygen format so you can easily understand how the project works by exploring it. - -> **Note** This is a school project, therefore it probably won't interest you if you are looking for something useful. +42sh is a shcool project aiming to implement a POSIX compliant shell in C. ## Getting started +TODO + ### Build run this command: `autoreconf --force --verbose --install` @@ -17,43 +16,27 @@ run this command: then: `make` -#### Build with ASan +#### asan run this command: `./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g -fsanitize=address'` - or for MacOS (Jean Here): `./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -I/opt/homebrew/include' LDFLAGS='-L/opt/homebrew/lib'` - + then: `make check` -## Project status - -### Implemented features - -* **Command Execution:** `$PATH` search and binary execution (via `fork` and `execvp`) with error return code handling. -* **Built-ins:** Native implementation of `echo`, `cd`, `exit`, `export`, `unset`, `set`, `.`, `true`, `false`, as well as loop management with `break` and `continue`. -* **Control Structures:** * Conditions: `if / then / elif / else / fi`. - * Loops: `while`, `until` and `for`. -* **Logical Operators:** Command chaining with `&&`, `||` and negation with `!`. -* **Pipelines and Redirections:** * Full management of pipes `|` to connect the output of one process to the input of another. - * Single and multiple redirections: `>`, `<`, `>>`, `>&`, `<&`, `<>`. -* **Variables Management:** Assignment, variable expansion, and special variables handling like `$?` (return code of the last command). -* **Command Grouping:** Execution blocks `{ ... }` and subshells creation `( ... )`. -* **Quoting:** Support for weak (`"`) and strong (`'`) quoting for special characters escaping. - -## Architecture - -The shell operates on a classic compilation/interpretation pipeline: - -1. **Lexer (Lexical Analysis):** Reads standard input (or script) character by character and generates a stream of "Tokens" (Words, Operators, Redirections). -2. **Parser (Syntax Analysis):** Syntax analyzer that transforms the token stream into a complex Abstract Syntax Tree (AST). This module strictly manages the nesting of control structures and enforces the rigid grammar of the Shell Command Language. -3. **Execution (AST Traversal):** The tree is traversed recursively. Redirections modify file descriptors (`dup2`), child processes are created (`fork`), and commands are executed. - - ## Authors -- Guillem George - Matteo Flebus - Jean Herail - William Valenduc +- Guillem George + +## Project status + +WIP + +## TODO + +# Autotools +implement functions in all .c files to see if everything compiles. diff --git a/check_flemme.sh b/check_flemme.sh index 0543001..12398ae 100755 --- a/check_flemme.sh +++ b/check_flemme.sh @@ -32,7 +32,7 @@ run_cmd "Running autoreconf" autoreconf --force --verbose --install if [[ "$(uname)" == "Darwin" ]]; then run_cmd "Configuring for MacOS" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -I/opt/homebrew/include' LDFLAGS='-L/opt/homebrew/lib' else - run_cmd "Configuring for Linux" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g -fsanitize=address' + run_cmd "Configuring for Linux" ./configure CFLAGS='-std=c99 -Werror -Wall -Wextra -Wvla -g' fi run_cmd "Cleaning build" make clean diff --git a/src/Makefile.am b/src/Makefile.am index 5f9fa1d..0dad66f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,9 +18,9 @@ bin_PROGRAMS = 42sh parser/libparser.a \ lexer/liblexer.a \ io_backend/libio_backend.a \ - utils/libutils.a \ execution/libexecution.a \ - expansion/libexpansion.a + expansion/libexpansion.a \ + utils/libutils.a # ================ TESTS ================ @@ -34,16 +34,15 @@ check_PROGRAMS = testsuite #testsuite_CFLAGS = $(42sh_CFLAGS) #testsuite_CFLAGS += $(CRITERION_CFLAGS) -testsuite_SOURCES = ../tests/unit/utils/utils_tests.c \ +testsuite_SOURCES = ../tests/unit/lexer/lexer_tests.c \ + ../tests/unit/utils/utils_tests.c \ + ../tests/unit/expansion/expand.c \ ../tests/unit/expansion/parse_var.c \ + ../tests/unit/io_backend/io_backend.c \ ../tests/unit/utils/args.c \ ../tests/unit/utils/hash_map.c \ ../tests/unit/utils/insert_into.c -# ../tests/unit/lexer/lexer_tests.c -# ../tests/unit/expansion/expand.c -# ../tests/unit/io_backend/io_backend.c - testsuite_CPPFLAGS = $(42sh_CPPFLAGS) testsuite_LDADD = $(42sh_LDADD) -lcriterion diff --git a/src/execution/execution.c b/src/execution/execution.c index 28726e5..cf751f7 100644 --- a/src/execution/execution.c +++ b/src/execution/execution.c @@ -1,15 +1,18 @@ #define _POSIX_C_SOURCE 200809L #include "execution.h" +#include #include #include +#include +#include #include #include #include #include "../expansion/expansion.h" +#include "../utils/ast/ast.h" #include "../utils/hash_map/hash_map.h" -#include "../utils/vars/vars.h" // Refactored: delegates to helpers in execution_helpers.c #include "execution_helpers.h" @@ -19,55 +22,25 @@ int execution(struct ast *ast, struct hash_map *vars) if (!ast) return 0; - int res; switch (ast->type) { case AST_VOID: case AST_END: - res = 0; - break; + return 0; case AST_CMD: { struct ast_command *command = ast_get_command(ast); if (!expand(command, vars)) fprintf(stderr, "Error: Variable expansion failed\n"); - res = exec_ast_command(command, vars); - break; + return exec_ast_command(command, vars); } case AST_IF: - res = exec_ast_if(ast_get_if(ast), vars); - break; + return exec_ast_if(ast_get_if(ast), vars); case AST_LIST: - res = exec_ast_list(ast_get_list(ast), vars); - break; + return exec_ast_list(ast_get_list(ast), vars); case AST_AND_OR: - res = exec_ast_and_or(ast_get_and_or(ast), vars); - break; - case AST_LOOP: - res = exec_ast_loop(ast_get_loop(ast), vars); - break; - case AST_SUBSHELL: - res = exec_ast_subshell(ast_get_subshell(ast), vars); - break; + return exec_ast_and_or(ast_get_and_or(ast), vars); default: - res = 127; - break; - } - - if (res == EXEC_SIGNAL_EXIT) - { - char *exit_val_str = get_var(vars, "EXIT_VALUE"); - if (exit_val_str == NULL) - { - fprintf( - stderr, - "Internal error: could not retrieve return value from exit\n"); - return 2; - } - return atoi(exit_val_str); - } - else - { - return res; + return 127; } } diff --git a/src/execution/execution_helpers.c b/src/execution/execution_helpers.c index 12d4e1f..cffcdb0 100644 --- a/src/execution/execution_helpers.c +++ b/src/execution/execution_helpers.c @@ -1,20 +1,151 @@ #define _POSIX_C_SOURCE 200809L #include "execution_helpers.h" +#include #include -#include #include #include #include #include #include +#include "../expansion/expansion.h" +#include "../utils/ast/ast.h" #include "../utils/hash_map/hash_map.h" #include "../utils/lists/lists.h" #include "../utils/vars/vars.h" #include "execution.h" -// === Static functions +static char **list_to_argv(struct list *command_list) +{ + size_t len = 0; + struct list *cur = command_list; + while (cur) + { + len++; + cur = cur->next; + } + char **argv = calloc(len + 1, sizeof(char *)); + if (!argv) + return NULL; + cur = command_list; + for (size_t i = 0; i < len; i++) + { + argv[i] = (char *)cur->data; + cur = cur->next; + } + argv[len] = NULL; + return argv; +} + +static int try_builtin(char **argv, struct hash_map *vars); + +int exec_ast_command(struct ast_command *command, struct hash_map *vars) +{ + (void)vars; + set_all_redir(command->redirections); + + if (!command || !(command->command)) + { + return 1; + } + + char **argv = list_to_argv(command->command); + if (!argv || !(argv[0])) + { + free(argv); + unset_all_redir(); + return 0; + } + + int builtin_ret = try_builtin(argv, vars); + if (builtin_ret != -1) + { + free(argv); + unset_all_redir(); + return builtin_ret; + } + + pid_t pid = fork(); + if (pid < 0) + { + perror("fork"); + free(argv); + unset_all_redir(); + return 1; + } + + if (pid == 0) + { + execvp(argv[0], argv); + perror("execvp"); + unset_all_redir(); + _exit(127); + } + + int status = 0; + waitpid(pid, &status, 0); + free(argv); + unset_all_redir(); + + if (WIFEXITED(status)) + { + return WEXITSTATUS(status); + } + + return 1; +} + +int exec_ast_if(struct ast_if *if_node, struct hash_map *vars) +{ + int cond = execution(if_node->condition, vars); + if (cond == 0) + return execution(if_node->then_clause, vars); + else + return execution(if_node->else_clause, vars); +} + +int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) +{ + struct list *cur = list_node->children; + int ret = 0; + while (cur) + { + struct ast *child = (struct ast *)cur->data; + if (!ast_is_void(child)) + ret = execution(child, vars); + cur = cur->next; + } + return ret; +} + +int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars) +{ + int left_ret = execution(ao_node->left, vars); + if (ao_node->type == AST_AND_OR_TYPE_AND) + { + if (left_ret == 0) + return execution(ao_node->right, vars); + return left_ret; + } + else + { + if (left_ret != 0) + return execution(ao_node->right, vars); + return left_ret; + } +} + +static int get_fd_target(const struct ast_redir *redir) +{ + if (redir->io_number != -1) + return redir->io_number; + if (redir->type == AST_REDIR_TYPE_LESS + || redir->type == AST_REDIR_TYPE_LESSGREAT + || redir->type == AST_REDIR_TYPE_LESSAND) + return 0; + return 1; +} static int open_redir_file(const struct ast_redir *redir, int *flags, int *mode) { @@ -38,19 +169,34 @@ static int open_redir_file(const struct ast_redir *redir, int *flags, int *mode) return -3; // not a file open } +static int handle_and_restore_fd(int saved_fd, int fd_target) +{ + if (saved_fd != -1) + { + dup2(saved_fd, fd_target); + close(saved_fd); + } + else + { + close(fd_target); + } + return 0; +} + static int set_all_redir(struct list *redir_list) { while (redir_list) { - struct ast *redir_node = (struct ast *)redir_list->data; - struct ast_redir *redir = ast_get_redir(redir_node); + struct ast_redir *redir = (struct ast_redir *)redir_list->data; int target_fd; + if (redir->io_number != -1) { target_fd = redir->io_number; } else { + // assign target_fd depending on redir type if (redir->type == AST_REDIR_TYPE_LESS || redir->type == AST_REDIR_TYPE_LESSGREAT || redir->type == AST_REDIR_TYPE_LESSAND) @@ -62,6 +208,7 @@ static int set_all_redir(struct list *redir_list) target_fd = 1; } } + redir->saved_fd = dup(target_fd); int new_fd = -1; int flags = 0; @@ -101,378 +248,68 @@ static int set_all_redir(struct list *redir_list) return 0; } -static char **list_to_argv(struct list *command_list) -{ - size_t len = 0; - struct list *cur = command_list; - while (cur) - { - len++; - cur = cur->next; - } - char **argv = calloc(len + 1, sizeof(char *)); - if (!argv) - return NULL; - cur = command_list; - for (size_t i = 0; i < len; i++) - { - argv[i] = (char *)cur->data; - cur = cur->next; - } - argv[len] = NULL; - return argv; -} - /* - * @brief parses string and returns the represented (unsigned) number - * @return the number contained by the string or -1 if number is invalid - */ -static int atou(char *str) +int exec_ast_redir(struct ast_redir *redir, struct hash_map *vars) { - if (str == NULL || *str == '\0') - return -1; - - int result = 0; - size_t i = 0; - while (str[i] != '\0') + int fd_target = get_fd_target(redir); + int saved_fd = dup(fd_target); + int new_fd = -1, flags = 0, mode = 0644; + if (redir->type == AST_REDIR_TYPE_GREAT + || redir->type == AST_REDIR_TYPE_CLOBBER + || redir->type == AST_REDIR_TYPE_DGREAT + || redir->type == AST_REDIR_TYPE_LESS) { - if (str[i] < '0' || str[i] > '9') - return -1; - - result *= 10; - result += str[i] - '0'; - i++; - } - - return result; -} - -static int try_builtin(char **argv, struct hash_map *vars); - -static int builtin_break(char **argv); -static int builtin_continue(char **argv); -static int builtin_unset(char **argv, struct hash_map *vars); - -static int exec_assignment(struct list *assignment_list, struct hash_map *vars) -{ - while (assignment_list != NULL) - { - if (!ast_is_assignment(assignment_list->data)) + new_fd = open_redir_file(redir, &flags, &mode); + if (new_fd == -1) { - fprintf(stderr, "list of assignements contains something else"); + perror("open"); + if (saved_fd != -1) + close(saved_fd); return 1; } - struct ast_assignment *assignment = - ast_get_assignment(assignment_list->data); - - if (assignment->global) + if (dup2(new_fd, fd_target) == -1) { - setenv(assignment->name, assignment->value, 1); + perror("dup2"); + close(new_fd); + if (saved_fd != -1) + close(saved_fd); + return 1; } - set_var_copy(vars, assignment->name, assignment->value); - assignment_list = assignment_list->next; + close(new_fd); } - return 0; -} - -// === Functions - -int exec_ast_command(struct ast_command *command, struct hash_map *vars) -{ - exec_assignment(command->assignments, vars); - set_all_redir(command->redirections); - - if (!command || !(command->command)) + else if (redir->type == AST_REDIR_TYPE_GREATAND + || redir->type == AST_REDIR_TYPE_LESSAND) { - return 1; - } - - char **argv = list_to_argv(command->command); - if (!argv || !(argv[0])) - { - free(argv); - unset_all_redir(command->redirections); - return 0; - } - - int builtin_ret = try_builtin(argv, vars); - if (builtin_ret != -1) - { - free(argv); - unset_all_redir(command->redirections); - return builtin_ret; - } - - pid_t pid = fork(); - if (pid < 0) - { - perror("fork"); - free(argv); - unset_all_redir(command->redirections); - return 1; - } - - if (pid == 0) - { - execvp(argv[0], argv); - perror("execvp"); - unset_all_redir(command->redirections); - _exit(127); - } - - int status = 0; - waitpid(pid, &status, 0); - free(argv); - unset_all_redir(command->redirections); - - if (WIFEXITED(status)) - { - return WEXITSTATUS(status); - } - - return 1; -} - -int exec_ast_if(struct ast_if *if_node, struct hash_map *vars) -{ - if (if_node == NULL) - return 2; - int cond = execution(if_node->condition, vars); - if (cond == EXEC_SIGNAL_BREAK || cond == EXEC_SIGNAL_CONTINUE - || cond == EXEC_SIGNAL_EXIT) - return cond; - if (cond == 0) - { - int r = execution(if_node->then_clause, vars); - return r; - } - else - { - int r = execution(if_node->else_clause, vars); - return r; - } -} - -int exec_ast_subshell(struct ast_subshell *subshell_node, - struct hash_map *vars) -{ - pid_t pid = fork(); - if (pid < 0) - { - perror("fork"); - return 1; - } - - if (pid == 0) - { - int res = execution(subshell_node->child, vars); - _exit(res); - } - - int status = 0; - waitpid(pid, &status, 0); - - if (WIFEXITED(status)) - { - return WEXITSTATUS(status); - } - - return 1; -} - -int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) -{ - struct list *cur = list_node->children; - int ret = 0; - while (cur) - { - struct ast *child = (struct ast *)cur->data; - if (!ast_is_void(child)) + new_fd = atoi(redir->filename); + if (dup2(new_fd, fd_target) == -1) { - int child_ret = execution(child, vars); - if (child_ret == EXEC_SIGNAL_BREAK - || child_ret == EXEC_SIGNAL_CONTINUE - || child_ret == EXEC_SIGNAL_EXIT) - return child_ret; - ret = child_ret; + perror("dup2"); + if (saved_fd != -1) + close(saved_fd); + return 1; } - cur = cur->next; } + int ret = execution(redir->child, vars); + handle_and_restore_fd(saved_fd, fd_target); return ret; -} - -int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars) -{ - int left_ret = execution(ao_node->left, vars); - if (left_ret == EXEC_SIGNAL_BREAK || left_ret == EXEC_SIGNAL_CONTINUE - || left_ret == EXEC_SIGNAL_EXIT) - return left_ret; - if (ao_node->type == AST_AND_OR_TYPE_AND) - { - if (left_ret == 0) - { - int right_ret = execution(ao_node->right, vars); - return right_ret; - } - return left_ret; - } - else - { - if (left_ret != 0) - { - int right_ret = execution(ao_node->right, vars); - return right_ret; - } - return left_ret; - } -} - -void unset_all_redir(struct list *redir_list) -{ - while (redir_list) - { - struct ast *redir_node = (struct ast *)redir_list->data; - struct ast_redir *redir = ast_get_redir(redir_node); - int target_fd; - if (redir->io_number != -1) - { - target_fd = redir->io_number; - } - else - { - if (redir->type == AST_REDIR_TYPE_LESS - || redir->type == AST_REDIR_TYPE_LESSGREAT - || redir->type == AST_REDIR_TYPE_LESSAND) - { - target_fd = 0; - } - else - { - target_fd = 1; - } - } - if (redir->saved_fd != -1) - { - dup2(redir->saved_fd, target_fd); - close(redir->saved_fd); - redir->saved_fd = -1; - } - redir_list = redir_list->next; - } -} - -int exec_ast_loop(struct ast_loop *loop_node, struct hash_map *vars) -{ - int res = 0; - while (execution(loop_node->condition, vars) == 0) - { - res = execution(loop_node->body, vars); - if (res == EXEC_SIGNAL_BREAK) - { - res = 0; - break; - } - else if (res == EXEC_SIGNAL_CONTINUE) - { - res = 0; - continue; - } - } - - return res; -} +} */ // --- Builtins --- -static void print_with_escapes(const char *str) -{ - while (*str) - { - if (*str == '\\') - { - str++; - if (*str == 'n') - putchar('\n'); - else if (*str == 't') - putchar('\t'); - else if (*str == '\\') - putchar('\\'); - else if (*str == 'a') - putchar('\a'); - else if (*str == 'b') - putchar('\b'); - else if (*str == 'f') - putchar('\f'); - else if (*str == 'r') - putchar('\r'); - else if (*str == 'v') - putchar('\v'); - else if (*str == 'c') - return; // stop printing - else - { - // unrecognized escape, print "\"" and the char - putchar('\\'); - if (*str) - putchar(*str); - } - } - else - { - putchar(*str); - } - str++; - } -} - static int builtin_echo(char **argv) { bool newline = true; - bool interpret_escapes = false; int i = 1; - // Parse options - while (argv[i] && argv[i][0] == '-') + if (argv[1] && strcmp(argv[1], "-n") == 0) { - char *opt = argv[i] + 1; // skip "-" - bool valid_option = false; - while (*opt) - { - if (*opt == 'n') - { - newline = false; - valid_option = true; - } - else if (*opt == 'e') - { - interpret_escapes = true; - valid_option = true; - } - else if (*opt == 'E') - { - interpret_escapes = false; - valid_option = true; - } - else - { - // invalid option so euh treat as regular argument - valid_option = false; - break; - } - opt++; - } - if (!valid_option) - break; // stop parsing options + newline = false; i++; } - // Print arguments for (; argv[i]; i++) { - if (interpret_escapes) - print_with_escapes(argv[i]); - else - printf("%s", argv[i]); + printf("%s", argv[i]); if (argv[i + 1]) printf(" "); } @@ -483,18 +320,6 @@ static int builtin_echo(char **argv) return 0; } -static int builtin_break(char **argv) -{ - (void)argv; - return EXEC_SIGNAL_BREAK; -} - -static int builtin_continue(char **argv) -{ - (void)argv; - return EXEC_SIGNAL_CONTINUE; -} - static int builtin_true(char **argv) { (void)argv; @@ -507,21 +332,13 @@ static int builtin_false(char **argv) return 1; } -static int builtin_exit(char **argv, struct hash_map *vars) +static int builtin_exit(char **argv) { int exit_val = 0; if (argv[1]) - { - exit_val = atou(argv[1]); - if (exit_val == -1) - { - fprintf(stderr, "exit: Illegal number %s\n", argv[1]); - return 2; - } - } - - set_var_int(vars, "EXIT_VALUE", exit_val); - return EXEC_SIGNAL_EXIT; + exit_val = atoi(argv[1]); + exit(exit_val); + return exit_val; } static int builtin_cd(char **argv, struct hash_map *vars) @@ -548,23 +365,6 @@ static int builtin_cd(char **argv, struct hash_map *vars) return 0; } -static int builtin_unset(char **argv, struct hash_map *vars) -{ - if (!argv) - return 0; - for (int i = 1; argv[i]; i++) - { - const char *name = argv[i]; - if (name == NULL || name[0] == '\0') - continue; - // remove from shell variables - hash_map_remove(vars, name); - // remove from environment variables - unsetenv(name); - } - return 0; -} - /** * @brief Tries to execute a builtin command if the command matches a builtin * @@ -578,18 +378,12 @@ static int try_builtin(char **argv, struct hash_map *vars) if (strcmp(argv[0], "echo") == 0) return builtin_echo(argv); - if (strcmp(argv[0], "unset") == 0) - return builtin_unset(argv, vars); if (strcmp(argv[0], "true") == 0) return builtin_true(argv); if (strcmp(argv[0], "false") == 0) return builtin_false(argv); - if (strcmp(argv[0], "break") == 0) - return builtin_break(argv); - if (strcmp(argv[0], "continue") == 0) - return builtin_continue(argv); if (strcmp(argv[0], "exit") == 0) - return builtin_exit(argv, vars); + return builtin_exit(argv); if (strcmp(argv[0], "cd") == 0) return builtin_cd(argv, vars); diff --git a/src/execution/execution_helpers.h b/src/execution/execution_helpers.h index 8a56e18..fd8026c 100644 --- a/src/execution/execution_helpers.h +++ b/src/execution/execution_helpers.h @@ -4,18 +4,9 @@ #include "../utils/ast/ast.h" #include "../utils/hash_map/hash_map.h" -// Special execution signals used internally to implement loop control -#define EXEC_SIGNAL_CONTINUE (-2) -#define EXEC_SIGNAL_BREAK (-3) -#define EXEC_SIGNAL_EXIT (-4) - int exec_ast_command(struct ast_command *command, struct hash_map *vars); int exec_ast_if(struct ast_if *if_node, struct hash_map *vars); int exec_ast_list(struct ast_list *list_node, struct hash_map *vars); int exec_ast_and_or(struct ast_and_or *ao_node, struct hash_map *vars); -int exec_ast_loop(struct ast_loop *loop_node, struct hash_map *vars); -int exec_ast_subshell(struct ast_subshell *subshell_node, - struct hash_map *vars); -void unset_all_redir(struct list *redir_list); #endif // EXECUTION_HELPERS_H diff --git a/src/expansion/expansion.c b/src/expansion/expansion.c index e8f8577..4fea985 100644 --- a/src/expansion/expansion.c +++ b/src/expansion/expansion.c @@ -1,6 +1,4 @@ #define _POSIX_C_SOURCE 200809L -#include "expansion.h" - #include #include #include @@ -117,40 +115,6 @@ static bool expand_var(char **str, size_t pos, const struct hash_map *vars) return false; } -size_t parse_subshell_str(char *str, char **res) -{ - size_t i = 1; // skip the '(' - int paren_count = 1; - - if (str[i] == ')') - { - // empty subshell - *res = NULL; - return 0; - } - - while (str[i] != 0) - { - if (str[i] == '(') - paren_count++; - else if (str[i] == ')') - { - paren_count--; - - if (paren_count == 0) - { - *res = strndup(str + 1, i - 1); - return i + 1; - } - } - i++; - } - - // error: parenthesis not closed - *res = NULL; - return 0; -} - bool expand(struct ast_command *command, const struct hash_map *vars) { if (command == NULL) @@ -158,42 +122,34 @@ bool expand(struct ast_command *command, const struct hash_map *vars) char *str; size_t len; - enum quote_state quotes; + bool in_quotes; struct list *l = command->command; while (l != NULL) { - quotes = NO_QUOTE; + in_quotes = false; str = (char *)l->data; len = strlen(str); for (size_t i = 0; str[i] != 0; i++) { - if (str[i] == '\'' || str[i] == '\"') + if (str[i] == '\'') { - if (quotes == NO_QUOTE) - { - quotes = (str[i] == '\'') ? SINGLE_QUOTE : DOUBLE_QUOTE; - } - else if ((quotes == SINGLE_QUOTE && str[i] == '\'') - || (quotes == DOUBLE_QUOTE && str[i] == '\"')) - { - quotes = NO_QUOTE; - } - else - { - // inside the other quote type, do nothing - continue; - } - - // remove quote + // remove single quote + in_quotes = !in_quotes; memmove(str + i, str + i + 1, strlen(str + i + 1) + 1); i--; } - else if (quotes == SINGLE_QUOTE) + else if (in_quotes) { continue; // do nothing } + else if (str[i] == '\"') + { + // remove double quote + memmove(str + i, str + i + 1, strlen(str + i + 1) + 1); + i--; + } else if (str[i] == '$' && str[i + 1] != 0 && !isspace(str[i + 1])) { // variable expansion @@ -205,20 +161,21 @@ bool expand(struct ast_command *command, const struct hash_map *vars) } } - // if (quotes != NO_QUOTE) - // { - // // error: quote not closed - // fprintf(stderr, "Error: quote not closed in string: %s\n", str); - // return false; - // } + if (in_quotes) + { + // error: quote not closed + fprintf(stderr, "Error: quote not closed in string: %s\n", str); + return false; + } if (len != strlen(str)) { char *new_str = realloc(str, strlen(str) + 1); if (new_str == NULL) + { // error: realloc fail return false; - + } l->data = new_str; } diff --git a/src/expansion/expansion.h b/src/expansion/expansion.h index ce22e38..420ed02 100644 --- a/src/expansion/expansion.h +++ b/src/expansion/expansion.h @@ -7,13 +7,6 @@ #include "../utils/ast/ast.h" #include "../utils/hash_map/hash_map.h" -enum quote_state -{ - NO_QUOTE, - SINGLE_QUOTE, - DOUBLE_QUOTE -}; - /** * Parse a variable from a string starting with '$'. * @param str The input string starting with '$'. It must start with '$'. @@ -23,15 +16,6 @@ enum quote_state */ size_t parse_var_name(char *str, char **res); -/** - * Parse a subshell string enclosed in parentheses. - * @param str The input string starting with '('. It must start with '('. - * @param res Pointer to a char pointer that will be set to the extracted - * subshell string. - * @return The number of characters processed in the input string. - */ -size_t parse_subshell_str(char *str, char **res); - /** * Expand variables in an AST command using the provided variable map. * @param command The AST command to expand. diff --git a/src/lexer/lexer_utils.c b/src/lexer/lexer_utils.c index 53c3603..f860649 100644 --- a/src/lexer/lexer_utils.c +++ b/src/lexer/lexer_utils.c @@ -72,18 +72,10 @@ static void set_token_keyword(struct token *tok, char *begin, ssize_t size) tok->type = TOKEN_ELSE; else if (strncmp(begin, "elif", size) == 0 && size == 4) tok->type = TOKEN_ELIF; - else if (strncmp(begin, "for", size) == 0 && size == 3) - tok->type = TOKEN_FOR; - else if (strncmp(begin, "while", size) == 0 && size == 5) - tok->type = TOKEN_WHILE; - else if (strncmp(begin, "until", size) == 0 && size == 5) - tok->type = TOKEN_UNTIL; - else if (strncmp(begin, "do", size) == 0 && size == 2) - tok->type = TOKEN_DO; - else if (strncmp(begin, "done", size) == 0 && size == 4) - tok->type = TOKEN_DONE; - else if (strncmp(begin, "export", size) == 0 && size == 6) - tok->type = TOKEN_EXPORT; + else if (strncmp(begin, "&&", size) == 0 && size == 2) + tok->type = TOKEN_AND; + else if (strncmp(begin, "||", size) == 0 && size == 2) + tok->type = TOKEN_OR; // no keywords found. if (tok->type == TOKEN_NULL) @@ -105,15 +97,14 @@ static void set_token_operator(struct token *tok, char *begin, ssize_t size) { if (tok->type != TOKEN_NULL) return; - if (strncmp(begin, "&&", size) == 0 && size == 2) - { - tok->type = TOKEN_AND; - } - else if (strncmp(begin, "||", size) == 0 && size == 2) + if (strncmp(begin, "||", size) == 0 && size == 2) { tok->type = TOKEN_OR; } - + else if (strncmp(begin, "&&", size) == 0 && size == 2) + { + tok->type = TOKEN_AND; + } else if (strncmp(begin, ">", size) == 0 && size == 1) { tok->type = TOKEN_REDIR_RIGHT; @@ -248,26 +239,25 @@ struct token *new_token(char *begin, ssize_t size, struct token_info *info) set_token_assignment(tok, begin, size); else { - set_token_keyword(tok, begin, size); set_token_operator(tok, begin, size); set_token_spechar(tok, begin, size); + set_token_keyword(tok, begin, size); set_token_word(tok, begin, size); } return tok; } -void destroy_lexer_context(struct lexer_context *ctx) +void destroy_lexer_context(struct lexer_context **ctx) { - struct token *prev = ctx->previous_token; - struct token *cur = ctx->current_token; - if (ctx == NULL) + if (ctx == NULL || *ctx == NULL) return; - if (prev != NULL) - free_token(&prev); - if (cur != NULL) - free_token(&cur); - free(ctx); + if ((*ctx)->previous_token != NULL) + free((*ctx)->previous_token); + if ((*ctx)->current_token != NULL) + free((*ctx)->current_token); + free(*ctx); + *ctx = NULL; } void free_token(struct token **tok) @@ -304,19 +294,16 @@ ssize_t len_op_sepchar(char *stream, ssize_t i) if (!is_special_char(stream, i)) return -1; // should never happen - // OR - if (stream[i] == '|' && stream[i + 1] == '|') - return 2; + if (stream[i] == '|' || stream[i + 1] == '|') + return 2; // OR || - // AND if (stream[i] == '&' && stream[i + 1] == '&') - return 2; + return 2; // AND && - // special chars if (stream[i] != '>' && stream[i] != '<') - return 1; + return 1; // special character (cannot be operator) - // REDIRS + // operator if (stream[i] == '<') { diff --git a/src/lexer/lexer_utils.h b/src/lexer/lexer_utils.h index 32b3793..8fb5219 100644 --- a/src/lexer/lexer_utils.h +++ b/src/lexer/lexer_utils.h @@ -16,7 +16,7 @@ struct lexer_context /* @brief: frees all fields of ctx and sets ctx to NULL. */ -void destroy_lexer_context(struct lexer_context *ctx); +void destroy_lexer_context(struct lexer_context **ctx); enum lexing_mode { @@ -68,14 +68,7 @@ enum token_type TOKEN_FI, TOKEN_ELIF, TOKEN_AND, - TOKEN_OR, - TOKEN_FOR, - TOKEN_WHILE, - TOKEN_UNTIL, - TOKEN_CASE, - TOKEN_EXPORT, - TOKEN_DO, - TOKEN_DONE + TOKEN_OR }; struct token diff --git a/src/main.c b/src/main.c index cf3b791..c8988ca 100644 --- a/src/main.c +++ b/src/main.c @@ -7,9 +7,71 @@ #include "lexer/lexer.h" #include "parser/parser.h" #include "utils/args/args.h" -#include "utils/main_loop/main_loop.h" #include "utils/vars/vars.h" +// === Error codes + +#define SUCCESS 0 +#define ERR_INPUT_PROCESSING 2 +#define ERR_MALLOC 3 +#define ERR_GENERIC 4 + +// === Functions + +/* @brief: frees the hash map. + * @return: always ERR_INPUT_PROCESSING. + */ +static int err_input(struct hash_map **vars) +{ + hash_map_free(vars); + return ERR_INPUT_PROCESSING; +} + +static int main_loop(struct lexer_context *ctx, struct args_options *options, + struct hash_map *vars) +{ + int return_code = SUCCESS; + // init parser + if (!parser_init()) + { + perror("parser initialization failed."); + } + + // Retrieve and build first AST + struct ast *command_ast = get_ast(ctx); + + if (options->pretty_print) + { + ast_print_dot(command_ast); + } + + // Main parse-execute loop + while (command_ast != NULL && command_ast->type != AST_END) + { + if (command_ast->type != AST_VOID) + { + // Execute AST + return_code = execution(command_ast, vars); + + // set $? variable + set_var_int(vars, "?", return_code); + } + + ast_free(&command_ast); + + // Retrieve and build next AST + command_ast = get_ast(ctx); + } + + if (command_ast == NULL) + return err_input(&vars); + + ast_free(&command_ast); + parser_close(); + + return return_code; +} + int main(int argc, char **argv) { struct hash_map *vars = vars_init(); @@ -25,7 +87,7 @@ int main(int argc, char **argv) if (return_code != 0) { print_usage(stderr, argv[0]); - return err_input(&vars, NULL); + return err_input(&vars); } // args_print(&options); @@ -40,7 +102,7 @@ int main(int argc, char **argv) { fprintf(stderr, "Error: Failed to configure IO Backend from arguments\n"); - return err_input(&vars, NULL); + return err_input(&vars); } // Init IO Backend (with the context struct) @@ -50,22 +112,17 @@ int main(int argc, char **argv) fprintf(stderr, "Error: IO Backend initialization failed with code %d\n", return_code); - return err_input(&vars, NULL); + return err_input(&vars); } // init lexer context - struct lexer_context *ctx = calloc(1, sizeof(struct lexer_context)); + struct lexer_context ctx = { 0 }; - // init parser - if (!parser_init()) - { - perror("parser initialization failed."); - return err_input(&vars, ctx); - } + return_code = main_loop(&ctx, &options, vars); - return_code = main_loop(ctx, vars); + // === free - parser_close(); + hash_map_free(&vars); return return_code; } diff --git a/src/parser/grammar.c b/src/parser/grammar.c index e2a4887..95f1bd2 100644 --- a/src/parser/grammar.c +++ b/src/parser/grammar.c @@ -4,6 +4,7 @@ #include #include +#include "../lexer/lexer.h" #include "grammar_basic.h" // === Static variables @@ -56,23 +57,6 @@ static bool add_first(enum rule rule, enum token_type token) return true; } -/* @brief Add a list of tokens to a rule's firsts (in firsts_map) - * - * @arg rule the rule to which add a first - * @arg tokens_list the list of tokens to add to the rule's firsts - * @return true on success, false on error - */ -static bool add_firsts(enum rule rule, struct firsts_list *tokens_list) -{ - for (size_t i = 0; i < tokens_list->list_length; i++) - { - bool res = add_first(rule, tokens_list->tokens[i]); - if (!res) - return false; - } - return true; -} - /* @brief initializes the firsts_map static variable (does not populate it) * @return true on success, false on error */ @@ -89,125 +73,24 @@ static bool init_firsts_map(void) return true; } -static void add_first_redir(void) +/* @brief: add all the redirection token_types to the first of [rule]. + * this also contains IONUMBER + */ +static void add_first_redir(enum rule rule) { - add_first(RULE_REDIRECTION, TOKEN_IONUMBER); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_DOUBLE_RIGHT); - add_first(RULE_REDIRECTION, TOKEN_REDIR_LEFT_AMP); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_AMP); - add_first(RULE_REDIRECTION, TOKEN_REDIR_RIGHT_PIPE); -} - -// Adds only direct tokens to rules firsts into the firsts map -static void add_firsts_tokens(void) -{ - // Redirection - add_first_redir(); - // %RIP Matteo 30/01/2026 - // %RAX Guillem 30/01/2026 hehe - - // If - add_first(RULE_IF, TOKEN_IF); - - // Else clause - add_first(RULE_ELSE_CLAUSE, TOKEN_ELSE); - add_first(RULE_ELSE_CLAUSE, TOKEN_ELIF); - - // For - add_first(RULE_FOR, TOKEN_FOR); - - // While - add_first(RULE_WHILE, TOKEN_WHILE); - - // Until - add_first(RULE_UNTIL, TOKEN_UNTIL); - - // Case - add_first(RULE_CASE, TOKEN_CASE); - - // Case item - add_first(RULE_CASE_ITEM, TOKEN_LEFT_PAREN); - add_first(RULE_CASE_ITEM, TOKEN_WORD); - - // Shell command - add_first(RULE_SHELL_COMMAND, TOKEN_LEFT_BRACKET); - add_first(RULE_SHELL_COMMAND, TOKEN_LEFT_PAREN); - - // Simple command - add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD); - add_first(RULE_SIMPLE_COMMAND, TOKEN_EXPORT); - - // Element - add_first(RULE_ELEMENT, TOKEN_WORD); - add_first(RULE_ELEMENT, TOKEN_ASSIGNMENT_WORD); - - // Prefix - add_first(RULE_PREFIX, TOKEN_ASSIGNMENT_WORD); - - // Pipeline - add_first(RULE_PIPELINE, TOKEN_WORD); - - // Compound list - add_first(RULE_COMPOUND_LIST, TOKEN_NEWLINE); - - // Input - add_first(RULE_INPUT, TOKEN_NEWLINE); - add_first(RULE_INPUT, TOKEN_EOF); - - // Funcdec - add_first(RULE_FUNCDEC, TOKEN_WORD); -} - -// Adds only firsts that depend on other rules to the firsts map -// WARNING order matters -static void add_firsts_rec(void) -{ - // Case clause - add_firsts(RULE_CASE_CLAUSE, first(RULE_CASE_ITEM)); - - // Element - add_firsts(RULE_ELEMENT, first(RULE_REDIRECTION)); - - // Prefix - add_firsts(RULE_PREFIX, first(RULE_REDIRECTION)); - - // Shell command - add_firsts(RULE_SHELL_COMMAND, first(RULE_IF)); - add_firsts(RULE_SHELL_COMMAND, first(RULE_FOR)); - add_firsts(RULE_SHELL_COMMAND, first(RULE_WHILE)); - add_firsts(RULE_SHELL_COMMAND, first(RULE_UNTIL)); - add_firsts(RULE_SHELL_COMMAND, first(RULE_CASE)); - - // Simple command - add_firsts(RULE_SIMPLE_COMMAND, first(RULE_PREFIX)); - - // Command - add_firsts(RULE_COMMAND, first(RULE_SIMPLE_COMMAND)); - add_firsts(RULE_COMMAND, first(RULE_SHELL_COMMAND)); - add_firsts(RULE_COMMAND, first(RULE_FUNCDEC)); - - // Pipeline - add_firsts(RULE_PIPELINE, first(RULE_COMMAND)); - - // And Or - add_firsts(RULE_AND_OR, first(RULE_PIPELINE)); - - // Compound list - add_firsts(RULE_COMPOUND_LIST, first(RULE_AND_OR)); - - // List - add_firsts(RULE_LIST, first(RULE_AND_OR)); - - // Input - add_firsts(RULE_INPUT, first(RULE_LIST)); + add_first(rule, TOKEN_IONUMBER); + add_first(rule, TOKEN_REDIR_LEFT); + add_first(rule, TOKEN_REDIR_RIGHT); + add_first(rule, TOKEN_REDIR_LEFT_RIGHT); + add_first(rule, TOKEN_REDIR_DOUBLE_RIGHT); + add_first(rule, TOKEN_REDIR_LEFT_AMP); + add_first(rule, TOKEN_REDIR_RIGHT_AMP); + add_first(rule, TOKEN_REDIR_RIGHT_PIPE); } // === Functions -int grammar_init(void) +bool grammar_init(void) { // Initialize the firsts map bool success = init_firsts_map(); @@ -215,8 +98,46 @@ int grammar_init(void) return false; // Populate the firsts map - add_firsts_tokens(); - add_firsts_rec(); + 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_LIST, TOKEN_NEGATION); + + add_first(RULE_AND_OR, TOKEN_WORD); + add_first(RULE_AND_OR, TOKEN_IF); + add_first(RULE_AND_OR, TOKEN_NEGATION); + + add_first(RULE_PIPELINE, TOKEN_WORD); + add_first(RULE_PIPELINE, TOKEN_IF); + add_first(RULE_PIPELINE, TOKEN_NEGATION); + + add_first(RULE_COMMAND, TOKEN_WORD); + add_first(RULE_COMMAND, TOKEN_IF); + + add_first(RULE_SIMPLE_COMMAND, TOKEN_WORD); + + add_first(RULE_SHELL_COMMAND, TOKEN_IF); + + add_first(RULE_IF, TOKEN_IF); + + add_first(RULE_COMPOUND_LIST, TOKEN_NEWLINE); + add_first(RULE_COMPOUND_LIST, TOKEN_WORD); + add_first(RULE_COMPOUND_LIST, TOKEN_IF); + + add_first(RULE_ELSE_CLAUSE, TOKEN_ELSE); + add_first(RULE_ELSE_CLAUSE, TOKEN_ELIF); + + add_first(RULE_ELEMENT, TOKEN_WORD); + add_first_redir(RULE_ELEMENT); + + add_first_redir(RULE_REDIRECTION); + + add_first_redir(RULE_PREFIX); return true; } @@ -276,20 +197,16 @@ struct ast *parse_input(struct lexer_context *ctx) } struct ast *ast = parse_list(ctx); - token = PEEK_TOKEN(); - if (ast == NULL) + return NULL; + + token = PEEK_TOKEN(); + if (token->type == TOKEN_NEWLINE || token->type == TOKEN_EOF) { - if (token != NULL && token->type == TOKEN_EOF) + if (token->type == TOKEN_NEWLINE) { POP_TOKEN(); } - return NULL; - } - - if (token->type == TOKEN_NEWLINE || token->type == TOKEN_EOF) - { - POP_TOKEN(); return ast; } diff --git a/src/parser/grammar.h b/src/parser/grammar.h index 020d733..3c77747 100644 --- a/src/parser/grammar.h +++ b/src/parser/grammar.h @@ -41,13 +41,6 @@ enum rule RULE_ELEMENT, RULE_REDIRECTION, RULE_PREFIX, - RULE_FUNCDEC, - RULE_WHILE, - RULE_UNTIL, - RULE_FOR, - RULE_CASE, - RULE_CASE_CLAUSE, - RULE_CASE_ITEM, NUMBER_OF_RULES }; @@ -64,7 +57,7 @@ struct firsts_list * @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error * @warning Do not use outside the parser */ -int grammar_init(void); +bool grammar_init(void); /* * @brief Closes the grammar submodule @@ -96,7 +89,6 @@ bool is_first(struct token token, enum rule rule); * | '\n' * | EOF * ; - * @first first(list), '\n', EOF */ struct ast *parse_input(struct lexer_context *ctx); diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c index d1b7efa..d57e70e 100644 --- a/src/parser/grammar_advanced.c +++ b/src/parser/grammar_advanced.c @@ -9,8 +9,6 @@ #include "grammar.h" #include "grammar_basic.h" -// === Static functions - static enum ast_redir_type redir_tok_to_ast_type(enum token_type tok_type) { switch (tok_type) @@ -34,71 +32,6 @@ static enum ast_redir_type redir_tok_to_ast_type(enum token_type tok_type) } } -/* - * @brief parses a while/until loop starting with the condition - * (after the while/until keyword) - * @arg negate_condition Set to true for until loops, false for while loops - */ -static struct ast *parse_loop(struct lexer_context *ctx, bool negate_condition) -{ - // condition - struct ast *condition = parse_compound_list(ctx); - if (condition == NULL) - return NULL; - if (negate_condition) - { - condition = - ast_create_neg(true, condition); // TODO check result (beware to not - // exceed the function lines limit) - } - - struct token *token = PEEK_TOKEN(); - - // 'do' - if (token->type != TOKEN_DO) - { - ast_free(&condition); - perror("Syntax error: expected the 'do' keyowrd but got a different " - "token"); - return NULL; - } - POP_TOKEN(); - token = PEEK_TOKEN(); - - // body - struct ast *body = parse_compound_list(ctx); - if (body == NULL) - { - ast_free(&condition); - return NULL; - } - token = PEEK_TOKEN(); - - // 'done' - if (token->type != TOKEN_DONE) - { - ast_free(&condition); - perror("Syntax error: expected the 'done' keyowrd but got a different " - "token"); - return NULL; - } - POP_TOKEN(); - - struct ast *result = ast_create_loop(condition, body); - if (result == NULL) - { - ast_free(&condition); - ast_free(&body); - perror("Internal error: could not create ast node (is your memory full " - "?)"); - return NULL; - } - - return result; -} - -// === Functions - struct ast *parse_redirection(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); @@ -138,7 +71,7 @@ struct ast *parse_prefix(struct lexer_context *ctx) if (token->type == TOKEN_ASSIGNMENT_WORD) { token = POP_TOKEN(); - return ast_create_assignment(token->data, false); + return ast_create_assignment(token->data); } else if (is_first(*token, RULE_REDIRECTION)) return parse_redirection(ctx); @@ -148,93 +81,3 @@ struct ast *parse_prefix(struct lexer_context *ctx) return NULL; } } - -struct ast *parse_funcdec(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - struct ast *value = NULL; - char *func_name = NULL; - - if (token->type != TOKEN_WORD) - { - return NULL; - } - - // word -> func name - POP_TOKEN(); - func_name = strdup(token->data); - - // ( - token = PEEK_TOKEN(); - if (token->type != TOKEN_LEFT_PAREN) - { - free(func_name); - return NULL; - } - POP_TOKEN(); - - // ) - token = PEEK_TOKEN(); - if (token->type != TOKEN_RIGHT_PAREN) - { - free(func_name); - return NULL; - } - POP_TOKEN(); - token = PEEK_TOKEN(); - - // { \n } - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // shell_command -> value - value = parse_shell_command(ctx); - if (value == NULL) - { - free(func_name); - return NULL; - } - return ast_create_function(func_name, value); -} - -struct ast *parse_for(struct lexer_context *ctx) -{ - (void)ctx; - perror("Error: usage of a not implemented function (parse_for)"); - return NULL; -} - -struct ast *parse_while(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - - // 'while' - if (token->type != TOKEN_WHILE) - { - perror( - "Internal error: expected a TOKEN_WHILE but got a different type"); - return NULL; - } - POP_TOKEN(); - - return parse_loop(ctx, false); -} - -struct ast *parse_until(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - - // 'until' - if (token->type != TOKEN_UNTIL) - { - perror( - "Internal error: expected a TOKEN_UNTIL but got a different type"); - return NULL; - } - POP_TOKEN(); - - return parse_loop(ctx, true); -} diff --git a/src/parser/grammar_advanced.h b/src/parser/grammar_advanced.h index 3829c49..f3c27ae 100644 --- a/src/parser/grammar_advanced.h +++ b/src/parser/grammar_advanced.h @@ -24,45 +24,4 @@ struct ast *parse_redirection(struct lexer_context *ctx); */ struct ast *parse_prefix(struct lexer_context *ctx); -/* - * @brief parses a funcdec rule - * @warning Work in progress - * - * @code funcdec = WORD '(' ')' {'\n'} shell_command ; - * - * @first WORD - */ -struct ast *parse_funcdec(struct lexer_context *ctx); - -/* - * @brief parses a for rule - * - * @code rule_for = 'for' WORD - * ( [';'] | [ {'\n'} 'in' { WORD } ( ';' | '\n' ) ] ) - * {'\n'} 'do' compound_list 'done' ; - * - * @first TOKEN_FOR - */ -struct ast *parse_for(struct lexer_context *ctx); - -/* - * @brief parses a while rule - * @warning NOT IMPLEMENTED - * - * @code rule_while = 'while' compound_list 'do' compound_list 'done' ; - * - * @first TOKEN_WHILE - */ -struct ast *parse_while(struct lexer_context *ctx); - -/* - * @brief parses an until rule - * @warning NOT IMPLEMENTED - * - * @code rule_until = 'until' compound_list 'do' compound_list 'done' ; - * - * @first TOKEN_UNTIL - */ -struct ast *parse_until(struct lexer_context *ctx); - #endif /* ! GRAMMAR_ADVANCED_H */ diff --git a/src/parser/grammar_basic.c b/src/parser/grammar_basic.c index 92113fe..598c70a 100644 --- a/src/parser/grammar_basic.c +++ b/src/parser/grammar_basic.c @@ -26,58 +26,6 @@ static enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) } } -/* @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) @@ -97,13 +45,8 @@ struct ast *parse_list(struct lexer_context *ctx) token = PEEK_TOKEN(); while (token->type == TOKEN_SEMICOLON) { - // Forward - POP_TOKEN(); + token = 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); @@ -130,11 +73,16 @@ struct ast *parse_and_or(struct lexer_context *ctx) 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); + // Set left part + struct ast *left = result; - POP_TOKEN(); + // eat and_or token + token = POP_TOKEN(); + + // Set type + enum ast_and_or_type type = and_or_tok_to_ast(token->type); + token = PEEK_TOKEN(); // Skip newlines @@ -146,12 +94,6 @@ struct ast *parse_and_or(struct lexer_context *ctx) // 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) @@ -178,33 +120,30 @@ struct ast *parse_pipeline(struct lexer_context *ctx) token = PEEK_TOKEN(); } - // command rule struct ast *left = parse_command(ctx); - token = PEEK_TOKEN(); if (negation) { left = ast_create_neg(negation, left); } - // Pipes + token = PEEK_TOKEN(); while (token->type == TOKEN_PIPE) { POP_TOKEN(); - token = PEEK_TOKEN(); // skip newlines + token = PEEK_TOKEN(); 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); + token = PEEK_TOKEN(); } return left; @@ -221,19 +160,24 @@ struct ast *parse_command(struct lexer_context *ctx) } else if (is_first(*token, RULE_SHELL_COMMAND)) { - result = parse_shell_command(ctx); - } - else if (is_first(*token, RULE_FUNCDEC)) - { - result = parse_funcdec(ctx); + parse_shell_command(ctx); } else { perror("Syntax error: unexpected token"); return NULL; } +} - return result; +/* @brief: frees command_elements and redirections lists (helper func) + * @return: NULL + */ +static void *err_simple_command(struct list *command_elements, + struct list *redirections) +{ + list_deep_destroy(command_elements); + list_deep_destroy(redirections); + return NULL; } struct ast *parse_simple_command(struct lexer_context *ctx) @@ -252,7 +196,7 @@ struct ast *parse_simple_command(struct lexer_context *ctx) struct ast *prefix = parse_prefix(ctx); if (prefix == NULL) { - return err_s_com(command_elements, redirections, assignments); + return err_simple_command(command_elements, redirections); } if (prefix->type == AST_ASSIGNMENT) { @@ -268,69 +212,66 @@ struct ast *parse_simple_command(struct lexer_context *ctx) if (token->type != TOKEN_WORD) { - if (!has_prefix && token->type != TOKEN_EXPORT) + if (!has_prefix) { 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); + return err_simple_command(command_elements, redirections); } + // else : only prefixes } - else // TOKEN WORD + else { - char *command = strdup(token->data); - command_elements = list_append(command_elements, command); + if (token->type == 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); + 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_simple_command(command_elements, redirections); + } - // 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); - } + // Get element type + if (ast_is_word(element)) + { + struct ast_word *element_word = ast_get_word(element); - // Forward - token = PEEK_TOKEN(); + // TODO test this fix for the memory leaks + char *word = strdup(element_word->word); + ast_free(&element); + command_elements = list_append(command_elements, word); + // end of fix + } + 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_simple_command(command_elements, redirections); + } + + // 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 err_simple_command(command_elements, redirections); } return result; } @@ -338,28 +279,15 @@ struct ast *parse_simple_command(struct lexer_context *ctx) struct ast *parse_element(struct lexer_context *ctx) { struct token *token = PEEK_TOKEN(); - if (token->type == TOKEN_WORD || token->type == TOKEN_ASSIGNMENT_WORD) + if (token->type == TOKEN_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; + token = POP_TOKEN(); + return ast_create_word(token->data); } 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"); @@ -369,66 +297,19 @@ struct ast *parse_element(struct lexer_context *ctx) struct ast *parse_shell_command(struct lexer_context *ctx) { - struct token *token = PEEK_TOKEN(); - struct ast *result = NULL; + return parse_if_rule(ctx); +} - // '{' - if (token->type == TOKEN_LEFT_BRACKET) - { - POP_TOKEN(); - result = parse_compound_list(ctx); - if (result == NULL) - return NULL; - - // '}' - token = PEEK_TOKEN(); - if (token->type == TOKEN_LEFT_BRACKET) - { - ast_free(&result); - perror("Syntax error: bracket mismatch"); - return NULL; - } - - POP_TOKEN(); - return result; - } - // '(' - else if (token->type == TOKEN_LEFT_PAREN) - { - POP_TOKEN(); - result = parse_compound_list(ctx); - if (result == NULL) - return NULL; - - // ')' - token = PEEK_TOKEN(); - if (token->type == TOKEN_LEFT_PAREN) - { - ast_free(&result); - perror("Syntax error: parenthesis mismatch"); - return NULL; - } - POP_TOKEN(); - return ast_create_subshell(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; - } +/* @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; } struct ast *parse_if_rule(struct lexer_context *ctx) @@ -444,18 +325,14 @@ struct ast *parse_if_rule(struct lexer_context *ctx) // Condition content struct ast *condition_content = parse_compound_list(ctx); - if (condition_content == NULL) - return NULL; - token = PEEK_TOKEN(); // Then keyword + token = POP_TOKEN(); if (token->type != TOKEN_THEN) { - perror("Syntax error: Expected the 'then' keyword but token has " - "different type"); + perror("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); @@ -463,7 +340,6 @@ struct ast *parse_if_rule(struct lexer_context *ctx) { return err_if_rule(&condition_content, &then_content, NULL); } - token = PEEK_TOKEN(); struct ast *else_content = NULL; // Eventual else/elif clause(s) @@ -474,16 +350,15 @@ struct ast *parse_if_rule(struct lexer_context *ctx) { return err_if_rule(&condition_content, &then_content, NULL); } - token = PEEK_TOKEN(); } // Fi keyword + token = POP_TOKEN(); 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 = @@ -506,7 +381,7 @@ struct ast *parse_compound_list(struct lexer_context *ctx) // Skip newlines while (token->type == TOKEN_NEWLINE) { - POP_TOKEN(); + token = POP_TOKEN(); token = PEEK_TOKEN(); } @@ -515,9 +390,9 @@ struct ast *parse_compound_list(struct lexer_context *ctx) if (current_cmd == NULL) return NULL; result_list = list_append(result_list, current_cmd); - token = PEEK_TOKEN(); // Following commands + token = PEEK_TOKEN(); while (token->type == TOKEN_SEMICOLON || token->type == TOKEN_NEWLINE) { POP_TOKEN(); @@ -526,7 +401,7 @@ struct ast *parse_compound_list(struct lexer_context *ctx) // Skip newlines while (token->type == TOKEN_NEWLINE) { - POP_TOKEN(); + token = POP_TOKEN(); token = PEEK_TOKEN(); } @@ -537,21 +412,22 @@ struct ast *parse_compound_list(struct lexer_context *ctx) if (current_cmd == NULL) return NULL; result_list = list_append(result_list, current_cmd); - token = PEEK_TOKEN(); } + + token = PEEK_TOKEN(); } - // Eventual semicolon + // Eventual semicolons if (token->type == TOKEN_SEMICOLON) { - POP_TOKEN(); + token = POP_TOKEN(); token = PEEK_TOKEN(); } // Skip newlines while (token->type == TOKEN_NEWLINE) { - POP_TOKEN(); + token = POP_TOKEN(); token = PEEK_TOKEN(); } @@ -574,37 +450,25 @@ struct ast *parse_else_clause(struct lexer_context *ctx) token = POP_TOKEN(); if (token->type != TOKEN_THEN) { - perror("Expected the 'then' keyword but got a different token " - "type"); + perror( + "Expected the 'then' keyword but got a different token type"); return NULL; } + // Then clause 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; + token = PEEK_TOKEN(); 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); + struct ast *result = + ast_create_if(condition, then_content, else_content); + return result; } // Eventual else content @@ -613,15 +477,12 @@ struct ast *parse_else_clause(struct lexer_context *ctx) if (token->type == TOKEN_ELSE) { - token = POP_TOKEN(); + token = POP_TOKEN(); // eat else result = parse_compound_list(ctx); - if (result == NULL) - return NULL; } - else - { + + if (result == NULL) result = ast_create_void(); - } return result; } diff --git a/src/parser/grammar_basic.h b/src/parser/grammar_basic.h index 3ee5355..5ef6a56 100644 --- a/src/parser/grammar_basic.h +++ b/src/parser/grammar_basic.h @@ -43,7 +43,6 @@ struct ast *parse_pipeline(struct lexer_context *ctx); * * @code command = simple_command * | shell_command - * * ; * @first first(simple_command), first(shell_command) */ @@ -73,10 +72,7 @@ struct ast *parse_element(struct lexer_context *ctx); /* * @brief Only parses if rules for the moment * - * @code shell_command = '{' compound_list '}' - * | '(' compound_list ')' - * | if_rule - * ; + * @code shell_command = if_rule ; * * @first first(if_rule) */ @@ -103,7 +99,7 @@ struct ast *parse_if_rule(struct lexer_context *ctx); struct ast *parse_compound_list(struct lexer_context *ctx); /* - * @brief parses an else clause rule (inside if) + * @brief * * @code else_clause = 'else' compound_list * | 'elif' compound_list 'then' compound_list [else_clause] diff --git a/src/parser/parser.c b/src/parser/parser.c index d1f0ebc..c2b8aaf 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -10,12 +10,12 @@ static enum parser_state state = PARSER_STATE_NOT_INITIALIZED; // === Functions -int parser_init(void) +bool parser_init(void) { if (state == PARSER_STATE_READY) { perror("Internal error: tried to initialize the parser module twice."); - return false; + return NULL; } int success = grammar_init(); if (success == false) diff --git a/src/parser/parser.h b/src/parser/parser.h index dc01a5e..a79497b 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -18,7 +18,7 @@ enum parser_state * * @return Returns false on error and true on success */ -int parser_init(void); +bool parser_init(void); /* @brief Closes the parser module after use */ diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index e40612e..c2cf250 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -17,13 +17,8 @@ libutils_a_SOURCES = \ ast/ast_word.c \ ast/ast_neg.c \ ast/ast_pipe.c \ - ast/ast_loop.c \ args/args.c \ - vars/vars.c \ - main_loop/main_loop.c \ - ast/ast_assignment.c \ - ast/ast_subshell.c \ - ast/ast_function.c + vars/vars.c libutils_a_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/utils/args/args.c b/src/utils/args/args.c index 69374e2..a957382 100644 --- a/src/utils/args/args.c +++ b/src/utils/args/args.c @@ -67,7 +67,7 @@ int args_handler(int argc, char **argv, struct args_options *options, { options->type = INPUT_UNDEFINED; options->input_source = NULL; - // options->pretty_print = false; + options->pretty_print = false; options->verbose = false; struct list *args_list = NULL; @@ -76,11 +76,11 @@ int args_handler(int argc, char **argv, struct args_options *options, for (int i = 1; i < argc; i++) { - /* if (strcmp(argv[i], "--pretty-print") == 0) + if (strcmp(argv[i], "--pretty-print") == 0) { options->pretty_print = true; - } */ - if (strcmp(argv[i], "--verbose") == 0) + } + else if (strcmp(argv[i], "--verbose") == 0) { options->verbose = true; } @@ -121,7 +121,7 @@ int args_handler(int argc, char **argv, struct args_options *options, } args_in_var(vars, args_list); - list_destroy(&args_list); + list_destroy(args_list); if (options->type == INPUT_UNDEFINED) options->type = INPUT_STDIN; @@ -138,7 +138,7 @@ void args_print(struct args_options *options) : "UNDEFINED"); printf("Input source: %s\n", options->input_source ? options->input_source : "NULL"); - // printf("Pretty print: %s\n", options->pretty_print ? "true" : "false"); + printf("Pretty print: %s\n", options->pretty_print ? "true" : "false"); printf("Verbose: %s\n", options->verbose ? "true" : "false"); } @@ -147,8 +147,7 @@ void print_usage(FILE *std, const char *program_name) fprintf(std, "Usage: %s [OPTIONS] [SCRIPT] [ARGUMENTS...]\n", program_name); fprintf(std, "Options:\n"); fprintf(std, " -c [SCRIPT] Execute the given command string.\n"); - // fprintf(std, " --pretty-print Enable pretty printing of - // outputs.\n"); + fprintf(std, " --pretty-print Enable pretty printing of outputs.\n"); fprintf(std, " --verbose Enable verbose mode.\n"); fprintf(std, "If no SCRIPT is provided, input is read from standard input.\n"); diff --git a/src/utils/args/args.h b/src/utils/args/args.h index 3c02fc4..6eac20c 100644 --- a/src/utils/args/args.h +++ b/src/utils/args/args.h @@ -22,7 +22,7 @@ struct args_options /** Type of the input source */ enum input_type type; /** Enable or disable pretty printing of outputs */ - // bool pretty_print; + bool pretty_print; /** Enable or disable verbose mode */ bool verbose; }; diff --git a/src/utils/ast/ast.c b/src/utils/ast/ast.c index d16a9cb..16db64e 100644 --- a/src/utils/ast/ast.c +++ b/src/utils/ast/ast.c @@ -11,7 +11,7 @@ void ast_free(struct ast **node) { if (node == NULL || *node == NULL) { - fprintf(stderr, + perror( "WARNING: Internal error: failed to free AST node (NULL argument)"); return; } @@ -39,28 +39,14 @@ void ast_free(struct ast **node) case AST_WORD: ast_free_word(ast_get_word(*node)); break; - case AST_ASSIGNMENT: - ast_free_assignment(ast_get_assignment(*node)); - break; - case AST_NEG: - ast_free_neg(ast_get_neg(*node)); - break; - case AST_LOOP: - ast_free_loop(ast_get_loop(*node)); - break; - case AST_FUNCTION: - ast_free_function(ast_get_function(*node)); - break; - case AST_SUBSHELL: - ast_free_subshell(ast_get_subshell(*node)); - break; + case AST_VOID: case AST_END: break; default: - fprintf(stderr, "WARNING: Internal error:" - " failed to free an AST node (Unknown type)"); + perror("WARNING: Internal error: failed to free an AST node (Unknown " + "type)"); return; } @@ -80,7 +66,7 @@ struct ast *ast_create(enum ast_type type, void *data) return node; } -/* // TODO handle new types (AST_WORD, AST_PIPE, etc.) +// TODO handle new types (AST_WORD, AST_PIPE, etc.) static void ast_print_dot_recursive(struct ast *node, FILE *out) { if (!node) @@ -171,4 +157,3 @@ void ast_print_dot(struct ast *ast) fprintf(dot_pipe, "}\n"); pclose(dot_pipe); } - */ diff --git a/src/utils/ast/ast.h b/src/utils/ast/ast.h index 2eac62f..9827d8d 100644 --- a/src/utils/ast/ast.h +++ b/src/utils/ast/ast.h @@ -6,15 +6,17 @@ #include "ast_base.h" #include "ast_command.h" #include "ast_end.h" -#include "ast_function.h" #include "ast_if.h" #include "ast_list.h" -#include "ast_loop.h" #include "ast_neg.h" #include "ast_pipe.h" #include "ast_redir.h" #include "ast_void.h" #include "ast_word.h" -#include "ast_subshell.h" + +/** + * Prints the Graphviz DOT representation of the given AST to stdout. + */ +void ast_print_dot(struct ast *ast); #endif /* ! AST_H */ diff --git a/src/utils/ast/ast_assignment.c b/src/utils/ast/ast_assignment.c index 72cfdb6..a839e9e 100644 --- a/src/utils/ast/ast_assignment.c +++ b/src/utils/ast/ast_assignment.c @@ -3,7 +3,6 @@ #include "ast_assignment.h" #include -#include bool ast_is_assignment(struct ast *node) { @@ -34,15 +33,14 @@ static void init_assignments(struct ast_assignment *ast_assignment, ast_assignment->value = strdup(split_pos + 1); } -struct ast *ast_create_assignment(char *assignment, bool global) +struct ast *ast_create_assignment(char *assignment) { struct ast_assignment *assignment_data = calloc(1, sizeof(struct ast_assignment)); if (!assignment_data) return NULL; - init_assignments(assignment_data, assignment); - assignment_data->global = global; + init_assignments(assignement_data); return ast_create(AST_ASSIGNMENT, assignment_data); } diff --git a/src/utils/ast/ast_assignment.h b/src/utils/ast/ast_assignment.h index 3ebac14..5be56c8 100644 --- a/src/utils/ast/ast_assignment.h +++ b/src/utils/ast/ast_assignment.h @@ -7,12 +7,11 @@ struct ast_assignment { char *name; char *value; - bool global; }; bool ast_is_assignment(struct ast *node); struct ast_assignment *ast_get_assignment(struct ast *node); -struct ast *ast_create_assignment(char *assignment, bool global); +struct ast *ast_create_assignment(char *assignment); void ast_free_assignment(struct ast_assignment *assignment_data); #endif /* ! AST_ASSIGNMENT_H */ diff --git a/src/utils/ast/ast_base.h b/src/utils/ast/ast_base.h index de7dcfa..ae99a39 100644 --- a/src/utils/ast/ast_base.h +++ b/src/utils/ast/ast_base.h @@ -16,10 +16,7 @@ enum ast_type AST_WORD, AST_PIPE, AST_NEG, - AST_LOOP, - AST_ASSIGNMENT, - AST_FUNCTION, - AST_SUBSHELL + AST_ASSIGNMENT }; struct ast diff --git a/src/utils/ast/ast_command.c b/src/utils/ast/ast_command.c index 81dd10e..3865e0e 100644 --- a/src/utils/ast/ast_command.c +++ b/src/utils/ast/ast_command.c @@ -4,10 +4,9 @@ #include #include "../lists/lists.h" -#include "ast_list.h" struct ast *ast_create_command(struct list *command, struct list *redirections, - struct list *assignments) + struct ast_list *assignments) { struct ast_command *command_data = malloc(sizeof(struct ast_command)); if (!command_data) @@ -15,7 +14,6 @@ struct ast *ast_create_command(struct list *command, struct list *redirections, command_data->command = command; command_data->redirections = redirections; - command_data->assignments = assignments; return ast_create(AST_CMD, command_data); } diff --git a/src/utils/ast/ast_command.h b/src/utils/ast/ast_command.h index 7b24a2d..75c8b2e 100644 --- a/src/utils/ast/ast_command.h +++ b/src/utils/ast/ast_command.h @@ -7,7 +7,7 @@ struct ast_command { struct list *command; // A list of words (char*) - struct list *redirections; // A list of ASTs, all ast_redir + struct ast_list *redirections; // A list of ASTs, all ast_redir struct list *assignments; // A list of ASTs, all ast_assignment }; diff --git a/src/utils/ast/ast_function.c b/src/utils/ast/ast_function.c deleted file mode 100644 index 62b5016..0000000 --- a/src/utils/ast/ast_function.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "ast_function.h" - -#include -#include - -#include "ast_base.h" - -bool ast_is_function(struct ast *node) -{ - return node != NULL && node->type == AST_FUNCTION; -} - -struct ast_function *ast_get_function(struct ast *node) -{ - if (!ast_is_function(node)) - return NULL; - return (struct ast_function *)node->data; -} - -struct ast *ast_create_function(char *name, struct ast *value) -{ - struct ast_function *function_data = malloc(sizeof(struct ast_function)); - if (!function_data) - return NULL; - - function_data->name = name; - function_data->value = value; - - return ast_create(AST_FUNCTION, function_data); -} - -void ast_free_function(struct ast_function *function_data) -{ - if (function_data) - { - free(function_data->name); - // WARNING: this ast will be stored in the function hashmap. - // thus, it will be freed from the hashmap. - // ast_free(&function_data->value); - free(function_data); - } -} diff --git a/src/utils/ast/ast_function.h b/src/utils/ast/ast_function.h deleted file mode 100644 index 3bbc551..0000000 --- a/src/utils/ast/ast_function.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef AST_FUNCTION_H -#define AST_FUNCTION_H - -#include - -struct ast_function -{ - char *name; - struct ast *value; -}; - -/** - * @brief: Checks if the given AST node is an ast_function - */ -bool ast_is_function(struct ast *node); - -/** - * @brief: Retrieves the function data from the given AST node. - * Assumes that the node is of type AST_function. - */ -struct ast_function *ast_get_function(struct ast *node); - -/** - * @brief: Creates a new AST node representing an AST_function - * @warning: name must be already allocated. - */ -struct ast *ast_create_function(char *name, struct ast *value); -/* - * @brief: frees the given ast_function and sets the pointer to NULL. - */ -void ast_free_function(struct ast_function *function_data); - -#endif /* AST_FUNCTION_H */ diff --git a/src/utils/ast/ast_if.c b/src/utils/ast/ast_if.c index 6b0ff5d..1402ff6 100644 --- a/src/utils/ast/ast_if.c +++ b/src/utils/ast/ast_if.c @@ -19,7 +19,7 @@ struct ast *ast_create_if(struct ast *condition, struct ast *then_clause, struct ast_if *ast_get_if(struct ast *node) { - if (node == NULL || node->type != AST_IF) + if (node == NULL || node->type == AST_IF) return NULL; return node->data; } diff --git a/src/utils/ast/ast_loop.c b/src/utils/ast/ast_loop.c deleted file mode 100644 index fded922..0000000 --- a/src/utils/ast/ast_loop.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "ast_loop.h" - -#include -#include - -struct ast *ast_create_loop(struct ast *condition, struct ast *body) -{ - struct ast_loop *node_data = malloc(sizeof(struct ast_loop)); - if (!node_data) - return NULL; - - node_data->condition = condition; - node_data->body = body; - - return ast_create(AST_LOOP, node_data); -} - -struct ast_loop *ast_get_loop(struct ast *node) -{ - if (node == NULL || node->type != AST_LOOP) - return NULL; - return (struct ast_loop *)node->data; -} - -bool ast_is_loop(struct ast *node) -{ - return node != NULL && node->type == AST_LOOP; -} - -void ast_free_loop(struct ast_loop *loop_data) -{ - if (loop_data == NULL) - return; - ast_free(&loop_data->condition); - ast_free(&loop_data->body); - free(loop_data); -} diff --git a/src/utils/ast/ast_loop.h b/src/utils/ast/ast_loop.h deleted file mode 100644 index 9718db1..0000000 --- a/src/utils/ast/ast_loop.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef AST_LOOP_H -#define AST_LOOP_H - -#include "ast_base.h" - -struct ast_loop -{ - // Repeat body while condition is true - struct ast *condition; - struct ast *body; -}; - -/** - * Checks if the given AST node is a loop. - */ -bool ast_is_loop(struct ast *node); - -/** - * Retrieves the loop data from the given AST node. - * Assumes that the node is of type AST_LOOP. - */ -struct ast_loop *ast_get_loop(struct ast *node); - -/** - * Creates a new AST node representing a loop. - */ -struct ast *ast_create_loop(struct ast *condition, struct ast *body); - -/* - * @brief: frees the given ast_loop and sets the pointer to NULL. - */ -void ast_free_loop(struct ast_loop *loop_node); - -#endif /* ! AST_LOOP_H */ diff --git a/src/utils/ast/ast_subshell.c b/src/utils/ast/ast_subshell.c deleted file mode 100644 index 36efd6f..0000000 --- a/src/utils/ast/ast_subshell.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "ast_subshell.h" - -bool ast_is_subshell(struct ast *node) -{ - return node != NULL && node->type == AST_SUBSHELL; -} - -struct ast_subshell *ast_get_subshell(struct ast *node) -{ - if (ast_is_subshell(node)) - return (struct ast_subshell *)node->data; - return NULL; -} - -struct ast *ast_create_subshell(struct ast *child) -{ - struct ast_subshell *subshell = calloc(1, sizeof(struct ast_subshell)); - if (subshell == NULL) - return NULL; - subshell->child = child; - - return ast_create(AST_SUBSHELL, subshell); -} - -void ast_free_subshell(struct ast_subshell *subshell) -{ - if (!subshell) - return; - ast_free(&subshell->child); - free(subshell); -} diff --git a/src/utils/ast/ast_subshell.h b/src/utils/ast/ast_subshell.h deleted file mode 100644 index a4648ef..0000000 --- a/src/utils/ast/ast_subshell.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef AST_SUBSHELL_H -#define AST_SUBSHELL_H - -#include "ast_base.h" - -struct ast_subshell -{ - struct ast *child; -}; - -bool ast_is_subshell(struct ast *node); - -struct ast_subshell *ast_get_subshell(struct ast *node); - -struct ast *ast_create_subshell(struct ast *child); - -void ast_free_subshell(struct ast_subshell *subshell); - -#endif /* ! AST_SUBSHELL_H */ \ No newline at end of file diff --git a/src/utils/ast/ast_word.c b/src/utils/ast/ast_word.c index d83489c..6870c50 100644 --- a/src/utils/ast/ast_word.c +++ b/src/utils/ast/ast_word.c @@ -2,6 +2,7 @@ #include "ast_word.h" #include +#include #include #include @@ -42,8 +43,6 @@ void ast_free_word(struct ast_word *ast_node) if (ast_node == NULL) return; - if (ast_node->word != NULL) - free(ast_node->word); - + free(ast_node->word); free(ast_node); } diff --git a/src/utils/hash_map/hash_map.c b/src/utils/hash_map/hash_map.c index 0da4fc4..3d77734 100644 --- a/src/utils/hash_map/hash_map.c +++ b/src/utils/hash_map/hash_map.c @@ -7,8 +7,6 @@ #include #include -#include "../ast/ast.h" - /* ** Hash the key using FNV-1a 32 bits hash algorithm. */ @@ -38,14 +36,6 @@ static void destroy_pair_list(struct pair_list **p) *p = NULL; } -static void destroy_pair_list_ast(struct pair_list **p) -{ - free((char *)(*p)->key); - ast_free((*p)->value); - free((*p)); - *p = NULL; -} - struct hash_map *hash_map_init(size_t size) { struct hash_map *p = malloc(sizeof(struct hash_map)); @@ -126,30 +116,6 @@ void hash_map_free(struct hash_map **hash_map) } free((*hash_map)->data); free(*hash_map); - *hash_map = NULL; - } -} - -void hash_map_free_ast(struct hash_map **hash_map) -{ - struct pair_list *l; - struct pair_list *prev; - - if (hash_map != NULL && *hash_map != NULL) - { - for (size_t i = 0; i < (*hash_map)->size; i++) - { - l = (*hash_map)->data[i]; - while (l != NULL) - { - prev = l; - l = l->next; - destroy_pair_list_ast(&prev); - } - } - free((*hash_map)->data); - free(*hash_map); - *hash_map = NULL; } } diff --git a/src/utils/lists/lists.h b/src/utils/lists/lists.h index 9f8cebb..9b5d38e 100644 --- a/src/utils/lists/lists.h +++ b/src/utils/lists/lists.h @@ -31,7 +31,7 @@ void list_print(struct list *list); ** Release the memory used by the list. ** Does nothing if `list` is `NULL`. */ -void list_destroy(struct list **list); +void list_destroy(struct list *list); /* ** Release the memory used by the list and its content diff --git a/src/utils/lists/lists1.c b/src/utils/lists/lists1.c index 521e131..d191bf7 100644 --- a/src/utils/lists/lists1.c +++ b/src/utils/lists/lists1.c @@ -49,9 +49,9 @@ void list_print(struct list *list) } } -void list_destroy(struct list **list) +void list_destroy(struct list *list) { - struct list *elt = *list; + struct list *elt = list; struct list *next_elt; while (elt != NULL) { @@ -59,7 +59,6 @@ void list_destroy(struct list **list) free(elt); elt = next_elt; } - *list = NULL; } struct list *list_append(struct list *list, void *value) diff --git a/src/utils/main_loop/main_loop.c b/src/utils/main_loop/main_loop.c deleted file mode 100644 index 362f640..0000000 --- a/src/utils/main_loop/main_loop.c +++ /dev/null @@ -1,111 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "main_loop.h" - -// === Includes -#include -#include -#include -#include -#include -#include - -#include "../../execution/execution.h" -#include "../../io_backend/io_backend.h" -#include "../../lexer/lexer.h" -#include "../../parser/parser.h" -#include "../args/args.h" -#include "../vars/vars.h" - -// === Functions - -int err_input(struct hash_map **vars, struct lexer_context *ctx) -{ - hash_map_free(vars); - destroy_lexer_context(ctx); - return ERR_INPUT_PROCESSING; -} - -int main_loop(struct lexer_context *ctx, struct hash_map *vars) -{ - int return_code = SUCCESS; - - // Retrieve and build first AST - struct ast *command_ast = get_ast(ctx); - - // Main parse-execute loop - while (command_ast != NULL && command_ast->type != AST_END) - { - if (command_ast->type != AST_VOID) - { - // Execute AST - return_code = execution(command_ast, vars); - - // set $? variable - set_var_int(vars, "?", return_code); - } - - ast_free(&command_ast); - - // Retrieve and build next AST - command_ast = get_ast(ctx); - } - - if (command_ast == NULL) - return err_input(&vars, ctx); - - // === free - - ast_free(&command_ast); - hash_map_free(&vars); - destroy_lexer_context(ctx); - - return return_code; -} - -/* @brief: initializes a lexer context from a command string. - * @return: pointer to the lexer context, or NULL on failure. - */ -static struct lexer_context *lexer_init_from_string(char *command) -{ - // Create a lexer context from the command string - struct lexer_context *ctx = calloc(1, sizeof(struct lexer_context)); - if (ctx == NULL) - return NULL; - - ctx->end_previous_token = strdup(command); - if (ctx->end_previous_token == NULL) - { - free(ctx); - return NULL; - } - ctx->remaining_chars = strlen(command); - - return ctx; -} - -int start_subshell(struct hash_map **parent_vars, char *command) -{ - int fd = fork(); - if (fd < 0) - return ERR_GENERIC; - - else if (fd == 0) // Child process - { - struct lexer_context *ctx = lexer_init_from_string(command); - if (ctx == NULL) - return ERR_MALLOC; - int return_code = main_loop(ctx, *parent_vars); - exit(return_code); - } - else // Parent process - { - int status; - if (waitpid(fd, &status, 0) == -1) - return ERR_GENERIC; - if (WIFEXITED(status)) - return WEXITSTATUS(status); - else - return ERR_GENERIC; - } -} diff --git a/src/utils/main_loop/main_loop.h b/src/utils/main_loop/main_loop.h deleted file mode 100644 index 286c5b0..0000000 --- a/src/utils/main_loop/main_loop.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef MAIN_LOOP_H -#define MAIN_LOOP_H - -#include "../../utils/vars/vars.h" -#include "../../lexer/lexer.h" - -// === Error codes -#define SUCCESS 0 -#define ERR_INPUT_PROCESSING 2 -#define ERR_MALLOC 3 -#define ERR_GENERIC 4 - -/* @brief: main loop called from main. - * @return: exit code. - */ -int main_loop(struct lexer_context *ctx, struct hash_map *vars); - -/* - * @brief: frees the hash map and lexer context. - * @return: ERR_INPUT_PROCESSING. - */ -int err_input(struct hash_map **vars, struct lexer_context *ctx); - -/* - * @brief: starts a subshell and builds the intern lexer context - * from the string. - * @return: exit code of the subshell. - */ -int start_subshell(struct hash_map **parent_vars, char *command); - -#endif /* MAIN_LOOP_H */ \ No newline at end of file diff --git a/tests/functional/and_ors.sh b/tests/functional/and_ors.sh deleted file mode 100755 index c2f4cf0..0000000 --- a/tests/functional/and_ors.sh +++ /dev/null @@ -1,17 +0,0 @@ -true -echo 'true =' $? - -false -echo 'false =' $? - -false && true -echo 'false && true =' $? - -true && false -echo 'true && false =' $? - -true || false -echo 'true || false =' $? - -false || true -echo 'false || true =' $? diff --git a/tests/functional/func.sh b/tests/functional/func.sh deleted file mode 100755 index dbd0590..0000000 --- a/tests/functional/func.sh +++ /dev/null @@ -1,21 +0,0 @@ -func() -{ - echo hello -} - -arg_func() -{ - echo first argument is "$1" -} - -func_in_func() -{ - func -} - -func_one_line() { echo "this is on one line"; } - -func -arg_func "HERE" -func_in_func -func_one_line diff --git a/tests/functional/loops.sh b/tests/functional/loops.sh deleted file mode 100755 index c4d9896..0000000 --- a/tests/functional/loops.sh +++ /dev/null @@ -1,20 +0,0 @@ -echo "starting tests" - -while false; -do - echo "should NOT be printed" -done - -a='yes' -while [ "$a" -eq "yes" ]; -do - a="no" - echo "should be printed only once" -done; - -while true; -do - echo "yes" -done; - -echo "tests done" diff --git a/tests/functional/run-tests.sh b/tests/functional/run-tests.sh index 5592869..c21ee88 100755 --- a/tests/functional/run-tests.sh +++ b/tests/functional/run-tests.sh @@ -251,7 +251,7 @@ summarize() { # Print echo -e $BWhite "\n\n""===========" $UWhite"Summary"$Color_Off "\n" - echo -e " Passed $coverage_color$passed_tests/$total_tests$Color_Off tests ($coverage_color$tests_percentage%$Color_Off)" + echo -e " Passed $coverage_color$passed_tests/$total_tests$Color_Off tests ($coverage_color$tests_percentage$Color_Off%)" echo -e " Got $timeouts_color$timeouts_count timeout(s)$Color_Off" if [ "$OUTPUT_FILE" != "" ]; then echo $tests_percentage > "$OUTPUT_FILE"; @@ -269,6 +269,9 @@ echo -e "\n\n""===$BGreen TestsuitatorX Ultra Pro Max+ 365 Premium Gris Sidéral + +# + echo -e "\n$BBlue=== Builtins ===$Color_Off" # echo test_str "Hello" "echo Hello" @@ -374,14 +377,9 @@ test_str "If with negation" "if ! false; then echo Yes; fi" test_str "If faut aller niquer sa mere" "if false; ! false; then echo Embrasse moi; fi" -echo -e "\n$BBlue=== Loops ===$Color_Off" -test_str "While false" "while false; do false; done" -test_str "While(false) true" "while false; do true; done" -test_str "Until(true) false" "until true; do false; done" -test_str "Until true" "until true; do true; done" -# test_str "While var" "a=2; while [ \$a -eq 2 ]; do \$a=3; done" -test_str "While arithmetic" "i=0; while [ \$i -lt 3 ]; do echo \$i; i=\$((i+1)); done" -test_str "Until arithmetic" "i=0; until [ \$i -ge 3 ]; do echo \$i; i=\$((i+1)); done" +echo -e "\n$BBlue=== For/While ===$Color_Off" +test_str "While loop" "i=0; while [ \$i -lt 3 ]; do echo \$i; i=\$((i+1)); done" +test_str "Until loop" "i=0; until [ \$i -ge 3 ]; do echo \$i; i=\$((i+1)); done" test_str "While break" "while true; do echo break; break; done" test_str "While continue" "i=0; while [ \$i -lt 3 ]; do i=\$((i+1)); if [ \$i -eq 2 ]; then continue; fi; echo \$i; done" test_str "For loop basic" "for i in a b c; do echo \$i; done" diff --git a/tests/unit/expansion/expand.c b/tests/unit/expansion/expand.c index 777083e..859256e 100644 --- a/tests/unit/expansion/expand.c +++ b/tests/unit/expansion/expand.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include #include +#include #include #include @@ -16,7 +17,7 @@ Test(expand, no_expansion) char str[] = "echo something"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); bool ret = expand(ast_command, NULL); @@ -31,7 +32,7 @@ Test(expand, single_quotes_no_expansion) char str[] = "echo '$VAR'"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -50,7 +51,7 @@ Test(expand, single_dollar) char str[] = "echo $ sign"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -69,7 +70,7 @@ Test(expand, empty_braces_no_expansion) char str[] = "echo ${}"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -86,7 +87,7 @@ Test(expand, basic_expansion) char str[] = "echo $VAR"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -105,7 +106,7 @@ Test(expand, multiple_expansion) char str[] = "echo $VAR1 $VAR2 ${VAR3}"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -127,7 +128,7 @@ Test(expand, env_variable) char str[] = "echo $MY_ENV_VAR"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); setenv("MY_ENV_VAR", "environment", 0); @@ -144,7 +145,7 @@ Test(expand, undefined_variable) char str[] = "echo $UNDEFINED"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -162,7 +163,7 @@ Test(expand, nested_expansion) char str[] = "echo $B"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -177,34 +178,34 @@ Test(expand, nested_expansion) hash_map_free(&vars); } -// Test(expand, mixed_quotes_expansion) -// { -// char str[] = "echo \"$VAR1 and '$VAR2'\""; -// char *str_heap = strdup(str); -// struct list *list = list_append(NULL, str_heap); -// struct ast *ast = ast_create_command(list, NULL, NULL); -// struct ast_command *ast_command = ast_get_command(ast); +Test(expand, mixed_quotes_expansion) +{ + char str[] = "echo \"$VAR1 and '$VAR2'\""; + char *str_heap = strdup(str); + struct list *list = list_append(NULL, str_heap); + struct ast *ast = ast_create_command(list); + struct ast_command *ast_command = ast_get_command(ast); -// struct hash_map *vars = vars_init(); -// set_var_copy(vars, "VAR1", "expanded"); -// set_var_copy(vars, "VAR2", "not_expanded"); + struct hash_map *vars = vars_init(); + set_var_copy(vars, "VAR1", "expanded"); + set_var_copy(vars, "VAR2", "not_expanded"); -// bool ret = expand(ast_command, vars); -// cr_expect(ret, "expansion failed with %s", str); -// cr_expect_str_eq((char *)ast_command->command->data, -// "echo \"expanded and $VAR2\"", -// "Variable in double quotes should expand, while variable " -// "in single quotes should not"); -// ast_free(&ast); -// hash_map_free(&vars); -// } + bool ret = expand(ast_command, vars); + cr_expect(ret, "expansion failed with %s", str); + cr_expect_str_eq((char *)ast_command->command->data, + "echo \"expanded and $VAR2\"", + "Variable in double quotes should expand, while variable " + "in single quotes should not"); + ast_free(&ast); + hash_map_free(&vars); +} Test(expand, adjacent_variables) { char str[] = "echo $VAR1$VAR2"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -224,7 +225,7 @@ Test(expand, random) char str[] = "$RANDOM"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); bool ret = expand(ast_command, NULL); @@ -240,7 +241,7 @@ Test(expand, pid) char str[] = "$$"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); @@ -258,7 +259,7 @@ Test(expand, default_last_exit_code) char str[] = "$?"; char *str_heap = strdup(str); struct list *list = list_append(NULL, str_heap); - struct ast *ast = ast_create_command(list, NULL, NULL); + struct ast *ast = ast_create_command(list); struct ast_command *ast_command = ast_get_command(ast); struct hash_map *vars = vars_init(); diff --git a/tests/unit/expansion/parse_subshell.c b/tests/unit/expansion/parse_subshell.c deleted file mode 100644 index 34ada43..0000000 --- a/tests/unit/expansion/parse_subshell.c +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include - -#include "../../../src/expansion/expansion.h" - -TestSuite(parse_subshell_str); - -Test(parse_subshell_str, basic_subshell) -{ - char *input = "(ls -l)"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 7); - cr_expect_str_eq(extracted_var, "ls -l"); - free(extracted_var); -} - -Test(parse_subshell_str, multi_basic_subshell) -{ - char *input = "(echo hello) and (echo world)"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 12); - cr_expect_str_eq(extracted_var, "echo hello"); - free(extracted_var); - - input += r + 5; // skip " and " - r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 12); - cr_expect_str_eq(extracted_var, "echo world"); - free(extracted_var); -} - -Test(parse_subshell_str, incomplete_braces) -{ - char *input = "(echo hello"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} - -Test(parse_subshell_str, empty_braces) -{ - char *input = "()"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 0); - cr_expect(extracted_var == NULL); -} - -Test(parse_subshell_str, nested_subshell) -{ - char *input = "(echo (nested))"; - char *extracted_var = NULL; - size_t r = parse_subshell_str(input, &extracted_var); - - cr_expect(r == 15); - cr_expect_str_eq(extracted_var, "echo (nested)"); - free(extracted_var); - - char *nested = input + 6; // point to the nested subshell - r = parse_subshell_str(nested, &extracted_var); - cr_expect(r == 8); - cr_expect_str_eq(extracted_var, "nested"); - free(extracted_var); -} diff --git a/tests/unit/expansion/parse_var.c b/tests/unit/expansion/parse_var.c index 4dc9e08..27a4b94 100644 --- a/tests/unit/expansion/parse_var.c +++ b/tests/unit/expansion/parse_var.c @@ -1,5 +1,6 @@ #include #include +#include #include "../../../src/expansion/expansion.h" diff --git a/tests/unit/utils/args.c b/tests/unit/utils/args.c index 9f601bf..672f6e9 100644 --- a/tests/unit/utils/args.c +++ b/tests/unit/utils/args.c @@ -19,7 +19,7 @@ Test(utils_args, basic_command) int r = args_handler(argc, input, &options, vars); cr_expect(r == 0); - // cr_expect(options.pretty_print == false); + cr_expect(options.pretty_print == false); cr_expect(options.verbose == false); cr_expect(options.type == INPUT_CMD); cr_expect(eq(options.input_source, "echo Hello, World!")); @@ -30,16 +30,14 @@ Test(utils_args, basic_command_with_flags) { int argc = 5; struct args_options options; - /* char *input[] = { "program", "--pretty-print", "-c", "echo Hello, - World!", - "--verbose" };*/ - char *input[] = { "program", "-c", "echo Hello, World!", "--verbose" }; + char *input[] = { "program", "--pretty-print", "-c", "echo Hello, World!", + "--verbose" }; struct hash_map *vars = vars_init(); int r = args_handler(argc, input, &options, vars); cr_expect(r == 0); - // cr_expect(options.pretty_print == true); + cr_expect(options.pretty_print == true); cr_expect(options.verbose == true); cr_expect(options.type == INPUT_CMD); cr_expect(eq(options.input_source, "echo Hello, World!")); @@ -56,7 +54,7 @@ Test(utils_args, basic_file_input) int r = args_handler(argc, input, &options, vars); cr_expect(r == 0); - // cr_expect(options.pretty_print == false); + cr_expect(options.pretty_print == false); cr_expect(options.verbose == false); cr_expect(options.type == INPUT_FILE); cr_expect(eq(options.input_source, "input.txt")); @@ -67,15 +65,13 @@ Test(utils_args, basic_file_input_with_flags) { int argc = 4; struct args_options options; - // char *input[] = { "program", "--verbose", "input.txt", "--pretty-print" - // }; - char *input[] = { "program", "--verbose", "input.txt" }; + char *input[] = { "program", "--verbose", "input.txt", "--pretty-print" }; struct hash_map *vars = vars_init(); int r = args_handler(argc, input, &options, vars); cr_expect(r == 0); - // cr_expect(options.pretty_print == true); + cr_expect(options.pretty_print == true); cr_expect(options.verbose == true); cr_expect(options.type == INPUT_FILE); cr_expect(eq(options.input_source, "input.txt")); @@ -92,7 +88,7 @@ Test(utils_args, basic_stdin_input) int r = args_handler(argc, input, &options, vars); cr_expect(r == 0); - // cr_expect(options.pretty_print == false); + cr_expect(options.pretty_print == false); cr_expect(options.verbose == false); cr_expect(options.type == INPUT_STDIN); cr_expect(options.input_source == NULL); @@ -103,14 +99,13 @@ Test(utils_args, pretty_print_and_verbose_flags) { int argc = 3; struct args_options options; - // char *input[] = { "program", "--pretty-print", "--verbose" }; - char *input[] = { "program", "--verbose" }; + char *input[] = { "program", "--pretty-print", "--verbose" }; struct hash_map *vars = vars_init(); int r = args_handler(argc, input, &options, vars); cr_expect(r == 0); - // cr_expect(options.pretty_print == true); + cr_expect(options.pretty_print == true); cr_expect(options.verbose == true); cr_expect(options.type == INPUT_STDIN); cr_expect(options.input_source == NULL); diff --git a/tests/unit/utils/lists.c b/tests/unit/utils/lists.c deleted file mode 100644 index c852803..0000000 --- a/tests/unit/utils/lists.c +++ /dev/null @@ -1,305 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "../../../src/utils/lists/lists.h" - -#include -#include -#include -#include -#include -#include -#include - -TestSuite(lists); - -Test(lists, append_empty) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, prepend_empty) -{ - struct list *lst = NULL; - lst = list_prepend(lst, (void *)1); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, insert_empty) -{ - struct list *lst = NULL; - lst = list_insert(lst, (void *)1, 0); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, insert_out_of_bounds) -{ - struct list *lst = NULL; - lst = list_insert(lst, (void *)1, 5); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, remove_out_of_bounds) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_remove(lst, 5); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, append_multiple) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - lst = list_append(lst, (void *)3); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next->data == (void *)2); - cr_expect(lst->next->next->data == (void *)3); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, prepend_multiple) -{ - struct list *lst = NULL; - lst = list_prepend(lst, (void *)1); - lst = list_prepend(lst, (void *)2); - lst = list_prepend(lst, (void *)3); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)3); - cr_expect(lst->next->data == (void *)2); - cr_expect(lst->next->next->data == (void *)1); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, insert_multiple) -{ - struct list *lst = NULL; - lst = list_insert(lst, (void *)1, 0); - lst = list_insert(lst, (void *)3, 1); - lst = list_insert(lst, (void *)2, 1); - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next->data == (void *)2); - cr_expect(lst->next->next->data == (void *)3); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, append) -{ - struct list *lst = NULL; - lst = list_prepend(lst, (void *)2); - lst = list_prepend(lst, (void *)1); - - lst = list_append(lst, (void *)3); - - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)1); - cr_expect(lst->next->data == (void *)2); - cr_expect(lst->next->next->data == (void *)3); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, prepend) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - - lst = list_prepend(lst, (void *)0); - - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)0); - cr_expect(lst->next->data == (void *)1); - cr_expect(lst->next->next->data == (void *)2); - cr_expect(lst->next->next->next == NULL); - cr_expect(list_length(lst) == 3); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, insert) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)3); - - lst = list_insert(lst, (void *)2, 1); - lst = list_insert(lst, (void *)0, 0); - - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)0); - cr_expect(lst->next->data == (void *)1); - cr_expect(lst->next->next->data == (void *)2); - cr_expect(lst->next->next->next->data == (void *)3); - cr_expect(lst->next->next->next->next == NULL); - cr_expect(list_length(lst) == 4); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, remove) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - lst = list_append(lst, (void *)3); - lst = list_append(lst, (void *)4); - - lst = list_remove(lst, 1); // remove 2 - lst = list_remove(lst, 2); // remove 4 - lst = list_remove(lst, 0); // remove 1 - - cr_expect(lst != NULL); - cr_expect(lst->data == (void *)3); - cr_expect(lst->next == NULL); - cr_expect(list_length(lst) == 1); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, destroy_null) -{ - struct list *lst = NULL; - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, deep_destroy_null) -{ - struct list *lst = NULL; - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, deep_destroy) -{ - struct list *lst = NULL; - lst = list_append(lst, strdup("string1")); - lst = list_append(lst, strdup("string2")); - lst = list_append(lst, strdup("string3")); - - list_deep_destroy(lst); -} - -Test(lists, length_empty) -{ - struct list *lst = NULL; - cr_expect(list_length(lst) == 0); -} - -Test(lists, print_empty, .init = cr_redirect_stdout) -{ - struct list *lst = NULL; - list_print(lst); - cr_expect_stdout_eq_str(""); -} - -Test(lists, print_non_empty, .init = cr_redirect_stdout) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - lst = list_append(lst, (void *)3); - - list_print(lst); - fflush(stdout); - cr_expect_stdout_eq_str("0x1 0x2 0x3\n"); - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -Test(lists, find_empty) -{ - struct list *lst = NULL; - cr_expect(list_find(lst, (void *)1) == -1); -} - -Test(lists, find_non_empty) -{ - struct list *lst = NULL; - lst = list_append(lst, (void *)1); - lst = list_append(lst, (void *)2); - lst = list_append(lst, (void *)3); - - cr_expect(list_find(lst, (void *)1) == 0); - cr_expect(list_find(lst, (void *)2) == 1); - cr_expect(list_find(lst, (void *)3) == 2); - cr_expect(list_find(lst, (void *)4) == -1); // not found - - list_destroy(&lst); - cr_expect(lst == NULL); -} - -static void fold_func(void *acc, void *data) -{ - *(int *)acc += *(int *)data; -} - -Test(lists, fold) -{ - struct list *lst = NULL; - int v1 = 10, v2 = 20, v3 = 30; - lst = list_append(lst, &v1); - lst = list_append(lst, &v2); - lst = list_append(lst, &v3); - - int sum = 0; - list_fold(lst, &sum, fold_func); - cr_expect(sum == 60); - - list_destroy(&lst); - cr_expect(lst == NULL); -} diff --git a/tests/unit/utils/vars.c b/tests/unit/utils/vars.c deleted file mode 100644 index 73784cf..0000000 --- a/tests/unit/utils/vars.c +++ /dev/null @@ -1,85 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "../../../src/utils/vars/vars.h" - -#include -#include -#include -#include -#include - -#include "../../../src/utils/hash_map/hash_map.h" -#include "../../../src/utils/string_utils/string_utils.h" - -TestSuite(utils_vars); - -Test(utils_vars, init_free) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - hash_map_free(&map); -} - -Test(utils_vars, get_defaults) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - cr_assert_str_eq(get_var(map, "?"), "0"); - char int_str[11]; - int_to_str((int)getpid(), int_str); - cr_assert_str_eq(get_var(map, "$"), int_str); - int_to_str((int)getuid(), int_str); - cr_assert_str_eq(get_var(map, "UID"), int_str); - - hash_map_free(&map); -} - -Test(utils_vars, set_vars) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - set_var_copy(map, "key1", "value1"); - cr_assert_str_eq(get_var(map, "key1"), "value1"); - set_var_copy(map, "key2", "value2"); - cr_assert_str_eq(get_var(map, "key2"), "value2"); - - hash_map_free(&map); -} - -Test(utils_vars, get_env_vars) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - cr_assert_eq(get_var_or_env(map, "ENV_TEST"), NULL); - setenv("ENV_TEST", "value1", 0); - cr_assert_str_eq(get_var_or_env(map, "ENV_TEST"), "value1"); - setenv("ENV_TEST", "value2", 1); - cr_assert_str_eq(get_var_or_env(map, "ENV_TEST"), "value2"); - - hash_map_free(&map); -} - -Test(utils_vars, set_vars_update) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - set_var_copy(map, "key", "value1"); - cr_assert_str_eq(get_var(map, "key"), "value1"); - set_var_copy(map, "key", "value2"); - cr_assert_str_eq(get_var(map, "key"), "value2"); - - hash_map_free(&map); -} - -Test(utils_vars, set_vars_int) -{ - struct hash_map *map = vars_init(); - cr_expect_not_null(map); - set_var_int(map, "key1", 100); - cr_assert_str_eq(get_var(map, "key1"), "100"); - set_var_int(map, "key1", 200); - cr_assert_str_eq(get_var(map, "key1"), "200"); - set_var_int(map, "key2", 10); - cr_assert_str_eq(get_var(map, "key2"), "10"); - - hash_map_free(&map); -}