/* * 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; }; 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, ¤t->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, ")"); } } void printer_visit_pragma_node(struct Visit* visit, struct Pragma_Node* node) { DATA_FOR_VISIT(struct Tree_Printer, printer); const ascii* pragma_name = pragma_type_to_string(node->type); fprintf(printer->output, "(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: fprintf(printer->output, " (number %ld)", arg->value.number); break; case PRAGMA_ARGUMENT_DECIMAL: fprintf(printer->output, " (decimal %lf)", arg->value.decimal); break; case PRAGMA_ARGUMENT_NAME_OR_STRING: fprintf(printer->output, " (name/string '%s')", arg->value.name_or_string.data); break; default: failure("unexpected pragma argument type in `printer_visit_pragma_node`"); } } fprintf(printer->output, ")"); if (node->next) { fprintf(printer->output, " "); 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 }; struct Visit visit = { .table = &printer_visit_functions, .user_data = &printer }; walk(&visit, tree); }