Files
TDT4205/ps2/src/tree.c

143 lines
3.7 KiB
C

#include "vslc.h"
#include "stdarg.h"
#include "stdlib.h"
/* #include "nodetypes.h" */
// Global root for abstract syntax tree
node_t* root;
// Declarations of helper functions defined further down in this file
static void node_print(node_t* node, int nesting);
static void destroy_subtree(node_t* discard);
// Initialize a node with the given type and children
node_t* node_create(node_type_t type, size_t n_children, ...)
{
node_t *node = malloc(sizeof(node_t));
node->type = type;
node->n_children = n_children;
node->children = malloc(sizeof(node_t) * n_children);
va_list args;
va_start(args, n_children);
for (int i = 0; i < n_children; i++) {
node->children[i] = va_arg(args, node_t*);
}
va_end(args);
return node;
}
// Append an element to the given LIST node, returns the list node
node_t* append_to_list_node(node_t* list_node, node_t* element)
{
assert(list_node->type == LIST);
// Calculate the minimum size of the new allocation
size_t min_allocation_size = list_node->n_children + 1;
// Round up to the next power of two
size_t new_allocation_size = 1;
while (new_allocation_size < min_allocation_size)
new_allocation_size *= 2;
// Resize the allocation
list_node->children = realloc(list_node->children, new_allocation_size * sizeof(node_t*));
// Insert the new element and increase child count by 1
list_node->children[list_node->n_children] = element;
list_node->n_children++;
return list_node;
}
// Outputs the entire syntax tree to the terminal
void print_syntax_tree(void)
{
// If the environment variable GRAPHVIZ_OUTPUT is set, print a GraphViz graph in the dot format
if (getenv("GRAPHVIZ_OUTPUT") != NULL)
graphviz_node_print(root);
else
node_print(root, 0);
}
// Frees all memory held by the syntax tree
void destroy_syntax_tree(void)
{
destroy_subtree(root);
root = NULL;
}
// The rest of this file contains private helper functions used by the above functions
// Prints out the given node and all its children recursively
static void node_print(node_t* node, int nesting)
{
// Indent the line based on how deep the node is in the syntax tree
printf("%*s", nesting, "");
if (node == NULL)
{
printf("(NULL)\n");
return;
}
printf("%s", NODE_TYPE_NAMES[node->type]);
// For nodes with extra data, include it in the printout
switch (node->type)
{
case OPERATOR:
printf(" (%s)", node->data.operator);
break;
case IDENTIFIER:
printf(" (%s)", node->data.identifier);
break;
case NUMBER_LITERAL:
printf(" (%ld)", node->data.number_literal);
break;
case STRING_LITERAL:
printf(" (%s)", node->data.string_literal);
break;
case STRING_LIST_REFERENCE:
printf(" (%zu)", node->data.string_list_index);
break;
default:
break;
}
putchar('\n');
// Recursively print children, with some more indentation
for (size_t i = 0; i < node->n_children; i++)
node_print(node->children[i], nesting + 1);
}
// Frees the memory owned by the given node, but does not touch its children
static void node_finalize(node_t* discard)
{
if (discard->type == IDENTIFIER || discard->type == STRING_LITERAL)
free(&discard->data);
free(discard->children);
free(discard);
}
// Recursively frees the memory owned by the given node, and all its children
static void destroy_subtree(node_t* discard)
{
if (discard->n_children == 0)
node_finalize(discard);
for (int i = 0; i < discard->n_children; i++) {
node_t *child = discard->children[i];
destroy_subtree(child);
}
}
// Definition of the global string array NODE_TYPE_NAMES
const char* NODE_TYPE_NAMES[NODE_TYPE_COUNT] = {
#define NODE_TYPE(node_type) #node_type
#include "nodetypes.h"
};