about summary refs log tree commit diff
path: root/boot/transpile.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/transpile.c')
-rw-r--r--boot/transpile.c191
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");