about summary refs log tree commit diff
diff options
context:
space:
mode:
-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.