aboutsummaryrefslogtreecommitdiff
path: root/boot/visit
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-03 01:30:28 +0200
committerMel <mel@rnrd.eu>2026-05-03 01:30:28 +0200
commit1512abed55dd3f779eb774eb03f605397a4a45fe (patch)
tree42ea2ae10c5667bcbd46e30432ed574b620e681a /boot/visit
parentfb13c2be5538b10ae456bcb30bfef6622ab6911f (diff)
downloadcatskill-1512abed55dd3f779eb774eb03f605397a4a45fe.tar.zst
catskill-1512abed55dd3f779eb774eb03f605397a4a45fe.zip
Move tree visit definitions in visit.c to visit/tree.c for future consistency
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot/visit')
-rw-r--r--boot/visit/tree.c1185
1 files changed, 1185 insertions, 0 deletions
diff --git a/boot/visit/tree.c b/boot/visit/tree.c
new file mode 100644
index 0000000..001cd36
--- /dev/null
+++ b/boot/visit/tree.c
@@ -0,0 +1,1185 @@
+/*
+ * 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.
+ *
+ * Copyright (c) 2025, Mel G. <mel@rnrd.eu>
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ */
+
+#pragma once
+
+#include "../catboot.h"
+
+struct Tree_Visit
+{
+ struct Tree_Visit_Table* table;
+ void* user_data;
+};
+
+struct Tree_Visit_Table
+{
+ void (*visit_tree)(struct Tree_Visit* visitor, struct Tree* tree);
+
+ void (*visit_statement)(struct Tree_Visit* visitor, struct Tree_Statement* stmt);
+ void (*visit_statement_declaration)(struct Tree_Visit* visitor, struct Tree_Statement* stmt);
+ void (*visit_statement_conditional)(struct Tree_Visit* visitor, struct Tree_Statement* stmt);
+ void (*visit_statement_loop)(struct Tree_Visit* visitor, struct Tree_Statement* stmt);
+ void (*visit_statement_return)(struct Tree_Visit* visitor, struct Tree_Statement* stmt);
+ void (*visit_statement_break)(struct Tree_Visit* visitor, struct Tree_Statement* stmt);
+ void (*visit_statement_continue)(struct Tree_Visit* visitor, struct Tree_Statement* stmt);
+ void (*visit_statement_defer)(struct Tree_Visit* visitor, struct Tree_Statement* stmt);
+
+ void (*visit_expression)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_integer_literal)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_float_literal)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_string_literal)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_boolean_literal)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_name)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_unary_operation)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_binary_operation)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_group)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_call)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_construct)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_subscript)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_member)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_increment_decrement)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_try)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_must)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+ void (*visit_expression_function)(struct Tree_Visit* visitor, struct Tree_Expression* expr);
+
+ void (*visit_type_node)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_name)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_array)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_reference)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_maybe)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_tuple)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_map)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_function)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_structure)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_variant)(struct Tree_Visit* visitor, struct Tree_Type* node);
+ void (*visit_type_node_class)(struct Tree_Visit* visitor, struct Tree_Type* node);
+
+ void (*visit_block_node)(struct Tree_Visit* visitor, struct Tree_Block* node);
+ void (*visit_bare_declaration_node)(struct Tree_Visit* visitor, struct Tree_Bare_Declaration* node);
+ void (*visit_function_header_node)(struct Tree_Visit* visitor, struct Tree_Function_Header* header);
+ void (*visit_argument_group_node)(struct Tree_Visit* visitor, struct Tree_Argument_Group* node);
+ void (*visit_pragma_node)(struct Tree_Visit* visitor, struct Tree_Pragma* node);
+};
+
+#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); }
+
+// tree_walk functions are the default traversal mechanism
+
+// define a tree_walk function which goes nowhere.
+#define TREE_WALK_LEAF_FUNCTION(name, type) \
+ void name(struct Tree_Visit* visit, type node) {}
+
+// top-level tree_walk function for beginning the visit flow.
+void
+tree_walk(struct Tree_Visit* visit, struct Tree* tree)
+{
+ VISIT(visit_tree, tree);
+}
+
+void
+tree_walk_tree(struct Tree_Visit* visit, struct Tree* tree)
+{
+ FOR_EACH (struct Tree_Statement*, statement, tree->top_level_statements) {
+ VISIT(visit_statement, statement);
+ }
+}
+
+void
+tree_walk_statement(struct Tree_Visit* visit, struct Tree_Statement* statement)
+{
+ switch (statement->kind) {
+ case TREE_STATEMENT_EXPRESSION:
+ VISIT(visit_expression, statement->value.expression.inner);
+ break;
+ case TREE_STATEMENT_DECLARATION:
+ VISIT(visit_statement_declaration, statement);
+ break;
+ case TREE_STATEMENT_BLOCK:
+ VISIT(visit_block_node, &statement->value.block.inner);
+ break;
+ case TREE_STATEMENT_CONDITIONAL:
+ VISIT(visit_statement_conditional, statement);
+ break;
+ case TREE_STATEMENT_LOOP:
+ VISIT(visit_statement_loop, statement);
+ break;
+ case TREE_STATEMENT_RETURN:
+ VISIT(visit_statement_return, statement);
+ break;
+ case TREE_STATEMENT_BREAK:
+ VISIT(visit_statement_break, statement);
+ break;
+ case TREE_STATEMENT_CONTINUE:
+ VISIT(visit_statement_continue, statement);
+ break;
+ case TREE_STATEMENT_DEFER:
+ VISIT(visit_statement_defer, statement);
+ break;
+ case TREE_STATEMENT_PRAGMA:
+ VISIT(visit_pragma_node, statement->value.pragma.inner);
+ break;
+ default:
+ failure("unexpected statement kind in `tree_walk_statement`");
+ }
+}
+
+void
+tree_walk_statement_declaration(struct Tree_Visit* visit, struct Tree_Statement* statement)
+{
+ VISIT(visit_bare_declaration_node, &statement->value.declaration.inner);
+}
+
+void
+tree_walk_statement_conditional(struct Tree_Visit* visit, struct Tree_Statement* statement)
+{
+ struct Tree_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
+tree_walk_statement_loop(struct Tree_Visit* visit, struct Tree_Statement* statement)
+{
+ struct Tree_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
+tree_walk_statement_return(struct Tree_Visit* visit, struct Tree_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.
+TREE_WALK_LEAF_FUNCTION(tree_walk_statement_break, struct Tree_Statement*);
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_statement_continue, struct Tree_Statement*);
+
+void
+tree_walk_statement_defer(struct Tree_Visit* visit, struct Tree_Statement* statement)
+{
+ VISIT_MAYBE(visit_expression, statement->value.defer.expression);
+ VISIT(visit_block_node, &statement->value.defer.block);
+}
+
+void
+tree_walk_expression(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ switch (expression->kind) {
+ case TREE_EXPRESSION_INTEGER_LITERAL:
+ VISIT(visit_expression_integer_literal, expression);
+ break;
+ case TREE_EXPRESSION_FLOAT_LITERAL:
+ VISIT(visit_expression_float_literal, expression);
+ break;
+ case TREE_EXPRESSION_STRING_LITERAL:
+ VISIT(visit_expression_string_literal, expression);
+ break;
+ case TREE_EXPRESSION_BOOLEAN_LITERAL:
+ VISIT(visit_expression_boolean_literal, expression);
+ break;
+ case TREE_EXPRESSION_NAME:
+ VISIT(visit_expression_name, expression);
+ break;
+ case TREE_EXPRESSION_UNARY_OPERATION:
+ VISIT(visit_expression_unary_operation, expression);
+ break;
+ case TREE_EXPRESSION_BINARY_OPERATION:
+ VISIT(visit_expression_binary_operation, expression);
+ break;
+ case TREE_EXPRESSION_GROUP:
+ VISIT(visit_expression_group, expression);
+ break;
+ case TREE_EXPRESSION_CALL:
+ VISIT(visit_expression_call, expression);
+ break;
+ case TREE_EXPRESSION_CONSTRUCT:
+ VISIT(visit_expression_construct, expression);
+ break;
+ case TREE_EXPRESSION_SUBSCRIPT:
+ VISIT(visit_expression_subscript, expression);
+ break;
+ case TREE_EXPRESSION_MEMBER:
+ VISIT(visit_expression_member, expression);
+ break;
+ case TREE_EXPRESSION_INCREMENT_DECREMENT:
+ VISIT(visit_expression_increment_decrement, expression);
+ break;
+ case TREE_EXPRESSION_TRY:
+ VISIT(visit_expression_try, expression);
+ break;
+ case TREE_EXPRESSION_MUST:
+ VISIT(visit_expression_must, expression);
+ break;
+ case TREE_EXPRESSION_FUNCTION:
+ VISIT(visit_expression_function, expression);
+ break;
+ case TREE_EXPRESSION_TYPE:
+ VISIT(visit_type_node, expression->value.type.type);
+ break;
+ default:
+ failure("unexpected expression kind in `tree_walk_expression`");
+ }
+}
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_expression_integer_literal, struct Tree_Expression*);
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_expression_float_literal, struct Tree_Expression*);
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_expression_string_literal, struct Tree_Expression*);
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_expression_boolean_literal, struct Tree_Expression*);
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_expression_name, struct Tree_Expression*);
+
+void
+tree_walk_expression_unary_operation(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ VISIT(visit_expression, expression->value.unary_operator.operand);
+}
+
+void
+tree_walk_expression_binary_operation(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ VISIT(visit_expression, expression->value.binary_operator.left_operand);
+ VISIT(visit_expression, expression->value.binary_operator.right_operand);
+}
+
+void
+tree_walk_expression_group(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ VISIT(visit_expression, expression->value.group.inner_expression);
+}
+
+void
+tree_walk_expression_call(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ struct Tree_Expression_Call* call = &expression->value.call;
+ VISIT(visit_expression, call->subject);
+ VISIT(visit_argument_group_node, &call->argument_group);
+}
+
+void
+tree_walk_expression_construct(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ struct Tree_Expression_Construct* construct = &expression->value.construct;
+ VISIT(visit_expression, construct->subject);
+ VISIT(visit_argument_group_node, &construct->argument_group);
+}
+
+void
+tree_walk_expression_subscript(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ VISIT(visit_expression, expression->value.subscript.subject);
+ VISIT(visit_expression, expression->value.subscript.index);
+}
+
+void
+tree_walk_expression_member(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ VISIT(visit_expression, expression->value.member.subject);
+}
+
+void
+tree_walk_expression_increment_decrement(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ VISIT(visit_expression, expression->value.increment_decrement.subject);
+}
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_expression_try, struct Tree_Expression*);
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_expression_must, struct Tree_Expression*);
+
+void
+tree_walk_expression_function(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ VISIT(visit_function_header_node, &expression->value.function.header);
+ VISIT(visit_block_node, &expression->value.function.body);
+}
+
+void
+tree_walk_type(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ switch (node->type) {
+ case TREE_TYPE_NONE:
+ break;
+ case TREE_TYPE_NAME:
+ VISIT(visit_type_node_name, node);
+ break;
+ case TREE_TYPE_ARRAY:
+ VISIT(visit_type_node_array, node);
+ break;
+ case TREE_TYPE_REFERENCE:
+ VISIT(visit_type_node_reference, node);
+ break;
+ case TREE_TYPE_MAYBE:
+ VISIT(visit_type_node_maybe, node);
+ break;
+ case TREE_TYPE_TUPLE:
+ VISIT(visit_type_node_tuple, node);
+ break;
+ case TREE_TYPE_MAP:
+ VISIT(visit_type_node_map, node);
+ break;
+ case TREE_TYPE_FUNCTION:
+ VISIT(visit_type_node_function, node);
+ break;
+ case TREE_TYPE_STRUCTURE:
+ VISIT(visit_type_node_structure, node);
+ break;
+ case TREE_TYPE_VARIANT:
+ VISIT(visit_type_node_variant, node);
+ break;
+ case TREE_TYPE_CLASS:
+ VISIT(visit_type_node_class, node);
+ break;
+ default:
+ failure("unexpected type node kind in `tree_walk_type`");
+ }
+}
+
+TREE_WALK_LEAF_FUNCTION(tree_walk_type_name, struct Tree_Type*);
+
+void
+tree_walk_type_array(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ VISIT(visit_type_node, node->value.array.element_type);
+}
+
+void
+tree_walk_type_reference(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ VISIT(visit_type_node, node->value.reference.referenced_type);
+}
+
+void
+tree_walk_type_maybe(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ VISIT(visit_type_node, node->value.maybe.inner_type);
+}
+
+void
+tree_walk_type_tuple(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ FOR_EACH (struct Tree_Type*, current, node->value.tuple.head) {
+ VISIT(visit_type_node, current);
+ }
+}
+
+void
+tree_walk_type_map(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ VISIT(visit_type_node, node->value.map.key_type);
+ VISIT(visit_type_node, node->value.map.value_type);
+}
+
+void
+tree_walk_type_function(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ VISIT(visit_function_header_node, &node->value.function.header);
+}
+
+void
+tree_walk_type_structure(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ FOR_EACH (struct Tree_Type*, field, node->value.structure.fields) {
+ VISIT(visit_type_node, field);
+ }
+}
+
+void
+tree_walk_type_variant(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ FOR_EACH (struct Tree_Type*, method, node->value.variant.variants) {
+ VISIT(visit_type_node, method);
+ }
+}
+
+void
+tree_walk_type_class(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ FOR_EACH (struct Tree_Type*, method, node->value.class.methods) {
+ VISIT(visit_type_node, method);
+ }
+}
+
+void
+tree_walk_block(struct Tree_Visit* visit, struct Tree_Block* node)
+{
+ FOR_EACH (struct Tree_Statement*, statement, node->statements) { VISIT(visit_statement, statement); }
+}
+
+void
+tree_walk_bare_declaration(struct Tree_Visit* visit, struct Tree_Bare_Declaration* node)
+{
+ VISIT_MAYBE(visit_type_node, node->type);
+ VISIT(visit_expression, node->initializer);
+}
+
+void
+tree_walk_function_header(struct Tree_Visit* visit, struct Tree_Function_Header* header)
+{
+ FOR_EACH (struct Tree_Type*, param_type, header->parameters_type_and_name) {
+ VISIT(visit_type_node, param_type);
+ }
+ VISIT_MAYBE(visit_type_node, header->return_type);
+}
+
+void
+tree_walk_argument_group(struct Tree_Visit* visit, struct Tree_Argument_Group* node)
+{
+ FOR_EACH (struct Tree_Expression*, argument, node->arguments) { VISIT(visit_expression, argument); }
+}
+
+void
+tree_walk_pragma(struct Tree_Visit* visit, struct Tree_Pragma* node)
+{
+ // visit each pragma node in the linked list.
+ VISIT_MAYBE(visit_pragma_node, node->next);
+}
+
+struct Tree_Visit_Table tree_walk_functions = {
+ .visit_tree = tree_walk_tree,
+
+ .visit_statement = tree_walk_statement,
+ .visit_statement_declaration = tree_walk_statement_declaration,
+ .visit_statement_conditional = tree_walk_statement_conditional,
+ .visit_statement_loop = tree_walk_statement_loop,
+ .visit_statement_return = tree_walk_statement_return,
+ .visit_statement_break = tree_walk_statement_break,
+ .visit_statement_continue = tree_walk_statement_continue,
+ .visit_statement_defer = tree_walk_statement_defer,
+
+ .visit_expression = tree_walk_expression,
+ .visit_expression_integer_literal = tree_walk_expression_integer_literal,
+ .visit_expression_float_literal = tree_walk_expression_float_literal,
+ .visit_expression_string_literal = tree_walk_expression_string_literal,
+ .visit_expression_boolean_literal = tree_walk_expression_boolean_literal,
+ .visit_expression_name = tree_walk_expression_name,
+ .visit_expression_unary_operation = tree_walk_expression_unary_operation,
+ .visit_expression_binary_operation = tree_walk_expression_binary_operation,
+ .visit_expression_group = tree_walk_expression_group,
+ .visit_expression_call = tree_walk_expression_call,
+ .visit_expression_construct = tree_walk_expression_construct,
+ .visit_expression_subscript = tree_walk_expression_subscript,
+ .visit_expression_member = tree_walk_expression_member,
+ .visit_expression_increment_decrement = tree_walk_expression_increment_decrement,
+ .visit_expression_function = tree_walk_expression_function,
+
+ .visit_type_node = tree_walk_type,
+ .visit_type_node_name = tree_walk_type_name,
+ .visit_type_node_array = tree_walk_type_array,
+ .visit_type_node_reference = tree_walk_type_reference,
+ .visit_type_node_maybe = tree_walk_type_maybe,
+ .visit_type_node_tuple = tree_walk_type_tuple,
+ .visit_type_node_map = tree_walk_type_map,
+ .visit_type_node_function = tree_walk_type_function,
+ .visit_type_node_structure = tree_walk_type_structure,
+ .visit_type_node_variant = tree_walk_type_variant,
+ .visit_type_node_class = tree_walk_type_class,
+
+ .visit_block_node = tree_walk_block,
+ .visit_bare_declaration_node = tree_walk_bare_declaration,
+ .visit_function_header_node = tree_walk_function_header,
+ .visit_argument_group_node = tree_walk_argument_group,
+ .visit_pragma_node = tree_walk_pragma,
+};
+
+// fills in the visit table with default tree_walk functions
+void
+tree_visit_table_fill_defaults(struct Tree_Visit_Table* table)
+{
+ // not portable, nor safe, but sometimes you need to have a bit of fun! :3
+
+ // assumptions:
+ // - sizeof(struct Tree_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 Tree_Visit_Table to a function pointer array is undefined, but safe.
+ const uint function_count = sizeof(struct Tree_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* tree_walk = &((Function_Ptr*)&tree_walk_functions)[fi];
+ if (!*function_slot) *function_slot = *tree_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;
+ bool did_print_last_visit;
+};
+
+#define TREE_PRINTER_PREAMBLE \
+ DATA_FOR_VISIT(struct Tree_Printer, printer); \
+ tree_printer_reset_print_state(printer);
+
+void
+tree_printer_out(struct Tree_Printer* printer, const ascii* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(printer->output, format, args);
+ va_end(args);
+
+ printer->did_print_last_visit = true;
+}
+
+#define PRINT(...) tree_printer_out(printer, __VA_ARGS__)
+#define PRINT_WHITESPACE_IF_NEEDED(s) \
+ do { \
+ if (printer->did_print_last_visit) { PRINT(s); } \
+ tree_printer_reset_print_state(printer); \
+ } while (0)
+
+void
+tree_printer_reset_print_state(struct Tree_Printer* printer)
+{
+ printer->did_print_last_visit = false;
+}
+
+void
+tree_printer_indent(struct Tree_Printer* printer)
+{
+ for (uint i = 0; i < printer->indentation_level; ++i) PRINT("\t");
+}
+
+void
+tree_printer_visit_tree(struct Tree_Visit* visit, struct Tree* tree)
+{
+ TREE_PRINTER_PREAMBLE
+
+ FOR_EACH (struct Tree_Statement*, statement, tree->top_level_statements) {
+ VISIT(visit_statement, statement);
+ PRINT("\n");
+ }
+}
+
+void
+tree_printer_visit_statement(struct Tree_Visit* visit, struct Tree_Statement* statement)
+{
+ TREE_PRINTER_PREAMBLE
+
+ tree_printer_indent(printer);
+ tree_walk_statement(visit, statement);
+}
+
+void
+tree_printer_visit_statement_declaration(struct Tree_Visit* visit, struct Tree_Statement* stmt)
+{
+ TREE_PRINTER_PREAMBLE
+
+ if (stmt->value.declaration.kind == TREE_STATEMENT_DECLARATION_VARIABLE)
+ PRINT("(variable ");
+ else if (stmt->value.declaration.kind == TREE_STATEMENT_DECLARATION_CONSTANT)
+ PRINT("(constant ");
+
+ VISIT(visit_bare_declaration_node, &stmt->value.declaration.inner);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_statement_conditional(struct Tree_Visit* visit, struct Tree_Statement* stmt)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(conditional");
+ for (uint i = 0; i < stmt->value.conditional.condition_count; ++i) {
+ const struct Tree_Statement_Conditional_Branch* branch = &stmt->value.conditional.conditions[i];
+ PRINT(" ");
+ if (branch->when) {
+ PRINT("(when ");
+ VISIT(visit_expression, branch->when);
+ PRINT(") ");
+ } else {
+ PRINT("(always) ");
+ }
+ VISIT(visit_block_node, (struct Tree_Block*)&branch->then);
+ }
+ PRINT(")");
+}
+
+void
+tree_printer_visit_statement_loop(struct Tree_Visit* visit, struct Tree_Statement* stmt)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(loop ");
+
+ switch (stmt->value.loop.style) {
+ case TREE_STATEMENT_LOOP_STYLE_C:
+ PRINT("c-style ");
+ VISIT(visit_bare_declaration_node, &stmt->value.loop.declaration);
+ PRINT(" (condition ");
+ VISIT(visit_expression, stmt->value.loop.condition);
+ PRINT(") (iteration ");
+ VISIT(visit_expression, stmt->value.loop.iteration);
+ PRINT(") ");
+ break;
+ case TREE_STATEMENT_LOOP_STYLE_FOR_EACH:
+ PRINT("for-each ");
+ VISIT(visit_bare_declaration_node, &stmt->value.loop.declaration);
+ PRINT(" ");
+ break;
+ case TREE_STATEMENT_LOOP_STYLE_WHILE:
+ PRINT("while (condition ");
+ VISIT(visit_expression, stmt->value.loop.condition);
+ PRINT(") ");
+ break;
+ case TREE_STATEMENT_LOOP_STYLE_ENDLESS:
+ PRINT("endless ");
+ break;
+ default:
+ fprintf(stderr, "unexpected loop style in `tree_printer_visit_statement_loop`");
+ break;
+ }
+
+ VISIT(visit_block_node, &stmt->value.loop.body);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_statement_return(struct Tree_Visit* visit, struct Tree_Statement* stmt)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(return");
+ if (stmt->value.return_value.value) {
+ PRINT(" ");
+ VISIT(visit_expression, stmt->value.return_value.value);
+ }
+ PRINT(")");
+}
+
+void
+tree_printer_visit_statement_break(struct Tree_Visit* visit, struct Tree_Statement* stmt)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(break)");
+}
+
+void
+tree_printer_visit_statement_continue(struct Tree_Visit* visit, struct Tree_Statement* stmt)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(continue)");
+}
+
+void
+tree_printer_visit_statement_defer(struct Tree_Visit* visit, struct Tree_Statement* stmt)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(defer ");
+ if (stmt->value.defer.expression) {
+ VISIT(visit_expression, stmt->value.defer.expression);
+ } else {
+ VISIT(visit_block_node, &stmt->value.defer.block);
+ }
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression(struct Tree_Visit* visit, struct Tree_Expression* expression)
+{
+ TREE_PRINTER_PREAMBLE
+ PRINT("(expr ");
+ tree_walk_expression(visit, expression);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_integer_literal(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("%ld", expr->value.integer_literal.value);
+}
+
+void
+tree_printer_visit_expression_float_literal(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("%lf", expr->value.float_literal.value);
+}
+
+void
+tree_printer_visit_expression_string_literal(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("\"%s\"", expr->value.string_literal.value.data);
+}
+
+void
+tree_printer_visit_expression_boolean_literal(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("%s", expr->value.bool_literal.value ? "true" : "false");
+}
+
+void
+tree_printer_visit_expression_name(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(name %s)", expr->value.name.name.data);
+}
+
+void
+tree_printer_visit_expression_unary_operation(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(unary %s ", unary_operation_to_string(expr->value.unary_operator.operation));
+ VISIT(visit_expression, expr->value.unary_operator.operand);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_binary_operation(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(binary %s ", binary_operation_to_string(expr->value.binary_operator.operation));
+ VISIT(visit_expression, expr->value.binary_operator.left_operand);
+ PRINT(" ");
+ VISIT(visit_expression, expr->value.binary_operator.right_operand);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_group(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(group ");
+ VISIT(visit_expression, expr->value.group.inner_expression);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_call(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ struct Tree_Expression_Call* call = &expr->value.call;
+ PRINT("(call ");
+ VISIT(visit_expression, call->subject);
+ VISIT(visit_argument_group_node, &call->argument_group);
+
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_construct(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ struct Tree_Expression_Construct* construct = &expr->value.construct;
+ PRINT("(construct ");
+ VISIT(visit_expression, construct->subject);
+ VISIT(visit_argument_group_node, &construct->argument_group);
+
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_subscript(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(subscript ");
+ VISIT(visit_expression, expr->value.subscript.subject);
+ PRINT(" ");
+ VISIT(visit_expression, expr->value.subscript.index);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_member(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(member of ");
+ VISIT(visit_expression, expr->value.member.subject);
+ PRINT(" named %s)", expr->value.member.name.data);
+}
+
+void
+tree_printer_visit_expression_increment_decrement(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ const struct Tree_Expression_Increment_Decrement* inc_dec = &expr->value.increment_decrement;
+ const ascii* prefix_or_postfix = inc_dec->prefix ? "prefix" : "postfix";
+ PRINT("(increment/decrement %s %s ",
+ increment_decrement_operation_to_string(inc_dec->operation), prefix_or_postfix);
+ VISIT(visit_expression, inc_dec->subject);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_try(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(try ");
+ VISIT(visit_expression, expr->value.try.expression);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_must(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(must ");
+ VISIT(visit_expression, expr->value.must.expression);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_expression_function(struct Tree_Visit* visit, struct Tree_Expression* expr)
+{
+ TREE_PRINTER_PREAMBLE
+
+ struct Tree_Expression_Function* fun = &expr->value.function;
+ PRINT("(function ");
+ VISIT(visit_function_header_node, &fun->header);
+ PRINT_WHITESPACE_IF_NEEDED(" ");
+ VISIT(visit_block_node, (struct Tree_Block*)&fun->body);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_type(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+ PRINT("(type ");
+ if (node->variadic) PRINT("variadic ");
+ tree_walk_type(visit, node);
+ PRINT(")");
+}
+
+void
+tree_printer_visit_type_name(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("name %s", node->value.name.name.data);
+}
+
+void
+tree_printer_visit_type_array(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("array of ");
+ VISIT(visit_type_node, node->value.array.element_type);
+}
+
+void
+tree_printer_visit_type_reference(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("reference to ");
+ VISIT(visit_type_node, node->value.reference.referenced_type);
+}
+
+void
+tree_printer_visit_type_maybe(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("maybe ");
+ VISIT(visit_type_node, node->value.maybe.inner_type);
+}
+
+void
+tree_printer_visit_type_tuple(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("tuple");
+ FOR_EACH (struct Tree_Type*, current, node->value.tuple.head) {
+ PRINT(" ");
+ VISIT(visit_type_node, current);
+ }
+}
+
+void
+tree_printer_visit_type_map(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("map ");
+ VISIT(visit_type_node, node->value.map.key_type);
+ PRINT(" = ");
+ VISIT(visit_type_node, node->value.map.value_type);
+}
+
+void
+tree_printer_visit_type_function(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("function ");
+ VISIT(visit_function_header_node, &node->value.function.header);
+}
+
+void
+tree_printer_visit_type_structure(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("structure");
+ FOR_EACH (struct Tree_Type*, current, node->value.structure.fields) {
+ PRINT(" (field %s) ", current->value_name.data);
+ VISIT(visit_type_node, current);
+ }
+}
+
+void
+tree_printer_visit_type_variant(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("variant");
+ FOR_EACH (struct Tree_Type*, current, node->value.variant.variants) {
+ if (current->type == TREE_TYPE_NONE) {
+ PRINT(" (variant %s)", current->value_name.data);
+ } else {
+ PRINT(" (variant %s of ", current->value_name.data);
+ VISIT(visit_type_node, current);
+ PRINT(")");
+ }
+ }
+}
+
+void
+tree_printer_visit_type_class(struct Tree_Visit* visit, struct Tree_Type* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("class");
+ FOR_EACH (struct Tree_Type*, current, node->value.class.methods) {
+ PRINT(" (method %s ", current->value_name.data);
+ VISIT(visit_function_header_node, &current->value.function.header);
+ PRINT(")");
+ }
+}
+
+void
+tree_printer_visit_block(struct Tree_Visit* visit, struct Tree_Block* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(block");
+ if (node->statements) {
+ PRINT("\n");
+ printer->indentation_level++;
+ FOR_EACH (struct Tree_Statement*, statement, node->statements) {
+ VISIT(visit_statement, statement);
+ PRINT("\n");
+ }
+ printer->indentation_level--;
+ tree_printer_indent(printer);
+ }
+ PRINT(")");
+}
+
+void
+tree_printer_visit_bare_declaration(struct Tree_Visit* visit, struct Tree_Bare_Declaration* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ PRINT("(declaration ");
+ FOR_EACH_ARRAY(struct String, name, &node->names, {
+ PRINT("%s ", name.data);
+ })
+
+ if (node->type && node->type->type != TREE_TYPE_NONE) {
+ VISIT(visit_type_node, node->type);
+ PRINT(" ");
+ }
+
+ if (node->initializer) {
+ PRINT("(initializer ");
+ VISIT(visit_expression, node->initializer);
+ PRINT(")");
+ }
+ PRINT(")");
+}
+
+void
+printer_visit_function_header(struct Tree_Visit* visit, struct Tree_Function_Header* header)
+{
+ TREE_PRINTER_PREAMBLE
+
+ FOR_EACH (struct Tree_Type*, current, header->parameters_type_and_name) {
+ // if (current != header->parameters_type_and_name) PRINT(" ");
+ PRINT_WHITESPACE_IF_NEEDED(" ");
+
+ if (current->value_name.data && current->value_name.data[0] != '\0')
+ PRINT("(param %s) ", current->value_name.data);
+ else
+ PRINT("(param) ");
+
+ VISIT(visit_type_node, current);
+ }
+
+ PRINT_WHITESPACE_IF_NEEDED(" ");
+
+ if (header->return_type) {
+ PRINT("(returns ");
+ VISIT(visit_type_node, header->return_type);
+ PRINT(")");
+ }
+}
+
+void
+tree_printer_visit_pragma(struct Tree_Visit* visit, struct Tree_Pragma* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ const ascii* pragma_name = tree_pragma_type_to_string(node->type);
+
+ PRINT("(pragma %s", pragma_name);
+ for (uint ai = 0; ai < node->argument_count; ++ai) {
+ struct Tree_Pragma_Argument* arg = &node->arguments[ai];
+ switch (arg->type) {
+ case TREE_PRAGMA_ARGUMENT_NUMBER:
+ PRINT(" (number %ld)", arg->value.number);
+ break;
+ case TREE_PRAGMA_ARGUMENT_DECIMAL:
+ PRINT(" (decimal %lf)", arg->value.decimal);
+ break;
+ case TREE_PRAGMA_ARGUMENT_NAME_OR_STRING:
+ PRINT(" (name/string '%s')", arg->value.name_or_string.data);
+ break;
+ default:
+ failure("unexpected pragma argument type in `tree_printer_visit_pragma`");
+ }
+ }
+ PRINT(")");
+
+ if (node->next) {
+ PRINT(" ");
+ VISIT(visit_pragma_node, node->next);
+ }
+}
+
+void
+tree_printer_visit_argument_group(struct Tree_Visit* visit, struct Tree_Argument_Group* node)
+{
+ TREE_PRINTER_PREAMBLE
+
+ uint i = 0;
+ FOR_EACH (struct Tree_Expression*, argument, node->arguments) {
+ struct String name = *array_at(struct String, &node->argument_names, i++);
+ if (name.data && name.data[0] != '\0') {
+ PRINT(" (named arg '%s' ", name.data);
+ } else {
+ PRINT(" (arg ");
+ }
+ VISIT(visit_expression, argument);
+ PRINT(")");
+ }
+}
+
+struct Tree_Visit_Table tree_printer_visit_functions = {
+ .visit_tree = tree_printer_visit_tree,
+
+ .visit_statement = tree_printer_visit_statement,
+ .visit_statement_declaration = tree_printer_visit_statement_declaration,
+ .visit_statement_conditional = tree_printer_visit_statement_conditional,
+ .visit_statement_loop = tree_printer_visit_statement_loop,
+ .visit_statement_return = tree_printer_visit_statement_return,
+ .visit_statement_break = tree_printer_visit_statement_break,
+ .visit_statement_continue = tree_printer_visit_statement_continue,
+ .visit_statement_defer = tree_printer_visit_statement_defer,
+
+ .visit_expression = tree_printer_visit_expression,
+ .visit_expression_integer_literal = tree_printer_visit_expression_integer_literal,
+ .visit_expression_float_literal = tree_printer_visit_expression_float_literal,
+ .visit_expression_string_literal = tree_printer_visit_expression_string_literal,
+ .visit_expression_boolean_literal = tree_printer_visit_expression_boolean_literal,
+ .visit_expression_name = tree_printer_visit_expression_name,
+ .visit_expression_unary_operation = tree_printer_visit_expression_unary_operation,
+ .visit_expression_binary_operation = tree_printer_visit_expression_binary_operation,
+ .visit_expression_group = tree_printer_visit_expression_group,
+ .visit_expression_call = tree_printer_visit_expression_call,
+ .visit_expression_construct = tree_printer_visit_expression_construct,
+ .visit_expression_subscript = tree_printer_visit_expression_subscript,
+ .visit_expression_member = tree_printer_visit_expression_member,
+ .visit_expression_increment_decrement = tree_printer_visit_expression_increment_decrement,
+ .visit_expression_try = tree_printer_visit_expression_try,
+ .visit_expression_must = tree_printer_visit_expression_must,
+ .visit_expression_function = tree_printer_visit_expression_function,
+
+ .visit_type_node = tree_printer_visit_type,
+ .visit_type_node_name = tree_printer_visit_type_name,
+ .visit_type_node_array = tree_printer_visit_type_array,
+ .visit_type_node_reference = tree_printer_visit_type_reference,
+ .visit_type_node_maybe = tree_printer_visit_type_maybe,
+ .visit_type_node_tuple = tree_printer_visit_type_tuple,
+ .visit_type_node_map = tree_printer_visit_type_map,
+ .visit_type_node_function = tree_printer_visit_type_function,
+ .visit_type_node_structure = tree_printer_visit_type_structure,
+ .visit_type_node_variant = tree_printer_visit_type_variant,
+ .visit_type_node_class = tree_printer_visit_type_class,
+
+ .visit_block_node = tree_printer_visit_block,
+ .visit_bare_declaration_node = tree_printer_visit_bare_declaration,
+ .visit_function_header_node = printer_visit_function_header,
+ .visit_argument_group_node = tree_printer_visit_argument_group,
+ .visit_pragma_node = tree_printer_visit_pragma,
+};
+
+void
+tree_printer(struct Tree* tree)
+{
+ struct Tree_Printer printer = {
+ .indentation_level = 0, .output = stdout, .did_print_last_visit = false
+ };
+ struct Tree_Visit visit = { .table = &tree_printer_visit_functions, .user_data = &printer };
+
+ tree_walk(&visit, tree);
+}
+
+#undef PRINT
+#undef PRINT_WHITESPACE_IF_NEEDED
+#undef TREE_PRINTER_PREAMBLE