diff options
Diffstat (limited to 'boot/transpile.c')
| -rw-r--r-- | boot/transpile.c | 191 |
1 files changed, 163 insertions, 28 deletions
diff --git a/boot/transpile.c b/boot/transpile.c index 5b27967..a239b2b 100644 --- a/boot/transpile.c +++ b/boot/transpile.c @@ -93,45 +93,175 @@ transpiler_new(struct Transpiler* transpiler, struct Transpile_Output output) }; } +#define TRANSPILE_PREAMBLE DATA_FOR_VISIT(struct Transpiler, transpiler); + #define TRANSPILE_WRITE(...) transpile_output_write(&transpiler->output, __VA_ARGS__) -// emit the forward declaration for a single type. -// TODO: per-kind emission lands in a follow-up commit. +// look up a type by id in the active translation unit. +// nil if the identifier is unknown. +struct Type* +transpile_type_at(struct Transpiler* transpiler, Type_Id id) +{ + if (id >= array_length(&transpiler->unit->types.entries)) return nil; + return *array_at(struct Type*, &transpiler->unit->types.entries, id); +} + +// the c-side form of a primitive catskill type. +// nil if the given id is not one of the primitives. +const ascii* +transpile_primitive_c_name(struct Transpiler* transpiler, Type_Id id) +{ + // TODO: far-out, eventually we want to wrap these types not only in an + // empty "primitive" type shape, but actually define these concretely + // as real types with functions in the catskill standard library! + struct Type_Table* t = &transpiler->unit->types; + if (id == t->primitive_int_id) return "integer"; + if (id == t->primitive_uint_id) return "uint"; + if (id == t->primitive_bool_id) return "bool"; + if (id == t->primitive_string_id) return "struct String"; + if (id == t->primitive_float_id) return "real"; + if (id == t->primitive_byte_id) return "byte"; + if (id == t->primitive_ascii_id) return "ascii"; + if (id == t->primitive_void_id) return "void"; + return nil; +} + +// emit the c form of a use of a type. +void +transpile_visit_type_ref(struct Visit* visit, struct Type_Ref* ref) +{ + TRANSPILE_PREAMBLE + + struct Type* target = transpile_type_at(transpiler, ref->type_id); + const ascii* primitive = transpile_primitive_c_name(transpiler, ref->type_id); + + if (primitive) { + TRANSPILE_WRITE("%s", primitive); + } else if (target && (target->kind == TYPE_STRUCTURE || target->kind == TYPE_VARIANT)) { + TRANSPILE_WRITE("struct %s", string_c_str(target->name)); + } else if (target) { + TRANSPILE_WRITE("%s", string_c_str(target->name)); + } else { + TRANSPILE_WRITE("/* unknown type %lu */", ref->type_id); + } + + // TODO: the other mods! + FOR_EACH_ARRAY (enum Type_Modifier, mod, &ref->mods) { + if (*mod == TYPE_MOD_REFERENCE) TRANSPILE_WRITE("*"); + } +} + +void +transpile_visit_type_forward_structure(struct Visit* visit, struct Type* type) +{ + TRANSPILE_PREAMBLE + TRANSPILE_WRITE("struct %s;\n", string_c_str(type->name)); +} + +void +transpile_visit_type_forward_variant(struct Visit* visit, struct Type* type) +{ + TRANSPILE_PREAMBLE + TRANSPILE_WRITE("struct %s;\n", string_c_str(type->name)); +} + void -transpile_type_forward(struct Transpiler* transpiler, struct Type* type) +transpile_visit_type_forward_function(struct Visit* visit, struct Type* type) { - (void)transpiler; - (void)type; + TRANSPILE_PREAMBLE + + // referenced function types are emitted as a typedef + TRANSPILE_WRITE("typedef "); + VISIT(visit_type_ref, &type->value.function.return_type); + TRANSPILE_WRITE(" (*%s)(", string_c_str(type->name)); + if (array_length(&type->value.function.params) == 0) { + TRANSPILE_WRITE("void"); + } else { + bool first = true; + FOR_EACH_ARRAY (struct Type_Ref, param, &type->value.function.params) { + if (!first) TRANSPILE_WRITE(", "); + VISIT(visit_type_ref, param); + first = false; + } + if (type->value.function.variadic) TRANSPILE_WRITE(", ..."); + } + TRANSPILE_WRITE(");\n"); } -// emit the full definition for a single type. -// TODO: per-kind emission lands in a follow-up commit. void -transpile_type_definition(struct Transpiler* transpiler, struct Type* type) +transpile_visit_type_definition_alias(struct Visit* visit, struct Type* type) { - (void)transpiler; - (void)type; + TRANSPILE_PREAMBLE + + // aliases map to typedefs + // TODO: maybe aliases are too much sugar for the lowered representation, + // technically any reference to an alias could directly map to the underlying type. + struct Type_Ref bare = { .type_id = type->value.alias.target_id, .mods = { 0 } }; + TRANSPILE_WRITE("typedef "); + VISIT(visit_type_ref, &bare); + TRANSPILE_WRITE(" %s;\n", string_c_str(type->name)); } -// emit a function signature (used for both forward-decls and the line -// preceding a function body). -// TODO: per-kind emission lands in a follow-up commit. void -transpile_function_signature(struct Transpiler* transpiler, struct Function* function) +transpile_visit_type_definition_structure(struct Visit* visit, struct Type* type) { - (void)transpiler; - (void)function; + TRANSPILE_PREAMBLE + + TRANSPILE_WRITE("struct %s {\n", string_c_str(type->name)); + FOR_EACH_ARRAY (struct Field, field, &type->value.structure.fields) { + TRANSPILE_WRITE(" "); + VISIT(visit_type_ref, &field->type); + TRANSPILE_WRITE(" %s;\n", string_c_str(field->name)); + } + TRANSPILE_WRITE("};\n"); } -// emit a function body in the form `signature { block }`. -// TODO: per-kind emission lands in a follow-up commit. void -transpile_function_body(struct Transpiler* transpiler, struct Function* function) +transpile_visit_type_definition_variant(struct Visit* visit, struct Type* type) { - (void)transpiler; - (void)function; + TRANSPILE_PREAMBLE + + TRANSPILE_WRITE("struct %s {\n", string_c_str(type->name)); + TRANSPILE_WRITE(" uint32 tag;\n"); + bool any_payload = false; + FOR_EACH_ARRAY (struct Variant_Case, one_case, &type->value.variant.cases) { + if (one_case->has_payload) { + any_payload = true; + break; + } + } + if (any_payload) { + TRANSPILE_WRITE(" union {\n"); + FOR_EACH_ARRAY (struct Variant_Case, one_case, &type->value.variant.cases) { + if (!one_case->has_payload) continue; + TRANSPILE_WRITE(" "); + VISIT(visit_type_ref, &one_case->payload); + TRANSPILE_WRITE(" %s;\n", string_c_str(one_case->name)); + } + TRANSPILE_WRITE(" } as;\n"); + } + TRANSPILE_WRITE("};\n"); } +// transpilation pass 1 visit table +// emits forward declarations of types +struct Visit_Table transpile_forward_visit_functions = { + .visit_type_ref = transpile_visit_type_ref, + .visit_type_structure = transpile_visit_type_forward_structure, + .visit_type_variant = transpile_visit_type_forward_variant, + .visit_type_function = transpile_visit_type_forward_function, +}; + +// transpilation pass 2 visit table +// emits full definitions of types +struct Visit_Table transpile_definition_visit_functions = { + .visit_type_ref = transpile_visit_type_ref, + .visit_type_alias = transpile_visit_type_definition_alias, + .visit_type_structure = transpile_visit_type_definition_structure, + .visit_type_variant = transpile_visit_type_definition_variant, + .visit_type_function = walk_type_primitive, // no-op, already done in pass 1 +}; + // walk a lowered translation unit and emit c source into the transpiler's output. // the order of the translation unit, and thus the final source output is fixed. // preamble, then imports, type forwards, type definitions in topological dependency @@ -141,36 +271,41 @@ transpile_unit(struct Transpiler* transpiler, struct Unit* unit) { transpiler->unit = unit; + visit_table_fill_defaults(&transpile_forward_visit_functions); + visit_table_fill_defaults(&transpile_definition_visit_functions); + + struct Visit visit_value = { .user_data = transpiler }; + struct Visit* visit = &visit_value; + TRANSPILE_WRITE("#include \"core.c\"\n"); FOR_EACH_ARRAY (struct Import, import, &unit->imports) { TRANSPILE_WRITE("#include \"%.*s\"\n", (int)import->path.length, import->path.data); } + // trigger pass 1 walk + visit->table = &transpile_forward_visit_functions; FOR_EACH_ARRAY (struct Type*, type_slot, &unit->types.entries) { - transpile_type_forward(transpiler, *type_slot); + VISIT(visit_type, *type_slot); } + // trigger pass 2 walk + visit->table = &transpile_definition_visit_functions; FOR_EACH_ARRAY (Type_Id, ordered_id, &unit->type_emission_order) { struct Type* type = *array_at(struct Type*, &unit->types.entries, *ordered_id); - transpile_type_definition(transpiler, type); + VISIT(visit_type, type); } bool main_takes_args = false; bool has_main = false; FOR_EACH_ARRAY (struct Function*, function_slot, &unit->functions.entries) { struct Function* function = *function_slot; - transpile_function_signature(transpiler, function); if (function->is_main) { has_main = true; main_takes_args = function->main_takes_args; } } - FOR_EACH_ARRAY (struct Function*, function_slot, &unit->functions.entries) { - transpile_function_body(transpiler, *function_slot); - } - if (has_main) { if (main_takes_args) TRANSPILE_WRITE("#define CATSKILL_MAIN_TAKES_ARGS\n"); TRANSPILE_WRITE("#include \"runtime.c\"\n"); |
