aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-05 04:18:23 +0200
committerMel <mel@rnrd.eu>2026-05-05 04:18:23 +0200
commit1c994a9d9129dba6a8a8943d5a8f6ccb47accb2f (patch)
treee3a390a25106789233997d84db73b8d4a66c23a8 /boot
parent285cc6b0eaf3800248219edbba0646ece6ae2f3a (diff)
downloadcatskill-1c994a9d9129dba6a8a8943d5a8f6ccb47accb2f.tar.zst
catskill-1c994a9d9129dba6a8a8943d5a8f6ccb47accb2f.zip
Emit transpiled function bodies, blocks, statements, expressions, etc.
Signed-off-by: Mel <mel@rnrd.eu>
Diffstat (limited to 'boot')
-rw-r--r--boot/transpile.c356
1 files changed, 355 insertions, 1 deletions
diff --git a/boot/transpile.c b/boot/transpile.c
index 384b7c1..7264085 100644
--- a/boot/transpile.c
+++ b/boot/transpile.c
@@ -83,6 +83,15 @@ struct Transpiler
{
struct Transpile_Output output;
struct Unit* unit;
+
+ // current nesting depth, used to indent statement bodies.
+ // mostly for decoration and easier observability!
+ uint indent_level;
+
+ // minimum precedence level of child expressions.
+ // expressions use this to decide whether to wrap themselves in parentheses
+ // in the case where their parent expression is stronger than them.
+ uint expression_parent_precedence;
};
void
@@ -308,7 +317,324 @@ transpile_visit_function_body(struct Visit* visit, struct Function* function)
{
TRANSPILE_PREAMBLE
transpile_function_emit_signature(visit, function);
- TRANSPILE_WRITE(" {\n}\n");
+ TRANSPILE_WRITE(" ");
+ if (function->body) {
+ VISIT(visit_block, function->body);
+ } else {
+ TRANSPILE_WRITE("{\n}");
+ }
+ TRANSPILE_WRITE("\n");
+}
+
+void
+transpile_emit_indent(struct Transpiler* transpiler)
+{
+ for (uint i = 0; i < transpiler->indent_level; ++i) TRANSPILE_WRITE(" ");
+}
+
+// dispatch on an expression with a parent-precedence value supplied by the caller.
+void
+transpile_emit_expression(
+ struct Visit* visit, struct Expression* expression, uint parent_precedence)
+{
+ TRANSPILE_PREAMBLE
+ transpiler->expression_parent_precedence = parent_precedence;
+ VISIT(visit_expression, expression);
+}
+
+// precedence levels for non-binary operators! higher means tighter.
+#define TRANSPILE_PRECEDENCE_UNARY 14
+#define TRANSPILE_PRECEDENCE_POSTFIX 15
+#define TRANSPILE_PRECEDENCE_PRIMARY 16
+
+void
+transpile_visit_expression_integer_literal(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("%ld", expression->value.integer_literal.value);
+}
+
+void
+transpile_visit_expression_float_literal(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("%f", expression->value.float_literal.value);
+}
+
+void
+transpile_visit_expression_string_literal(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ // TODO: emit a real, typed catskill string object once the standard
+ // library has one and it can be wired in as a primitive.
+ TRANSPILE_WRITE("\"%s\"", string_c_str(expression->value.string_literal.value));
+}
+
+void
+transpile_visit_expression_boolean_literal(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("%s", expression->value.bool_literal.value ? "true" : "false");
+}
+
+void
+transpile_visit_expression_name(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("%s", string_c_str(expression->value.name.name));
+}
+
+void
+transpile_visit_expression_unary_operation(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+
+ uint parent = transpiler->expression_parent_precedence;
+ bool needs_parens = TRANSPILE_PRECEDENCE_UNARY < parent;
+ if (needs_parens) TRANSPILE_WRITE("(");
+
+ TRANSPILE_WRITE("%s", unary_operation_to_string(expression->value.unary_operator.operation));
+ transpile_emit_expression(
+ visit, expression->value.unary_operator.operand, TRANSPILE_PRECEDENCE_UNARY);
+
+ if (needs_parens) TRANSPILE_WRITE(")");
+}
+
+void
+transpile_visit_expression_binary_operation(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+
+ enum Binary_Operation op = expression->value.binary_operator.operation;
+ uint my_precedence = binary_operation_precedence(op);
+ uint parent = transpiler->expression_parent_precedence;
+ bool needs_parens = my_precedence < parent;
+
+ enum Binary_Operation_Associativity assoc = binary_operation_associativity(op);
+ uint left_parent = (assoc == BINARY_ASSOCIATIVITY_RIGHT) ? my_precedence + 1 : my_precedence;
+ uint right_parent = (assoc == BINARY_ASSOCIATIVITY_LEFT) ? my_precedence + 1 : my_precedence;
+
+ if (needs_parens) TRANSPILE_WRITE("(");
+ transpile_emit_expression(visit, expression->value.binary_operator.left_operand, left_parent);
+ TRANSPILE_WRITE(" %s ", binary_operation_to_string(op));
+ transpile_emit_expression(visit, expression->value.binary_operator.right_operand, right_parent);
+ if (needs_parens) TRANSPILE_WRITE(")");
+}
+
+void
+transpile_visit_expression_sizeof_operation(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("sizeof(");
+ VISIT(visit_type_ref, &expression->value.sizeof_operator.target);
+ TRANSPILE_WRITE(")");
+}
+
+void
+transpile_visit_expression_call(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+
+ transpile_emit_expression(visit, expression->value.call.subject, TRANSPILE_PRECEDENCE_POSTFIX);
+ TRANSPILE_WRITE("(");
+ bool first = true;
+ FOR_EACH_ARRAY (struct Expression*, argument, &expression->value.call.arguments) {
+ if (!first) TRANSPILE_WRITE(", ");
+ // call arguments are comma-separated, each fresh from precedence 0.
+ transpile_emit_expression(visit, *argument, 0);
+ first = false;
+ }
+ TRANSPILE_WRITE(")");
+}
+
+void
+transpile_visit_expression_member(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ transpile_emit_expression(
+ visit, expression->value.member.subject, TRANSPILE_PRECEDENCE_POSTFIX);
+ // TODO: this only produces direct member references, however when the
+ // subject is behind a pointer c forces us to use `x->y` to refer to them. fix!
+ TRANSPILE_WRITE(".%s", string_c_str(expression->value.member.name));
+}
+
+void
+transpile_visit_expression_subscript(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ transpile_emit_expression(
+ visit, expression->value.subscript.subject, TRANSPILE_PRECEDENCE_POSTFIX);
+ TRANSPILE_WRITE("[");
+ transpile_emit_expression(visit, expression->value.subscript.index, 0);
+ TRANSPILE_WRITE("]");
+}
+
+void
+transpile_visit_expression_cast(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("(");
+ VISIT(visit_type_ref, &expression->value.cast.target);
+ TRANSPILE_WRITE(")");
+ transpile_emit_expression(visit, expression->value.cast.operand, TRANSPILE_PRECEDENCE_UNARY);
+}
+
+void
+transpile_visit_expression_construct(struct Visit* visit, struct Expression* expression)
+{
+ TRANSPILE_PREAMBLE
+
+ struct Type_Ref bare = { .type_id = expression->value.construct.type_id, .mods = { 0 } };
+ TRANSPILE_WRITE("(");
+ VISIT(visit_type_ref, &bare);
+ TRANSPILE_WRITE("){");
+ bool first = true;
+ FOR_EACH_ARRAY (struct Construct_Field, field, &expression->value.construct.fields) {
+ if (!first) TRANSPILE_WRITE(", ");
+ if (field->name.length > 0) TRANSPILE_WRITE(".%s = ", string_c_str(field->name));
+ transpile_emit_expression(visit, field->value, 0);
+ first = false;
+ }
+ TRANSPILE_WRITE("}");
+}
+
+// statements always emit their own indentation and trailing newline.
+// the dispatcher writes the indentation then routes via normal statement apparatus.
+void
+transpile_visit_statement(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ transpile_emit_indent(transpiler);
+ walk_statement(visit, stmt);
+}
+
+void
+transpile_visit_statement_declaration(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ VISIT(visit_type_ref, &stmt->value.declaration.type);
+ TRANSPILE_WRITE(" %s", string_c_str(stmt->value.declaration.name));
+ if (stmt->value.declaration.initializer) {
+ TRANSPILE_WRITE(" = ");
+ transpile_emit_expression(visit, stmt->value.declaration.initializer, 0);
+ }
+ TRANSPILE_WRITE(";\n");
+}
+
+void
+transpile_visit_statement_assign(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ transpile_emit_expression(visit, stmt->value.assign.lhs, 0);
+ TRANSPILE_WRITE(" = ");
+ transpile_emit_expression(visit, stmt->value.assign.rhs, 0);
+ TRANSPILE_WRITE(";\n");
+}
+
+void
+transpile_visit_statement_expression(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ transpile_emit_expression(visit, stmt->value.expression.inner, 0);
+ TRANSPILE_WRITE(";\n");
+}
+
+void
+transpile_visit_statement_conditional(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ bool first = true;
+ FOR_EACH_ARRAY (struct If_Branch, branch, &stmt->value.conditional.branches) {
+ if (first) {
+ TRANSPILE_WRITE("if (");
+ transpile_emit_expression(visit, branch->condition, 0);
+ TRANSPILE_WRITE(") ");
+ } else if (branch->condition) {
+ TRANSPILE_WRITE(" else if (");
+ transpile_emit_expression(visit, branch->condition, 0);
+ TRANSPILE_WRITE(") ");
+ } else {
+ TRANSPILE_WRITE(" else ");
+ }
+ VISIT(visit_block, branch->body);
+ first = false;
+ }
+ TRANSPILE_WRITE("\n");
+}
+
+void
+transpile_visit_statement_loop(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("while (");
+ transpile_emit_expression(visit, stmt->value.loop.condition, 0);
+ TRANSPILE_WRITE(") ");
+ VISIT(visit_block, stmt->value.loop.body);
+ TRANSPILE_WRITE("\n");
+}
+
+void
+transpile_visit_statement_return(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ if (stmt->value.return_value.value) {
+ TRANSPILE_WRITE("return ");
+ transpile_emit_expression(visit, stmt->value.return_value.value, 0);
+ TRANSPILE_WRITE(";\n");
+ } else {
+ TRANSPILE_WRITE("return;\n");
+ }
+}
+
+void
+transpile_visit_statement_break(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE(void) stmt;
+ TRANSPILE_WRITE("break;\n");
+}
+
+void
+transpile_visit_statement_continue(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE(void) stmt;
+ TRANSPILE_WRITE("continue;\n");
+}
+
+void
+transpile_visit_statement_block(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ VISIT(visit_block, stmt->value.block.inner);
+ TRANSPILE_WRITE("\n");
+}
+
+void
+transpile_visit_statement_label(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("%s:;\n", string_c_str(stmt->value.label.name));
+}
+
+void
+transpile_visit_statement_goto(struct Visit* visit, struct Statement* stmt)
+{
+ TRANSPILE_PREAMBLE
+ TRANSPILE_WRITE("goto %s;\n", string_c_str(stmt->value.goto_target.target));
+}
+
+void
+transpile_visit_block(struct Visit* visit, struct Block* block)
+{
+ TRANSPILE_PREAMBLE
+
+ TRANSPILE_WRITE("{\n");
+ transpiler->indent_level++;
+ FOR_EACH_ARRAY (struct Statement*, statement, &block->statements) {
+ VISIT(visit_statement, *statement);
+ }
+ transpiler->indent_level--;
+ transpile_emit_indent(transpiler);
+ TRANSPILE_WRITE("}");
}
// transpilation pass 3 visit table
@@ -323,6 +649,34 @@ struct Visit_Table transpile_signature_visit_functions = {
struct Visit_Table transpile_body_visit_functions = {
.visit_type_ref = transpile_visit_type_ref,
.visit_function = transpile_visit_function_body,
+ .visit_block = transpile_visit_block,
+
+ .visit_statement = transpile_visit_statement,
+ .visit_statement_declaration = transpile_visit_statement_declaration,
+ .visit_statement_assign = transpile_visit_statement_assign,
+ .visit_statement_expression = transpile_visit_statement_expression,
+ .visit_statement_conditional = transpile_visit_statement_conditional,
+ .visit_statement_loop = transpile_visit_statement_loop,
+ .visit_statement_return = transpile_visit_statement_return,
+ .visit_statement_break = transpile_visit_statement_break,
+ .visit_statement_continue = transpile_visit_statement_continue,
+ .visit_statement_block = transpile_visit_statement_block,
+ .visit_statement_label = transpile_visit_statement_label,
+ .visit_statement_goto = transpile_visit_statement_goto,
+
+ .visit_expression_integer_literal = transpile_visit_expression_integer_literal,
+ .visit_expression_float_literal = transpile_visit_expression_float_literal,
+ .visit_expression_string_literal = transpile_visit_expression_string_literal,
+ .visit_expression_boolean_literal = transpile_visit_expression_boolean_literal,
+ .visit_expression_name = transpile_visit_expression_name,
+ .visit_expression_unary_operation = transpile_visit_expression_unary_operation,
+ .visit_expression_binary_operation = transpile_visit_expression_binary_operation,
+ .visit_expression_sizeof_operation = transpile_visit_expression_sizeof_operation,
+ .visit_expression_call = transpile_visit_expression_call,
+ .visit_expression_member = transpile_visit_expression_member,
+ .visit_expression_subscript = transpile_visit_expression_subscript,
+ .visit_expression_cast = transpile_visit_expression_cast,
+ .visit_expression_construct = transpile_visit_expression_construct,
};
// walk a lowered translation unit and emit c source into the transpiler's output.