diff options
Diffstat (limited to 'boot/transpile.c')
| -rw-r--r-- | boot/transpile.c | 356 |
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. |
