diff options
Diffstat (limited to '42sh/src/ast')
| -rw-r--r-- | 42sh/src/ast/Makefile.am | 14 | ||||
| -rw-r--r-- | 42sh/src/ast/ast.c | 164 | ||||
| -rw-r--r-- | 42sh/src/ast/ast.h | 221 | ||||
| -rw-r--r-- | 42sh/src/ast/ast_accessors.c | 326 | ||||
| -rw-r--r-- | 42sh/src/ast/ast_accessors.h | 63 | ||||
| -rw-r--r-- | 42sh/src/ast/ast_pretty_print.c | 247 | ||||
| -rw-r--r-- | 42sh/src/ast/ast_redirect.h | 13 |
7 files changed, 1048 insertions, 0 deletions
diff --git a/42sh/src/ast/Makefile.am b/42sh/src/ast/Makefile.am new file mode 100644 index 0000000..6967aa1 --- /dev/null +++ b/42sh/src/ast/Makefile.am @@ -0,0 +1,14 @@ +lib_LIBRARIES = libast.a + +libast_a_SOURCES = \ + ast.h \ + ast.c \ + ast_accessors.h \ + ast_accessors.c \ + ast_pretty_print.c + +libast_a_CPPFLAGS = -I$(top_srcdir)/src + +libast_a_CFLAGS = -std=c99 -Werror -Wall -Wextra -Wvla -pedantic + +noinst_LIBRARIES = libast.a diff --git a/42sh/src/ast/ast.c b/42sh/src/ast/ast.c new file mode 100644 index 0000000..41b7a55 --- /dev/null +++ b/42sh/src/ast/ast.c @@ -0,0 +1,164 @@ +#include "ast/ast.h" + +#include <stdio.h> +#include <stdlib.h> + +#include "ast/ast_accessors.h" + +static const size_t ast_size_ltable[] = { + [AST_COMMAND] = sizeof(struct ast_command), + [AST_IF] = sizeof(struct ast_if), + [AST_LOGICAL] = sizeof(struct ast_logical), + [AST_LIST] = sizeof(struct ast_list), + [AST_REDIRECTION] = sizeof(struct ast_redirection), + [AST_PIPELINE] = sizeof(struct ast_pipeline), + [AST_WHILE] = sizeof(struct ast_while), + [AST_ASSIGN] = sizeof(struct ast_assign), + [AST_FOR] = sizeof(struct ast_for), + [AST_FUNCTION] = sizeof(struct ast_function), + [AST_SUBSHELL] = sizeof(struct ast_subshell) +}; + +struct ast *ast_create(enum ast_node type) +{ + struct ast *a = calloc(1, ast_size_ltable[type]); + if (!a) + { + fprintf(stderr, "ast_create: calloc failed.\n"); + return NULL; + } + + a->type = type; + return a; +} + +static void free_str_args(struct string **args) +{ + if (!args) + { + return; + } + size_t i = 0; + while (args[i]) + { + string_free(args[i]); + i++; + } + free(args); +} + +static void _free_ast_command(struct ast *ast) +{ + free_str_args(((struct ast_command *)ast)->args); + free(ast); +} + +static void _free_ast_if(struct ast *ast) +{ + ast_free(get_i(ast, 0)); + ast_free(get_i(ast, 1)); + ast_free(get_i(ast, 2)); + free(ast); +} + +static void _free_ast_list(struct ast *ast) +{ + for (size_t i = 0; i < ((struct ast_list *)ast)->nb_children; i++) + { + ast_free(get_i(ast, i)); + } + free(((struct ast_list *)ast)->children); + free(ast); +} + +static void _free_ast_logical(struct ast *ast) +{ + ast_free(((struct ast_logical *)ast)->left); + ast_free(((struct ast_logical *)ast)->right); + free(ast); +} + +static void _free_ast_pipeline(struct ast *ast) +{ + ast_free(((struct ast_pipeline *)ast)->left); + ast_free(((struct ast_pipeline *)ast)->right); + free(ast); +} + +static void _free_redir_struct(struct ast *ast) +{ + struct ast_redirection *ast_r = ((struct ast_redirection *)ast); + string_free(ast_r->redirect.fd); + string_free(ast_r->redirect.file); + string_free(ast_r->redirect.redir); +} + +static void _free_ast_redirection(struct ast *ast) +{ + ast_free(((struct ast_redirection *)ast)->expr); + _free_redir_struct(ast); + free(ast); +} + +static void _free_ast_while(struct ast *ast) +{ + ast_free(((struct ast_while *)ast)->cond); + ast_free(((struct ast_while *)ast)->body); + free(ast); +} + +static void _free_ast_assign(struct ast *ast) +{ + string_free(((struct ast_assign *)ast)->name); + string_free(((struct ast_assign *)ast)->val); + free(ast); +} + +static void _free_ast_for(struct ast *ast) +{ + struct ast_for *ast_f = ((struct ast_for *)ast); + ast_free(ast_f->left); + ast_free(ast_f->right); + string_free(ast_f->var); + // Someone probably forgot to free the ast itself + free(ast); +} + +static void _free_ast_function(struct ast *ast) +{ + struct ast_function *ast_fun = ((struct ast_function *)ast); + ast_free(ast_fun->body); + string_free(ast_fun->name); + free(ast); +} + +static void _free_ast_subshell(struct ast *ast) +{ + ast_free(((struct ast_subshell *)ast)->body); + free(ast); +} + +typedef void (*free_fct)(struct ast *); + +static const free_fct free_function_ltable[] = { + [AST_COMMAND] = _free_ast_command, + [AST_IF] = _free_ast_if, + [AST_LOGICAL] = _free_ast_logical, + [AST_LIST] = _free_ast_list, + [AST_PIPELINE] = _free_ast_pipeline, + [AST_REDIRECTION] = _free_ast_redirection, + [AST_WHILE] = _free_ast_while, + [AST_ASSIGN] = _free_ast_assign, + [AST_FOR] = _free_ast_for, + [AST_FUNCTION] = _free_ast_function, + [AST_SUBSHELL] = _free_ast_subshell +}; + +void ast_free(struct ast *ast) +{ + if (!ast) + { + return; + } + free_function_ltable[ast->type](ast); +} diff --git a/42sh/src/ast/ast.h b/42sh/src/ast/ast.h new file mode 100644 index 0000000..3a1d20a --- /dev/null +++ b/42sh/src/ast/ast.h @@ -0,0 +1,221 @@ +#ifndef AST_H +#define AST_H + +#include <stdlib.h> + +#include "ast/ast_redirect.h" +#include "lexer/token.h" +#include "utils/libstring.h" + +/** + * @brief Each node type represent a class of elements from the SCL grammar. + */ +enum ast_node +{ + // STEP 1 + AST_COMMAND, + AST_IF, + AST_LOGICAL, + AST_LIST, + + // STEP 2 + AST_PIPELINE, + AST_REDIRECTION, + AST_WHILE, + AST_ASSIGN, + AST_FOR, + + // STEP 3 + AST_FUNCTION, + AST_SUBSHELL +}; + +/** + * @brief Base struct for the AST implementation. + */ +struct ast +{ + enum ast_node type; /**< Type of the targeted AST. Used for inheritance. */ +}; + +/** + * @brief AST which represent any kind of `command`. + * It can be a `shell command` or a `simple command`. + * Both are evaluated the same. + */ +struct ast_command +{ + struct ast base; /**< Base struct for the AST implementation. */ + struct string **args; /**< `args` used to call the given command. */ +}; + +/** + * @brief AST which represent a `if/then/elif/else` block. + * An `if` statement may not contain an `elif` or an `else` block. + * If the `if` statement contains an `elif` block, it is stored as an + * `ast_if` in the `else_body`. + */ +struct ast_if +{ + struct ast base; /**< Base struct for the AST implementation. */ + struct ast *condition; /**< Contains the AST to evaluate as a condition. */ + struct ast *then_body; /**< Contains the AST of the `then` block. */ + struct ast *else_body; /**< Contains the AST of the `else` block. */ +}; + +/** + * @brief AST which represent a shell list. + * A list can be either a `list` or a `compound list`. + * @note Although this struct contains a `nb_children` value, + * it is parsed as a binary tree, thus `nb_children` never exceeds 2. + */ +struct ast_list +{ + struct ast base; /**< Base struct for the AST implementation. */ + size_t nb_children; /**< Number of elements in the list */ + struct ast ** + children; /** Array of ASTs which represents the elements of the list */ +}; + +/** + * @brief AST which represents any logical block. + * A logical block can be either a `not` block, a `and` block or a `or` block. + * The type of the logical block is determined by the `type` value. + * The `type` value can be either `TOKEN_AND`, `TOKEN_OR` or `TOKEN_NEG`. + * The binary logical operators are evaluated following the lazy method. + * @note If the `type` value is not one of the listed above, the evaluation + * of the tree will always exit this the code 1 and write to `stderr`. + */ +struct ast_logical +{ + struct ast base; /**< Base struct for the AST implementation. */ + enum token_type type; /**< Type of the logical operator. */ + struct ast *left; /**< First block to be evaluated (always evaluated). */ + struct ast *right; /**< Second block to be evaluated (may not be evaluated, + see `note`). */ +}; + +/** + * @brief AST which represents a pipeline. + * The pipeline evaluation creates a context in which the output of + * `left` is redirected to the input of `right`. + */ +struct ast_pipeline +{ + struct ast base; /**< Base struct for the AST implementation. */ + struct ast *left; /**< The output of this AST is given to `right`. */ + struct ast *right; /**< This AST gets its input from `left`. */ +}; + +/** + * @brief AST used to represent redirections + * Redirect is a struct used to store info about this redirection + * Expr is the expression who the output shall be redirected + */ +struct ast_redirection +{ + struct ast base; /**< Base struct for the AST implementation. */ + struct redirect redirect; + struct ast *expr; +}; + +/** + * @brief AST used to represent the while loops (and also the until loops) + * Cond is the condition used to know if the loop shall still iterate + * Body is the tree to execute while the condition is still valid + */ +struct ast_while +{ + struct ast base; + struct ast *cond; + struct ast *body; +}; + +/** + * @brief Struct used for variable assignment + * Name is the name of the variable + * Val is the value of said variable + */ +struct ast_assign +{ + struct ast base; + struct string *name; + struct string *val; +}; + +/** + * @brief Left contains the args (the `in { WORD }` part). + * Right contains the body to execute. + */ +struct ast_for +{ + struct ast base; + struct ast *left; + struct ast *right; + struct string *var; +}; + +/** + * @brief AST used for function definition. + * Name contains the name of the function + * Body contains the actual ast that will be executed each time + * the function is called + */ +struct ast_function +{ + struct ast base; + struct string *name; + struct ast *body; +}; + +/** + * @brief AST used for subshell + * Body just contains the compound list to be executed in the subshell + */ +struct ast_subshell +{ + struct ast base; + struct ast *body; +}; + +union ast_caster +{ + struct ast *ast; + struct ast_command *ast_c; + struct ast_if *ast_i; + struct ast_list *ast_l; + struct ast_logical *ast_lo; + struct ast_pipeline *ast_p; + struct ast_redirection *ast_r; + struct ast_while *ast_w; + struct ast_assign *ast_a; + struct ast_for *ast_f; + struct ast_function *ast_func; + struct ast_subshell *ast_sub; +}; + +/** + * @brief Creates a heap-allocated AST of the size of ast_`type`. + * The AST is returned as a `struct ast*` but it can be casted into the right + * ast type struct. + * @param type The type of the AST to be created. + */ +struct ast *ast_create(enum ast_node type); + +/** + * @brief Recursively frees the given AST. Any heap-allocated can be passed. + * @param ast The AST to be freed. + * @note This method does not exit when `ast` is `NULL`. + */ +void ast_free(struct ast *ast); + +/** + * @brief Creates a file named `ast.dot` in the current folder which + * contains the DOT representation of the given AST. + * @param ast The AST to represent. + * @note This function is called only when the program is called with + * the `--pretty-print` flag. + */ +void pretty_print(struct ast *ast); + +#endif /* ! AST_H */ diff --git a/42sh/src/ast/ast_accessors.c b/42sh/src/ast/ast_accessors.c new file mode 100644 index 0000000..39affad --- /dev/null +++ b/42sh/src/ast/ast_accessors.c @@ -0,0 +1,326 @@ +#include "ast_accessors.h" + +static struct ast *_get_from_command(struct ast *ast, size_t index) +{ + (void)ast; + (void)index; + return NULL; +} + +static struct ast *_get_from_if(struct ast *ast, size_t index) +{ + switch (index) + { + case 0: + return ((struct ast_if *)ast)->condition; + case 1: + return ((struct ast_if *)ast)->then_body; + case 2: + return ((struct ast_if *)ast)->else_body; + default: + return NULL; + } +} + +static struct ast *_get_from_list(struct ast *ast, size_t index) +{ + if (index >= ((struct ast_list *)ast)->nb_children) + { + return NULL; + } + return ((struct ast_list *)ast)->children[index]; +} + +static struct ast *_get_from_logical(struct ast *ast, size_t index) +{ + switch (index) + { + case 0: + return ((struct ast_logical *)ast)->left; + case 1: + return ((struct ast_logical *)ast)->right; + default: + return NULL; + } +} + +static struct ast *_get_from_pipeline(struct ast *ast, size_t index) +{ + switch (index) + { + case 0: + return ((struct ast_pipeline *)ast)->left; + case 1: + return ((struct ast_pipeline *)ast)->right; + default: + return NULL; + } +} + +static struct ast *_get_from_redirection(struct ast *ast, size_t index) +{ + switch (index) + { + case 0: + return ((struct ast_redirection *)ast)->expr; + default: + return NULL; + } +} + +static struct ast *_get_from_while(struct ast *ast, size_t index) +{ + switch (index) + { + case 0: + return ((struct ast_while *)ast)->cond; + case 1: + return ((struct ast_while *)ast)->body; + default: + return NULL; + } +} + +static struct ast *_get_from_assign(struct ast *ast, size_t index) +{ + (void)ast; + (void)index; + return NULL; +} + +static struct ast *_get_from_for(struct ast *ast, size_t index) +{ + switch (index) + { + case 0: + return ((struct ast_for *)ast)->left; + case 1: + return ((struct ast_for *)ast)->right; + default: + return NULL; + } +} + +static struct ast *_get_from_function(struct ast *ast, size_t index) +{ + switch (index) + { + case 0: + return ((struct ast_function *)ast)->body; + default: + return NULL; + } +} + +static struct ast *_get_from_subshell(struct ast *ast, size_t index) +{ + switch (index) + { + case 0: + return ((struct ast_subshell *)ast)->body; + default: + return NULL; + } +} + +typedef struct ast *(*ast_getters)(struct ast *, size_t); + +static const ast_getters ast_getters_ltable[] = { + [AST_COMMAND] = _get_from_command, + [AST_IF] = _get_from_if, + [AST_LOGICAL] = _get_from_logical, + [AST_LIST] = _get_from_list, + [AST_PIPELINE] = _get_from_pipeline, + [AST_REDIRECTION] = _get_from_redirection, + [AST_WHILE] = _get_from_while, + [AST_ASSIGN] = _get_from_assign, + [AST_FOR] = _get_from_for, + [AST_FUNCTION] = _get_from_function, + [AST_SUBSHELL] = _get_from_subshell +}; + +struct ast *get_i(struct ast *ast, size_t index) +{ + return ast_getters_ltable[ast->type](ast, index); +} + +struct ast *get_left(struct ast *ast) +{ + return get_i(ast, 0); +} + +struct ast *get_right(struct ast *ast) +{ + return get_i(ast, 1); +} + +static void _set_on_command(struct ast *ast, struct ast *child, size_t index) +{ + (void)ast; + (void)child; + (void)index; + return; +} + +static void _set_on_if(struct ast *ast, struct ast *child, size_t index) +{ + switch (index) + { + case 0: + ((struct ast_if *)ast)->condition = child; + return; + case 1: + ((struct ast_if *)ast)->then_body = child; + return; + case 2: + ((struct ast_if *)ast)->else_body = child; + return; + default: + return; + } +} + +static void _set_on_logical(struct ast *ast, struct ast *child, size_t index) +{ + switch (index) + { + case 0: + ((struct ast_logical *)ast)->left = child; + break; + case 1: + ((struct ast_logical *)ast)->right = child; + break; + default: + return; + } +} + +static void _set_on_list(struct ast *ast, struct ast *child, size_t index) +{ + if (index > ((struct ast_list *)ast)->nb_children) + { + return; + } + if (index == ((struct ast_list *)ast)->nb_children) + { + ((struct ast_list *)ast)->nb_children++; + ((struct ast_list *)ast)->children = realloc( + ((struct ast_list *)ast)->children, + ((struct ast_list *)ast)->nb_children * sizeof(struct ast_command)); + } + ((struct ast_list *)ast)->children[index] = child; +} + +static void _set_on_pipeline(struct ast *ast, struct ast *child, size_t index) +{ + switch (index) + { + case 0: + ((struct ast_pipeline *)ast)->left = child; + break; + case 1: + ((struct ast_pipeline *)ast)->right = child; + break; + default: + return; + } +} + +static void _set_on_redirection(struct ast *ast, struct ast *child, + size_t index) +{ + switch (index) + { + case 0: + ((struct ast_redirection *)ast)->expr = child; + break; + default: + return; + } +} + +static void _set_on_while(struct ast *ast, struct ast *child, size_t index) +{ + switch (index) + { + case 0: + ((struct ast_while *)ast)->cond = child; + break; + case 1: + ((struct ast_while *)ast)->body = child; + break; + default: + return; + } +} + +static void _set_on_assign(struct ast *ast, struct ast *child, size_t index) +{ + (void)ast; + (void)child; + (void)index; + return; +} + +static void _set_on_for(struct ast *ast, struct ast *child, size_t index) +{ + switch (index) + { + case 0: + ((struct ast_for *)ast)->left = child; + return; + case 1: + ((struct ast_for *)ast)->right = child; + return; + default: + return; + } +} + +static void _set_on_function(struct ast *ast, struct ast *child, size_t index) +{ + switch (index) + { + case 0: + ((struct ast_function *)ast)->body = child; + default: + return; + } +} + +static void _set_on_subshell(struct ast *ast, struct ast *child, size_t index) +{ + switch (index) + { + case 0: + ((struct ast_subshell *)ast)->body = child; + default: + return; + } +} + +typedef void (*ast_setters)(struct ast *, struct ast *, size_t); + +static const ast_setters ast_setters_ltable[] = { + [AST_COMMAND] = _set_on_command, [AST_IF] = _set_on_if, + [AST_LOGICAL] = _set_on_logical, [AST_LIST] = _set_on_list, + [AST_PIPELINE] = _set_on_pipeline, [AST_REDIRECTION] = _set_on_redirection, + [AST_WHILE] = _set_on_while, [AST_ASSIGN] = _set_on_assign, + [AST_FOR] = _set_on_for, [AST_FUNCTION] = _set_on_function, + [AST_SUBSHELL] = _set_on_subshell +}; + +void set_i(struct ast *ast, struct ast *child, size_t index) +{ + ast_setters_ltable[ast->type](ast, child, index); +} + +void set_left(struct ast *ast, struct ast *child) +{ + set_i(ast, child, 0); +} + +void set_right(struct ast *ast, struct ast *child) +{ + set_i(ast, child, 1); +} diff --git a/42sh/src/ast/ast_accessors.h b/42sh/src/ast/ast_accessors.h new file mode 100644 index 0000000..757bff5 --- /dev/null +++ b/42sh/src/ast/ast_accessors.h @@ -0,0 +1,63 @@ +#ifndef AST_ACCESSORS_H +#define AST_ACCESSORS_H + +#include <stddef.h> + +#include "ast/ast.h" + +/** + * @brief Returns the i-th child of `ast`. + * The children are stored as follows: + * `index` 0: left child, or expression in unary trees, or `condition` in `if` + * blocks. `index` 1: right child, or `then` expression in `if` blocks. `index` + * 2+: middle children, or `else` expression in `if` blocks (only `index` 2). + * @param ast Any kind of AST. + * @param index The index of the child to get. + * @note All kinds of ASTs can be passed to this function. + * All `index` can be passed to this function too. + * However, if the given type does not have any child or if the `index` is too + * big, `get_i` returns `NULL.` + */ +struct ast *get_i(struct ast *ast, size_t index); + +/** + * @brief Alias for `get_i(ast, 0)`. + * @note See `get_i` for a full explanation. + */ +struct ast *get_left(struct ast *ast); + +/** + * @brief Alias for `get_i(ast, 1)`. + * @note See `get_i` for a full explanation. + */ +struct ast *get_right(struct ast *ast); + +/** + * @brief Sets the `child` as the i-th child of `ast`. + * The children are stored as follows: + * `index` 0: left child, or expression in unary trees, or `condition` in `if` + * blocks. `index` 1: right child, or `then` expression in `if` blocks. `index` + * 2+: middle children, or `else` expression in `if` blocks (only `index` 2). + * @param ast Any kind of AST. + * @param child The child to be assigned in `ast`. + * @param index The position of the child in the `ast`. + * @note All kinds of ASTs can be passed to this function. + * All `index` can be passed to this function too. + * However, if the given type can not contain any child or if the `index` is too + * big, `set_i` does nothing. + */ +void set_i(struct ast *ast, struct ast *child, size_t index); + +/** + * @brief Alias for `set_i(ast, child, 0)`. + * @note See `set_i` for a full explanation. + */ +void set_left(struct ast *ast, struct ast *child); + +/** + * @brief Alias for `set_i(ast, child, 1)`. + * @note See `set_i` for a full explanation. + */ +void set_right(struct ast *ast, struct ast *child); + +#endif /* ! AST_ACCESSORS_H */ diff --git a/42sh/src/ast/ast_pretty_print.c b/42sh/src/ast/ast_pretty_print.c new file mode 100644 index 0000000..17c5b2a --- /dev/null +++ b/42sh/src/ast/ast_pretty_print.c @@ -0,0 +1,247 @@ +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L + +#include <stdio.h> +#include <stdlib.h> + +#include "ast/ast.h" +#include "ast/ast_accessors.h" +#include "ast/ast_redirect.h" +#include "lexer/token.h" + +#define BUFFER_SIZE 10000 + +static char *_type_to_string[] = { [AST_LOGICAL] = "logical block", + [AST_COMMAND] = "command", + [AST_IF] = "conditionnal block", + [AST_LIST] = "list", + [AST_PIPELINE] = "pipeline", + [AST_REDIRECTION] = "redirection", + [AST_WHILE] = "while", + [AST_ASSIGN] = "assign", + [AST_FOR] = "for" }; + +typedef char *(*tostring)(struct ast *); + +static char *_args_tostring(struct string **args) +{ + struct string *output = string_create(NULL); + size_t i = 0; + while (args[i]) + { + struct string *c = string_deepcopy(args[i++]); + string_catenate(output, c); + if (args[i]) + string_pushstr(output, " - "); + } + char *data = output->data; + free(output); + return data; +} + +static char *_ast_command_tostring(struct ast *ast) +{ + char buf[BUFFER_SIZE] = { 0 }; + char *args_to_string = _args_tostring(((struct ast_command *)ast)->args); + int len = snprintf(buf, BUFFER_SIZE, "%s\\nargs: %s", + _type_to_string[ast->type], args_to_string); + free(args_to_string); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static char *_ast_if_tostring(struct ast *ast) +{ + char buf[BUFFER_SIZE] = { 0 }; + int len = snprintf(buf, BUFFER_SIZE, "%s\\n", _type_to_string[ast->type]); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static char *_get_token_type_to_str(struct ast *ast) +{ + enum token_type t = ((struct ast_logical *)ast)->type; + switch (t) + { + case TOKEN_AND: + return "&&"; + case TOKEN_OR: + return "||"; + case TOKEN_NEG: + return "!"; + default: + return "unknown"; + } +} + +static char *_ast_logical_tostring(struct ast *ast) +{ + char buf[BUFFER_SIZE] = { 0 }; + int len = snprintf(buf, BUFFER_SIZE, "%s\\n%s", _type_to_string[ast->type], + _get_token_type_to_str(ast)); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static char *_ast_list_tostring(struct ast *ast) +{ + char buf[BUFFER_SIZE] = { 0 }; + size_t nb_c = ((struct ast_list *)ast)->nb_children; + int len = snprintf(buf, BUFFER_SIZE, "%s\\n%lu child(ren)", + _type_to_string[ast->type], nb_c); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static char *_ast_pipeline_tostring(struct ast *ast) +{ + char buf[BUFFER_SIZE] = { 0 }; + int len = snprintf(buf, BUFFER_SIZE, "%s\\nleft | right", + _type_to_string[ast->type]); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static char *_ast_assign_tostring(struct ast *ast) +{ + char buf[BUFFER_SIZE] = { 0 }; + struct string *name = ((struct ast_assign *)ast)->name; + struct string *value = ((struct ast_assign *)ast)->val; + int len = snprintf(buf, BUFFER_SIZE, "%s\\n%s=%s", + _type_to_string[ast->type], name->data, value->data); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static char *_ast_redirection_tostring(struct ast *ast) +{ + struct redirect r = ((struct ast_redirection *)ast)->redirect; + char buf[BUFFER_SIZE] = { 0 }; + int len = + snprintf(buf, BUFFER_SIZE, "%s\\n%s %s %s", _type_to_string[ast->type], + r.fd->data, r.redir->data, r.file->data); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static char *_ast_while_tostring(struct ast *ast) +{ + char buf[BUFFER_SIZE] = { 0 }; + int len = snprintf(buf, BUFFER_SIZE, "%s\\nleft is cond\nright is body", + _type_to_string[ast->type]); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static char *_ast_for_tostring(struct ast *ast) +{ + char buf[BUFFER_SIZE] = { 0 }; + int len = snprintf(buf, BUFFER_SIZE, "%s\\nleft is words\nright is body", + _type_to_string[ast->type]); + char *str = malloc((len + 1) * sizeof(char)); + for (int i = 0; i < len; i++) + { + str[i] = buf[i]; + } + str[len] = 0; + return str; +} + +static const tostring _node_to_string_ltable[] = { + [AST_LOGICAL] = _ast_logical_tostring, + [AST_COMMAND] = _ast_command_tostring, + [AST_IF] = _ast_if_tostring, + [AST_LIST] = _ast_list_tostring, + [AST_PIPELINE] = _ast_pipeline_tostring, + [AST_REDIRECTION] = _ast_redirection_tostring, + [AST_WHILE] = _ast_while_tostring, + [AST_ASSIGN] = _ast_assign_tostring, + [AST_FOR] = _ast_for_tostring +}; + +static char *_ast_to_string(struct ast *ast) +{ + return _node_to_string_ltable[ast->type](ast); +} + +static void _pretty_print_internal(struct ast *ast, FILE *f) +{ + if (!ast) + { + return; + } + char *format = _ast_to_string(ast); + fprintf(f, "\t%lu [label=\"%s\"];\n", (size_t)ast, format); + free(format); + size_t i = 0; + struct ast *child = NULL; + do + { + child = get_i(ast, i); + if (!child) + { + break; + } + i++; + fprintf(f, "\t%lu -- %lu;\n", (size_t)ast, (size_t)child); + _pretty_print_internal(child, f); + } while (child); +} + +void pretty_print(struct ast *ast) +{ + FILE *f = fopen("ast.dot", "w"); + if (!f) + { + fprintf(stderr, + "pretty_print: unable to open a file to write the DOT AST.\n"); + return; + } + + fprintf(f, "graph {\n"); + + _pretty_print_internal(ast, f); + + fprintf(f, "}\n"); + fclose(f); +} diff --git a/42sh/src/ast/ast_redirect.h b/42sh/src/ast/ast_redirect.h new file mode 100644 index 0000000..2f573a8 --- /dev/null +++ b/42sh/src/ast/ast_redirect.h @@ -0,0 +1,13 @@ +#ifndef AST_REDIRECT_H +#define AST_REDIRECT_H + +#include "utils/libstring.h" + +struct redirect +{ + struct string *fd; // src + struct string *redir; // ('<' | '>' | '>>' | etc ...) + struct string *file; // dest +}; + +#endif /* ! AST_REDIRECT_H */ |
