aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2025-06-30 02:49:42 +0200
committerMel <mel@rnrd.eu>2025-06-30 02:49:42 +0200
commitb16cf0fc98940554ef0febfe443ef690618d5083 (patch)
tree15cd49710d66988bf0acbb1ce92f64e3ea01d473
parent3d983097329607cc89b63425e97527c5ec0390c8 (diff)
downloadcatskill-b16cf0fc98940554ef0febfe443ef690618d5083.tar.zst
catskill-b16cf0fc98940554ef0febfe443ef690618d5083.zip
Implement Visitor system for syntax tree, with re-written tree printer as first usage
Signed-off-by: Mel <mel@rnrd.eu>
-rw-r--r--Makefile2
-rw-r--r--boot/catboot.c3
-rw-r--r--boot/tree.c66
-rw-r--r--boot/visit.c1033
4 files changed, 1070 insertions, 34 deletions
diff --git a/Makefile b/Makefile
index 0d4eb74..e99038a 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
CFLAGS ?= -std=c99 -Wall -Werror -static -g -O0
.DEFAULT_GOAL := all
-BOOTSTRAP_SOURCES = boot/catboot.c boot/common.c boot/lex.c boot/tree.c boot/parse.c
+BOOTSTRAP_SOURCES = boot/catboot.c boot/common.c boot/lex.c boot/tree.c boot/visit.c boot/parse.c
SOURCES = src/catskill.csk
build/catskill: build $(SOURCES)
diff --git a/boot/catboot.c b/boot/catboot.c
index 79a5e2b..7ab9c4f 100644
--- a/boot/catboot.c
+++ b/boot/catboot.c
@@ -24,6 +24,7 @@
#include "lex.c"
#include "tree.c"
#include "parse.c"
+#include "visit.c"
const ascii*
read_file(const ascii* path)
@@ -81,6 +82,8 @@ main(const int32 argc, const ascii* argv[])
}
tree_print(&tree);
+ printf("\n");
+ tree_printer(&tree);
return EXIT_SUCCESS;
}
diff --git a/boot/tree.c b/boot/tree.c
index a1dea24..1fcaa62 100644
--- a/boot/tree.c
+++ b/boot/tree.c
@@ -872,8 +872,7 @@ expression_print(const struct Expression* expression)
printf(")");
break;
case EXPRESSION_CALL_OR_CONSTRUCT: {
- const struct Expression_Call_Or_Construct* coc =
- &expression->value.call_or_construct;
+ const struct Expression_Call_Or_Construct* coc = &expression->value.call_or_construct;
printf("(call/construct ");
expression_print(coc->subject);
uint i = 0;
@@ -901,7 +900,7 @@ expression_print(const struct Expression* expression)
case EXPRESSION_MEMBER:
printf("(member of ");
expression_print(expression->value.member.subject);
- printf(" named %s" , expression->value.member.name.data);
+ printf(" named %s", expression->value.member.name.data);
printf(")");
break;
case EXPRESSION_INCREMENT_DECREMENT: {
@@ -997,12 +996,13 @@ struct Statement_Value_Conditional
uint condition_count;
};
-enum Statement_Loop_Style {
+enum Statement_Loop_Style
+{
STATEMENT_LOOP_STYLE_NONE,
- STATEMENT_LOOP_STYLE_C, // for i int = 0; i < 10; ++i {}
+ STATEMENT_LOOP_STYLE_C, // for i int = 0; i < 10; ++i {}
STATEMENT_LOOP_STYLE_FOR_EACH, // for x Obj = list {}
- STATEMENT_LOOP_STYLE_WHILE, // while true {}
- STATEMENT_LOOP_STYLE_ENDLESS, // while {}
+ STATEMENT_LOOP_STYLE_WHILE, // while true {}
+ STATEMENT_LOOP_STYLE_ENDLESS, // while {}
};
// stands for both `for` and `while` loops.
@@ -1158,31 +1158,31 @@ statement_print(const struct Statement* statement)
printf("(loop ");
switch (statement->value.loop.style) {
- case STATEMENT_LOOP_STYLE_C:
- printf("c-style ");
- bare_declaration_node_print(&statement->value.loop.declaration);
- printf(" (condition ");
- expression_print(statement->value.loop.condition);
- printf(") (iteration ");
- expression_print(statement->value.loop.iteration);
- printf(") ");
- break;
- case STATEMENT_LOOP_STYLE_FOR_EACH:
- printf("for-each ");
- bare_declaration_node_print(&statement->value.loop.declaration);
- printf(" ");
- break;
- case STATEMENT_LOOP_STYLE_WHILE:
- printf("while (condition ");
- expression_print(statement->value.loop.condition);
- printf(") ");
- break;
- case STATEMENT_LOOP_STYLE_ENDLESS:
- printf("endless ");
- break;
- default:
- failure("unexpected loop style in `statement_print`");
- break;
+ case STATEMENT_LOOP_STYLE_C:
+ printf("c-style ");
+ bare_declaration_node_print(&statement->value.loop.declaration);
+ printf(" (condition ");
+ expression_print(statement->value.loop.condition);
+ printf(") (iteration ");
+ expression_print(statement->value.loop.iteration);
+ printf(") ");
+ break;
+ case STATEMENT_LOOP_STYLE_FOR_EACH:
+ printf("for-each ");
+ bare_declaration_node_print(&statement->value.loop.declaration);
+ printf(" ");
+ break;
+ case STATEMENT_LOOP_STYLE_WHILE:
+ printf("while (condition ");
+ expression_print(statement->value.loop.condition);
+ printf(") ");
+ break;
+ case STATEMENT_LOOP_STYLE_ENDLESS:
+ printf("endless ");
+ break;
+ default:
+ failure("unexpected loop style in `statement_print`");
+ break;
}
block_node_print(&statement->value.loop.body);
@@ -1235,4 +1235,4 @@ tree_print(const struct Tree* tree)
statement_print(statement);
printf("\n");
}
-} \ No newline at end of file
+}
diff --git a/boot/visit.c b/boot/visit.c
new file mode 100644
index 0000000..396f8b8
--- /dev/null
+++ b/boot/visit.c
@@ -0,0 +1,1033 @@
+/*
+ * visit system for the catskill syntax tree,
+ * implementing a default traversal mechanism,
+ * allowing for selective operations on it.
+ * includes a simple tree printer as the primary example of usage.
+ */
+
+struct Visit
+{
+ struct Visit_Table* table;
+ void* user_data;
+};
+
+struct Visit_Table
+{
+ void (*visit_tree)(struct Visit* visitor, struct Tree* tree);
+
+ void (*visit_statement)(struct Visit* visitor, struct Statement* stmt);
+ void (*visit_statement_declaration)(struct Visit* visitor, struct Statement* stmt);
+ void (*visit_statement_conditional)(struct Visit* visitor, struct Statement* stmt);
+ void (*visit_statement_loop)(struct Visit* visitor, struct Statement* stmt);
+ void (*visit_statement_return)(struct Visit* visitor, struct Statement* stmt);
+ void (*visit_statement_break)(struct Visit* visitor, struct Statement* stmt);
+ void (*visit_statement_continue)(struct Visit* visitor, struct Statement* stmt);
+ void (*visit_statement_defer)(struct Visit* visitor, struct Statement* stmt);
+
+ void (*visit_expression)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_integer_literal)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_float_literal)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_string_literal)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_boolean_literal)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_name)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_unary_operation)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_binary_operation)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_group)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_call_or_construct)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_subscript)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_member)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_increment_decrement)(struct Visit* visitor, struct Expression* expr);
+ void (*visit_expression_function)(struct Visit* visitor, struct Expression* expr);
+
+ void (*visit_type_node)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_name)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_array)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_reference)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_maybe)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_tuple)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_map)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_function)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_structure)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_variant)(struct Visit* visitor, struct Type_Node* node);
+ void (*visit_type_node_class)(struct Visit* visitor, struct Type_Node* node);
+
+ void (*visit_block_node)(struct Visit* visitor, struct Block_Node* node);
+ void (*visit_bare_declaration_node)(struct Visit* visitor, struct Bare_Declaration_Node* node);
+ void (*visit_function_header_node)(struct Visit* visitor, struct Function_Header_Node* header);
+};
+
+#define VISIT(visit_function, node) visit->table->visit_function(visit, node);
+
+#define VISIT_MAYBE(visit_function, node) \
+ if (node) visit->table->visit_function(visit, node)
+
+#define DATA_FOR_VISIT(type, name) \
+ type* name = (type*)visit->user_data; \
+ if (!name) { failure("visit user data is NULL for " #name); }
+
+// walk functions are the default traversal mechanism
+
+// define a walk function which goes nowhere.
+#define WALK_LEAF_FUNCTION(name, type) \
+ void name(struct Visit* visit, type node) {}
+
+// top-level walk function for beginning the visit flow.
+void
+walk(struct Visit* visit, struct Tree* tree)
+{
+ VISIT(visit_tree, tree);
+}
+
+void
+walk_tree(struct Visit* visit, struct Tree* tree)
+{
+ FOR_EACH(struct Statement*, statement, tree->top_level_statements)
+ {
+ VISIT(visit_statement, statement);
+ }
+}
+
+void
+walk_statement(struct Visit* visit, struct Statement* statement)
+{
+ switch (statement->kind) {
+ case STATEMENT_EXPRESSION:
+ VISIT(visit_expression, statement->value.expression.inner);
+ break;
+ case STATEMENT_DECLARATION:
+ VISIT(visit_statement_declaration, statement);
+ break;
+ case STATEMENT_BLOCK:
+ VISIT(visit_block_node, &statement->value.block.inner);
+ break;
+ case STATEMENT_CONDITIONAL:
+ VISIT(visit_statement_conditional, statement);
+ break;
+ case STATEMENT_LOOP:
+ VISIT(visit_statement_loop, statement);
+ break;
+ case STATEMENT_RETURN:
+ VISIT(visit_statement_return, statement);
+ break;
+ case STATEMENT_BREAK:
+ VISIT(visit_statement_break, statement);
+ break;
+ case STATEMENT_CONTINUE:
+ VISIT(visit_statement_continue, statement);
+ break;
+ case STATEMENT_DEFER:
+ VISIT(visit_statement_defer, statement);
+ break;
+ default:
+ failure("unexpected statement kind in `walk_statement`");
+ }
+}
+
+void
+walk_statement_declaration(struct Visit* visit, struct Statement* statement)
+{
+ VISIT(visit_bare_declaration_node, &statement->value.declaration.inner);
+}
+
+void
+walk_statement_conditional(struct Visit* visit, struct Statement* statement)
+{
+ struct Statement_Value_Conditional* conditional = &statement->value.conditional;
+ for (uint i = 0; i < conditional->condition_count; ++i) {
+ VISIT_MAYBE(visit_expression, conditional->conditions[i].when);
+ VISIT(visit_block_node, &conditional->conditions[i].then);
+ }
+}
+
+void
+walk_statement_loop(struct Visit* visit, struct Statement* statement)
+{
+ struct Statement_Value_Loop* loop = &statement->value.loop;
+ VISIT_MAYBE(visit_expression, loop->condition);
+ VISIT_MAYBE(visit_expression, loop->iteration);
+ VISIT(visit_block_node, &loop->body);
+}
+
+void
+walk_statement_return(struct Visit* visit, struct Statement* statement)
+{
+ VISIT_MAYBE(visit_expression, statement->value.return_value.value);
+}
+
+// although.. maybe with `break` we could make a loop become an expression,
+// this returning the break value as the value of the loop?
+// something to think about.
+WALK_LEAF_FUNCTION(walk_statement_break, struct Statement*);
+
+WALK_LEAF_FUNCTION(walk_statement_continue, struct Statement*);
+
+void
+walk_statement_defer(struct Visit* visit, struct Statement* statement)
+{
+ VISIT_MAYBE(visit_expression, statement->value.defer.expression);
+ VISIT(visit_block_node, &statement->value.defer.block);
+}
+
+void
+walk_expression(struct Visit* visit, struct Expression* expression)
+{
+ switch (expression->kind) {
+ case EXPRESSION_INTEGER_LITERAL:
+ VISIT(visit_expression_integer_literal, expression);
+ break;
+ case EXPRESSION_FLOAT_LITERAL:
+ VISIT(visit_expression_float_literal, expression);
+ break;
+ case EXPRESSION_STRING_LITERAL:
+ VISIT(visit_expression_string_literal, expression);
+ break;
+ case EXPRESSION_BOOLEAN_LITERAL:
+ VISIT(visit_expression_boolean_literal, expression);
+ break;
+ case EXPRESSION_NAME:
+ VISIT(visit_expression_name, expression);
+ break;
+ case EXPRESSION_UNARY_OPERATION:
+ VISIT(visit_expression_unary_operation, expression);
+ break;
+ case EXPRESSION_BINARY_OPERATION:
+ VISIT(visit_expression_binary_operation, expression);
+ break;
+ case EXPRESSION_GROUP:
+ VISIT(visit_expression_group, expression);
+ break;
+ case EXPRESSION_CALL_OR_CONSTRUCT:
+ VISIT(visit_expression_call_or_construct, expression);
+ break;
+ case EXPRESSION_SUBSCRIPT:
+ VISIT(visit_expression_subscript, expression);
+ break;
+ case EXPRESSION_MEMBER:
+ VISIT(visit_expression_member, expression);
+ break;
+ case EXPRESSION_INCREMENT_DECREMENT:
+ VISIT(visit_expression_increment_decrement, expression);
+ break;
+ case EXPRESSION_FUNCTION:
+ VISIT(visit_expression_function, expression);
+ break;
+ case EXPRESSION_TYPE:
+ VISIT(visit_type_node, expression->value.type.type);
+ break;
+ default:
+ failure("unexpected expression kind in `walk_expression`");
+ }
+}
+
+WALK_LEAF_FUNCTION(walk_expression_integer_literal, struct Expression*);
+
+WALK_LEAF_FUNCTION(walk_expression_float_literal, struct Expression*);
+
+WALK_LEAF_FUNCTION(walk_expression_string_literal, struct Expression*);
+
+WALK_LEAF_FUNCTION(walk_expression_boolean_literal, struct Expression*);
+
+WALK_LEAF_FUNCTION(walk_expression_name, struct Expression*);
+
+void
+walk_expression_unary_operation(struct Visit* visit, struct Expression* expression)
+{
+ VISIT(visit_expression, expression->value.unary_operator.operand);
+}
+
+void
+walk_expression_binary_operation(struct Visit* visit, struct Expression* expression)
+{
+ VISIT(visit_expression, expression->value.binary_operator.left_operand);
+ VISIT(visit_expression, expression->value.binary_operator.right_operand);
+}
+
+void
+walk_expression_group(struct Visit* visit, struct Expression* expression)
+{
+ VISIT(visit_expression, expression->value.group.inner_expression);
+}
+
+void
+walk_expression_call_or_construct(struct Visit* visit, struct Expression* expression)
+{
+ struct Expression_Call_Or_Construct* coc = &expression->value.call_or_construct;
+ VISIT(visit_expression, coc->subject);
+ FOR_EACH(struct Expression*, argument, coc->arguments)
+ {
+ VISIT(visit_expression, argument);
+ }
+}
+
+void
+walk_expression_subscript(struct Visit* visit, struct Expression* expression)
+{
+ VISIT(visit_expression, expression->value.subscript.subject);
+ VISIT(visit_expression, expression->value.subscript.index);
+}
+
+void
+walk_expression_member(struct Visit* visit, struct Expression* expression)
+{
+ VISIT(visit_expression, expression->value.member.subject);
+}
+
+void
+walk_expression_increment_decrement(struct Visit* visit, struct Expression* expression)
+{
+ VISIT(visit_expression, expression->value.increment_decrement.subject);
+}
+
+void
+walk_expression_function(struct Visit* visit, struct Expression* expression)
+{
+ VISIT(visit_function_header_node, &expression->value.function.header);
+ VISIT(visit_block_node, &expression->value.function.body);
+}
+
+void
+walk_type_node(struct Visit* visit, struct Type_Node* node)
+{
+ switch (node->type) {
+ case TYPE_NODE_NONE:
+ break;
+ case TYPE_NODE_NAME:
+ VISIT(visit_type_node_name, node);
+ break;
+ case TYPE_NODE_ARRAY:
+ VISIT(visit_type_node_array, node);
+ break;
+ case TYPE_NODE_REFERENCE:
+ VISIT(visit_type_node_reference, node);
+ break;
+ case TYPE_NODE_MAYBE:
+ VISIT(visit_type_node_maybe, node);
+ break;
+ case TYPE_NODE_TUPLE:
+ VISIT(visit_type_node_tuple, node);
+ break;
+ case TYPE_NODE_MAP:
+ VISIT(visit_type_node_map, node);
+ break;
+ case TYPE_NODE_FUNCTION:
+ VISIT(visit_type_node_function, node);
+ break;
+ case TYPE_NODE_STRUCTURE:
+ VISIT(visit_type_node_structure, node);
+ break;
+ case TYPE_NODE_VARIANT:
+ VISIT(visit_type_node_variant, node);
+ break;
+ case TYPE_NODE_CLASS:
+ VISIT(visit_type_node_class, node);
+ break;
+ default:
+ failure("unexpected type node kind in `walk_type_node`");
+ }
+}
+
+WALK_LEAF_FUNCTION(walk_type_node_name, struct Type_Node*);
+
+void
+walk_type_node_array(struct Visit* visit, struct Type_Node* node)
+{
+ VISIT(visit_type_node, node->value.array.element_type);
+}
+
+void
+walk_type_node_reference(struct Visit* visit, struct Type_Node* node)
+{
+ VISIT(visit_type_node, node->value.reference.referenced_type);
+}
+
+void
+walk_type_node_maybe(struct Visit* visit, struct Type_Node* node)
+{
+ VISIT(visit_type_node, node->value.maybe.inner_type);
+}
+
+void
+walk_type_node_tuple(struct Visit* visit, struct Type_Node* node)
+{
+ FOR_EACH(struct Type_Node*, current, node->value.tuple.head)
+ {
+ VISIT(visit_type_node, current);
+ }
+}
+
+void
+walk_type_node_map(struct Visit* visit, struct Type_Node* node)
+{
+ VISIT(visit_type_node, node->value.map.key_type);
+ VISIT(visit_type_node, node->value.map.value_type);
+}
+
+void
+walk_type_node_function(struct Visit* visit, struct Type_Node* node)
+{
+ VISIT(visit_function_header_node, &node->value.function.header);
+}
+
+void
+walk_type_node_structure(struct Visit* visit, struct Type_Node* node)
+{
+ FOR_EACH(struct Type_Node*, field, node->value.structure.fields)
+ {
+ VISIT(visit_type_node, field);
+ }
+}
+
+void
+walk_type_node_variant(struct Visit* visit, struct Type_Node* node)
+{
+ FOR_EACH(struct Type_Node*, method, node->value.variant.variants)
+ {
+ VISIT(visit_type_node, method);
+ }
+}
+
+void
+walk_type_node_class(struct Visit* visit, struct Type_Node* node)
+{
+ FOR_EACH(struct Type_Node*, method, node->value.class.methods)
+ {
+ VISIT(visit_type_node, method);
+ }
+}
+
+void
+walk_block_node(struct Visit* visit, struct Block_Node* node)
+{
+ FOR_EACH(struct Statement*, statement, node->statements)
+ {
+ VISIT(visit_statement, statement);
+ }
+}
+
+void
+walk_bare_declaration_node(struct Visit* visit, struct Bare_Declaration_Node* node)
+{
+ VISIT_MAYBE(visit_type_node, node->type);
+ VISIT(visit_expression, node->initializer);
+}
+
+void
+walk_function_header_node(struct Visit* visit, struct Function_Header_Node* header)
+{
+ FOR_EACH(struct Type_Node*, param_type, header->parameters_type_and_name)
+ {
+ VISIT(visit_type_node, param_type);
+ }
+ VISIT_MAYBE(visit_type_node, header->return_type);
+}
+
+struct Visit_Table walk_functions = {
+ .visit_tree = walk_tree,
+
+ .visit_statement = walk_statement,
+ .visit_statement_declaration = walk_statement_declaration,
+ .visit_statement_conditional = walk_statement_conditional,
+ .visit_statement_loop = walk_statement_loop,
+ .visit_statement_return = walk_statement_return,
+ .visit_statement_break = walk_statement_break,
+ .visit_statement_continue = walk_statement_continue,
+ .visit_statement_defer = walk_statement_defer,
+
+ .visit_expression = walk_expression,
+ .visit_expression_integer_literal = walk_expression_integer_literal,
+ .visit_expression_float_literal = walk_expression_float_literal,
+ .visit_expression_string_literal = walk_expression_string_literal,
+ .visit_expression_boolean_literal = walk_expression_boolean_literal,
+ .visit_expression_name = walk_expression_name,
+ .visit_expression_unary_operation = walk_expression_unary_operation,
+ .visit_expression_binary_operation = walk_expression_binary_operation,
+ .visit_expression_group = walk_expression_group,
+ .visit_expression_call_or_construct = walk_expression_call_or_construct,
+ .visit_expression_subscript = walk_expression_subscript,
+ .visit_expression_member = walk_expression_member,
+ .visit_expression_increment_decrement = walk_expression_increment_decrement,
+ .visit_expression_function = walk_expression_function,
+
+ .visit_type_node = walk_type_node,
+ .visit_type_node_name = walk_type_node_name,
+ .visit_type_node_array = walk_type_node_array,
+ .visit_type_node_reference = walk_type_node_reference,
+ .visit_type_node_maybe = walk_type_node_maybe,
+ .visit_type_node_tuple = walk_type_node_tuple,
+ .visit_type_node_map = walk_type_node_map,
+ .visit_type_node_function = walk_type_node_function,
+ .visit_type_node_structure = walk_type_node_structure,
+ .visit_type_node_variant = walk_type_node_variant,
+ .visit_type_node_class = walk_type_node_class,
+
+ .visit_block_node = walk_block_node,
+ .visit_bare_declaration_node = walk_bare_declaration_node,
+ .visit_function_header_node = walk_function_header_node,
+};
+
+// fills in the visit table with default walk functions
+void
+visit_table_fill_defaults(struct Visit_Table* table)
+{
+ // not portable, nor safe, but sometimes you need to have a bit of fun! :3
+
+ // assumptions:
+ // - sizeof(struct Visit_Table) is a multiple of the size of a function pointer,
+ // with no padding between function pointers.
+ // - the size of a function pointer is the same across all platforms we care about.
+ // - casting struct Visit_Table to a function pointer array is undefined, but safe.
+ const uint function_count = sizeof(struct Visit_Table) / sizeof(void (*)());
+ typedef void (*Function_Ptr)();
+
+ for (uint fi = 0; fi < function_count; ++fi) {
+ Function_Ptr* function_slot = &((Function_Ptr*)table)[fi];
+ Function_Ptr* walk = &((Function_Ptr*)&walk_functions)[fi];
+ if (!*function_slot) *function_slot = *walk;
+ }
+}
+
+// the most rudimentary usage of the visit system is a simple tree printer.
+// prints the tree in a simple, human-readable format that resembles Lisp syntax,
+// to a file or stdout (by default).
+struct Tree_Printer
+{
+ uint indentation_level;
+ FILE* output;
+};
+
+void
+tree_printer_indent(struct Tree_Printer* printer)
+{
+ for (uint i = 0; i < printer->indentation_level; ++i) fprintf(printer->output, "\t");
+}
+
+void
+printer_visit_tree(struct Visit* visit, struct Tree* tree)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+ FOR_EACH(struct Statement*, statement, tree->top_level_statements)
+ {
+ VISIT(visit_statement, statement);
+ fprintf(printer->output, "\n");
+ }
+}
+
+void
+printer_visit_statement(struct Visit* visit, struct Statement* statement)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+ tree_printer_indent(printer);
+ walk_statement(visit, statement);
+}
+
+void
+printer_visit_statement_declaration(struct Visit* visit, struct Statement* stmt)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ if (stmt->value.declaration.kind == STATEMENT_DECLARATION_VARIABLE)
+ fprintf(printer->output, "(variable ");
+ else if (stmt->value.declaration.kind == STATEMENT_DECLARATION_CONSTANT)
+ fprintf(printer->output, "(constant ");
+
+ VISIT(visit_bare_declaration_node, &stmt->value.declaration.inner);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_statement_conditional(struct Visit* visit, struct Statement* stmt)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(conditional");
+ for (uint i = 0; i < stmt->value.conditional.condition_count; ++i) {
+ const struct Statement_Conditional_Branch* branch = &stmt->value.conditional.conditions[i];
+ fprintf(printer->output, " ");
+ if (branch->when) {
+ fprintf(printer->output, "(when ");
+ VISIT(visit_expression, branch->when);
+ fprintf(printer->output, ") ");
+ } else {
+ fprintf(printer->output, "(always) ");
+ }
+ VISIT(visit_block_node, (struct Block_Node*)&branch->then);
+ }
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_statement_loop(struct Visit* visit, struct Statement* stmt)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(loop ");
+
+ switch (stmt->value.loop.style) {
+ case STATEMENT_LOOP_STYLE_C:
+ fprintf(printer->output, "c-style ");
+ VISIT(visit_bare_declaration_node, &stmt->value.loop.declaration);
+ fprintf(printer->output, " (condition ");
+ VISIT(visit_expression, stmt->value.loop.condition);
+ fprintf(printer->output, ") (iteration ");
+ VISIT(visit_expression, stmt->value.loop.iteration);
+ fprintf(printer->output, ") ");
+ break;
+ case STATEMENT_LOOP_STYLE_FOR_EACH:
+ fprintf(printer->output, "for-each ");
+ VISIT(visit_bare_declaration_node, &stmt->value.loop.declaration);
+ fprintf(printer->output, " ");
+ break;
+ case STATEMENT_LOOP_STYLE_WHILE:
+ fprintf(printer->output, "while (condition ");
+ VISIT(visit_expression, stmt->value.loop.condition);
+ fprintf(printer->output, ") ");
+ break;
+ case STATEMENT_LOOP_STYLE_ENDLESS:
+ fprintf(printer->output, "endless ");
+ break;
+ default:
+ fprintf(stderr, "unexpected loop style in `printer_visit_statement_loop`");
+ break;
+ }
+
+ VISIT(visit_block_node, &stmt->value.loop.body);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_statement_return(struct Visit* visit, struct Statement* stmt)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(return");
+ if (stmt->value.return_value.value) {
+ fprintf(printer->output, " ");
+ VISIT(visit_expression, stmt->value.return_value.value);
+ }
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_statement_break(struct Visit* visit, struct Statement* stmt)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(break)");
+}
+
+void
+printer_visit_statement_continue(struct Visit* visit, struct Statement* stmt)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(continue)");
+}
+
+void
+printer_visit_statement_defer(struct Visit* visit, struct Statement* stmt)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(defer ");
+ if (stmt->value.defer.expression) {
+ VISIT(visit_expression, stmt->value.defer.expression);
+ } else {
+ VISIT(visit_block_node, &stmt->value.defer.block);
+ }
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_expression(struct Visit* visit, struct Expression* expression)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+ fprintf(printer->output, "(expr ");
+ walk_expression(visit, expression);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_expression_integer_literal(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "%ld", expr->value.integer_literal.value);
+}
+
+void
+printer_visit_expression_float_literal(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "%lf", expr->value.float_literal.value);
+}
+
+void
+printer_visit_expression_string_literal(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "\"%s\"", expr->value.string_literal.value.data);
+}
+
+void
+printer_visit_expression_boolean_literal(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "%s", expr->value.bool_literal.value ? "true" : "false");
+}
+
+void
+printer_visit_expression_name(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(name %s)", expr->value.name.name.data);
+}
+
+void
+printer_visit_expression_unary_operation(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(unary %s ",
+ unary_operation_to_string(expr->value.unary_operator.operation));
+ VISIT(visit_expression, expr->value.unary_operator.operand);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_expression_binary_operation(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(binary %s ",
+ binary_operation_to_string(expr->value.binary_operator.operation));
+ VISIT(visit_expression, expr->value.binary_operator.left_operand);
+ fprintf(printer->output, " ");
+ VISIT(visit_expression, expr->value.binary_operator.right_operand);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_expression_group(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(group ");
+ VISIT(visit_expression, expr->value.group.inner_expression);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_expression_call_or_construct(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ struct Expression_Call_Or_Construct* coc = &expr->value.call_or_construct;
+ fprintf(printer->output, "(call/construct ");
+ VISIT(visit_expression, coc->subject);
+
+ uint i = 0;
+ FOR_EACH(struct Expression*, argument, coc->arguments)
+ {
+ struct String name = string_array_at(&coc->argument_names, i++);
+ if (name.data && name.data[0] != '\0') {
+ fprintf(printer->output, " (named arg '%s' ", name.data);
+ } else {
+ fprintf(printer->output, " (arg ");
+ }
+ VISIT(visit_expression, argument);
+ fprintf(printer->output, ")");
+ }
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_expression_subscript(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(subscript ");
+ VISIT(visit_expression, expr->value.subscript.subject);
+ fprintf(printer->output, " ");
+ VISIT(visit_expression, expr->value.subscript.index);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_expression_member(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(member of ");
+ VISIT(visit_expression, expr->value.member.subject);
+ fprintf(printer->output, " named %s)", expr->value.member.name.data);
+}
+
+void
+printer_visit_expression_increment_decrement(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ const struct Expression_Increment_Decrement* inc_dec = &expr->value.increment_decrement;
+ const ascii* prefix_or_postfix = inc_dec->prefix ? "prefix" : "postfix";
+ fprintf(printer->output, "(increment/decrement %s %s ",
+ increment_decrement_operation_to_string(inc_dec->operation), prefix_or_postfix);
+ VISIT(visit_expression, inc_dec->subject);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_expression_function(struct Visit* visit, struct Expression* expr)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ struct Expression_Function* fun = &expr->value.function;
+ fprintf(printer->output, "(function ");
+ VISIT(visit_function_header_node, &fun->header);
+ fprintf(printer->output, " ");
+ VISIT(visit_block_node, (struct Block_Node*)&fun->body);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_type_node(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+ fprintf(printer->output, "(type ");
+ walk_type_node(visit, node);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_type_node_name(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "name %s", node->value.name.name.data);
+}
+
+void
+printer_visit_type_node_array(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "array of ");
+ VISIT(visit_type_node, node->value.array.element_type);
+}
+
+void
+printer_visit_type_node_reference(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "reference to ");
+ VISIT(visit_type_node, node->value.reference.referenced_type);
+}
+
+void
+printer_visit_type_node_maybe(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "maybe ");
+ VISIT(visit_type_node, node->value.maybe.inner_type);
+}
+
+void
+printer_visit_type_node_tuple(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "tuple");
+ FOR_EACH(struct Type_Node*, current, node->value.tuple.head)
+ {
+ fprintf(printer->output, " ");
+ VISIT(visit_type_node, current);
+ }
+}
+
+void
+printer_visit_type_node_map(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "map ");
+ VISIT(visit_type_node, node->value.map.key_type);
+ fprintf(printer->output, " = ");
+ VISIT(visit_type_node, node->value.map.value_type);
+}
+
+void
+printer_visit_type_node_function(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "function ");
+ VISIT(visit_function_header_node, &node->value.function.header);
+}
+
+void
+printer_visit_type_node_structure(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "structure");
+ FOR_EACH(struct Type_Node*, current, node->value.structure.fields)
+ {
+ fprintf(printer->output, " (field %s) ", current->value_name.data);
+ VISIT(visit_type_node, current);
+ }
+}
+
+void
+printer_visit_type_node_variant(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "variant");
+ FOR_EACH(struct Type_Node*, current, node->value.variant.variants)
+ {
+ if (current->type == TYPE_NODE_NONE) {
+ fprintf(printer->output, " (variant %s)", current->value_name.data);
+ } else {
+ fprintf(printer->output, " (variant %s of ", current->value_name.data);
+ VISIT(visit_type_node, current);
+ fprintf(printer->output, ")");
+ }
+ }
+}
+
+void
+printer_visit_type_node_class(struct Visit* visit, struct Type_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "class");
+ FOR_EACH(struct Type_Node*, current, node->value.class.methods)
+ {
+ fprintf(printer->output, " (method %s ", current->value_name.data);
+ VISIT(visit_function_header_node, &current->value.function.header);
+ fprintf(printer->output, ")");
+ }
+}
+
+void
+printer_visit_block_node(struct Visit* visit, struct Block_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(block \n");
+ printer->indentation_level++;
+ FOR_EACH(struct Statement*, statement, node->statements)
+ {
+ VISIT(visit_statement, statement);
+ fprintf(printer->output, "\n");
+ }
+ printer->indentation_level--;
+ tree_printer_indent(printer);
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_bare_declaration_node(struct Visit* visit, struct Bare_Declaration_Node* node)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ fprintf(printer->output, "(declaration ");
+ STRING_ARRAY_FOR_EACH(i, name, node->names)
+ {
+ fprintf(printer->output, "%s ", name.data);
+ }
+
+ if (node->type && node->type->type != TYPE_NODE_NONE) {
+ VISIT(visit_type_node, node->type);
+ fprintf(printer->output, " ");
+ }
+
+ if (node->initializer) {
+ fprintf(printer->output, "(initializer ");
+ VISIT(visit_expression, node->initializer);
+ fprintf(printer->output, ")");
+ }
+ fprintf(printer->output, ")");
+}
+
+void
+printer_visit_function_header(struct Visit* visit, struct Function_Header_Node* header)
+{
+ DATA_FOR_VISIT(struct Tree_Printer, printer);
+
+ FOR_EACH(struct Type_Node*, current, header->parameters_type_and_name)
+ {
+ if (current != header->parameters_type_and_name) fprintf(printer->output, " ");
+
+ if (current->value_name.data && current->value_name.data[0] != '\0')
+ fprintf(printer->output, "(param %s) ", current->value_name.data);
+ else
+ fprintf(printer->output, "(param) ");
+
+ VISIT(visit_type_node, current);
+ }
+
+ if (header->return_type) {
+ fprintf(printer->output, " (returns ");
+ VISIT(visit_type_node, header->return_type);
+ fprintf(printer->output, ")");
+ }
+}
+
+struct Visit_Table printer_visit_functions = {
+ .visit_tree = printer_visit_tree,
+
+ .visit_statement = printer_visit_statement,
+ .visit_statement_declaration = printer_visit_statement_declaration,
+ .visit_statement_conditional = printer_visit_statement_conditional,
+ .visit_statement_loop = printer_visit_statement_loop,
+ .visit_statement_return = printer_visit_statement_return,
+ .visit_statement_break = printer_visit_statement_break,
+ .visit_statement_continue = printer_visit_statement_continue,
+ .visit_statement_defer = printer_visit_statement_defer,
+
+ .visit_expression = printer_visit_expression,
+ .visit_expression_integer_literal = printer_visit_expression_integer_literal,
+ .visit_expression_float_literal = printer_visit_expression_float_literal,
+ .visit_expression_string_literal = printer_visit_expression_string_literal,
+ .visit_expression_boolean_literal = printer_visit_expression_boolean_literal,
+ .visit_expression_name = printer_visit_expression_name,
+ .visit_expression_unary_operation = printer_visit_expression_unary_operation,
+ .visit_expression_binary_operation = printer_visit_expression_binary_operation,
+ .visit_expression_group = printer_visit_expression_group,
+ .visit_expression_call_or_construct = printer_visit_expression_call_or_construct,
+ .visit_expression_subscript = printer_visit_expression_subscript,
+ .visit_expression_member = printer_visit_expression_member,
+ .visit_expression_increment_decrement = printer_visit_expression_increment_decrement,
+ .visit_expression_function = printer_visit_expression_function,
+
+ .visit_type_node = printer_visit_type_node,
+ .visit_type_node_name = printer_visit_type_node_name,
+ .visit_type_node_array = printer_visit_type_node_array,
+ .visit_type_node_reference = printer_visit_type_node_reference,
+ .visit_type_node_maybe = printer_visit_type_node_maybe,
+ .visit_type_node_tuple = printer_visit_type_node_tuple,
+ .visit_type_node_map = printer_visit_type_node_map,
+ .visit_type_node_function = printer_visit_type_node_function,
+ .visit_type_node_structure = printer_visit_type_node_structure,
+ .visit_type_node_variant = printer_visit_type_node_variant,
+ .visit_type_node_class = printer_visit_type_node_class,
+
+ .visit_block_node = printer_visit_block_node,
+ .visit_bare_declaration_node = printer_visit_bare_declaration_node,
+ .visit_function_header_node = printer_visit_function_header,
+};
+
+void
+tree_printer(struct Tree* tree)
+{
+ struct Tree_Printer printer = { .indentation_level = 0, .output = stdout };
+ struct Visit visit = { .table = &printer_visit_functions, .user_data = &printer };
+
+ walk(&visit, tree);
+}