/*
* Copyright (C) 2024 olang maintainers
*
* 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 .
*/
#include "pretty_print_ast.h"
#include "arena.h"
#include "list.h"
#include
#include
#include
#include
#include
#include
#define ANSI_COLOR_MAGENTA "\x1b[35m"
#define ANSI_COLOR_RESET "\x1b[0m"
// clang-format off
#define PP_IS_BIT_SET(data, index) ((data) & 1 << index)
// clang-format on
typedef struct pretty_print_node
{
char *name;
list_t *children;
} pretty_print_node_t;
static bool
stdout_supports_color()
{
struct stat st;
fstat(STDOUT_FILENO, &st);
return S_ISCHR(st.st_mode);
}
static void
pretty_print_print_ident(uint64_t *prefix, size_t level, bool lst_children)
{
assert(level < 64);
bool support_color = stdout_supports_color();
if (support_color) {
printf(ANSI_COLOR_MAGENTA);
}
for (size_t i = 0; i < level; ++i) {
if (!PP_IS_BIT_SET(*prefix, i)) {
printf(" ");
continue;
}
bool last_index = i + 1 == level;
if (!last_index) {
printf("| ");
} else if (lst_children) {
printf("`-");
} else {
printf("|-");
}
}
if (support_color) {
printf(ANSI_COLOR_RESET);
}
}
static void
pretty_print_tree(pretty_print_node_t *node,
uint64_t *prefix,
size_t level,
bool lst_children)
{
pretty_print_print_ident(prefix, level, lst_children);
list_t *list = node->children;
if (list != NULL)
(*prefix) |= 1 << level;
if (lst_children)
(*prefix) ^= 1 << (level - 1);
printf("%s\n", node->name);
size_t size = list_size(list);
for (size_t i = 0; i < size; ++i) {
pretty_print_node_t *it =
(pretty_print_node_t *)list_get(list, i)->value;
pretty_print_tree(it, prefix, level + 1, i + 1 == size);
}
}
static pretty_print_node_t *
pretty_print_node_new(arena_t *arena)
{
pretty_print_node_t *node =
(pretty_print_node_t *)arena_alloc(arena, sizeof(pretty_print_node_t));
node->children = (list_t *)arena_alloc(arena, sizeof(list_t));
list_init(node->children, arena);
return node;
}
static pretty_print_node_t *
pretty_print_new_fn_param(ast_fn_param_t *param, arena_t *arena)
{
pretty_print_node_t *node = pretty_print_node_new(arena);
char name[256];
sprintf(name,
"Param_Definition ",
SV_ARG(param->id.name),
SV_ARG(param->type->id));
node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
strcpy(node->name, name);
return node;
}
static pretty_print_node_t *
ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
{
switch (ast->kind) {
case AST_NODE_TRANSLATION_UNIT: {
pretty_print_node_t *node = pretty_print_node_new(arena);
node->name = "Translation_Unit";
list_item_t *item = list_head(ast->as_translation_unit.decls);
while (item != NULL) {
ast_node_t *decl = (ast_node_t *)item->value;
pretty_print_node_t *fn_node =
ast_node_to_pretty_print_node(decl, arena);
list_append(node->children, fn_node);
item = list_next(item);
}
return node;
}
case AST_NODE_FN_DEF: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_fn_definition_t fn_def = ast->as_fn_def;
char name[256];
sprintf(name,
"Function_Definition %s",
SV_ARG(fn_def.id),
SV_ARG(fn_def.return_type->id),
fn_def._extern ? " " : "");
node->name =
(char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
strcpy(node->name, name);
list_item_t *param = list_head(fn_def.params);
while (param != NULL) {
list_append(node->children,
pretty_print_new_fn_param(param->value, arena));
param = list_next(param);
}
if (fn_def.block != NULL) {
pretty_print_node_t *block =
ast_node_to_pretty_print_node(fn_def.block, arena);
list_append(node->children, block);
}
return node;
}
case AST_NODE_FN_CALL: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_fn_call_t fn_call = ast->as_fn_call;
char name[256];
sprintf(name,
"Function_Call ",
SV_ARG(fn_call.id.name));
node->name =
(char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
strcpy(node->name, name);
list_item_t *item = list_head(fn_call.args);
while (item != NULL) {
list_append(node->children,
ast_node_to_pretty_print_node(item->value, arena));
item = list_next(item);
}
return node;
}
case AST_NODE_BLOCK: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_block_t block = ast->as_block;
node->name = "Block";
size_t block_nodes_size = list_size(block.nodes);
for (size_t i = 0; i < block_nodes_size; ++i) {
ast_node_t *ast_node =
(ast_node_t *)list_get(block.nodes, i)->value;
pretty_print_node_t *child =
ast_node_to_pretty_print_node(ast_node, arena);
list_append(node->children, child);
}
return node;
}
case AST_NODE_RETURN_STMT: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_return_stmt_t return_stmt = ast->as_return_stmt;
node->name = "Return_Statement";
pretty_print_node_t *child =
ast_node_to_pretty_print_node(return_stmt.value, arena);
list_append(node->children, child);
return node;
}
case AST_NODE_IF_STMT: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_if_stmt_t if_stmt = ast->as_if_stmt;
node->name = "If_Statement";
pretty_print_node_t *child =
ast_node_to_pretty_print_node(if_stmt.cond, arena);
list_append(node->children, child);
child = ast_node_to_pretty_print_node(if_stmt.then, arena);
list_append(node->children, child);
if (if_stmt._else != NULL) {
child = ast_node_to_pretty_print_node(if_stmt._else, arena);
list_append(node->children, child);
}
return node;
}
case AST_NODE_WHILE_STMT: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_while_stmt_t while_stmt = ast->as_while_stmt;
node->name = "While_Statement";
pretty_print_node_t *child =
ast_node_to_pretty_print_node(while_stmt.cond, arena);
list_append(node->children, child);
child = ast_node_to_pretty_print_node(while_stmt.then, arena);
list_append(node->children, child);
return node;
}
case AST_NODE_LITERAL: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_literal_t literal = ast->as_literal;
char name[256];
switch (literal.kind) {
case AST_LITERAL_U32: {
sprintf(
name, "Literal ", literal.as_u32);
node->name = (char *)arena_alloc(
arena, sizeof(char) * (strlen(name) + 1));
strcpy(node->name, name);
break;
}
default:
assert(0 && "literal not implemented");
}
return node;
}
case AST_NODE_VAR_DEF: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_var_definition_t var = ast->as_var_def;
char name[256];
sprintf(name,
"Var_Definition ",
SV_ARG(var.id.name),
SV_ARG(var.type->id));
node->name =
(char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
strcpy(node->name, name);
pretty_print_node_t *child =
ast_node_to_pretty_print_node(var.value, arena);
list_append(node->children, child);
return node;
}
case AST_NODE_REF: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_ref_t ref = ast->as_ref;
char name[256];
sprintf(name, "Reference ", SV_ARG(ref.id.name));
node->name =
(char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
strcpy(node->name, name);
return node;
}
case AST_NODE_BINARY_OP: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_binary_op_t binop = ast->as_bin_op;
switch (binop.kind) {
case AST_BINOP_ADDITION: {
node->name = "Binary_Operation (+)";
break;
}
case AST_BINOP_SUBTRACTION: {
node->name = "Binary_Operation (-)";
break;
}
case AST_BINOP_MULTIPLICATION: {
node->name = "Binary_Operation (*)";
break;
}
case AST_BINOP_DIVISION: {
node->name = "Binary_Operation (/)";
break;
}
case AST_BINOP_REMINDER: {
node->name = "Binary_Operation (%)";
break;
}
case AST_BINOP_BITWISE_LSHIFT: {
node->name = "Binary_Operation (<<)";
break;
}
case AST_BINOP_BITWISE_RSHIFT: {
node->name = "Binary_Operation (>>)";
break;
}
case AST_BINOP_BITWISE_XOR: {
node->name = "Binary_Operation (^)";
break;
}
case AST_BINOP_BITWISE_AND: {
node->name = "Binary_Operation (&)";
break;
}
case AST_BINOP_BITWISE_OR: {
node->name = "Binary_Operation (|)";
break;
}
case AST_BINOP_CMP_LT: {
node->name = "Binary_Operation (<)";
break;
}
case AST_BINOP_CMP_GT: {
node->name = "Binary_Operation (>)";
break;
}
case AST_BINOP_CMP_LEQ: {
node->name = "Binary_Operation (<=)";
break;
}
case AST_BINOP_CMP_GEQ: {
node->name = "Binary_Operation (>=)";
break;
}
case AST_BINOP_CMP_EQ: {
node->name = "Binary_Operation (==)";
break;
}
case AST_BINOP_CMP_NEQ: {
node->name = "Binary_Operation (!=)";
break;
}
case AST_BINOP_LOGICAL_AND: {
node->name = "Binary_Operation (&&)";
break;
}
case AST_BINOP_LOGICAL_OR: {
node->name = "Binary_Operation (|)";
break;
}
case AST_BINOP_ASSIGN: {
node->name = "Binary_Operation (=)";
break;
}
default:
assert(false && "binop not implemented");
}
pretty_print_node_t *lhs =
ast_node_to_pretty_print_node(binop.lhs, arena);
pretty_print_node_t *rhs =
ast_node_to_pretty_print_node(binop.rhs, arena);
list_append(node->children, lhs);
list_append(node->children, rhs);
return node;
}
case AST_NODE_UNARY_OP: {
pretty_print_node_t *node = pretty_print_node_new(arena);
ast_unary_op_t unary_op = ast->as_unary_op;
switch (unary_op.kind) {
case AST_UNARY_BITWISE_NOT: {
node->name = "Unary_Operation (~)";
break;
}
case AST_UNARY_DEREFERENCE: {
node->name = "Unary_Operation (*)";
break;
}
case AST_UNARY_NEGATIVE: {
node->name = "Unary_Operation (-)";
break;
}
case AST_UNARY_LOGICAL_NOT: {
node->name = "Unary_Operation (!)";
break;
}
case AST_UNARY_POSITIVE: {
node->name = "Unary_Operation (+)";
break;
}
case AST_UNARY_ADDRESSOF: {
node->name = "Unary_Operation (&)";
break;
}
}
pretty_print_node_t *expr =
ast_node_to_pretty_print_node(unary_op.operand, arena);
list_append(node->children, expr);
return node;
}
default: {
printf("node kind = '%d' not implmented\n", ast->kind);
assert(false);
}
}
return NULL;
}
void
pretty_print_ast(ast_node_t *ast)
{
arena_t arena = arena_new(8 * 1024);
pretty_print_node_t *root = ast_node_to_pretty_print_node(ast, &arena);
uint64_t prefix = 0;
pretty_print_tree(root, &prefix, 0, true);
arena_free(&arena);
}