/* * 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. * * SPDX-License-Identifier: MPL-2.0 */ #pragma once #include "catboot.h" 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); void (*visit_pragma_node)(struct Visit* visitor, struct Pragma_Node* 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); } // 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; case STATEMENT_PRAGMA: VISIT(visit_pragma_node, statement->value.pragma.inner); 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); } void walk_pragma_node(struct Visit* visit, struct Pragma_Node* node) { // visit each pragma node in the linked list. VISIT_MAYBE(visit_pragma_node, node->next); } 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, .visit_pragma_node = walk_pragma_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; 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 printer_visit_tree(struct Visit* visit, struct Tree* tree) { TREE_PRINTER_PREAMBLE FOR_EACH (struct Statement*, statement, tree->top_level_statements) { VISIT(visit_statement, statement); PRINT("\n"); } } void printer_visit_statement(struct Visit* visit, struct Statement* statement) { TREE_PRINTER_PREAMBLE tree_printer_indent(printer); walk_statement(visit, statement); } void printer_visit_statement_declaration(struct Visit* visit, struct Statement* stmt) { TREE_PRINTER_PREAMBLE if (stmt->value.declaration.kind == STATEMENT_DECLARATION_VARIABLE) PRINT("(variable "); else if (stmt->value.declaration.kind == STATEMENT_DECLARATION_CONSTANT) PRINT("(constant "); VISIT(visit_bare_declaration_node, &stmt->value.declaration.inner); PRINT(")"); } void printer_visit_statement_conditional(struct Visit* visit, struct Statement* stmt) { TREE_PRINTER_PREAMBLE PRINT("(conditional"); for (uint i = 0; i < stmt->value.conditional.condition_count; ++i) { const struct 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 Block_Node*)&branch->then); } PRINT(")"); } void printer_visit_statement_loop(struct Visit* visit, struct Statement* stmt) { TREE_PRINTER_PREAMBLE PRINT("(loop "); switch (stmt->value.loop.style) { case 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 STATEMENT_LOOP_STYLE_FOR_EACH: PRINT("for-each "); VISIT(visit_bare_declaration_node, &stmt->value.loop.declaration); PRINT(" "); break; case STATEMENT_LOOP_STYLE_WHILE: PRINT("while (condition "); VISIT(visit_expression, stmt->value.loop.condition); PRINT(") "); break; case STATEMENT_LOOP_STYLE_ENDLESS: PRINT("endless "); break; default: fprintf(stderr, "unexpected loop style in `printer_visit_statement_loop`"); break; } VISIT(visit_block_node, &stmt->value.loop.body); PRINT(")"); } void printer_visit_statement_return(struct Visit* visit, struct Statement* stmt) { TREE_PRINTER_PREAMBLE PRINT("(return"); if (stmt->value.return_value.value) { PRINT(" "); VISIT(visit_expression, stmt->value.return_value.value); } PRINT(")"); } void printer_visit_statement_break(struct Visit* visit, struct Statement* stmt) { TREE_PRINTER_PREAMBLE PRINT("(break)"); } void printer_visit_statement_continue(struct Visit* visit, struct Statement* stmt) { TREE_PRINTER_PREAMBLE PRINT("(continue)"); } void printer_visit_statement_defer(struct Visit* visit, struct 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 printer_visit_expression(struct Visit* visit, struct Expression* expression) { TREE_PRINTER_PREAMBLE PRINT("(expr "); walk_expression(visit, expression); PRINT(")"); } void printer_visit_expression_integer_literal(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE PRINT("%ld", expr->value.integer_literal.value); } void printer_visit_expression_float_literal(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE PRINT("%lf", expr->value.float_literal.value); } void printer_visit_expression_string_literal(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE PRINT("\"%s\"", expr->value.string_literal.value.data); } void printer_visit_expression_boolean_literal(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE PRINT("%s", expr->value.bool_literal.value ? "true" : "false"); } void printer_visit_expression_name(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE PRINT("(name %s)", expr->value.name.name.data); } void printer_visit_expression_unary_operation(struct Visit* visit, struct 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 printer_visit_expression_binary_operation(struct Visit* visit, struct 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 printer_visit_expression_group(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE PRINT("(group "); VISIT(visit_expression, expr->value.group.inner_expression); PRINT(")"); } void printer_visit_expression_call_or_construct(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE struct Expression_Call_Or_Construct* coc = &expr->value.call_or_construct; PRINT("(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') { PRINT(" (named arg '%s' ", name.data); } else { PRINT(" (arg "); } VISIT(visit_expression, argument); PRINT(")"); } PRINT(")"); } void printer_visit_expression_subscript(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE PRINT("(subscript "); VISIT(visit_expression, expr->value.subscript.subject); PRINT(" "); VISIT(visit_expression, expr->value.subscript.index); PRINT(")"); } void printer_visit_expression_member(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE PRINT("(member of "); VISIT(visit_expression, expr->value.member.subject); PRINT(" named %s)", expr->value.member.name.data); } void printer_visit_expression_increment_decrement(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE const struct 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 printer_visit_expression_function(struct Visit* visit, struct Expression* expr) { TREE_PRINTER_PREAMBLE struct Expression_Function* fun = &expr->value.function; PRINT("(function "); VISIT(visit_function_header_node, &fun->header); PRINT_WHITESPACE_IF_NEEDED(" "); VISIT(visit_block_node, (struct Block_Node*)&fun->body); PRINT(")"); } void printer_visit_type_node(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("(type "); walk_type_node(visit, node); PRINT(")"); } void printer_visit_type_node_name(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("name %s", node->value.name.name.data); } void printer_visit_type_node_array(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("array of "); VISIT(visit_type_node, node->value.array.element_type); } void printer_visit_type_node_reference(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("reference to "); VISIT(visit_type_node, node->value.reference.referenced_type); } void printer_visit_type_node_maybe(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("maybe "); VISIT(visit_type_node, node->value.maybe.inner_type); } void printer_visit_type_node_tuple(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("tuple"); FOR_EACH (struct Type_Node*, current, node->value.tuple.head) { PRINT(" "); VISIT(visit_type_node, current); } } void printer_visit_type_node_map(struct Visit* visit, struct Type_Node* 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 printer_visit_type_node_function(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("function "); VISIT(visit_function_header_node, &node->value.function.header); } void printer_visit_type_node_structure(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("structure"); FOR_EACH (struct Type_Node*, current, node->value.structure.fields) { PRINT(" (field %s) ", current->value_name.data); VISIT(visit_type_node, current); } } void printer_visit_type_node_variant(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("variant"); FOR_EACH (struct Type_Node*, current, node->value.variant.variants) { if (current->type == TYPE_NODE_NONE) { PRINT(" (variant %s)", current->value_name.data); } else { PRINT(" (variant %s of ", current->value_name.data); VISIT(visit_type_node, current); PRINT(")"); } } } void printer_visit_type_node_class(struct Visit* visit, struct Type_Node* node) { TREE_PRINTER_PREAMBLE PRINT("class"); FOR_EACH (struct Type_Node*, current, node->value.class.methods) { PRINT(" (method %s ", current->value_name.data); VISIT(visit_function_header_node, ¤t->value.function.header); PRINT(")"); } } void printer_visit_block_node(struct Visit* visit, struct Block_Node* node) { TREE_PRINTER_PREAMBLE PRINT("(block\n"); printer->indentation_level++; FOR_EACH (struct Statement*, statement, node->statements) { VISIT(visit_statement, statement); PRINT("\n"); } printer->indentation_level--; tree_printer_indent(printer); PRINT(")"); } void printer_visit_bare_declaration_node(struct Visit* visit, struct Bare_Declaration_Node* node) { TREE_PRINTER_PREAMBLE PRINT("(declaration "); STRING_ARRAY_FOR_EACH (i, name, node->names) { PRINT("%s ", name.data); } if (node->type && node->type->type != TYPE_NODE_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 Visit* visit, struct Function_Header_Node* header) { TREE_PRINTER_PREAMBLE FOR_EACH (struct Type_Node*, current, header->parameters_type_and_name) { if (current != header->parameters_type_and_name) PRINT(" "); 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 printer_visit_pragma_node(struct Visit* visit, struct Pragma_Node* node) { TREE_PRINTER_PREAMBLE const ascii* pragma_name = pragma_type_to_string(node->type); PRINT("(pragma %s", pragma_name); for (uint ai = 0; ai < node->argument_count; ++ai) { struct Pragma_Argument* arg = &node->arguments[ai]; switch (arg->type) { case PRAGMA_ARGUMENT_NUMBER: PRINT(" (number %ld)", arg->value.number); break; case PRAGMA_ARGUMENT_DECIMAL: PRINT(" (decimal %lf)", arg->value.decimal); break; case PRAGMA_ARGUMENT_NAME_OR_STRING: PRINT(" (name/string '%s')", arg->value.name_or_string.data); break; default: failure("unexpected pragma argument type in `printer_visit_pragma_node`"); } } PRINT(")"); if (node->next) { PRINT(" "); VISIT(visit_pragma_node, node->next); } } 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, .visit_pragma_node = printer_visit_pragma_node, }; void tree_printer(struct Tree* tree) { struct Tree_Printer printer = { .indentation_level = 0, .output = stdout, .did_print_last_visit = false }; struct Visit visit = { .table = &printer_visit_functions, .user_data = &printer }; walk(&visit, tree); } #undef PRINT #undef PRINT_WHITESPACE_IF_NEEDED #undef TREE_PRINTER_PREAMBLE