/* * visit system for the ir, * implementing a default traversal mechanism, * allowing for selective operations on it. * includes a simple ir printer as the primary example of usage. * * Copyright (c) 2026, Mel G. * * SPDX-License-Identifier: MPL-2.0 */ #pragma once #include "../catboot.h" #define WALK_LEAF_FUNCTION(name, type) \ void name(struct Visit* visit, type node) {} struct Visit { struct Visit_Table* table; void* user_data; }; struct Visit_Table { void (*visit_unit)(struct Visit* visitor, struct Unit* unit); void (*visit_type)(struct Visit* visitor, struct Type* type); void (*visit_type_primitive)(struct Visit* visitor, struct Type* type); void (*visit_type_alias)(struct Visit* visitor, struct Type* type); void (*visit_type_structure)(struct Visit* visitor, struct Type* type); void (*visit_type_variant)(struct Visit* visitor, struct Type* type); void (*visit_type_function)(struct Visit* visitor, struct Type* type); void (*visit_function)(struct Visit* visitor, struct Function* function); void (*visit_statement)(struct Visit* visitor, struct Statement* stmt); void (*visit_statement_declaration)(struct Visit* visitor, struct Statement* stmt); void (*visit_statement_assign)(struct Visit* visitor, struct Statement* stmt); void (*visit_statement_expression)(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_block)(struct Visit* visitor, struct Statement* stmt); void (*visit_statement_label)(struct Visit* visitor, struct Statement* stmt); void (*visit_statement_goto)(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_sizeof_operation)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_call)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_member)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_subscript)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_cast)(struct Visit* visitor, struct Expression* expr); void (*visit_expression_construct)(struct Visit* visitor, struct Expression* expr); void (*visit_block)(struct Visit* visitor, struct Block* block); void (*visit_type_ref)(struct Visit* visitor, struct Type_Ref* ref); void (*visit_import)(struct Visit* visitor, struct Import* import); void (*visit_diagnostic)(struct Visit* visitor, struct Diagnostic* diagnostic); }; void walk(struct Visit* visit, struct Unit* unit) { VISIT(visit_unit, unit); } void walk_unit(struct Visit* visit, struct Unit* unit) { FOR_EACH_ARRAY(struct Type*, type, &unit->types.entries, { VISIT(visit_type, type); }) FOR_EACH_ARRAY(struct Function*, function, &unit->functions.entries, { VISIT(visit_function, function); }) for (uint i = 0; i < array_length(&unit->imports); ++i) { VISIT(visit_import, array_at(struct Import, &unit->imports, i)); } for (uint i = 0; i < array_length(&unit->diagnostics); ++i) { VISIT(visit_diagnostic, array_at(struct Diagnostic, &unit->diagnostics, i)); } } void walk_type(struct Visit* visit, struct Type* type) { switch (type->kind) { case TYPE_PRIMITIVE: VISIT(visit_type_primitive, type); break; case TYPE_ALIAS: VISIT(visit_type_alias, type); break; case TYPE_STRUCTURE: VISIT(visit_type_structure, type); break; case TYPE_VARIANT: VISIT(visit_type_variant, type); break; case TYPE_FUNCTION: VISIT(visit_type_function, type); break; case TYPE_NONE: break; default: failure("unexpected type kind in `walk_type`"); } } WALK_LEAF_FUNCTION(walk_type_primitive, struct Type*); WALK_LEAF_FUNCTION(walk_type_alias, struct Type*); void walk_type_structure(struct Visit* visit, struct Type* type) { for (uint i = 0; i < array_length(&type->value.structure.fields); ++i) { struct Field* field = array_at(struct Field, &type->value.structure.fields, i); VISIT(visit_type_ref, &field->type); } } void walk_type_variant(struct Visit* visit, struct Type* type) { for (uint i = 0; i < array_length(&type->value.variant.cases); ++i) { struct Variant_Case* one_case = array_at(struct Variant_Case, &type->value.variant.cases, i); if (one_case->has_payload) VISIT(visit_type_ref, &one_case->payload); } } void walk_type_function(struct Visit* visit, struct Type* type) { VISIT(visit_type_ref, &type->value.function.return_type); for (uint i = 0; i < array_length(&type->value.function.params); ++i) { struct Type_Ref* param = array_at(struct Type_Ref, &type->value.function.params, i); VISIT(visit_type_ref, param); } } void walk_function(struct Visit* visit, struct Function* function) { VISIT(visit_type_ref, &function->return_type); for (uint i = 0; i < array_length(&function->params); ++i) { struct Param* param = array_at(struct Param, &function->params, i); VISIT(visit_type_ref, ¶m->type); } VISIT_MAYBE(visit_block, function->body); } void walk_statement(struct Visit* visit, struct Statement* stmt) { switch (stmt->kind) { case STATEMENT_DECLARATION: VISIT(visit_statement_declaration, stmt); break; case STATEMENT_ASSIGN: VISIT(visit_statement_assign, stmt); break; case STATEMENT_EXPRESSION: VISIT(visit_statement_expression, stmt); break; case STATEMENT_CONDITIONAL: VISIT(visit_statement_conditional, stmt); break; case STATEMENT_LOOP: VISIT(visit_statement_loop, stmt); break; case STATEMENT_RETURN: VISIT(visit_statement_return, stmt); break; case STATEMENT_BREAK: VISIT(visit_statement_break, stmt); break; case STATEMENT_CONTINUE: VISIT(visit_statement_continue, stmt); break; case STATEMENT_BLOCK: VISIT(visit_statement_block, stmt); break; case STATEMENT_LABEL: VISIT(visit_statement_label, stmt); break; case STATEMENT_GOTO: VISIT(visit_statement_goto, stmt); break; case STATEMENT_NONE: break; default: failure("unexpected statement kind in `walk_statement`"); } } void walk_statement_declaration(struct Visit* visit, struct Statement* stmt) { VISIT(visit_type_ref, &stmt->value.declaration.type); VISIT_MAYBE(visit_expression, stmt->value.declaration.initializer); } void walk_statement_assign(struct Visit* visit, struct Statement* stmt) { VISIT(visit_expression, stmt->value.assign.lhs); VISIT(visit_expression, stmt->value.assign.rhs); } void walk_statement_expression(struct Visit* visit, struct Statement* stmt) { VISIT(visit_expression, stmt->value.expression.inner); } void walk_statement_conditional(struct Visit* visit, struct Statement* stmt) { for (uint i = 0; i < array_length(&stmt->value.conditional.branches); ++i) { struct If_Branch* branch = array_at(struct If_Branch, &stmt->value.conditional.branches, i); VISIT_MAYBE(visit_expression, branch->condition); VISIT(visit_block, branch->body); } } void walk_statement_loop(struct Visit* visit, struct Statement* stmt) { VISIT(visit_expression, stmt->value.loop.condition); VISIT(visit_block, stmt->value.loop.body); } void walk_statement_return(struct Visit* visit, struct Statement* stmt) { VISIT_MAYBE(visit_expression, stmt->value.return_value.value); } WALK_LEAF_FUNCTION(walk_statement_break, struct Statement*); WALK_LEAF_FUNCTION(walk_statement_continue, struct Statement*); WALK_LEAF_FUNCTION(walk_statement_label, struct Statement*); WALK_LEAF_FUNCTION(walk_statement_goto, struct Statement*); void walk_statement_block(struct Visit* visit, struct Statement* stmt) { VISIT(visit_block, stmt->value.block.inner); } void walk_expression(struct Visit* visit, struct Expression* expr) { switch (expr->kind) { case EXPRESSION_INTEGER_LITERAL: VISIT(visit_expression_integer_literal, expr); break; case EXPRESSION_FLOAT_LITERAL: VISIT(visit_expression_float_literal, expr); break; case EXPRESSION_STRING_LITERAL: VISIT(visit_expression_string_literal, expr); break; case EXPRESSION_BOOLEAN_LITERAL: VISIT(visit_expression_boolean_literal, expr); break; case EXPRESSION_NAME: VISIT(visit_expression_name, expr); break; case EXPRESSION_UNARY_OPERATION: VISIT(visit_expression_unary_operation, expr); break; case EXPRESSION_BINARY_OPERATION: VISIT(visit_expression_binary_operation, expr); break; case EXPRESSION_SIZEOF_OPERATION: VISIT(visit_expression_sizeof_operation, expr); break; case EXPRESSION_CALL: VISIT(visit_expression_call, expr); break; case EXPRESSION_MEMBER: VISIT(visit_expression_member, expr); break; case EXPRESSION_SUBSCRIPT: VISIT(visit_expression_subscript, expr); break; case EXPRESSION_CAST: VISIT(visit_expression_cast, expr); break; case EXPRESSION_CONSTRUCT: VISIT(visit_expression_construct, expr); break; case EXPRESSION_NONE: 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* expr) { VISIT(visit_expression, expr->value.unary_operator.operand); } void walk_expression_binary_operation(struct Visit* visit, struct Expression* expr) { VISIT(visit_expression, expr->value.binary_operator.left_operand); VISIT(visit_expression, expr->value.binary_operator.right_operand); } void walk_expression_sizeof_operation(struct Visit* visit, struct Expression* expr) { VISIT(visit_type_ref, &expr->value.sizeof_operator.target); } void walk_expression_call(struct Visit* visit, struct Expression* expr) { VISIT(visit_expression, expr->value.call.subject); FOR_EACH_ARRAY(struct Expression*, argument, &expr->value.call.arguments, { VISIT(visit_expression, argument); }) } void walk_expression_member(struct Visit* visit, struct Expression* expr) { VISIT(visit_expression, expr->value.member.subject); } void walk_expression_subscript(struct Visit* visit, struct Expression* expr) { VISIT(visit_expression, expr->value.subscript.subject); VISIT(visit_expression, expr->value.subscript.index); } void walk_expression_cast(struct Visit* visit, struct Expression* expr) { VISIT(visit_type_ref, &expr->value.cast.target); VISIT(visit_expression, expr->value.cast.operand); } void walk_expression_construct(struct Visit* visit, struct Expression* expr) { for (uint i = 0; i < array_length(&expr->value.construct.fields); ++i) { struct Construct_Field* field = array_at(struct Construct_Field, &expr->value.construct.fields, i); VISIT(visit_expression, field->value); } } void walk_block(struct Visit* visit, struct Block* block){ FOR_EACH_ARRAY( struct Statement*, statement, &block -> statements, { VISIT(visit_statement, statement); }) } WALK_LEAF_FUNCTION(walk_type_ref, struct Type_Ref*); WALK_LEAF_FUNCTION(walk_import, struct Import*); WALK_LEAF_FUNCTION(walk_diagnostic, struct Diagnostic*); struct Visit_Table walk_functions = { .visit_unit = walk_unit, .visit_type = walk_type, .visit_type_primitive = walk_type_primitive, .visit_type_alias = walk_type_alias, .visit_type_structure = walk_type_structure, .visit_type_variant = walk_type_variant, .visit_type_function = walk_type_function, .visit_function = walk_function, .visit_statement = walk_statement, .visit_statement_declaration = walk_statement_declaration, .visit_statement_assign = walk_statement_assign, .visit_statement_expression = walk_statement_expression, .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_block = walk_statement_block, .visit_statement_label = walk_statement_label, .visit_statement_goto = walk_statement_goto, .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_sizeof_operation = walk_expression_sizeof_operation, .visit_expression_call = walk_expression_call, .visit_expression_member = walk_expression_member, .visit_expression_subscript = walk_expression_subscript, .visit_expression_cast = walk_expression_cast, .visit_expression_construct = walk_expression_construct, .visit_block = walk_block, .visit_type_ref = walk_type_ref, .visit_import = walk_import, .visit_diagnostic = walk_diagnostic, }; void visit_table_fill_defaults(struct Visit_Table* table) { 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* default_slot = &((Function_Ptr*)&walk_functions)[fi]; if (!*function_slot) *function_slot = *default_slot; } } struct Printer { uint indentation_level; FILE* output; bool did_print_last_visit; struct Unit* unit; }; #define PRINTER_PREAMBLE \ DATA_FOR_VISIT(struct Printer, p); \ p->did_print_last_visit = false; void printer_out(struct Printer* p, const ascii* format, ...) { va_list args; va_start(args, format); vfprintf(p->output, format, args); va_end(args); p->did_print_last_visit = true; } #define PRINT(...) printer_out(p, __VA_ARGS__) void printer_indent(struct Printer* p) { for (uint i = 0; i < p->indentation_level; ++i) PRINT("\t"); } struct Type* printer_type_at(struct Printer* p, Type_Id id) { if (id >= array_length(&p->unit->types.entries)) return nil; return *array_at(struct Type*, &p->unit->types.entries, id); } void printer_visit_unit(struct Visit* visit, struct Unit* unit) { PRINTER_PREAMBLE PRINT("(unit"); p->indentation_level++; if (array_length(&unit->types.entries) > 0) { PRINT("\n"); printer_indent(p); PRINT("(types"); p->indentation_level++; FOR_EACH_ARRAY(struct Type*, type, &unit->types.entries, { PRINT("\n"); printer_indent(p); VISIT(visit_type, type); }) p->indentation_level--; PRINT(")"); } if (array_length(&unit->functions.entries) > 0) { PRINT("\n"); printer_indent(p); PRINT("(functions"); p->indentation_level++; FOR_EACH_ARRAY(struct Function*, function, &unit->functions.entries, { PRINT("\n"); printer_indent(p); VISIT(visit_function, function); }) p->indentation_level--; PRINT(")"); } if (array_length(&unit->imports) > 0) { PRINT("\n"); printer_indent(p); PRINT("(imports"); for (uint i = 0; i < array_length(&unit->imports); ++i) { PRINT(" "); VISIT(visit_import, array_at(struct Import, &unit->imports, i)); } PRINT(")"); } if (array_length(&unit->type_emission_order) > 0) { PRINT("\n"); printer_indent(p); PRINT("(emission_order"); for (uint i = 0; i < array_length(&unit->type_emission_order); ++i) { PRINT(" %lu", *array_at(Type_Id, &unit->type_emission_order, i)); } PRINT(")"); } if (array_length(&unit->diagnostics) > 0) { PRINT("\n"); printer_indent(p); PRINT("(diagnostics"); p->indentation_level++; for (uint i = 0; i < array_length(&unit->diagnostics); ++i) { PRINT("\n"); printer_indent(p); VISIT(visit_diagnostic, array_at(struct Diagnostic, &unit->diagnostics, i)); } p->indentation_level--; PRINT(")"); } p->indentation_level--; PRINT(")\n"); } void printer_visit_type_ref(struct Visit* visit, struct Type_Ref* ref) { PRINTER_PREAMBLE PRINT("(ref"); for (uint i = 0; i < array_length(&ref->mods); ++i) { enum Type_Modifier mod = *array_at(enum Type_Modifier, &ref->mods, i); if (mod == TYPE_MOD_REFERENCE) PRINT(" &"); } struct Type* target = printer_type_at(p, ref->type_id); if (target) PRINT(" %s", string_c_str(target->name)); else PRINT(" #%lu", ref->type_id); PRINT(")"); } void printer_visit_type(struct Visit* visit, struct Type* type) { PRINTER_PREAMBLE PRINT("(type %lu %s", type->id, string_c_str(type->name)); if (type->synthetic) PRINT(" synthetic"); walk_type(visit, type); PRINT(")"); } void printer_visit_type_primitive(struct Visit* visit, struct Type* type) { PRINTER_PREAMBLE PRINT(" primitive"); } void printer_visit_type_alias(struct Visit* visit, struct Type* type) { PRINTER_PREAMBLE PRINT(" alias"); struct Type* target = printer_type_at(p, type->value.alias.target_id); if (target) PRINT(" %s", string_c_str(target->name)); else PRINT(" #%lu", type->value.alias.target_id); } void printer_visit_type_structure(struct Visit* visit, struct Type* type) { PRINTER_PREAMBLE PRINT(" structure"); for (uint i = 0; i < array_length(&type->value.structure.fields); ++i) { struct Field* field = array_at(struct Field, &type->value.structure.fields, i); PRINT(" (field %s ", string_c_str(field->name)); VISIT(visit_type_ref, &field->type); PRINT(")"); } } void printer_visit_type_variant(struct Visit* visit, struct Type* type) { PRINTER_PREAMBLE PRINT(" variant"); for (uint i = 0; i < array_length(&type->value.variant.cases); ++i) { struct Variant_Case* one_case = array_at(struct Variant_Case, &type->value.variant.cases, i); PRINT(" (case %s tag=%u", string_c_str(one_case->name), one_case->tag); if (one_case->has_payload) { PRINT(" "); VISIT(visit_type_ref, &one_case->payload); } PRINT(")"); } } void printer_visit_type_function(struct Visit* visit, struct Type* type) { PRINTER_PREAMBLE PRINT(" function (returns "); VISIT(visit_type_ref, &type->value.function.return_type); PRINT(")"); for (uint i = 0; i < array_length(&type->value.function.params); ++i) { struct Type_Ref* param = array_at(struct Type_Ref, &type->value.function.params, i); PRINT(" (param "); VISIT(visit_type_ref, param); PRINT(")"); } if (type->value.function.variadic) PRINT(" variadic"); } void printer_visit_function(struct Visit* visit, struct Function* function) { PRINTER_PREAMBLE PRINT("(function %lu %s", function->id, string_c_str(function->name)); if (function->is_main) PRINT(" main"); if (function->synthetic) PRINT(" synthetic"); PRINT(" (returns "); VISIT(visit_type_ref, &function->return_type); PRINT(")"); for (uint i = 0; i < array_length(&function->params); ++i) { struct Param* param = array_at(struct Param, &function->params, i); PRINT(" (param %s ", string_c_str(param->name)); VISIT(visit_type_ref, ¶m->type); PRINT(")"); } if (function->variadic) PRINT(" variadic"); if (function->body) { PRINT(" "); VISIT(visit_block, function->body); } PRINT(")"); } void printer_visit_statement(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE printer_indent(p); walk_statement(visit, stmt); } void printer_visit_statement_declaration(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE PRINT("(declaration %s ", string_c_str(stmt->value.declaration.name)); VISIT(visit_type_ref, &stmt->value.declaration.type); if (stmt->value.declaration.initializer) { PRINT(" (initializer "); VISIT(visit_expression, stmt->value.declaration.initializer); PRINT(")"); } PRINT(")"); } void printer_visit_statement_assign(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE PRINT("(assign "); VISIT(visit_expression, stmt->value.assign.lhs); PRINT(" "); VISIT(visit_expression, stmt->value.assign.rhs); PRINT(")"); } void printer_visit_statement_expression(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE PRINT("(expression-stmt "); VISIT(visit_expression, stmt->value.expression.inner); PRINT(")"); } void printer_visit_statement_conditional(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE PRINT("(conditional"); for (uint i = 0; i < array_length(&stmt->value.conditional.branches); ++i) { struct If_Branch* branch = array_at(struct If_Branch, &stmt->value.conditional.branches, i); PRINT(" "); if (branch->condition) { PRINT("(when "); VISIT(visit_expression, branch->condition); PRINT(") "); } else { PRINT("(else) "); } VISIT(visit_block, branch->body); } PRINT(")"); } void printer_visit_statement_loop(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE PRINT("(loop (condition "); VISIT(visit_expression, stmt->value.loop.condition); PRINT(") "); VISIT(visit_block, stmt->value.loop.body); PRINT(")"); } void printer_visit_statement_return(struct Visit* visit, struct Statement* stmt) { 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) { PRINTER_PREAMBLE PRINT("(break)"); } void printer_visit_statement_continue(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE PRINT("(continue)"); } void printer_visit_statement_block(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE VISIT(visit_block, stmt->value.block.inner); } void printer_visit_statement_label(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE PRINT("(label %s)", string_c_str(stmt->value.label.name)); } void printer_visit_statement_goto(struct Visit* visit, struct Statement* stmt) { PRINTER_PREAMBLE PRINT("(goto %s)", string_c_str(stmt->value.goto_target.target)); } void printer_visit_expression(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("(expr "); walk_expression(visit, expr); PRINT(")"); } void printer_visit_expression_integer_literal(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("%ld", expr->value.integer_literal.value); } void printer_visit_expression_float_literal(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("%lf", expr->value.float_literal.value); } void printer_visit_expression_string_literal(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("\"%s\"", string_c_str(expr->value.string_literal.value)); } void printer_visit_expression_boolean_literal(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("%s", expr->value.bool_literal.value ? "true" : "false"); } void printer_visit_expression_name(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("(name %s)", string_c_str(expr->value.name.name)); } void printer_visit_expression_unary_operation(struct Visit* visit, struct Expression* expr) { 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) { 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_sizeof_operation(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("(sizeof "); VISIT(visit_type_ref, &expr->value.sizeof_operator.target); PRINT(")"); } void printer_visit_expression_call(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("(call "); VISIT(visit_expression, expr->value.call.subject); FOR_EACH_ARRAY(struct Expression*, argument, &expr->value.call.arguments, { PRINT(" "); VISIT(visit_expression, argument); }) PRINT(")"); } void printer_visit_expression_member(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("(member "); VISIT(visit_expression, expr->value.member.subject); PRINT(" %s)", string_c_str(expr->value.member.name)); } void printer_visit_expression_subscript(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("(subscript "); VISIT(visit_expression, expr->value.subscript.subject); PRINT(" "); VISIT(visit_expression, expr->value.subscript.index); PRINT(")"); } void printer_visit_expression_cast(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("(cast "); VISIT(visit_type_ref, &expr->value.cast.target); PRINT(" "); VISIT(visit_expression, expr->value.cast.operand); PRINT(")"); } void printer_visit_expression_construct(struct Visit* visit, struct Expression* expr) { PRINTER_PREAMBLE PRINT("(construct"); struct Type* type = printer_type_at(p, expr->value.construct.type_id); if (type) PRINT(" %s", string_c_str(type->name)); else PRINT(" #%lu", expr->value.construct.type_id); for (uint i = 0; i < array_length(&expr->value.construct.fields); ++i) { struct Construct_Field* field = array_at(struct Construct_Field, &expr->value.construct.fields, i); if (field->name.length > 0) PRINT(" (field %s ", string_c_str(field->name)); else PRINT(" (positional "); VISIT(visit_expression, field->value); PRINT(")"); } PRINT(")"); } void printer_visit_block(struct Visit* visit, struct Block* block) { PRINTER_PREAMBLE PRINT("(block"); if (array_length(&block->statements) > 0) { PRINT("\n"); p->indentation_level++; FOR_EACH_ARRAY(struct Statement*, statement, &block->statements, { VISIT(visit_statement, statement); PRINT("\n"); }) p->indentation_level--; printer_indent(p); } PRINT(")"); } void printer_visit_import(struct Visit* visit, struct Import* import) { PRINTER_PREAMBLE PRINT("(import \"%s\")", string_c_str(import->path)); } void printer_visit_diagnostic(struct Visit* visit, struct Diagnostic* diagnostic) { PRINTER_PREAMBLE const ascii* sev = "note"; if (diagnostic->severity == DIAGNOSTIC_WARNING) sev = "warning"; else if (diagnostic->severity == DIAGNOSTIC_ERROR) sev = "error"; PRINT("(%s \"%s\")", sev, string_c_str(diagnostic->message)); } struct Visit_Table printer_visit_functions = { .visit_unit = printer_visit_unit, .visit_type = printer_visit_type, .visit_type_primitive = printer_visit_type_primitive, .visit_type_alias = printer_visit_type_alias, .visit_type_structure = printer_visit_type_structure, .visit_type_variant = printer_visit_type_variant, .visit_type_function = printer_visit_type_function, .visit_function = printer_visit_function, .visit_statement = printer_visit_statement, .visit_statement_declaration = printer_visit_statement_declaration, .visit_statement_assign = printer_visit_statement_assign, .visit_statement_expression = printer_visit_statement_expression, .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_block = printer_visit_statement_block, .visit_statement_label = printer_visit_statement_label, .visit_statement_goto = printer_visit_statement_goto, .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_sizeof_operation = printer_visit_expression_sizeof_operation, .visit_expression_call = printer_visit_expression_call, .visit_expression_member = printer_visit_expression_member, .visit_expression_subscript = printer_visit_expression_subscript, .visit_expression_cast = printer_visit_expression_cast, .visit_expression_construct = printer_visit_expression_construct, .visit_block = printer_visit_block, .visit_type_ref = printer_visit_type_ref, .visit_import = printer_visit_import, .visit_diagnostic = printer_visit_diagnostic, }; void printer(struct Unit* unit) { struct Printer p = { .indentation_level = 0, .output = stdout, .did_print_last_visit = false, .unit = unit, }; struct Visit visit = { .table = &printer_visit_functions, .user_data = &p }; walk(&visit, unit); } #undef PRINT #undef PRINTER_PREAMBLE