diff options
| author | Mel <mel@rnrd.eu> | 2026-05-03 19:54:15 +0200 |
|---|---|---|
| committer | Mel <mel@rnrd.eu> | 2026-05-03 19:54:15 +0200 |
| commit | 74ffd7a95c0894a96ab984e2c4d2cd9c75539af0 (patch) | |
| tree | ef8db3b3432dc1203b12079247c982892c7d21d1 /boot | |
| parent | 5211970448a05205b67d7604ad5e4e3925e4ddef (diff) | |
| download | catskill-74ffd7a95c0894a96ab984e2c4d2cd9c75539af0.tar.zst catskill-74ffd7a95c0894a96ab984e2c4d2cd9c75539af0.zip | |
IR visit system and pretty printer
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/catboot.h | 2 | ||||
| -rw-r--r-- | boot/visit/ir.c | 1032 | ||||
| -rw-r--r-- | boot/visit/tree.c | 11 | ||||
| -rw-r--r-- | boot/visit/visit.c | 20 |
4 files changed, 1054 insertions, 11 deletions
diff --git a/boot/catboot.h b/boot/catboot.h index e5ffe5e..71c2366 100644 --- a/boot/catboot.h +++ b/boot/catboot.h @@ -17,7 +17,9 @@ #include "lex.c" #include "tree.c" #include "parse.c" +#include "visit/visit.c" #include "visit/tree.c" #include "ir.c" +#include "visit/ir.c" #include "transpile.c" #include "build.c" diff --git a/boot/visit/ir.c b/boot/visit/ir.c new file mode 100644 index 0000000..13ea529 --- /dev/null +++ b/boot/visit/ir.c @@ -0,0 +1,1032 @@ +/* + * 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. <mel@rnrd.eu> + * + * 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_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`"); + } +} + +void walk_type_primitive(struct Visit* visit, struct Type* type) {} +void walk_type_alias(struct Visit* visit, struct Type* 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); +} + +void walk_statement_break(struct Visit* visit, struct Statement* stmt) {} +void walk_statement_continue(struct Visit* visit, struct Statement* stmt) {} +void walk_statement_label(struct Visit* visit, struct Statement* stmt) {} +void walk_statement_goto(struct Visit* visit, struct Statement* stmt) {} + +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`"); + } +} + +void walk_expression_integer_literal(struct Visit* visit, struct Expression* expr) {} +void walk_expression_float_literal(struct Visit* visit, struct Expression* expr) {} +void walk_expression_string_literal(struct Visit* visit, struct Expression* expr) {} +void walk_expression_boolean_literal(struct Visit* visit, struct Expression* expr) {} +void walk_expression_name(struct Visit* visit, struct Expression* expr) {} + +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); + }) +} + +void walk_type_ref(struct Visit* visit, struct Type_Ref* ref) {} +void walk_import(struct Visit* visit, struct Import* import) {} +void walk_diagnostic(struct Visit* visit, struct Diagnostic* 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 diff --git a/boot/visit/tree.c b/boot/visit/tree.c index 001cd36..48db391 100644 --- a/boot/visit/tree.c +++ b/boot/visit/tree.c @@ -69,17 +69,6 @@ struct Tree_Visit_Table void (*visit_pragma_node)(struct Tree_Visit* visitor, struct Tree_Pragma* node); }; -#define VISIT(visit_function, node) visit->table->visit_function(visit, node); - -#define VISIT_MAYBE(visit_function, node) \ - if (node) visit->table->visit_function(visit, node) - -#define DATA_FOR_VISIT(type, name) \ - type* name = (type*)visit->user_data; \ - if (!name) { failure("visit user data is NULL for " #name); } - -// tree_walk functions are the default traversal mechanism - // define a tree_walk function which goes nowhere. #define TREE_WALK_LEAF_FUNCTION(name, type) \ void name(struct Tree_Visit* visit, type node) {} diff --git a/boot/visit/visit.c b/boot/visit/visit.c new file mode 100644 index 0000000..8bc6cf7 --- /dev/null +++ b/boot/visit/visit.c @@ -0,0 +1,20 @@ +/* + * useful shared visit system helpers. + * + * Copyright (c) 2026, Mel G. <mel@rnrd.eu> + * + * SPDX-License-Identifier: MPL-2.0 + */ + +#pragma once + +#include "../catboot.h" + +#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); } |
