summaryrefslogtreecommitdiff
path: root/src/ast_pretty_printer.c
diff options
context:
space:
mode:
authorJohnny Richard <johnny@johnnyrichard.com>2023-05-06 00:01:25 +0200
committerCarlos Maniero <carlos@maniero.me>2023-05-05 19:53:40 -0300
commit7781e41927247bff9e567a47f9fa1862ed5596e6 (patch)
tree29635bb2a7fe8a830ed165a3257acb6882dc1ca3 /src/ast_pretty_printer.c
parent17ae189d4a6aa926d8931b1e4f7db8de6caddd90 (diff)
cli: Add AST pretty-printing option (--ast-dump)
Parsing can be a complex process, and it's not always easy to get a clear picture of what's happening with the AST. This commit adds a new feature to the CLI that allows us to pretty-print the AST (outputs to stdout), making it easier to visualize the tree structure and understand how the parser is working. The new --ast-dump option generates a human-readable representation of the AST, including node types, values, and child relationships. This information can be invaluable for debugging and understanding the parser's behavior. Signed-off-by: Johnny Richard <johnny@johnnyrichard.com> Reviewed-by: Carlos Maniero <carlos@maniero.me>
Diffstat (limited to 'src/ast_pretty_printer.c')
-rw-r--r--src/ast_pretty_printer.c224
1 files changed, 224 insertions, 0 deletions
diff --git a/src/ast_pretty_printer.c b/src/ast_pretty_printer.c
new file mode 100644
index 0000000..df17763
--- /dev/null
+++ b/src/ast_pretty_printer.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2023 Johnny Richard
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "ast_pretty_printer.h"
+#include "ast.h"
+#include "string_view.h"
+
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+const char operation_kinds[AST_BINOP_N] = { [AST_BINOP_ADITION] = '+',
+ [AST_BINOP_SUBTRACTION] = '-',
+ [AST_BINOP_MULTIPLICATION] = '*',
+ [AST_BINOP_DIVISION] = '/' };
+
+void
+ast_pretty_printer_init(ast_pretty_printer_t *printer, FILE *stream);
+
+inline static void
+ast_pretty_printer_add_indentation(ast_pretty_printer_t *printer);
+
+inline static void
+ast_pretty_printer_rm_indentation(ast_pretty_printer_t *printer);
+
+static void
+ast_pretty_printer_print_indentation(ast_pretty_printer_t *printer);
+
+static int
+ast_pretty_printer_printf(ast_pretty_printer_t *printer, const char *fmt, ...);
+
+void
+ast_pretty_printer_init(ast_pretty_printer_t *printer, FILE *stream)
+{
+ assert(printer);
+ assert(stream);
+
+ printer->indentation_fmt = 0;
+ printer->indentation_lst_children = 0;
+ printer->indentation_level = 0;
+ printer->stream = stream;
+}
+
+void
+ast_pretty_printer_print_ast(ast_pretty_printer_t *printer, ast_node_t *ast)
+{
+ assert(ast);
+
+ switch (ast->kind) {
+ case AST_BINARY_OPERATION: {
+ ast_binary_operation_t binop = ast->data.binary_operation;
+ ast_pretty_printer_printf(printer, "BinaryOperation operation='%c'\n", operation_kinds[binop.kind]);
+
+ ast_pretty_printer_add_indentation(printer);
+ {
+ ast_pretty_printer_printf(printer, "left:\n");
+
+ ast_pretty_printer_add_indentation(printer);
+ {
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ ast_pretty_printer_print_ast(printer, binop.left);
+
+ ast_pretty_printer_rm_indentation(printer);
+ }
+
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ ast_pretty_printer_printf(printer, "right:\n");
+
+ ast_pretty_printer_add_indentation(printer);
+ {
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ ast_pretty_printer_print_ast(printer, binop.right);
+
+ ast_pretty_printer_rm_indentation(printer);
+ }
+
+ ast_pretty_printer_rm_indentation(printer);
+ }
+ break;
+ }
+ case AST_FUNCTION_DECLARATION: {
+ ast_function_declaration_t function = ast->data.function;
+ ast_pretty_printer_printf(printer, "FunctionDecl name='" SVFMT "'\n", SVARG(&function.identifier.name));
+
+ ast_pretty_printer_add_indentation(printer);
+ for (size_t i = 0; i < function.body->size; ++i) {
+ if (i + 1 >= function.body->size) {
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ }
+ ast_pretty_printer_print_ast(printer, vector_at(function.body, i));
+ }
+ ast_pretty_printer_rm_indentation(printer);
+ break;
+ }
+ case AST_LITERAL: {
+ ast_literal_t literal = ast->data.literal;
+
+ switch (literal.kind) {
+ case AST_LITERAL_INTEGER: {
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ ast_pretty_printer_printf(printer, "Literal type=i32 value='%d'\n", literal.value.integer);
+ break;
+ }
+ }
+ break;
+ }
+ case AST_RETURN_STMT: {
+ ast_return_stmt_t return_stmt = ast->data.return_stmt;
+ ast_pretty_printer_printf(printer, "ReturnStmt\n");
+
+ ast_pretty_printer_add_indentation(printer);
+ {
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ ast_pretty_printer_print_ast(printer, return_stmt.argument);
+
+ ast_pretty_printer_rm_indentation(printer);
+ }
+ break;
+ }
+ case AST_VARIABLE_DECLARATION: {
+ ast_variable_declaration_t var_decl = ast->data.variable_declaration;
+ ast_pretty_printer_printf(printer, "VariableDecl name='" SVFMT "'\n", SVARG(&var_decl.identifier.name));
+
+ ast_pretty_printer_add_indentation(printer);
+ {
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ ast_pretty_printer_print_ast(printer, var_decl.value);
+
+ ast_pretty_printer_rm_indentation(printer);
+ }
+ break;
+ }
+ case AST_VARIABLE_ASSIGNMENT: {
+ ast_variable_assignment_t var_assign = ast->data.variable_assignment;
+ ast_pretty_printer_printf(printer, "VariableAssignment name='" SVFMT "'\n", SVARG(&var_assign.identifier->name));
+
+ ast_pretty_printer_add_indentation(printer);
+ {
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ ast_pretty_printer_printf(printer, "expression:\n");
+
+ ast_pretty_printer_add_indentation(printer);
+ {
+ printer->indentation_lst_children |= 1 << printer->indentation_level;
+ ast_pretty_printer_print_ast(printer, var_assign.expression);
+
+ ast_pretty_printer_rm_indentation(printer);
+ }
+ ast_pretty_printer_rm_indentation(printer);
+ }
+
+ break;
+ }
+ case AST_VARIABLE: {
+ ast_variable_t var = ast->data.variable;
+ ast_pretty_printer_printf(printer, "Variable name='" SVFMT "'\n", SVARG(&var.identifier->name));
+ break;
+ }
+ case AST_UNKOWN_NODE: {
+ fprintf(printer->stream, "AST_UNKOWN_NODE\n");
+ break;
+ }
+ }
+}
+
+static int
+ast_pretty_printer_printf(ast_pretty_printer_t *printer, const char *fmt, ...)
+{
+ int ret = 0;
+ va_list args;
+
+ va_start(args, fmt);
+ ast_pretty_printer_print_indentation(printer);
+ ret = vprintf(fmt, args);
+ va_end(args);
+
+ return ret;
+}
+
+inline static void
+ast_pretty_printer_add_indentation(ast_pretty_printer_t *printer)
+{
+ printer->indentation_level++;
+ printer->indentation_fmt |= 1 << printer->indentation_level;
+}
+
+inline static void
+ast_pretty_printer_rm_indentation(ast_pretty_printer_t *printer)
+{
+ printer->indentation_level--;
+}
+
+static void
+ast_pretty_printer_print_indentation(ast_pretty_printer_t *printer)
+{
+ for (size_t i = 0; i <= printer->indentation_level; ++i) {
+ if ((printer->indentation_fmt >> i) & 1) {
+ if (i + 1 > printer->indentation_level && (printer->indentation_lst_children & (1 << i))) {
+ printer->indentation_lst_children ^= (1 << i);
+ printer->indentation_fmt ^= (1 << i);
+ fprintf(printer->stream, "└─ ");
+ } else if (i + 1 > printer->indentation_level) {
+ fprintf(printer->stream, "├─ ");
+ } else {
+ fprintf(printer->stream, "│  ");
+ }
+ } else {
+ fprintf(printer->stream, " ");
+ }
+ }
+}