about summary refs log tree commit diff
path: root/boot/visit.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/visit.c')
-rw-r--r--boot/visit.c317
1 files changed, 177 insertions, 140 deletions
diff --git a/boot/visit.c b/boot/visit.c
index 500fb5a..3cc3e55 100644
--- a/boot/visit.c
+++ b/boot/visit.c
@@ -501,28 +501,59 @@ struct Tree_Printer
 {
     uint indentation_level;
     FILE* output;
+    bool did_print_last_visit;
 };
 
+#define TREE_PRINTER_PREAMBLE                     \
+    DATA_FOR_VISIT(struct Tree_Printer, printer); \
+    tree_printer_reset_print_state(printer);
+
+void
+tree_printer_out(struct Tree_Printer* printer, const ascii* format, ...)
+{
+    va_list args;
+    va_start(args, format);
+    vfprintf(printer->output, format, args);
+    va_end(args);
+
+    printer->did_print_last_visit = true;
+}
+
+#define PRINT(...) tree_printer_out(printer, __VA_ARGS__)
+#define PRINT_WHITESPACE_IF_NEEDED(s)                    \
+    do {                                                 \
+        if (printer->did_print_last_visit) { PRINT(s); } \
+        tree_printer_reset_print_state(printer);         \
+    } while (0)
+
+void
+tree_printer_reset_print_state(struct Tree_Printer* printer)
+{
+    printer->did_print_last_visit = false;
+}
+
 void
 tree_printer_indent(struct Tree_Printer* printer)
 {
-    for (uint i = 0; i < printer->indentation_level; ++i) fprintf(printer->output, "\t");
+    for (uint i = 0; i < printer->indentation_level; ++i) PRINT("\t");
 }
 
 void
 printer_visit_tree(struct Visit* visit, struct Tree* tree)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
+
     FOR_EACH (struct Statement*, statement, tree->top_level_statements) {
         VISIT(visit_statement, statement);
-        fprintf(printer->output, "\n");
+        PRINT("\n");
     }
 }
 
 void
 printer_visit_statement(struct Visit* visit, struct Statement* statement)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
+
     tree_printer_indent(printer);
     walk_statement(visit, statement);
 }
@@ -530,67 +561,67 @@ printer_visit_statement(struct Visit* visit, struct Statement* statement)
 void
 printer_visit_statement_declaration(struct Visit* visit, struct Statement* stmt)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
     if (stmt->value.declaration.kind == STATEMENT_DECLARATION_VARIABLE)
-        fprintf(printer->output, "(variable ");
+        PRINT("(variable ");
     else if (stmt->value.declaration.kind == STATEMENT_DECLARATION_CONSTANT)
-        fprintf(printer->output, "(constant ");
+        PRINT("(constant ");
 
     VISIT(visit_bare_declaration_node, &stmt->value.declaration.inner);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_statement_conditional(struct Visit* visit, struct Statement* stmt)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(conditional");
+    PRINT("(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, " ");
+        PRINT(" ");
         if (branch->when) {
-            fprintf(printer->output, "(when ");
+            PRINT("(when ");
             VISIT(visit_expression, branch->when);
-            fprintf(printer->output, ") ");
+            PRINT(") ");
         } else {
-            fprintf(printer->output, "(always) ");
+            PRINT("(always) ");
         }
         VISIT(visit_block_node, (struct Block_Node*)&branch->then);
     }
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_statement_loop(struct Visit* visit, struct Statement* stmt)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(loop ");
+    PRINT("(loop ");
 
     switch (stmt->value.loop.style) {
     case STATEMENT_LOOP_STYLE_C:
-        fprintf(printer->output, "c-style ");
+        PRINT("c-style ");
         VISIT(visit_bare_declaration_node, &stmt->value.loop.declaration);
-        fprintf(printer->output, " (condition ");
+        PRINT(" (condition ");
         VISIT(visit_expression, stmt->value.loop.condition);
-        fprintf(printer->output, ") (iteration ");
+        PRINT(") (iteration ");
         VISIT(visit_expression, stmt->value.loop.iteration);
-        fprintf(printer->output, ") ");
+        PRINT(") ");
         break;
     case STATEMENT_LOOP_STYLE_FOR_EACH:
-        fprintf(printer->output, "for-each ");
+        PRINT("for-each ");
         VISIT(visit_bare_declaration_node, &stmt->value.loop.declaration);
-        fprintf(printer->output, " ");
+        PRINT(" ");
         break;
     case STATEMENT_LOOP_STYLE_WHILE:
-        fprintf(printer->output, "while (condition ");
+        PRINT("while (condition ");
         VISIT(visit_expression, stmt->value.loop.condition);
-        fprintf(printer->output, ") ");
+        PRINT(") ");
         break;
     case STATEMENT_LOOP_STYLE_ENDLESS:
-        fprintf(printer->output, "endless ");
+        PRINT("endless ");
         break;
     default:
         fprintf(stderr, "unexpected loop style in `printer_visit_statement_loop`");
@@ -598,258 +629,256 @@ printer_visit_statement_loop(struct Visit* visit, struct Statement* stmt)
     }
 
     VISIT(visit_block_node, &stmt->value.loop.body);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_statement_return(struct Visit* visit, struct Statement* stmt)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(return");
+    PRINT("(return");
     if (stmt->value.return_value.value) {
-        fprintf(printer->output, " ");
+        PRINT(" ");
         VISIT(visit_expression, stmt->value.return_value.value);
     }
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_statement_break(struct Visit* visit, struct Statement* stmt)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(break)");
+    PRINT("(break)");
 }
 
 void
 printer_visit_statement_continue(struct Visit* visit, struct Statement* stmt)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(continue)");
+    PRINT("(continue)");
 }
 
 void
 printer_visit_statement_defer(struct Visit* visit, struct Statement* stmt)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(defer ");
+    PRINT("(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, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_expression(struct Visit* visit, struct Expression* expression)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
-    fprintf(printer->output, "(expr ");
+    TREE_PRINTER_PREAMBLE
+    PRINT("(expr ");
     walk_expression(visit, expression);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_expression_integer_literal(struct Visit* visit, struct Expression* expr)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "%ld", expr->value.integer_literal.value);
+    PRINT("%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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "%lf", expr->value.float_literal.value);
+    PRINT("%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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "\"%s\"", expr->value.string_literal.value.data);
+    PRINT("\"%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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "%s", expr->value.bool_literal.value ? "true" : "false");
+    PRINT("%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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(name %s)", expr->value.name.name.data);
+    PRINT("(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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(unary %s ",
-            unary_operation_to_string(expr->value.unary_operator.operation));
+    PRINT("(unary %s ", unary_operation_to_string(expr->value.unary_operator.operation));
     VISIT(visit_expression, expr->value.unary_operator.operand);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_expression_binary_operation(struct Visit* visit, struct Expression* expr)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(binary %s ",
-            binary_operation_to_string(expr->value.binary_operator.operation));
+    PRINT("(binary %s ", binary_operation_to_string(expr->value.binary_operator.operation));
     VISIT(visit_expression, expr->value.binary_operator.left_operand);
-    fprintf(printer->output, " ");
+    PRINT(" ");
     VISIT(visit_expression, expr->value.binary_operator.right_operand);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_expression_group(struct Visit* visit, struct Expression* expr)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(group ");
+    PRINT("(group ");
     VISIT(visit_expression, expr->value.group.inner_expression);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_expression_call_or_construct(struct Visit* visit, struct Expression* expr)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
     struct Expression_Call_Or_Construct* coc = &expr->value.call_or_construct;
-    fprintf(printer->output, "(call/construct ");
+    PRINT("(call/construct ");
     VISIT(visit_expression, coc->subject);
 
     uint i = 0;
     FOR_EACH (struct Expression*, argument, coc->arguments) {
         struct String name = string_array_at(&coc->argument_names, i++);
         if (name.data && name.data[0] != '\0') {
-            fprintf(printer->output, " (named arg '%s' ", name.data);
+            PRINT(" (named arg '%s' ", name.data);
         } else {
-            fprintf(printer->output, " (arg ");
+            PRINT(" (arg ");
         }
         VISIT(visit_expression, argument);
-        fprintf(printer->output, ")");
+        PRINT(")");
     }
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_expression_subscript(struct Visit* visit, struct Expression* expr)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(subscript ");
+    PRINT("(subscript ");
     VISIT(visit_expression, expr->value.subscript.subject);
-    fprintf(printer->output, " ");
+    PRINT(" ");
     VISIT(visit_expression, expr->value.subscript.index);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_expression_member(struct Visit* visit, struct Expression* expr)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(member of ");
+    PRINT("(member of ");
     VISIT(visit_expression, expr->value.member.subject);
-    fprintf(printer->output, " named %s)", expr->value.member.name.data);
+    PRINT(" 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);
+    TREE_PRINTER_PREAMBLE
 
     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);
+    PRINT("(increment/decrement %s %s ",
+          increment_decrement_operation_to_string(inc_dec->operation), prefix_or_postfix);
     VISIT(visit_expression, inc_dec->subject);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_expression_function(struct Visit* visit, struct Expression* expr)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
     struct Expression_Function* fun = &expr->value.function;
-    fprintf(printer->output, "(function ");
+    PRINT("(function ");
     VISIT(visit_function_header_node, &fun->header);
-    fprintf(printer->output, " ");
+    PRINT_WHITESPACE_IF_NEEDED(" ");
     VISIT(visit_block_node, (struct Block_Node*)&fun->body);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_type_node(struct Visit* visit, struct Type_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
-    fprintf(printer->output, "(type ");
+    TREE_PRINTER_PREAMBLE
+    PRINT("(type ");
     walk_type_node(visit, node);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_type_node_name(struct Visit* visit, struct Type_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "name %s", node->value.name.name.data);
+    PRINT("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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "array of ");
+    PRINT("array of ");
     VISIT(visit_type_node, node->value.array.element_type);
 }
 
 void
 printer_visit_type_node_reference(struct Visit* visit, struct Type_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "reference to ");
+    PRINT("reference to ");
     VISIT(visit_type_node, node->value.reference.referenced_type);
 }
 
 void
 printer_visit_type_node_maybe(struct Visit* visit, struct Type_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "maybe ");
+    PRINT("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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "tuple");
+    PRINT("tuple");
     FOR_EACH (struct Type_Node*, current, node->value.tuple.head) {
-        fprintf(printer->output, " ");
+        PRINT(" ");
         VISIT(visit_type_node, current);
     }
 }
@@ -857,31 +886,31 @@ printer_visit_type_node_tuple(struct Visit* visit, struct Type_Node* node)
 void
 printer_visit_type_node_map(struct Visit* visit, struct Type_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "map ");
+    PRINT("map ");
     VISIT(visit_type_node, node->value.map.key_type);
-    fprintf(printer->output, " = ");
+    PRINT(" = ");
     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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "function ");
+    PRINT("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);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "structure");
+    PRINT("structure");
     FOR_EACH (struct Type_Node*, current, node->value.structure.fields) {
-        fprintf(printer->output, " (field %s) ", current->value_name.data);
+        PRINT(" (field %s) ", current->value_name.data);
         VISIT(visit_type_node, current);
     }
 }
@@ -889,16 +918,16 @@ printer_visit_type_node_structure(struct Visit* visit, struct Type_Node* node)
 void
 printer_visit_type_node_variant(struct Visit* visit, struct Type_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "variant");
+    PRINT("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);
+            PRINT(" (variant %s)", current->value_name.data);
         } else {
-            fprintf(printer->output, " (variant %s of ", current->value_name.data);
+            PRINT(" (variant %s of ", current->value_name.data);
             VISIT(visit_type_node, current);
-            fprintf(printer->output, ")");
+            PRINT(")");
         }
     }
 }
@@ -906,104 +935,106 @@ printer_visit_type_node_variant(struct Visit* visit, struct Type_Node* node)
 void
 printer_visit_type_node_class(struct Visit* visit, struct Type_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "class");
+    PRINT("class");
     FOR_EACH (struct Type_Node*, current, node->value.class.methods) {
-        fprintf(printer->output, " (method %s ", current->value_name.data);
+        PRINT(" (method %s ", current->value_name.data);
         VISIT(visit_function_header_node, &current->value.function.header);
-        fprintf(printer->output, ")");
+        PRINT(")");
     }
 }
 
 void
 printer_visit_block_node(struct Visit* visit, struct Block_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(block \n");
+    PRINT("(block\n");
     printer->indentation_level++;
     FOR_EACH (struct Statement*, statement, node->statements) {
         VISIT(visit_statement, statement);
-        fprintf(printer->output, "\n");
+        PRINT("\n");
     }
     printer->indentation_level--;
     tree_printer_indent(printer);
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_bare_declaration_node(struct Visit* visit, struct Bare_Declaration_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
-    fprintf(printer->output, "(declaration ");
-    STRING_ARRAY_FOR_EACH (i, name, node->names) { fprintf(printer->output, "%s ", name.data); }
+    PRINT("(declaration ");
+    STRING_ARRAY_FOR_EACH (i, name, node->names) { PRINT("%s ", name.data); }
 
     if (node->type && node->type->type != TYPE_NODE_NONE) {
         VISIT(visit_type_node, node->type);
-        fprintf(printer->output, " ");
+        PRINT(" ");
     }
 
     if (node->initializer) {
-        fprintf(printer->output, "(initializer ");
+        PRINT("(initializer ");
         VISIT(visit_expression, node->initializer);
-        fprintf(printer->output, ")");
+        PRINT(")");
     }
-    fprintf(printer->output, ")");
+    PRINT(")");
 }
 
 void
 printer_visit_function_header(struct Visit* visit, struct Function_Header_Node* header)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
     FOR_EACH (struct Type_Node*, current, header->parameters_type_and_name) {
-        if (current != header->parameters_type_and_name) fprintf(printer->output, " ");
+        if (current != header->parameters_type_and_name) PRINT(" ");
 
         if (current->value_name.data && current->value_name.data[0] != '\0')
-            fprintf(printer->output, "(param %s) ", current->value_name.data);
+            PRINT("(param %s) ", current->value_name.data);
         else
-            fprintf(printer->output, "(param) ");
+            PRINT("(param) ");
 
         VISIT(visit_type_node, current);
     }
 
+    PRINT_WHITESPACE_IF_NEEDED(" ");
+
     if (header->return_type) {
-        fprintf(printer->output, " (returns ");
+        PRINT("(returns ");
         VISIT(visit_type_node, header->return_type);
-        fprintf(printer->output, ")");
+        PRINT(")");
     }
 }
 
 void
 printer_visit_pragma_node(struct Visit* visit, struct Pragma_Node* node)
 {
-    DATA_FOR_VISIT(struct Tree_Printer, printer);
+    TREE_PRINTER_PREAMBLE
 
     const ascii* pragma_name = pragma_type_to_string(node->type);
 
-    fprintf(printer->output, "(pragma %s", pragma_name);
+    PRINT("(pragma %s", pragma_name);
     for (uint ai = 0; ai < node->argument_count; ++ai) {
         struct Pragma_Argument* arg = &node->arguments[ai];
         switch (arg->type) {
         case PRAGMA_ARGUMENT_NUMBER:
-            fprintf(printer->output, " (number %ld)", arg->value.number);
+            PRINT(" (number %ld)", arg->value.number);
             break;
         case PRAGMA_ARGUMENT_DECIMAL:
-            fprintf(printer->output, " (decimal %lf)", arg->value.decimal);
+            PRINT(" (decimal %lf)", arg->value.decimal);
             break;
         case PRAGMA_ARGUMENT_NAME_OR_STRING:
-            fprintf(printer->output, " (name/string '%s')", arg->value.name_or_string.data);
+            PRINT(" (name/string '%s')", arg->value.name_or_string.data);
             break;
         default:
             failure("unexpected pragma argument type in `printer_visit_pragma_node`");
         }
     }
-    fprintf(printer->output, ")");
+    PRINT(")");
 
     if (node->next) {
-        fprintf(printer->output, " ");
+        PRINT(" ");
         VISIT(visit_pragma_node, node->next);
     }
 }
@@ -1056,8 +1087,14 @@ struct Visit_Table printer_visit_functions = {
 void
 tree_printer(struct Tree* tree)
 {
-    struct Tree_Printer printer = { .indentation_level = 0, .output = stdout };
+    struct Tree_Printer printer = {
+        .indentation_level = 0, .output = stdout, .did_print_last_visit = false
+    };
     struct Visit visit = { .table = &printer_visit_functions, .user_data = &printer };
 
     walk(&visit, tree);
 }
+
+#undef PRINT
+#undef PRINT_WHITESPACE_IF_NEEDED
+#undef TREE_PRINTER_PREAMBLE