aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMel <mel@rnrd.eu>2026-05-05 02:18:07 +0200
committerMel <mel@rnrd.eu>2026-05-05 02:18:07 +0200
commit1dc6425a76bdfa53d088ba150f1287726a2e7370 (patch)
treeb32570835749df53c4a46a4e4e4c98bd92a33a90
parent8b1b7aab9a580c0ff3879b8a011b422804872397 (diff)
downloadcatskill-1dc6425a76bdfa53d088ba150f1287726a2e7370.tar.zst
catskill-1dc6425a76bdfa53d088ba150f1287726a2e7370.zip
Type transpilation passes 1 & 2
Signed-off-by: Mel <mel@rnrd.eu>
-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");