summaryrefslogtreecommitdiff
path: root/42sh/src/ast
diff options
context:
space:
mode:
Diffstat (limited to '42sh/src/ast')
-rw-r--r--42sh/src/ast/Makefile.am14
-rw-r--r--42sh/src/ast/ast.c164
-rw-r--r--42sh/src/ast/ast.h221
-rw-r--r--42sh/src/ast/ast_accessors.c326
-rw-r--r--42sh/src/ast/ast_accessors.h63
-rw-r--r--42sh/src/ast/ast_pretty_print.c247
-rw-r--r--42sh/src/ast/ast_redirect.h13
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 */