diff --git a/README.md b/README.md index 0b91b92..be08206 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,25 @@ 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 +- Jean Hérail - William Valenduc +- Guillem George + +## Project status + +WIP + +## TODO + +# Autotools +implement functions in all .c files to see if everything compiles. diff --git a/build_flemme.sh b/build_flemme.sh deleted file mode 100755 index e16b9f0..0000000 --- a/build_flemme.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# build_only.sh - Automates build for 42sh with pretty output (no tests) - -set -e - -# Colors -GREEN="\033[1;32m" -RED="\033[1;31m" -YELLOW="\033[1;33m" -RESET="\033[0m" - -VERBOSE=0 -if [[ "$1" == "--verbose" ]]; then - VERBOSE=1 -fi - -run_cmd() { - local desc="$1" - shift - if [[ $VERBOSE -eq 1 ]]; then - echo -e "${YELLOW}${desc}...${RESET}" - "$@" - else - echo -ne "${YELLOW}${desc}...${RESET}" - "$@" &> /dev/null && echo -e " ${GREEN}done${RESET}" || { echo -e " ${RED}failed${RESET}"; exit 1; } - fi -} - -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' -fi - -run_cmd "Cleaning build" make clean - -if [[ $VERBOSE -eq 1 ]]; then - echo -e "${YELLOW}Building...${RESET}" - make -else - echo -ne "${YELLOW}Building...${RESET}" - echo - make -fi diff --git a/check_flemme.sh b/check_flemme.sh deleted file mode 100755 index 0543001..0000000 --- a/check_flemme.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash - -# build_and_test.sh - Automates build and test for 42sh with pretty output - -set -e - -# Colors -GREEN="\033[1;32m" -RED="\033[1;31m" -YELLOW="\033[1;33m" -RESET="\033[0m" - -VERBOSE=0 -if [[ "$1" == "--verbose" ]]; then - VERBOSE=1 -fi - -run_cmd() { - local desc="$1" - shift - if [[ $VERBOSE -eq 1 ]]; then - echo -e "${YELLOW}${desc}...${RESET}" - "$@" - else - echo -ne "${YELLOW}${desc}...${RESET}" - "$@" &> /dev/null && echo -e " ${GREEN}done${RESET}" || { echo -e " ${RED}failed${RESET}"; exit 1; } - fi -} - -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' -fi - -run_cmd "Cleaning build" make clean -if [[ $VERBOSE -eq 1 ]]; then - echo -e "${YELLOW}Running tests...${RESET}" - make check -else - echo -ne "${YELLOW}Running tests...${RESET}" - echo - make check -fi 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..82fcacf 100644 --- a/src/execution/execution.c +++ b/src/execution/execution.c @@ -1,15 +1,45 @@ #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" + +// --- Helpers --- + +/** + * @brief converts a linked list of command arguments to an argv array. Don't + * forget to free the result + * + * @param command_list Linked list of command arguments + * @return char** Array of command arguments suitable for execvp. Terminated by + * NULL + */ + +/** + * @brief Tries to execute a builtin command if the command matches a builtin + * + * @param argv Array of command arguments + * @return int Exit status of the builtin command, or -1 if not a builtin + */ + +// --- Execution Core --- + +/** + * @brief Executes a command represented by an ast_command structure + * + * @param command The command to execute + * @return int The exit status of the command + */ // Refactored: delegates to helpers in execution_helpers.c #include "execution_helpers.h" @@ -19,55 +49,26 @@ 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); + case AST_REDIR: + return exec_ast_redir(ast_get_redir(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..8041a3c 100644 --- a/src/execution/execution_helpers.c +++ b/src/execution/execution_helpers.c @@ -1,106 +1,19 @@ #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 int open_redir_file(const struct ast_redir *redir, int *flags, int *mode) -{ - *mode = 0644; - if (redir->type == AST_REDIR_TYPE_GREAT - || redir->type == AST_REDIR_TYPE_CLOBBER) - { - *flags = O_WRONLY | O_CREAT | O_TRUNC; - return open(redir->filename, *flags, *mode); - } - else if (redir->type == AST_REDIR_TYPE_DGREAT) - { - *flags = O_WRONLY | O_CREAT | O_APPEND; - return open(redir->filename, *flags, *mode); - } - else if (redir->type == AST_REDIR_TYPE_LESS) - { - *flags = O_RDONLY; - return open(redir->filename, *flags); - } - return -3; // not a file open -} - -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); - 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; - } - } - redir->saved_fd = dup(target_fd); - int new_fd = -1; - int flags = 0; - int 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) - { - new_fd = open_redir_file(redir, &flags, &mode); - if (new_fd == -1) - { - perror("open"); - return -1; - } - } - else if (redir->type == AST_REDIR_TYPE_GREATAND - || redir->type == AST_REDIR_TYPE_LESSAND) - { - new_fd = atoi(redir->filename); - } - if (dup2(new_fd, target_fd) == -1) - { - perror("dup2"); - if (new_fd != -1) - { - close(new_fd); - } - return -1; - } - if (new_fd != -1) - { - close(new_fd); - } - redir_list = redir_list->next; - } - return 0; -} - static char **list_to_argv(struct list *command_list) { size_t len = 0; @@ -123,161 +36,53 @@ static char **list_to_argv(struct list *command_list) 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) -{ - if (str == NULL || *str == '\0') - return -1; - - int result = 0; - size_t i = 0; - while (str[i] != '\0') - { - 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)) - { - fprintf(stderr, "list of assignements contains something else"); - return 1; - } - struct ast_assignment *assignment = - ast_get_assignment(assignment_list->data); - - if (assignment->global) - { - setenv(assignment->name, assignment->value, 1); - } - set_var_copy(vars, assignment->name, assignment->value); - assignment_list = assignment_list->next; - } - return 0; -} - -// === Functions +static int try_builtin(char **argv); int exec_ast_command(struct ast_command *command, struct hash_map *vars) { - exec_assignment(command->assignments, vars); - set_all_redir(command->redirections); - + (void)vars; if (!command || !(command->command)) - { 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); + int builtin_ret = try_builtin(argv); 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; - } + return execution(if_node->then_clause, vars); 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; + return execution(if_node->else_clause, vars); } int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) @@ -288,14 +93,7 @@ int exec_ast_list(struct ast_list *list_node, struct hash_map *vars) { struct ast *child = (struct ast *)cur->data; if (!ast_is_void(child)) - { - 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; - } + ret = execution(child, vars); cur = cur->next; } return ret; @@ -304,294 +102,115 @@ 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 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 execution(ao_node->right, vars); return left_ret; } else { if (left_ret != 0) - { - int right_ret = execution(ao_node->right, vars); - return right_ret; - } + return execution(ao_node->right, vars); return left_ret; } } -void unset_all_redir(struct list *redir_list) +static int get_fd_target(const struct ast_redir *redir) { - 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] == '-') - { - 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 - i++; - } - - // Print arguments - for (; argv[i]; i++) - { - if (interpret_escapes) - print_with_escapes(argv[i]); - else - printf("%s", argv[i]); - if (argv[i + 1]) - printf(" "); - } - if (newline) - printf("\n"); - - fflush(stdout); - 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; - return 0; -} - -static int builtin_false(char **argv) -{ - (void)argv; + if (redir->io_number != -1) + return redir->io_number; + if (redir->type == AST_REDIR_TYPE_LESS + || redir->type == AST_REDIR_TYPE_DLESS + || redir->type == AST_REDIR_TYPE_LESSAND) + return 0; return 1; } -static int builtin_exit(char **argv, struct hash_map *vars) +static int open_redir_file(const struct ast_redir *redir, int *flags, int *mode) { - int exit_val = 0; - if (argv[1]) + *mode = 0644; + if (redir->type == AST_REDIR_TYPE_GREAT + || redir->type == AST_REDIR_TYPE_CLOBBER) { - exit_val = atou(argv[1]); - if (exit_val == -1) - { - fprintf(stderr, "exit: Illegal number %s\n", argv[1]); - return 2; - } + *flags = O_WRONLY | O_CREAT | O_TRUNC; + return open(redir->filename, *flags, *mode); } - - set_var_int(vars, "EXIT_VALUE", exit_val); - return EXEC_SIGNAL_EXIT; + else if (redir->type == AST_REDIR_TYPE_DGREAT) + { + *flags = O_WRONLY | O_CREAT | O_APPEND; + return open(redir->filename, *flags, *mode); + } + else if (redir->type == AST_REDIR_TYPE_LESS) + { + *flags = O_RDONLY; + return open(redir->filename, *flags); + } + return -3; // not a file open } -static int builtin_cd(char **argv, struct hash_map *vars) +static int handle_and_restore_fd(int saved_fd, int fd_target) { - const char *path = argv[1]; - if (!path) + if (saved_fd != -1) { - path = getenv("HOME"); - if (!path) + dup2(saved_fd, fd_target); + close(saved_fd); + } + else + { + close(fd_target); + } + return 0; +} + +int exec_ast_redir(struct ast_redir *redir, struct hash_map *vars) +{ + 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) + { + new_fd = open_redir_file(redir, &flags, &mode); + if (new_fd == -1) { - fprintf(stderr, "cd: HOME not set\n"); + perror("open"); + if (saved_fd != -1) + close(saved_fd); + return 1; + } + if (dup2(new_fd, fd_target) == -1) + { + perror("dup2"); + close(new_fd); + if (saved_fd != -1) + close(saved_fd); + return 1; + } + close(new_fd); + } + else if (redir->type == AST_REDIR_TYPE_GREATAND + || redir->type == AST_REDIR_TYPE_LESSAND) + { + new_fd = atoi(redir->filename); + if (dup2(new_fd, fd_target) == -1) + { + perror("dup2"); + if (saved_fd != -1) + close(saved_fd); return 1; } } - // char *pwd = getcwd("", ""); - char *pwd = get_var_or_env(vars, "PWD"); - if (chdir(path) != 0) - { - perror("cd"); - return 1; - } - set_var_copy(vars, "OLD_PWD", pwd); - set_var_copy(vars, "PWD", path); - return 0; + int ret = execution(redir->child, vars); + handle_and_restore_fd(saved_fd, fd_target); + return ret; } -static int builtin_unset(char **argv, struct hash_map *vars) +// Dummy try_builtin for linking +static int try_builtin(char **argv) { - 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 - * - * @param argv Array of command arguments - * @return int Exit status of the builtin command, or -1 if not a builtin - */ -static int try_builtin(char **argv, struct hash_map *vars) -{ - if (!argv || !argv[0]) - return 0; - - 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); - if (strcmp(argv[0], "cd") == 0) - return builtin_cd(argv, vars); - + (void)argv; return -1; } diff --git a/src/execution/execution_helpers.h b/src/execution/execution_helpers.h index 8a56e18..ebac0cb 100644 --- a/src/execution/execution_helpers.h +++ b/src/execution/execution_helpers.h @@ -4,18 +4,10 @@ #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); +int exec_ast_redir(struct ast_redir *redir, 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..ab6241e 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,39 +122,25 @@ 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 + 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 } @@ -205,20 +155,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.c b/src/lexer/lexer.c index fd5764d..ff7f351 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -78,43 +78,21 @@ static bool update_lexing_mode(char *stream, ssize_t i, return *lexing_mode != mode_before_update; } -/* @brief: updates the flags only_digits and has_equal. - * according to the character at stream[i]. - */ -static void update_flags(char *stream, ssize_t i, struct token_info *info) -{ - if (stream[i] == '=' && !info->has_equal) - { - if (i == 0) - { - perror("Syntax error: word start with a '='"); - return; - } - else - info->has_equal = true; - } - else if (!isdigit(stream[i]) && info->only_digits) - { - info->only_digits = false; - } -} - struct token *peek_token(struct lexer_context *ctx) { - stream_init(ctx); - - // Usefull to know if we are inside a quote or double quote - enum lexing_mode lexing_mode = LEXER_NORMAL; - struct token_info info = { true, 0 }; - char *stream = ctx->end_previous_token; - ssize_t i = 0; - // we already created the upcoming token during the previous call to peek() if (ctx->current_token != NULL) { return ctx->current_token; } + stream_init(ctx); + char *stream = ctx->end_previous_token; + ssize_t i = 0; + + // Usefull to know if we are inside a quote or double quote + enum lexing_mode lexing_mode = LEXER_NORMAL; + while (i < ctx->remaining_chars) { // true if we didn't encounter a quote of any type at stream[i] @@ -122,7 +100,6 @@ struct token *peek_token(struct lexer_context *ctx) if (!update_lexing_mode(stream, i, &lexing_mode) && lexing_mode == LEXER_NORMAL) { - update_flags(stream, i, &info); if (is_special_char(stream, i)) { if (i == 0) // where we create spe_char token @@ -144,7 +121,7 @@ struct token *peek_token(struct lexer_context *ctx) i++; } - struct token *tok = new_token(stream, i, &info); + struct token *tok = new_token(stream, i, ctx->only_digits); // if token is comment, we don't want it if (tok->type == TOKEN_COMMENT) @@ -161,14 +138,6 @@ struct token *peek_token(struct lexer_context *ctx) struct token *pop_token(struct lexer_context *ctx) { - stream_init(ctx); - - // Usefull to know if we are inside a quote or double quote - enum lexing_mode lexing_mode = LEXER_NORMAL; - struct token_info info = { true, 0 }; - char *stream = ctx->end_previous_token; - ssize_t i = 0; - if (ctx->current_token != NULL && ctx->current_token->type == TOKEN_EOF) { // we reached end of input, frees all the token still allocated. @@ -176,6 +145,12 @@ struct token *pop_token(struct lexer_context *ctx) free_token(&ctx->current_token); return NULL; } + stream_init(ctx); + char *stream = ctx->end_previous_token; + ssize_t i = 0; + + // Usefull to know if we are inside a quote or double quote + enum lexing_mode lexing_mode = LEXER_NORMAL; while (i < ctx->remaining_chars) { @@ -184,7 +159,6 @@ struct token *pop_token(struct lexer_context *ctx) if (!update_lexing_mode(stream, i, &lexing_mode) && lexing_mode == LEXER_NORMAL) { - update_flags(stream, i, &info); if (is_special_char(stream, i)) { if (i == 0) // where we create spe_char token @@ -210,7 +184,7 @@ struct token *pop_token(struct lexer_context *ctx) // (this should never happen) if (ctx->current_token == NULL) { - ctx->current_token = new_token(stream, i, &info); + ctx->current_token = new_token(stream, i, ctx->only_digits); } save_state(stream, i, ctx); diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index 8c5b93e..e06bec0 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -4,12 +4,6 @@ #include #include "lexer_utils.h" - -/* - * @brief returns true if token is a redir type, false otherwise - */ -bool is_token_redir(struct token *token); - /* * @brief: returns the next (newly allocated) token without consuming it. * if end of input is reached, returns a token of type TOKEN_EOF. diff --git a/src/lexer/lexer_utils.c b/src/lexer/lexer_utils.c index 53c3603..833b274 100644 --- a/src/lexer/lexer_utils.c +++ b/src/lexer/lexer_utils.c @@ -60,30 +60,34 @@ static void set_token_keyword(struct token *tok, char *begin, ssize_t size) { if (tok->type != TOKEN_NULL || size == 0) return; - if (strncmp(begin, "!", size) == 0 && size == 1) - tok->type = TOKEN_NEGATION; - else if (strncmp(begin, "if", size) == 0 && size == 2) + if (strncmp(begin, "if", size) == 0 && size == 2) + { tok->type = TOKEN_IF; + } else if (strncmp(begin, "fi", size) == 0 && size == 2) + { tok->type = TOKEN_FI; + } else if (strncmp(begin, "then", size) == 0 && size == 4) + { tok->type = TOKEN_THEN; + } else if (strncmp(begin, "else", size) == 0 && size == 4) + { 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) @@ -91,10 +95,7 @@ static void set_token_keyword(struct token *tok, char *begin, ssize_t size) tok->data = calloc(size + 1, sizeof(char)); if (tok->data == NULL) - { - perror("could not allocate memory in lexer"); return; - } strncpy(tok->data, begin, size); } @@ -105,44 +106,35 @@ 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) - { - tok->type = TOKEN_OR; - } - - else if (strncmp(begin, ">", size) == 0 && size == 1) + if (strncmp(begin, ">", size) == 0) { tok->type = TOKEN_REDIR_RIGHT; } - else if (strncmp(begin, "<", size) == 0 && size == 1) + else if (strncmp(begin, "<", size) == 0) { tok->type = TOKEN_REDIR_LEFT; } - else if (strncmp(begin, ">>", size) == 0 && size == 2) + else if (strncmp(begin, ">>", size) == 0) { tok->type = TOKEN_REDIR_DOUBLE_RIGHT; } - else if (strncmp(begin, ">&", size) == 0 && size == 2) + else if (strncmp(begin, ">&", size) == 0) { tok->type = TOKEN_REDIR_RIGHT_AMP; } - else if (strncmp(begin, ">|", size) == 0 && size == 2) + else if (strncmp(begin, ">|", size) == 0) { tok->type = TOKEN_REDIR_RIGHT_PIPE; } - else if (strncmp(begin, "<&", size) == 0 && size == 2) + else if (strncmp(begin, "<&", size) == 0) { tok->type = TOKEN_REDIR_LEFT_AMP; } - else if (strncmp(begin, "<>", size) == 0 && size == 2) + else if (strncmp(begin, "<>", size) == 0) { tok->type = TOKEN_REDIR_LEFT_RIGHT; } - else if (strncmp(begin, "|", size) == 0 && size == 1) + else if (strncmp(begin, "|", size) == 0) { tok->type = TOKEN_PIPE; } @@ -163,21 +155,6 @@ static void set_token_word(struct token *tok, char *begin, ssize_t size) } } -/* @brief: Sets the token to an assignment_word - * Also allocates the data and fills it. - */ -static void set_token_assignment(struct token *tok, char *begin, ssize_t size) -{ - if (tok->type == TOKEN_NULL && size != 0) - { - tok->type = TOKEN_ASSIGNMENT_WORD; - tok->data = calloc(size + 1, sizeof(char)); - if (tok->data == NULL) - return; - strncpy(tok->data, begin, size); - } -} - /* @brief: Sets the token to an IO number * Also allocates the data and fills it. */ @@ -201,25 +178,6 @@ static bool is_end_of_line(char c) return c == EOF || c == '\n'; } -// === Functions - -bool is_token_redir(struct token *token) -{ - switch (token->type) - { - case TOKEN_REDIR_LEFT: - case TOKEN_REDIR_RIGHT: - case TOKEN_REDIR_LEFT_RIGHT: - case TOKEN_REDIR_DOUBLE_RIGHT: - case TOKEN_REDIR_LEFT_AMP: - case TOKEN_REDIR_RIGHT_AMP: - case TOKEN_REDIR_RIGHT_PIPE: - return true; - default: - return false; - } -} - bool is_special_char(char *stream, ssize_t i) { char c = stream[i]; @@ -236,38 +194,33 @@ bool is_special_char(char *stream, ssize_t i) return strchr(special_chars, c) != NULL; } -struct token *new_token(char *begin, ssize_t size, struct token_info *info) +struct token *new_token(char *begin, ssize_t size, bool only_digits) { struct token *tok = calloc(1, sizeof(struct token)); if (tok == NULL) return NULL; - if (info->only_digits) + if (only_digits) set_token_ION(tok, begin, size); - else if (info->has_equal) - 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_word(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 +257,10 @@ 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; - - // AND - if (stream[i] == '&' && stream[i + 1] == '&') - return 2; - - // 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..6173485 100644 --- a/src/lexer/lexer_utils.h +++ b/src/lexer/lexer_utils.h @@ -10,13 +10,17 @@ struct lexer_context char *end_previous_token; ssize_t remaining_chars; + // usefull to detect IO numbers. + // tells us if we only lexed digits in current token. + bool only_digits; + struct token *previous_token; struct token *current_token; }; /* @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 { @@ -27,16 +31,16 @@ enum lexing_mode enum token_type { - // Blanks + // Special characters TOKEN_NULL = 0, TOKEN_EOF, + TOKEN_WORD, TOKEN_NEWLINE, - // words - TOKEN_WORD, - TOKEN_ASSIGNMENT_WORD, + // WARNING: quote and double quote should never be used inside a token. + TOKEN_QUOTE, + TOKEN_DOUBLE_QUOTE, - // Special characters TOKEN_GRAVE, TOKEN_SEMICOLON, TOKEN_COMMENT, @@ -47,10 +51,8 @@ enum token_type TOKEN_RIGHT_PAREN, TOKEN_LEFT_BRACKET, TOKEN_RIGHT_BRACKET, - TOKEN_PIPE, - TOKEN_NEGATION, - // Redirections + // redirections TOKEN_REDIR_LEFT, TOKEN_REDIR_RIGHT, TOKEN_REDIR_LEFT_RIGHT, @@ -60,6 +62,7 @@ enum token_type TOKEN_REDIR_RIGHT_PIPE, TOKEN_IONUMBER, + TOKEN_PIPE, // Keywords TOKEN_IF, @@ -68,14 +71,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 @@ -84,29 +80,17 @@ struct token char *data; }; -// used to give info from lexing when creating a new token. -struct token_info -{ - // usefull to detect IO numbers. - // tells us if we only lexed digits in current token. - bool only_digits; - - // usefull to detect assignments, and syntax errors with '='. - bool has_equal; -}; - /* @return: true if a special character from the grammar was found at stream[i], * false otherwise. */ bool is_special_char(char *stream, ssize_t i); -/* @brief: return a newly allocated token, with the type corresponding - * to the info given in arguments. +/* @brief: return a newly allocated token, with the corresponding type. * The data contains [size] char, starting from [begin]. * * @return: NULL on error, a token otherwise. */ -struct token *new_token(char *begin, ssize_t size, struct token_info *info); +struct token *new_token(char *begin, ssize_t size, bool only_digits); /* @brief: frees the token given in argument */ diff --git a/src/main.c b/src/main.c index cf3b791..f1d0c00 100644 --- a/src/main.c +++ b/src/main.c @@ -7,9 +7,64 @@ #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; + // 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); + + return return_code; +} + int main(int argc, char **argv) { struct hash_map *vars = vars_init(); @@ -25,7 +80,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 +95,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 +105,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/Makefile.am b/src/parser/Makefile.am index 642414b..3b7b6f5 100644 --- a/src/parser/Makefile.am +++ b/src/parser/Makefile.am @@ -2,9 +2,7 @@ lib_LIBRARIES = libparser.a libparser_a_SOURCES = \ parser.c \ - grammar.c \ - grammar_basic.c \ - grammar_advanced.c + parsing_utils.c libparser_a_CPPFLAGS = -I$(top_srcdir)/src diff --git a/src/parser/grammar.c b/src/parser/grammar.c deleted file mode 100644 index e2a4887..0000000 --- a/src/parser/grammar.c +++ /dev/null @@ -1,299 +0,0 @@ -// === Includes -#include "grammar.h" - -#include -#include - -#include "grammar_basic.h" - -// === Static variables - -// rule-indexed array containing firsts -static struct firsts_list *firsts_map = NULL; - -// === Static functions - -/* @brief Add a token to a rule's firsts (in firsts_map) - * - * @arg rule the rule to which add a first - * @arg token the token to add to the rule's firsts - * @return true on success, false on error - */ -static bool add_first(enum rule rule, enum token_type token) -{ - struct firsts_list *item = &firsts_map[rule]; - if (item->tokens != NULL) - { - // Check for duplicates - for (size_t i = 0; i < item->list_length; i++) - { - if (item->tokens[i] == token) - return true; - } - - // Append - item->list_length++; - item->tokens = realloc(item->tokens, - (item->list_length) * sizeof(enum token_type)); - } - else - { - // Create entry - item->list_length = 1; - item->tokens = calloc(1, sizeof(enum token_type)); - } - - // Check for alloc error - if (item->tokens == NULL) - { - item->list_length = 0; - return false; - } - - // Fill - item->tokens[item->list_length - 1] = 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 - */ -static bool init_firsts_map(void) -{ - firsts_map = calloc(NUMBER_OF_RULES, sizeof(struct firsts_list)); - if (firsts_map == NULL) - { - perror("Internal error: couldn't create the firsts_map (is your memory " - "full ?)"); - return false; - } - - return true; -} - -static void add_first_redir(void) -{ - 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)); -} - -// === Functions - -int grammar_init(void) -{ - // Initialize the firsts map - bool success = init_firsts_map(); - if (success != true) - return false; - - // Populate the firsts map - add_firsts_tokens(); - add_firsts_rec(); - - return true; -} - -void grammar_close(void) -{ - // Deep free firsts map - for (int i = 0; i < NUMBER_OF_RULES; i++) - { - if (firsts_map[i].tokens != NULL) - { - free(firsts_map[i].tokens); - } - } - free(firsts_map); - firsts_map = NULL; -} - -struct firsts_list *first(enum rule rule) -{ - if (firsts_map == NULL || firsts_map[rule].tokens == NULL) - { - perror("Internal error: attempted to get the firsts of a rule without " - "properly initializing the firsts map"); - return NULL; - } - - return &firsts_map[rule]; -} - -bool is_first(struct token token, enum rule rule) -{ - struct firsts_list *firsts = &firsts_map[rule]; - for (size_t i = 0; i < firsts->list_length; i++) - { - if (firsts->tokens[i] == token.type) - return true; - } - - return false; -} - -struct ast *parse_input(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - - if (token->type == TOKEN_EOF) - { - POP_TOKEN(); - return ast_create_end(); - } - - if (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - return ast_create_list(NULL); - } - - struct ast *ast = parse_list(ctx); - token = PEEK_TOKEN(); - - if (ast == NULL) - { - if (token != NULL && token->type == TOKEN_EOF) - { - POP_TOKEN(); - } - return NULL; - } - - if (token->type == TOKEN_NEWLINE || token->type == TOKEN_EOF) - { - POP_TOKEN(); - return ast; - } - - perror("Syntax error: expected newline or EOF after list"); - ast_free(&ast); - return NULL; -} diff --git a/src/parser/grammar.h b/src/parser/grammar.h deleted file mode 100644 index 020d733..0000000 --- a/src/parser/grammar.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef GRAMMAR_H -#define GRAMMAR_H - -#include - -#include "../lexer/lexer.h" - -// === Macros - -#define PEEK_TOKEN() \ - peek_token(ctx); \ - if (token == NULL) \ - { \ - perror("Internal error: cannot get the following token"); \ - return NULL; \ - } - -#define POP_TOKEN() \ - pop_token(ctx); \ - if (token == NULL) \ - { \ - perror("Internal error: cannot get the following token"); \ - return NULL; \ - } - -// === Structures - -enum rule -{ - RULE_NULL = 0, - RULE_INPUT, - RULE_LIST, - RULE_AND_OR, - RULE_PIPELINE, - RULE_COMMAND, - RULE_SIMPLE_COMMAND, - RULE_SHELL_COMMAND, - RULE_IF, - RULE_COMPOUND_LIST, - RULE_ELSE_CLAUSE, - 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 -}; - -struct firsts_list -{ - enum token_type *tokens; // Heap allocated array - size_t list_length; -}; - -// === Functions - -/* - * @brief Initializes the grammar submodule - * @return PARSER_INIT_SUCCESS on success PARSER_INIT_ERROR on error - * @warning Do not use outside the parser - */ -int grammar_init(void); - -/* - * @brief Closes the grammar submodule - * @warning Do not use outside the parser - */ -void grammar_close(void); - -/* - * @brief get the first accepted tokens of a rule - * - * @arg r the rule - * @return the accepted tokens as a firsts_list struct - */ -struct firsts_list *first(enum rule r); - -/* - * @brief tells is token belong to the firsts of a specific rule - * - * @arg token - * @arg rule - * @return true if token belongs to rule's firsts, false otherwise - */ -bool is_first(struct token token, enum rule rule); - -/* @brief Acts as the entry point of the parser, calls parse_list - * - * @code input = list '\n' - * | list EOF - * | '\n' - * | EOF - * ; - * @first first(list), '\n', EOF - */ -struct ast *parse_input(struct lexer_context *ctx); - -#endif /* ! GRAMMAR_H */ diff --git a/src/parser/grammar_advanced.c b/src/parser/grammar_advanced.c deleted file mode 100644 index d1b7efa..0000000 --- a/src/parser/grammar_advanced.c +++ /dev/null @@ -1,240 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "grammar_advanced.h" - -#include -#include -#include - -#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) - { - case TOKEN_REDIR_LEFT: - return AST_REDIR_TYPE_LESS; - case TOKEN_REDIR_RIGHT: - return AST_REDIR_TYPE_GREAT; - case TOKEN_REDIR_DOUBLE_RIGHT: - return AST_REDIR_TYPE_DGREAT; - case TOKEN_REDIR_LEFT_RIGHT: - return AST_REDIR_TYPE_LESSGREAT; - case TOKEN_REDIR_LEFT_AMP: - return AST_REDIR_TYPE_LESSAND; - case TOKEN_REDIR_RIGHT_AMP: - return AST_REDIR_TYPE_GREATAND; - case TOKEN_REDIR_RIGHT_PIPE: - return AST_REDIR_TYPE_CLOBBER; - default: - return AST_REDIR_TYPE_NULL; - } -} - -/* - * @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(); - int io_number = -1; - if (token->type == TOKEN_IONUMBER) - { - io_number = atoi(token->data); - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - if (!is_token_redir(token)) - { - perror("Syntax error: expected a redirection token but got something " - "else"); - return NULL; - } - - enum ast_redir_type redir_type = redir_tok_to_ast_type(token->type); - POP_TOKEN(); - - token = PEEK_TOKEN(); - if (token->type != TOKEN_WORD) - { - perror("Syntax error: expected a word after redirection"); - return NULL; - } - char *target = strdup(token->data); - POP_TOKEN(); - - return ast_create_redir(target, io_number, redir_type); -} - -struct ast *parse_prefix(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - if (token->type == TOKEN_ASSIGNMENT_WORD) - { - token = POP_TOKEN(); - return ast_create_assignment(token->data, false); - } - else if (is_first(*token, RULE_REDIRECTION)) - return parse_redirection(ctx); - else - { - perror("Syntax error: expected a prefix (redirection or assignment)"); - 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 deleted file mode 100644 index 3829c49..0000000 --- a/src/parser/grammar_advanced.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef GRAMMAR_ADVANCED_H -#define GRAMMAR_ADVANCED_H - -#include "grammar.h" - -// === Functions - -/* - * @brief parses a redirection rule - * - * @code redirection = [IONUMBER] ( '>' | '<' | '>>' | '>&' | '<&' | '>|' | '<>' - * ) WORD ; - * - * @first TOKEN_IONUMBER, TOKEN_REDIRECTION - */ -struct ast *parse_redirection(struct lexer_context *ctx); - -/* - * @brief parses a prefix rule - * - * @code prefix = redirection ; - * - * @first first(redirection) - */ -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 deleted file mode 100644 index 92113fe..0000000 --- a/src/parser/grammar_basic.c +++ /dev/null @@ -1,627 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "grammar_basic.h" - -#include -#include -#include - -#include "../utils/lists/lists.h" -#include "grammar.h" -#include "grammar_advanced.h" - -// === Static functions - -static enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) -{ - switch (tok_type) - { - case TOKEN_AND: - return AST_AND_OR_TYPE_AND; - case TOKEN_OR: - return AST_AND_OR_TYPE_OR; - default: - fprintf(stderr, "and_or impossible to init, wrong token type"); - return AST_AND_OR_NULL; - } -} - -/* @brief: frees all the arguments. (helper func) - * @return: NULL. - */ -static void *err_if_rule(struct ast **cond, struct ast **then_clause, - struct ast **else_clause) -{ - ast_free(cond); - ast_free(then_clause); - ast_free(else_clause); - return NULL; -} - -/* @brief: frees command_elements and redirections lists (helper func) - * @return: NULL - */ -static void *err_s_com(struct list *command_elements, struct list *redirections, - struct list *assignments) -{ - list_deep_destroy(command_elements); - list_deep_destroy(redirections); - list_deep_destroy(assignments); - return NULL; -} - -/* @brief: used when export keyword is found, and expects an assignment after. - * @return: an ast_assignment with the field [global] set to true. - */ -static struct ast *parse_export(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - if (token->type != TOKEN_EXPORT) - { - fprintf(stderr, "expected the export keyword in parse_export"); - return NULL; - } - // export - POP_TOKEN(); - - token = PEEK_TOKEN(); - - if (token->type != TOKEN_ASSIGNMENT_WORD) - { - fprintf(stderr, "in parser: export must be followed by 'x=y'"); - return NULL; - } - - // assignment - POP_TOKEN(); - - return ast_create_assignment(token->data, true); -} - -// === Functions - -struct ast *parse_list(struct lexer_context *ctx) -{ - struct list *result_list = NULL; - struct ast *current_node = NULL; - - struct token *token = PEEK_TOKEN(); - - // and_or - current_node = parse_and_or(ctx); - if (current_node == NULL) - return NULL; - result_list = list_append(result_list, current_node); - - // Following and_or commands - token = PEEK_TOKEN(); - while (token->type == TOKEN_SEMICOLON) - { - // Forward - POP_TOKEN(); - token = PEEK_TOKEN(); - - // TODO seems a little akward (not fully compliant with the grammar) - // but it's time consuming to rewrite to only cover edge cases. - // So it'll probably stay like that for now - if (is_first(*token, RULE_AND_OR)) - { - current_node = parse_and_or(ctx); - if (current_node == NULL) - { - struct ast *tmp = ast_create_list(result_list); - ast_free(&tmp); - return NULL; - } - result_list = list_append(result_list, current_node); - token = PEEK_TOKEN(); - } - } - - return ast_create_list(result_list); -} - -struct ast *parse_and_or(struct lexer_context *ctx) -{ - struct ast *result = parse_pipeline(ctx); - if (result == NULL) - return NULL; - struct token *token = PEEK_TOKEN(); - - while (token->type == TOKEN_AND || token->type == TOKEN_OR) - { - // Build AST (left part) - enum ast_and_or_type type = and_or_tok_to_ast(token->type); - struct ast *left = result; - - POP_TOKEN(); - token = PEEK_TOKEN(); - - // Skip newlines - while (token->type == TOKEN_NEWLINE) - { - token = POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // Right part - struct ast *right = parse_pipeline(ctx); - if (right == NULL) - { - ast_free(&left); - return NULL; - } - token = PEEK_TOKEN(); - - result = ast_create_and_or(left, right, type); - if (result == NULL) - { - ast_free(&left); - ast_free(&right); - return NULL; - } - } - - return result; -} - -struct ast *parse_pipeline(struct lexer_context *ctx) -{ - bool negation = false; - struct token *token = PEEK_TOKEN(); - - // Eventual '!' - if (token->type == TOKEN_NEGATION) - { - negation = true; - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // command rule - struct ast *left = parse_command(ctx); - token = PEEK_TOKEN(); - if (negation) - { - left = ast_create_neg(negation, left); - } - - // Pipes - while (token->type == TOKEN_PIPE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - - // skip newlines - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // command rule - struct ast *right = parse_command(ctx); - token = PEEK_TOKEN(); - - // Create AST - left = ast_create_pipe(left, right); - } - - return left; -} - -struct ast *parse_command(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - struct ast *result = NULL; - - if (is_first(*token, RULE_SIMPLE_COMMAND)) - { - result = parse_simple_command(ctx); - } - else if (is_first(*token, RULE_SHELL_COMMAND)) - { - result = parse_shell_command(ctx); - } - else if (is_first(*token, RULE_FUNCDEC)) - { - result = parse_funcdec(ctx); - } - else - { - perror("Syntax error: unexpected token"); - return NULL; - } - - return result; -} - -struct ast *parse_simple_command(struct lexer_context *ctx) -{ - struct list *command_elements = NULL; - struct list *redirections = NULL; // list of redirection ASTs - struct list *assignments = NULL; - - bool has_prefix = false; - struct token *token = PEEK_TOKEN(); - if (is_first(*token, RULE_PREFIX)) - { - has_prefix = true; - while (is_first(*token, RULE_PREFIX)) - { - struct ast *prefix = parse_prefix(ctx); - if (prefix == NULL) - { - return err_s_com(command_elements, redirections, assignments); - } - if (prefix->type == AST_ASSIGNMENT) - { - assignments = list_append(assignments, prefix); - } - else if (prefix->type == AST_REDIR) - { - redirections = list_append(redirections, prefix); - } - token = PEEK_TOKEN(); - } - } - - if (token->type != TOKEN_WORD) - { - if (!has_prefix && token->type != TOKEN_EXPORT) - { - perror("Expected a command but got a different token type"); - return err_s_com(command_elements, redirections, assignments); - } - if (token->type == TOKEN_EXPORT) - { - struct ast *assignment_export = parse_export(ctx); - if (assignment_export == NULL) - return err_s_com(command_elements, redirections, assignments); - - assignments = list_append(assignments, assignment_export); - } - } - else // TOKEN WORD - { - char *command = strdup(token->data); - command_elements = list_append(command_elements, command); - - POP_TOKEN(); - } - token = PEEK_TOKEN(); - // Eventual elements - while (is_first(*token, RULE_ELEMENT)) - { - // Get element - struct ast *element = parse_element(ctx); - if (element == NULL) - { - return err_s_com(command_elements, redirections, assignments); - } - - // Get element type - if (ast_is_word(element)) - { - // Extract word - struct ast_word *element_word = ast_get_word(element); - char *word = element_word->word; - element_word->word = NULL; // Prevents word to be freed - ast_free(&element); - command_elements = list_append(command_elements, word); - } - else if (ast_is_redir(element)) - { - // append redirections to the list of redirections - redirections = list_append(redirections, element); - } - else - { - perror("Internal error: unexpected return value from " - "parse_element in parse_simple_command"); - return err_s_com(command_elements, redirections, assignments); - } - - // Forward - token = PEEK_TOKEN(); - } - - struct ast *result = - ast_create_command(command_elements, redirections, assignments); - if (result == NULL) - { - return err_s_com(command_elements, redirections, assignments); - } - return result; -} - -struct ast *parse_element(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - if (token->type == TOKEN_WORD || token->type == TOKEN_ASSIGNMENT_WORD) - { - POP_TOKEN(); - - struct ast *result = ast_create_word(token->data); - if (result == NULL) - { - perror("Internal error: could not create ast node (is your memory " - "full ?)"); - return NULL; - } - - return result; - } - else if (token->type == TOKEN_IONUMBER || is_token_redir(token)) - { - return parse_redirection(ctx); - } - else if (token->type == TOKEN_EXPORT) - { - return parse_export(ctx); - } - else - { - perror("Syntax error: unexpected token at parse_element"); - return NULL; - } -} - -struct ast *parse_shell_command(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - struct ast *result = NULL; - - // '{' - 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; - } -} - -struct ast *parse_if_rule(struct lexer_context *ctx) -{ - // If keyword - struct token *token = POP_TOKEN(); - if (token->type != TOKEN_IF) - { - perror("Internal error: expected a if rule but token has different " - "type"); - return NULL; - } - - // Condition content - struct ast *condition_content = parse_compound_list(ctx); - if (condition_content == NULL) - return NULL; - token = PEEK_TOKEN(); - - // Then keyword - if (token->type != TOKEN_THEN) - { - perror("Syntax error: Expected the 'then' keyword but token has " - "different type"); - return err_if_rule(&condition_content, NULL, NULL); - } - POP_TOKEN(); - - // Then content - struct ast *then_content = parse_compound_list(ctx); - if (then_content == NULL) - { - return err_if_rule(&condition_content, &then_content, NULL); - } - token = PEEK_TOKEN(); - - struct ast *else_content = NULL; - // Eventual else/elif clause(s) - if (is_first(*token, RULE_ELSE_CLAUSE)) - { - else_content = parse_else_clause(ctx); - if (else_content == NULL) - { - return err_if_rule(&condition_content, &then_content, NULL); - } - token = PEEK_TOKEN(); - } - - // Fi keyword - if (token->type != TOKEN_FI) - { - perror("Expected the 'fi' keyword but token has different type"); - return err_if_rule(&condition_content, &then_content, &else_content); - } - POP_TOKEN(); - - // Result - struct ast *result = - ast_create_if(condition_content, then_content, else_content); - if (result == NULL) - { - perror("Internal error: could not create a new AST (AST_IF)"); - return err_if_rule(&condition_content, &then_content, &else_content); - } - - return result; -} - -struct ast *parse_compound_list(struct lexer_context *ctx) -{ - struct list *result_list = NULL; // ast* list - struct ast *current_cmd = NULL; - struct token *token = PEEK_TOKEN(); - - // Skip newlines - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // And/or - current_cmd = parse_and_or(ctx); - if (current_cmd == NULL) - return NULL; - result_list = list_append(result_list, current_cmd); - token = PEEK_TOKEN(); - - // Following commands - while (token->type == TOKEN_SEMICOLON || token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - - // Skip newlines - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // And/or - if (is_first(*token, RULE_AND_OR)) - { - current_cmd = parse_and_or(ctx); - if (current_cmd == NULL) - return NULL; - result_list = list_append(result_list, current_cmd); - token = PEEK_TOKEN(); - } - } - - // Eventual semicolon - if (token->type == TOKEN_SEMICOLON) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - // Skip newlines - while (token->type == TOKEN_NEWLINE) - { - POP_TOKEN(); - token = PEEK_TOKEN(); - } - - struct ast *result = ast_create_list(result_list); - return result; -} - -struct ast *parse_else_clause(struct lexer_context *ctx) -{ - struct token *token = PEEK_TOKEN(); - - // Eventual elif content - while (token->type == TOKEN_ELIF) - { - // Condition - token = POP_TOKEN(); - struct ast *condition = parse_compound_list(ctx); - - // Then keyword - token = POP_TOKEN(); - if (token->type != TOKEN_THEN) - { - perror("Expected the 'then' keyword but got a different token " - "type"); - return NULL; - } - - struct ast *then_content = parse_compound_list(ctx); - if (then_content == NULL) - { - ast_free(&condition); - return NULL; - } - token = PEEK_TOKEN(); - - // Eventual else clause (recursive) - struct ast *else_content = NULL; - if (token->type == TOKEN_ELSE || token->type == TOKEN_ELIF) - { - else_content = parse_else_clause(ctx); - if (else_content == NULL) - { - ast_free(&then_content); - ast_free(&condition); - return NULL; - } - } - else - { - else_content = ast_create_void(); - } - - return ast_create_if(condition, then_content, else_content); - } - - // Eventual else content - - struct ast *result = NULL; - - if (token->type == TOKEN_ELSE) - { - token = POP_TOKEN(); - result = parse_compound_list(ctx); - if (result == NULL) - return NULL; - } - else - { - result = ast_create_void(); - } - - return result; -} diff --git a/src/parser/grammar_basic.h b/src/parser/grammar_basic.h deleted file mode 100644 index 3ee5355..0000000 --- a/src/parser/grammar_basic.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef GRAMMAR_BASIC_H -#define GRAMMAR_BASIC_H - -#include "../lexer/lexer.h" -#include "../utils/ast/ast.h" - -// === Functions - -/* - * @brief parses a list of [and_or] rules separated by semicolons and that - * ends by a newline - * - * @code list = and_or { ';' and_or } [ ';' ] ; - * - * @first first(and_or) - */ -struct ast *parse_list(struct lexer_context *ctx); - -/* - * @brief Only parses a pipeline rule for the moment - * - * @code and_or = pipeline { ( '&&' | '||' ) {'\n'} pipeline } ; - * - * @first first(pipeline) - */ -struct ast *parse_and_or(struct lexer_context *ctx); - -/* - * @brief Only parses a command rule for the moment - * - * @code pipeline = ['!'] command { '|' {'\n'} command } ; - * - * @first '!', first(command) - */ -struct ast *parse_pipeline(struct lexer_context *ctx); - -/* - * @brief Parses a simple command rule or a shell command rule depending on - * the first token. - * @note - * TOKEN_WORD => simple_command - * TOKEN_IF => shell_command - * - * @code command = simple_command - * | shell_command - * - * ; - * @first first(simple_command), first(shell_command) - */ -struct ast *parse_command(struct lexer_context *ctx); - -/* - * @brief Parses a simple list of words (command and arguments) - * ending by a separator - * - * @code simple_command = WORD { element } ; - * - * @first WORD - */ -struct ast *parse_simple_command(struct lexer_context *ctx); - -/* - * @brief Parses an element rule - * - * @code element = WORD - * | redirection - * ; - * - * @first WORD, first(redirection) - */ -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 - * ; - * - * @first first(if_rule) - */ -struct ast *parse_shell_command(struct lexer_context *ctx); - -/* - * @brief Parses a if rule (condition, then-clause, elif-clause, else-clause) - * - * @code if_rule = 'if' compound_list 'then' compound_list [else_clause] 'fi' ; - * - * @first TOKEN_IF - */ -struct ast *parse_if_rule(struct lexer_context *ctx); - -/* - * @brief parses commands inside if/else clauses and returns the corresponding - * AST list - * - * @code compound_list = {'\n'} and_or { ( ';' | '\n' ) {'\n'} and_or } [';'] - * {'\n'} ; - * - * @first TOKEN_NEWLINE, first(and_or) - */ -struct ast *parse_compound_list(struct lexer_context *ctx); - -/* - * @brief parses an else clause rule (inside if) - * - * @code else_clause = 'else' compound_list - * | 'elif' compound_list 'then' compound_list [else_clause] - * ; - * - * @first TOKEN_ELSE, TOKEN_ELIF - */ -struct ast *parse_else_clause(struct lexer_context *ctx); - -#endif /* ! GRAMMAR_BASIC_H */ diff --git a/src/parser/parser.c b/src/parser/parser.c index d1f0ebc..dbe3f72 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -1,62 +1,49 @@ #include "parser.h" +#include +#include #include +#include +#include -#include "grammar.h" +#include "../lexer/lexer.h" +#include "../parser/parsing_utils.h" +#include "../utils/lists/lists.h" -// === Static variables - -static enum parser_state state = PARSER_STATE_NOT_INITIALIZED; +// === Static functions +// ... // === Functions -int parser_init(void) -{ - if (state == PARSER_STATE_READY) - { - perror("Internal error: tried to initialize the parser module twice."); - return false; - } - int success = grammar_init(); - if (success == false) - return false; - - state = PARSER_STATE_READY; - return true; -} - -void parser_close(void) -{ - if (state != PARSER_STATE_READY) - { - perror("trying to close parser which was not opened"); - } - state = PARSER_STATE_CLOSED; - // TODO close grammar -} - struct ast *get_ast(struct lexer_context *ctx) { - if (ctx == NULL) + struct token *token = PEEK_TOKEN(); + struct ast *res; + + if (token->type == TOKEN_EOF) { - perror("Internal error: called parser with no lexer context (NULL " - "pointer). Aborting."); - return NULL; + token = pop_token(ctx); + return ast_create_end(); } - if (state == PARSER_STATE_NOT_INITIALIZED) + else if (token->type == TOKEN_NEWLINE) { - perror("Internal error: attempted to call parser without initializing " - "it. Aborting."); - return NULL; + token = pop_token(ctx); + return ast_create_void(); } - if (state == PARSER_STATE_CLOSED) + else // TOKEN WORD { - perror("Internal error: attempted to call parser after closing it. " - "Aborting."); - return NULL; + res = parse_list(ctx); } - return parse_input(ctx); + /* + if (token == NULL) + { + puts("Internal error: cannot get the following token"); + puts("Hint: EOF might be missing"); + return NULL; + } + */ + return res; } // TODO @@ -64,4 +51,4 @@ struct ast *get_ast_str(char *command) { (void)command; return NULL; -} +} \ No newline at end of file diff --git a/src/parser/parser.h b/src/parser/parser.h index dc01a5e..1837e75 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -1,29 +1,9 @@ #ifndef PARSER_H #define PARSER_H -#include - #include "../lexer/lexer.h" #include "../utils/ast/ast.h" -enum parser_state -{ - PARSER_STATE_NOT_INITIALIZED = 0, - PARSER_STATE_READY, - PARSER_STATE_CLOSED -}; - -/* @brief Initializes the parser module - * @warning parser needs to be closed after use with parser_close() - * - * @return Returns false on error and true on success - */ -int parser_init(void); - -/* @brief Closes the parser module after use - */ -void parser_close(void); - /* @brief Builds the AST representation of the next command to execute. * * @return Returns the AST representation of the next command to execute. diff --git a/src/parser/parsing_utils.c b/src/parser/parsing_utils.c new file mode 100644 index 0000000..2daeb3d --- /dev/null +++ b/src/parser/parsing_utils.c @@ -0,0 +1,393 @@ +#define _POSIX_C_SOURCE 200809L + +// === Includes +#include "parsing_utils.h" + +#include +#include +#include +#include + +#include "../lexer/lexer.h" +#include "../utils/ast/ast.h" + +// === Static functions + +enum ast_and_or_type and_or_tok_to_ast(enum token_type tok_type) +{ + switch (tok_type) + { + case TOKEN_AND: + return AST_AND_OR_TYPE_AND; + case TOKEN_OR: + return AST_AND_OR_TYPE_OR; + default: + fprintf(stderr, "and_or impossible to init, wrong token type"); + return AST_AND_OR_NULL; + } +} + +/* Returns true if c is a command terminator, false otherwise +static bool isterminator(struct token *token) +{ + if (token == NULL) + return false; + + switch (token->type) + { + case TOKEN_NEWLINE: + case TOKEN_SEMICOLON: + case TOKEN_EOF: + return true; + default: + return false; + } +} + + */ + +/* @brief: returns true if token is an end of list indicator. + * @warning: not used + */ + +/* +static bool is_end_of_list(struct token *token) +{ + if (token == NULL) + return false; + + switch (token->type) + { + case TOKEN_NEWLINE: + case TOKEN_EOF: + return true; + default: + return false; + } +} +*/ + +// === Functions + +struct ast *parse_input(struct lexer_context *ctx) +{ + return parse_list(ctx); +} + +struct ast *parse_list(struct lexer_context *ctx) +{ + struct list *result_list = NULL; + struct ast *current_node = NULL; + + struct token *token = PEEK_TOKEN(); + + // and_or + current_node = parse_and_or(ctx); + if (current_node == NULL) + return NULL; + result_list = list_append(result_list, current_node); + + // Following and_or commands + token = PEEK_TOKEN(); + while (token->type == TOKEN_SEMICOLON) + { + token = POP_TOKEN(); + // if (!isterminator(token)) // Follow(list) + // { + current_node = parse_and_or(ctx); + if (current_node == NULL) + { + // TODO free list + // There must be a function for that + return NULL; + } + result_list = list_append(result_list, current_node); + // } + token = PEEK_TOKEN(); + } + // result_list = list_append(result_list, current_node); + + return ast_create_list(result_list); +} + +struct ast *parse_and_or(struct lexer_context *ctx) +{ + struct ast *result = parse_pipeline(ctx); + struct token *token = PEEK_TOKEN(); + + if (token->type == TOKEN_AND || token->type == TOKEN_OR) + { + // set left part + + struct ast *left = result; + + // 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 + while (token->type == TOKEN_NEWLINE) + { + token = POP_TOKEN(); + token = PEEK_TOKEN(); + } + + // right part + struct ast *right = parse_pipeline(ctx); + + result = ast_create_and_or(left, right, type); + } + + return result; +} + +struct ast *parse_pipeline(struct lexer_context *ctx) +{ + return parse_command(ctx); +} + +struct ast *parse_command(struct lexer_context *ctx) +{ + struct token *token = PEEK_TOKEN(); + + if (token->type == TOKEN_WORD) + { + return parse_simple_command(ctx); + } + else if (token->type == TOKEN_IF) + { + return parse_shell_command(ctx); + } + else + { + return ast_create_void(); // TODO not sure what to do + } +} + +struct ast *parse_simple_command(struct lexer_context *ctx) +{ + struct list *command_elements = NULL; + struct token *token = PEEK_TOKEN(); + if (token->type != TOKEN_WORD) + { + puts("Expected a command but got a different token type"); + return NULL; + } + + while (token->type == TOKEN_WORD) + { + token = pop_token(ctx); + if (token == NULL) + { + // TODO free + return NULL; + } + char *word = strdup(token->data); + if (word == NULL) + { + // TODO free + puts("Internal error: Couldn't copy token content (is memory full " + "?)"); + return NULL; + } + command_elements = list_append(command_elements, word); + token = peek_token(ctx); + if (token == NULL) + { + // TODO free + return NULL; + } + } + + struct ast *result = ast_create_command(command_elements); + if (result == NULL) + { + // TODO free + } + return result; +} + +struct ast *parse_shell_command(struct lexer_context *ctx) +{ + return parse_if_rule(ctx); +} + +struct ast *parse_if_rule(struct lexer_context *ctx) +{ + // If keyword + struct token *token = POP_TOKEN(); + if (token->type != TOKEN_IF) + { + puts("Internal error: expected a if rule but token has different " + "type"); + return NULL; + } + + // Condition content + struct ast *condition_content = parse_compound_list(ctx); + + // Then keyword + token = POP_TOKEN(); + if (token->type != TOKEN_THEN) + { + ast_free(&condition_content); + puts("Expected the 'then' keyword but token has different type"); + return NULL; + } + + // Then content + struct ast *then_content = parse_compound_list(ctx); + if (then_content == NULL) + { + ast_free(&condition_content); + ast_free(&then_content); + return NULL; + } + + // Eventual else/elif clause(s) + struct ast *else_content = parse_else_clause(ctx); + if (else_content == NULL) + { + ast_free(&condition_content); + ast_free(&then_content); + return NULL; + } + + token = POP_TOKEN(); + if (token->type != TOKEN_FI) + { + ast_free(&condition_content); + ast_free(&then_content); + ast_free(&else_content); + puts("Expected the 'fi' keyword but token has different type"); + return NULL; + } + + struct ast *result = + ast_create_if(condition_content, then_content, else_content); + if (result == NULL) + { + ast_free(&condition_content); + ast_free(&then_content); + ast_free(&else_content); + puts("Internal error: could not create a new AST (AST_IF)"); + return NULL; + } + + return result; +} + +struct ast *parse_compound_list(struct lexer_context *ctx) +{ + struct list *result_list = NULL; // ast* list + struct ast *current_cmd = NULL; + struct token *token = PEEK_TOKEN(); + + // Skip newlines + while (token->type == TOKEN_NEWLINE) + { + token = POP_TOKEN(); + token = PEEK_TOKEN(); + } + + // and_or + current_cmd = parse_and_or(ctx); + if (current_cmd == NULL) + return NULL; + result_list = list_append(result_list, current_cmd); + + // Following commands + token = PEEK_TOKEN(); + while (token->type == TOKEN_SEMICOLON || token->type == TOKEN_NEWLINE) + { + POP_TOKEN(); + token = PEEK_TOKEN(); + + // Skip newlines + while (token->type == TOKEN_NEWLINE) + { + token = POP_TOKEN(); + token = PEEK_TOKEN(); + } + + // and_or + current_cmd = parse_and_or(ctx); + if (current_cmd == NULL) + return NULL; + result_list = list_append(result_list, current_cmd); + + token = PEEK_TOKEN(); + } + + // Eventual semicolons + if (token->type == TOKEN_SEMICOLON) + { + token = POP_TOKEN(); + token = PEEK_TOKEN(); + } + + // Skip newlines + while (token->type == TOKEN_NEWLINE) + { + token = POP_TOKEN(); + token = PEEK_TOKEN(); + } + + struct ast *result = ast_create_list(result_list); + return result; +} + +struct ast *parse_else_clause(struct lexer_context *ctx) +{ + struct token *token = PEEK_TOKEN(); + + // Eventual elif content + while (token->type == TOKEN_ELIF) + { + // Condition + token = POP_TOKEN(); + struct ast *condition = parse_compound_list(ctx); + + // 'then' + token = POP_TOKEN(); + if (token->type != TOKEN_THEN) + { + puts("Expected the 'then' keyword but got a different token type"); + return NULL; + } + + // Then clause + struct ast *then_content = parse_compound_list(ctx); + + // 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); + } + + struct ast *result = + ast_create_if(condition, then_content, else_content); + return result; + } + + // Eventual else content + + struct ast *result = NULL; + + if (token->type == TOKEN_ELSE) + { + token = POP_TOKEN(); // eat else + result = parse_compound_list(ctx); + } + + if (result == NULL) + result = ast_create_void(); + + return result; +} diff --git a/src/parser/parsing_utils.h b/src/parser/parsing_utils.h new file mode 100644 index 0000000..89eb4bf --- /dev/null +++ b/src/parser/parsing_utils.h @@ -0,0 +1,100 @@ +#ifndef PARSING_UTILS_H +#define PARSING_UTILS_H + +#include "../lexer/lexer.h" + +// === Macros + +#define PEEK_TOKEN() \ + peek_token(ctx); \ + if (token == NULL) \ + { \ + puts("Internal error: cannot get the following token"); \ + return NULL; \ + } + +#define POP_TOKEN() \ + pop_token(ctx); \ + if (token == NULL) \ + { \ + puts("Internal error: cannot get the following token"); \ + return NULL; \ + } + +/* @brief Acts as the entry point of the parser, calls parse_list + * + * @code input = list '\n' + * | list EOF + * | '\n' + * | EOF + * ; + */ +struct ast *parse_input(struct lexer_context *ctx); + +/* @brief: parses a list of [and_or] rules separated by semicolons and that + * ends by a newline + * + * @code list = and_or { ';' and_or } [ ';' ] ; + */ +struct ast *parse_list(struct lexer_context *ctx); + +/* @brief Only parses a pipeline rule for the moment + * + * @code and_or = pipeline ; + */ +struct ast *parse_and_or(struct lexer_context *ctx); + +/* @brief Only parses a command rule for the moment + * + * @code pipeline = command ; + */ +struct ast *parse_pipeline(struct lexer_context *ctx); + +/* @brief Parses a simple command rule or a shell command rule depending on + * the first token. + * @note + * TOKEN_WORD => simple_command + * TOKEN_IF => shell_command + * + * @code command = simple_command + * | shell_command + * ; + */ +struct ast *parse_command(struct lexer_context *ctx); + +/* @brief Parses a simple list of words (command and arguments) + * ending by a separator + * + * @code simple_command = WORD { element } ; + */ +struct ast *parse_simple_command(struct lexer_context *ctx); + +/* @brief Only parses if rules for the moment + * + * @code shell_command = if_rule ; + */ +struct ast *parse_shell_command(struct lexer_context *ctx); + +/* @brief Parses a if rule (condition, then-clause, elif-clause, else-clause) + * + * @code if_rule = 'if' compound_list 'then' compound_list [else_clause] 'fi' ; + */ +struct ast *parse_if_rule(struct lexer_context *ctx); + +/* @brief parses commands inside if/else clauses and returns the corresponding + * AST list + * + * @code compound_list = {'\n'} and_or { ( ';' | '\n' ) {'\n'} and_or } [';'] + * {'\n'} ; + */ +struct ast *parse_compound_list(struct lexer_context *ctx); + +/* @brief + * + * @code else_clause = 'else' compound_list + * | 'elif' compound_list 'then' compound_list [else_clause] + * ; + */ +struct ast *parse_else_clause(struct lexer_context *ctx); + +#endif /* ! PARSING_UTILS_H */ diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am index e40612e..65cc594 100644 --- a/src/utils/Makefile.am +++ b/src/utils/Makefile.am @@ -14,16 +14,8 @@ libutils_a_SOURCES = \ ast/ast_redir.c \ ast/ast_void.c \ ast/ast_end.c \ - 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..ae042ee 100644 --- a/src/utils/ast/ast.c +++ b/src/utils/ast/ast.c @@ -2,6 +2,7 @@ #define _POSIX_C_SOURCE 200809L #include "ast.h" +#include #include #include #include @@ -9,60 +10,19 @@ void ast_free(struct ast **node) { - if (node == NULL || *node == NULL) - { - fprintf(stderr, - "WARNING: Internal error: failed to free AST node (NULL argument)"); + if (*node == NULL) return; - } - - switch ((*node)->type) - { - case AST_IF: + // ast void does not need to be freed. + if (ast_is_if(*node)) ast_free_if(ast_get_if(*node)); - break; - case AST_CMD: + else if (ast_is_command(*node)) ast_free_command(ast_get_command(*node)); - break; - case AST_LIST: + else if (ast_is_list(*node)) ast_free_list(ast_get_list(*node)); - break; - case AST_AND_OR: + else if (ast_is_and_or(*node)) ast_free_and_or(ast_get_and_or(*node)); - break; - case AST_REDIR: + else if (ast_is_redir(*node)) ast_free_redir(ast_get_redir(*node)); - break; - case AST_PIPE: - ast_free_pipe(ast_get_pipe(*node)); - break; - case AST_WORD: - ast_free_word(ast_get_word(*node)); - break; - case AST_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)"); - return; - } free(*node); *node = NULL; @@ -80,7 +40,6 @@ struct ast *ast_create(enum ast_type type, void *data) return node; } -/* // TODO handle new types (AST_WORD, AST_PIPE, etc.) static void ast_print_dot_recursive(struct ast *node, FILE *out) { if (!node) @@ -171,4 +130,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..a98d88b 100644 --- a/src/utils/ast/ast.h +++ b/src/utils/ast/ast.h @@ -2,19 +2,17 @@ #define AST_H #include "ast_and_or.h" -#include "ast_assignment.h" #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_and_or.h b/src/utils/ast/ast_and_or.h index 4813592..344cfbf 100644 --- a/src/utils/ast/ast_and_or.h +++ b/src/utils/ast/ast_and_or.h @@ -1,6 +1,8 @@ #ifndef AST_AND_OR_H #define AST_AND_OR_H +#include + #include "ast_base.h" enum ast_and_or_type diff --git a/src/utils/ast/ast_assignment.c b/src/utils/ast/ast_assignment.c deleted file mode 100644 index 72cfdb6..0000000 --- a/src/utils/ast/ast_assignment.c +++ /dev/null @@ -1,56 +0,0 @@ -#define _POSIX_C_SOURCE 200809L - -#include "ast_assignment.h" - -#include -#include - -bool ast_is_assignment(struct ast *node) -{ - return node != NULL && node->type == AST_ASSIGNMENT; -} - -struct ast_assignment *ast_get_assignment(struct ast *node) -{ - if (node == NULL || node->type != AST_ASSIGNMENT) - return NULL; - return (struct ast_assignment *)node->data; -} - -/* @brief: splits the assignement 'name=value' into 2 parts, - * and fills the fields of ast_assignment with it. - */ -static void init_assignments(struct ast_assignment *ast_assignment, - char *assignment) -{ - if (assignment == NULL) - return; - char *split_pos = strchr(assignment, '='); - if (split_pos == NULL) - return; - - *split_pos = '\0'; - ast_assignment->name = strdup(assignment); - ast_assignment->value = strdup(split_pos + 1); -} - -struct ast *ast_create_assignment(char *assignment, bool global) -{ - 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; - return ast_create(AST_ASSIGNMENT, assignment_data); -} - -void ast_free_assignment(struct ast_assignment *assignment_data) -{ - if (assignment_data == NULL) - return; - free(assignment_data->name); - free(assignment_data->value); - free(assignment_data); -} diff --git a/src/utils/ast/ast_assignment.h b/src/utils/ast/ast_assignment.h deleted file mode 100644 index 3ebac14..0000000 --- a/src/utils/ast/ast_assignment.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AST_ASSIGNMENT_H -#define AST_ASSIGNMENT_H - -#include "ast_base.h" - -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); -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..e09b25d 100644 --- a/src/utils/ast/ast_base.h +++ b/src/utils/ast/ast_base.h @@ -1,7 +1,6 @@ #ifndef AST_BASE_H #define AST_BASE_H -#include #include enum ast_type @@ -12,14 +11,7 @@ enum ast_type AST_AND_OR, AST_REDIR, AST_VOID, - AST_CMD, - AST_WORD, - AST_PIPE, - AST_NEG, - AST_LOOP, - AST_ASSIGNMENT, - AST_FUNCTION, - AST_SUBSHELL + AST_CMD }; struct ast @@ -33,7 +25,6 @@ struct ast * - struct ast_command* (AST_CMD) * - struct ast_and_or* (AST_AND_OR) * - struct ast_redir* (AST_REDIR) - * - and a lot more now... */ void *data; }; diff --git a/src/utils/ast/ast_command.c b/src/utils/ast/ast_command.c index 81dd10e..e52787e 100644 --- a/src/utils/ast/ast_command.c +++ b/src/utils/ast/ast_command.c @@ -1,35 +1,33 @@ #include "ast_command.h" +#include #include #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 *ast_create_command(struct list *command) { struct ast_command *command_data = malloc(sizeof(struct ast_command)); if (!command_data) return NULL; command_data->command = command; - command_data->redirections = redirections; - command_data->assignments = assignments; return ast_create(AST_CMD, command_data); } struct ast_command *ast_get_command(struct ast *node) { - if (node == NULL || node->type != AST_CMD) - return NULL; + assert(node != NULL); + assert(node->type == AST_CMD); return (struct ast_command *)node->data; } bool ast_is_command(struct ast *node) { - return node != NULL && node->type == AST_CMD; + assert(node != NULL); + return node->type == AST_CMD; } void ast_free_command(struct ast_command *command_data) @@ -37,7 +35,5 @@ void ast_free_command(struct ast_command *command_data) if (command_data == NULL) return; list_deep_destroy(command_data->command); - ast_list_deep_destroy(command_data->redirections); - ast_list_deep_destroy(command_data->assignments); free(command_data); } diff --git a/src/utils/ast/ast_command.h b/src/utils/ast/ast_command.h index 7b24a2d..089225f 100644 --- a/src/utils/ast/ast_command.h +++ b/src/utils/ast/ast_command.h @@ -1,14 +1,14 @@ #ifndef AST_COMMAND_H #define AST_COMMAND_H +#include + #include "../lists/lists.h" #include "ast_base.h" struct ast_command { struct list *command; // A list of words (char*) - struct list *redirections; // A list of ASTs, all ast_redir - struct list *assignments; // A list of ASTs, all ast_assignment }; /** @@ -25,8 +25,7 @@ struct ast_command *ast_get_command(struct ast *node); /** * Creates a new AST node representing a command. */ -struct ast *ast_create_command(struct list *command, struct list *redirections, - struct list *assignments); +struct ast *ast_create_command(struct list *command); /* * @brief: frees the given ast_command and sets the pointer to NULL. diff --git a/src/utils/ast/ast_end.c b/src/utils/ast/ast_end.c index 0d314bc..fbde72f 100644 --- a/src/utils/ast/ast_end.c +++ b/src/utils/ast/ast_end.c @@ -1,11 +1,13 @@ #include "ast_end.h" +#include #include #include bool ast_is_end(struct ast *node) { - return node != NULL && node->type == AST_END; + assert(node != NULL); + return node->type == AST_END; } struct ast *ast_create_end(void) diff --git a/src/utils/ast/ast_end.h b/src/utils/ast/ast_end.h index 55b5322..d846cad 100644 --- a/src/utils/ast/ast_end.h +++ b/src/utils/ast/ast_end.h @@ -1,6 +1,8 @@ #ifndef AST_END_H #define AST_END_H +#include + #include "../lists/lists.h" #include "ast_base.h" 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..cff0320 100644 --- a/src/utils/ast/ast_if.c +++ b/src/utils/ast/ast_if.c @@ -1,5 +1,6 @@ #include "ast_if.h" +#include #include #include @@ -19,14 +20,15 @@ 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) - return NULL; - return node->data; + assert(node != NULL); + assert(node->type == AST_IF); + return (struct ast_if *)node->data; } bool ast_is_if(struct ast *node) { - return node != NULL && node->type == AST_IF; + assert(node != NULL); + return node->type == AST_IF; } void ast_free_if(struct ast_if *if_data) diff --git a/src/utils/ast/ast_if.h b/src/utils/ast/ast_if.h index f1842bd..f54a795 100644 --- a/src/utils/ast/ast_if.h +++ b/src/utils/ast/ast_if.h @@ -1,6 +1,8 @@ #ifndef AST_IF_H #define AST_IF_H +#include + #include "ast_base.h" struct ast_if diff --git a/src/utils/ast/ast_list.c b/src/utils/ast/ast_list.c index cb4aaa6..2b96a23 100644 --- a/src/utils/ast/ast_list.c +++ b/src/utils/ast/ast_list.c @@ -1,4 +1,6 @@ -#include "ast_list.h" +#include + +#include "ast.h" struct ast *ast_create_list(struct list *list) { @@ -13,14 +15,13 @@ struct ast *ast_create_list(struct list *list) struct ast_list *ast_get_list(struct ast *node) { - if (node == NULL) - return NULL; - return node->data; + assert(node != NULL); + return (struct ast_list *)node->data; } bool ast_is_list(struct ast *node) { - return node != NULL && node->type == AST_LIST; + return node->type == AST_LIST; } void ast_free_list(struct ast_list *ast_list) diff --git a/src/utils/ast/ast_list.h b/src/utils/ast/ast_list.h index 21b24fb..ee98ea5 100644 --- a/src/utils/ast/ast_list.h +++ b/src/utils/ast/ast_list.h @@ -1,6 +1,8 @@ #ifndef AST_LIST_H #define AST_LIST_H +#include + #include "../lists/lists.h" #include "ast_base.h" 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_neg.c b/src/utils/ast/ast_neg.c deleted file mode 100644 index 80dc4f3..0000000 --- a/src/utils/ast/ast_neg.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "ast_neg.h" - -#include - -bool ast_is_neg(struct ast *node) -{ - return node != NULL && node->type == AST_NEG; -} - -struct ast_neg *ast_get_neg(struct ast *node) -{ - if (ast_is_neg(node)) - return node->data; - return NULL; -} - -struct ast *ast_create_neg(bool negation, struct ast *child) -{ - struct ast_neg *node = malloc(sizeof(struct ast_neg)); - if (!node) - return NULL; - - node->negation = negation; - node->child = child; - return ast_create(AST_NEG, node); -} - -void ast_free_neg(struct ast_neg *node) -{ - if (!node) - return; - ast_free(&node->child); - free(node); -} diff --git a/src/utils/ast/ast_neg.h b/src/utils/ast/ast_neg.h deleted file mode 100644 index 738c246..0000000 --- a/src/utils/ast/ast_neg.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef AST_NEG_H -#define AST_NEG_H - -#include "ast_base.h" - -struct ast_neg -{ - bool negation; // True negates the child's output - struct ast *child; -}; - -bool ast_is_neg(struct ast *node); -struct ast_neg *ast_get_neg(struct ast *node); -struct ast *ast_create_neg(bool negation, struct ast *child); -void ast_free_neg(struct ast_neg *node); - -#endif /* ! AST_NEG_H */ diff --git a/src/utils/ast/ast_pipe.c b/src/utils/ast/ast_pipe.c deleted file mode 100644 index 92754cf..0000000 --- a/src/utils/ast/ast_pipe.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "ast_pipe.h" - -#include - -bool ast_is_pipe(struct ast *node) -{ - return node != NULL && node->type == AST_REDIR; -} - -struct ast_pipe *ast_get_pipe(struct ast *node) -{ - if (ast_is_pipe(node)) - return node->data; - return NULL; -} - -struct ast *ast_create_pipe(struct ast *left, struct ast *right) -{ - struct ast_pipe *node = malloc(sizeof(struct ast_pipe)); - if (!node) - return NULL; - node->left = left; - node->right = right; - - return ast_create(AST_PIPE, node); -} - -void ast_free_pipe(struct ast_pipe *node) -{ - if (!node) - return; - ast_free(&node->left); - ast_free(&node->right); - free(node); -} diff --git a/src/utils/ast/ast_pipe.h b/src/utils/ast/ast_pipe.h deleted file mode 100644 index 930cb2c..0000000 --- a/src/utils/ast/ast_pipe.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef AST_PIPE_H -#define AST_PIPE_H - -#include "ast_base.h" - -struct ast_pipe -{ - struct ast *left; - struct ast *right; - // Output of left will be redirected to right stdin -}; - -bool ast_is_pipe(struct ast *node); -struct ast_pipe *ast_get_pipe(struct ast *node); -struct ast *ast_create_pipe(struct ast *left, struct ast *right); -void ast_free_pipe(struct ast_pipe *node); - -#endif /* ! AST_PIPE_H */ diff --git a/src/utils/ast/ast_redir.c b/src/utils/ast/ast_redir.c index d1dcedb..8f0b1e9 100644 --- a/src/utils/ast/ast_redir.c +++ b/src/utils/ast/ast_redir.c @@ -14,12 +14,13 @@ struct ast_redir *ast_get_redir(struct ast *node) return NULL; } -struct ast *ast_create_redir(char *filename, int io_number, +struct ast *ast_create_redir(struct ast *child, char *filename, int io_number, enum ast_redir_type type) { struct ast_redir *redir = malloc(sizeof(struct ast_redir)); if (!redir) return NULL; + redir->child = child; redir->filename = filename; // Takes ownership? Usually yes in simple ASTs, or dup. Let's // assume pointer copy for now, but user must manage memory. @@ -33,6 +34,7 @@ void ast_free_redir(struct ast_redir *redir) { if (!redir) return; + ast_free(&redir->child); free(redir->filename); free(redir); } diff --git a/src/utils/ast/ast_redir.h b/src/utils/ast/ast_redir.h index 9d9a9d3..4b2eb63 100644 --- a/src/utils/ast/ast_redir.h +++ b/src/utils/ast/ast_redir.h @@ -1,14 +1,15 @@ #ifndef AST_REDIR_H #define AST_REDIR_H +#include + #include "ast_base.h" enum ast_redir_type { - AST_REDIR_TYPE_NULL, AST_REDIR_TYPE_LESS, // < AST_REDIR_TYPE_GREAT, // > - AST_REDIR_TYPE_LESSGREAT, // <> + AST_REDIR_TYPE_DLESS, // << AST_REDIR_TYPE_DGREAT, // >> AST_REDIR_TYPE_LESSAND, // <& AST_REDIR_TYPE_GREATAND, // >& @@ -17,16 +18,16 @@ enum ast_redir_type struct ast_redir { + struct ast *child; char *filename; int io_number; // The FD being redirected (default -1 if not specified, // implies 0 or 1 based on type) enum ast_redir_type type; - int saved_fd; // To store the original FD for restoration (-1 before save) }; bool ast_is_redir(struct ast *node); struct ast_redir *ast_get_redir(struct ast *node); -struct ast *ast_create_redir(char *filename, int io_number, +struct ast *ast_create_redir(struct ast *child, char *filename, int io_number, enum ast_redir_type type); void ast_free_redir(struct ast_redir *redir); 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_void.c b/src/utils/ast/ast_void.c index e7d2dee..213413b 100644 --- a/src/utils/ast/ast_void.c +++ b/src/utils/ast/ast_void.c @@ -1,11 +1,13 @@ #include "ast_void.h" +#include #include #include bool ast_is_void(struct ast *node) { - return node != NULL && node->type == AST_VOID; + assert(node != NULL); + return node->type == AST_VOID; } struct ast *ast_create_void(void) diff --git a/src/utils/ast/ast_void.h b/src/utils/ast/ast_void.h index 05a5933..ecbdcf8 100644 --- a/src/utils/ast/ast_void.h +++ b/src/utils/ast/ast_void.h @@ -1,6 +1,8 @@ #ifndef AST_VOID_H #define AST_VOID_H +#include + #include "../lists/lists.h" #include "ast_base.h" diff --git a/src/utils/ast/ast_word.c b/src/utils/ast/ast_word.c deleted file mode 100644 index d83489c..0000000 --- a/src/utils/ast/ast_word.c +++ /dev/null @@ -1,49 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "ast_word.h" - -#include -#include -#include - -struct ast *ast_create_word(char *word) -{ - struct ast_word *ast_node = malloc(sizeof(struct ast_word)); - if (ast_node == NULL) - return NULL; - - ast_node->type = AST_WORD; - ast_node->word = strdup(word); - struct ast *res = ast_create(AST_WORD, ast_node); - if (res == NULL) - { - free(ast_node->word); - free(ast_node); - return NULL; - } - - return res; -} - -struct ast_word *ast_get_word(struct ast *node) -{ - if (node == NULL || node->type != AST_WORD) - return NULL; - - return node->data; -} - -bool ast_is_word(struct ast *node) -{ - return node && node->type == AST_WORD; -} - -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); -} diff --git a/src/utils/ast/ast_word.h b/src/utils/ast/ast_word.h deleted file mode 100644 index a049003..0000000 --- a/src/utils/ast/ast_word.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef AST_WORD_H -#define AST_WORD_H - -#include "ast_base.h" - -struct ast_word -{ - enum ast_type type; - char *word; -}; - -/** - * Checks if the given AST node is a command. - */ -bool ast_is_word(struct ast *node); - -/** - * Retrieves the command data from the given AST node. - * Assumes that the node is of type AST_CMD. - */ -struct ast_word *ast_get_word(struct ast *node); - -/** - * Creates a new AST node representing a command. - */ -struct ast *ast_create_word(char *word); - -/* - * @brief: frees the given ast_command and sets the pointer to NULL. - */ -void ast_free_word(struct ast_word *ast_node); - -#endif /* ! AST_WORD_H */ 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..f2cbda1 100755 --- a/tests/functional/run-tests.sh +++ b/tests/functional/run-tests.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash ################### @@ -125,7 +125,7 @@ check_result() { echo -e ' ' "Expected code $ref_code but got $actual_code" test_failed=1 # Check output - elif [[ $(diff $output $ref_output > /dev/null) -ne 0 ]]; then + elif diff $output $ref_output > /dev/null; then echo -e $BRed "FAILED" $Color_Off echo -e ' ' "on '$command'" echo -e ' ' "Output is not the one expected" @@ -226,13 +226,9 @@ test_script() { summarize() { - # Compute statistics (( passed_tests = $total_tests - $errors_count )) (( tests_percentage = 100 * $passed_tests / $total_tests )) - # Adapt color depending on results - - # Percentage if [[ tests_percentage -gt 80 ]]; then coverage_color=$BGreen elif [[ tests_percentage -gt 50 ]]; then @@ -242,19 +238,12 @@ summarize() { else coverage_color=$BRed fi - # Timeouts - if [[ $timeouts_count -eq 0 ]]; then - timeouts_color=$BGreen - else - timeouts_color=$BRed - fi - # 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 " Got $timeouts_color$timeouts_count timeout(s)$Color_Off" - if [ "$OUTPUT_FILE" != "" ]; then - echo $tests_percentage > "$OUTPUT_FILE"; + echo -e " Passed $coverage_color$passed_tests/$total_tests$Color_Off tests ($coverage_color$tests_percentage$Color_Off%)" + echo -e " Got $BRed$timeouts_count timeout(s)$Color_Off" + if [ "$OUTPUT_FILE" != "" ]; + then echo $tests_percentage > "$OUTPUT_FILE"; fi echo @@ -269,6 +258,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 +366,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/io_backend/io_backend.c b/tests/unit/io_backend/io_backend.c index 4449a57..1fa9dfa 100644 --- a/tests/unit/io_backend/io_backend.c +++ b/tests/unit/io_backend/io_backend.c @@ -9,19 +9,27 @@ TestSuite(IO_Backend); Test(IO_Backend, init_null) { - struct iob_context ctx = { .mode = IOB_MODE_NULL, .args = NULL }; + struct iob_context ctx = + { + .mode = IOB_MODE_NULL, + .args = NULL + }; int actual = iob_init(&ctx); - int expected = IOB_ERROR_BAD_ARG; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +int expected = IOB_ERROR_BAD_ARG; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); } Test(IO_Backend, init_stdin) { - struct iob_context ctx = { .mode = IOB_MODE_STDIN, .args = NULL }; + struct iob_context ctx = + { + .mode = IOB_MODE_STDIN, + .args = NULL + }; int actual = iob_init(&ctx); - int expected = 0; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); - iob_close(); +int expected = 0; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +iob_close(); } // WARNING: this one could fail because of iob_close in the previous test @@ -29,62 +37,82 @@ Test(IO_Backend, init_stdin) Test(IO_Backend, init_script) { char *script_name = "script.tmp"; - struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = script_name }; + struct iob_context ctx = { + .mode = IOB_MODE_SCRIPT, + .args = script_name + }; // Create file FILE *f = fopen(script_name, "w"); fclose(f); int actual = iob_init(&ctx); - int expected = 0; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); - iob_close(); - remove(script_name); +int expected = 0; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +iob_close(); +remove(script_name); } Test(IO_Backend, init_script_not_a_file) { char *script_name = "not_a_file.tmp"; - struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = script_name }; + struct iob_context ctx = { + .mode = IOB_MODE_SCRIPT, + .args = script_name + }; int actual = iob_init(&ctx); - int expected = IOB_ERROR_CANNOT_OPEN_FILE; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +int expected = IOB_ERROR_CANNOT_OPEN_FILE; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); } Test(IO_Backend, init_script_null) { - struct iob_context ctx = { .mode = IOB_MODE_SCRIPT, .args = NULL }; + struct iob_context ctx = + { + .mode = IOB_MODE_SCRIPT, + .args = NULL + }; int actual = iob_init(&ctx); - int expected = IOB_ERROR_CANNOT_OPEN_FILE; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +int expected = IOB_ERROR_CANNOT_OPEN_FILE; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); } Test(IO_Backend, init_cmd) { char *cmd = "iamacommand --yesido"; - struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = cmd }; + struct iob_context ctx = { + .mode = IOB_MODE_CMD, + .args = cmd + }; int actual = iob_init(&ctx); - int expected = 0; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); - iob_close(); +int expected = 0; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +iob_close(); } Test(IO_Backend, init_cmd_null) { - struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = NULL }; + struct iob_context ctx = + { + .mode = IOB_MODE_CMD, + .args = NULL + }; int actual = iob_init(&ctx); - int expected = IOB_ERROR_BAD_ARG; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +int expected = IOB_ERROR_BAD_ARG; +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); } Test(IO_Backend, init_already_init) { char *cmd = "iamacommand --yesido"; - struct iob_context ctx = { .mode = IOB_MODE_CMD, .args = cmd }; + struct iob_context ctx = { + .mode = IOB_MODE_CMD, + .args = cmd + }; iob_init(&ctx); int actual = iob_init(&ctx); int expected = IOB_ERROR_MODULE_ALREADY_INITIALIZED; - cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); - iob_close(); +cr_expect(actual == expected, "Expected: %d. Got: %d", expected, actual); +iob_close(); } Test(IO_Backend, close_not_init) 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); -} diff --git a/tests/wrap.sh b/tests/wrap.sh index 58519d3..97ebd72 100755 --- a/tests/wrap.sh +++ b/tests/wrap.sh @@ -1,5 +1,3 @@ -#!/bin/sh - if [ "$BIN_PATH" = "" ]; then export BIN_PATH="$(pwd)/42sh" fi