about summary refs log tree commit diff
path: root/boot/lower.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/lower.c')
-rw-r--r--boot/lower.c297
1 files changed, 243 insertions, 54 deletions
diff --git a/boot/lower.c b/boot/lower.c
index e8bf8d0..8f41c1f 100644
--- a/boot/lower.c
+++ b/boot/lower.c
@@ -68,6 +68,8 @@ lower_function_lookup_by_name(struct Unit* unit, struct String name, Function_Id
     return false;
 }
 
+// registers a basic type object for a primitive, so other types
+// can depend on it and reference it in the same manner as any other type.
 Type_Id
 lower_seed_primitive(struct Unit* unit, const ascii* name)
 {
@@ -82,12 +84,14 @@ lower_seed_primitive(struct Unit* unit, const ascii* name)
     return id;
 }
 
-// turns a tree-side type expression into an ir Type_Ref. for now this only
-// resolves simple named types (primitives + user-named types already in the
-// table); compound and structural shapes get deferred to later commits.
+// turns a source type expression into a concrete type reference object.
+// resolving named types from the type table.
+// unwraps any type modifiers (like references, arrays, etc.),
+// adding them into the type reference object.
 struct Type_Ref
 lower_intern_type_ref(struct Unit* unit, struct Tree_Type* tree_type)
 {
+    // TODO: support synthetic structural types.
     struct Type_Ref ref = {
         .type_id = unit->types.primitive_void_id,
         .mods = array_new(enum Type_Modifier, 4),
@@ -95,31 +99,97 @@ lower_intern_type_ref(struct Unit* unit, struct Tree_Type* tree_type)
 
     if (!tree_type || tree_type->type == TREE_TYPE_NONE) return ref;
 
-    switch (tree_type->type) {
+    struct Tree_Type* current = tree_type;
+    while (current && current->type == TREE_TYPE_REFERENCE) {
+        enum Type_Modifier mod = TYPE_MOD_REFERENCE;
+        array_push(&ref.mods, &mod);
+        current = current->value.reference.referenced_type;
+    }
+
+    if (!current || current->type == TREE_TYPE_NONE) return ref;
+
+    switch (current->type) {
     case TREE_TYPE_NAME: {
         Type_Id id;
-        if (lower_type_lookup_by_name(unit, tree_type->value.name.name, &id)) {
+        // at this point we know all top-level types (independent of order),
+        // if we fail to find the type referenced by the name, we know for a fact
+        // this type does not exist.
+        if (lower_type_lookup_by_name(unit, current->value.name.name, &id)) {
             ref.type_id = id;
         } else {
             lower_emit_error(
-                unit, tree_type->span,
+                unit, current->span,
                 string_concatenate(
-                    ARG_ASCII, "undefined type '", ARG_STRING, tree_type->value.name.name,
-                    ARG_ASCII, "'", ARG_END));
+                    ARG_ASCII, "undefined type '", ARG_STRING, current->value.name.name, ARG_ASCII,
+                    "'", ARG_END));
         }
         return ref;
     }
     default:
-        // compound types (T?, [T], (T1, T2), &T, etc.) arrive in a later commit.
         lower_emit_error_c(
-            unit, tree_type->span, "unimplemented: only simple named types are supported for now");
+            unit, current->span,
+            "unimplemented: only named types and references are supported for now");
         return ref;
     }
 }
 
+// adds a by-value dependency. by-pointer references don't add — a forward
+// declaration of the target is enough to use it through a pointer.
 void
-lower_declare_function(
-    struct Unit* unit, struct String name, struct Tree_Expression* fn_expr, struct Span span)
+lower_add_dependency(struct Type* type, struct Type_Ref ref)
+{
+    if (array_length(&ref.mods) > 0) {
+        enum Type_Modifier outer = *array_at(enum Type_Modifier, &ref.mods, 0);
+        if (outer == TYPE_MOD_REFERENCE) return;
+    }
+    array_push(&type->depends_on, &ref.type_id);
+}
+
+// is this source statement a function?
+// if it is, unwrap it and return true, otherwise false.
+bool
+lower_match_function_decl(
+    struct Tree_Statement* stmt, struct String* out_name, struct Tree_Expression** out_fn_expr)
+{
+    if (stmt->kind != TREE_STATEMENT_EXPRESSION) return false;
+    struct Tree_Expression* expr = stmt->value.expression.inner;
+    if (!expr || expr->kind != TREE_EXPRESSION_BINARY_OPERATION) return false;
+    if (expr->value.binary_operator.operation != BINARY_ASSIGN) return false;
+
+    struct Tree_Expression* lhs = expr->value.binary_operator.left_operand;
+    struct Tree_Expression* rhs = expr->value.binary_operator.right_operand;
+    if (!lhs || lhs->kind != TREE_EXPRESSION_NAME) return false;
+    if (!rhs || rhs->kind != TREE_EXPRESSION_FUNCTION) return false;
+
+    *out_name = lhs->value.name.name;
+    *out_fn_expr = rhs;
+    return true;
+}
+
+// is this source statement a type?
+// if it is, unwrap it and return true, otherwise false.
+bool
+lower_match_type_decl(
+    struct Tree_Statement* stmt, struct String* out_name, struct Tree_Type** out_tree_type)
+{
+    if (stmt->kind != TREE_STATEMENT_EXPRESSION) return false;
+    struct Tree_Expression* expr = stmt->value.expression.inner;
+    if (!expr || expr->kind != TREE_EXPRESSION_BINARY_OPERATION) return false;
+    if (expr->value.binary_operator.operation != BINARY_ASSIGN) return false;
+
+    struct Tree_Expression* lhs = expr->value.binary_operator.left_operand;
+    struct Tree_Expression* rhs = expr->value.binary_operator.right_operand;
+    if (!lhs || lhs->kind != TREE_EXPRESSION_NAME) return false;
+    if (!rhs || rhs->kind != TREE_EXPRESSION_TYPE) return false;
+
+    *out_name = lhs->value.name.name;
+    *out_tree_type = rhs->value.type.type;
+    return true;
+}
+
+// register a function shell object, only listing a name and assigning a unique identifier.
+bool
+lower_register_function_shell(struct Unit* unit, struct String name, struct Span span)
 {
     Function_Id existing;
     if (lower_function_lookup_by_name(unit, name, &existing)) {
@@ -127,13 +197,47 @@ lower_declare_function(
             unit, span,
             string_concatenate(
                 ARG_ASCII, "duplicate function '", ARG_STRING, name, ARG_ASCII, "'", ARG_END));
-        return;
+        return false;
     }
 
     Function_Id id = array_length(&unit->functions.entries);
     struct Function* fn = function_new(id, name);
+    array_push(&unit->functions.entries, &fn);
+
+    struct Function_Name_To_Id mapping = { .name = name, .id = id };
+    array_push(&unit->functions.by_name, &mapping);
+    return true;
+}
+
+// register a type shell object, only listing a name and assigning a unique identifier.
+// used to resolve references to types which are defined out-of-order in the source.
+bool
+lower_register_type_shell(struct Unit* unit, struct String name, struct Span span)
+{
+    Type_Id existing;
+    if (lower_type_lookup_by_name(unit, name, &existing)) {
+        lower_emit_error(
+            unit, span,
+            string_concatenate(
+                ARG_ASCII, "duplicate type '", ARG_STRING, name, ARG_ASCII, "'", ARG_END));
+        return false;
+    }
 
-    fn->is_main = string_equals_c_str(name, "main");
+    Type_Id id = array_length(&unit->types.entries);
+    struct Type* type = type_new(id, TYPE_NONE, name, span);
+    type->depends_on = array_new(Type_Id, 16);
+    array_push(&unit->types.entries, &type);
+
+    struct Type_Name_To_Id mapping = { .name = name, .id = id };
+    array_push(&unit->types.by_name, &mapping);
+    return true;
+}
+
+void
+lower_fill_function_signature(
+    struct Unit* unit, struct Function* fn, struct Tree_Expression* fn_expr)
+{
+    fn->is_main = string_equals_c_str(fn->name, "main");
     fn->return_type = lower_intern_type_ref(unit, fn_expr->value.function.header.return_type);
     fn->params = array_new(struct Param, 16);
 
@@ -151,65 +255,140 @@ lower_declare_function(
     fn->main_takes_args = fn->is_main && array_length(&fn->params) > 0;
     fn->ast_body = &fn_expr->value.function.body;
     fn->body = nil;
-
-    array_push(&unit->functions.entries, &fn);
-
-    struct Function_Name_To_Id mapping = { .name = name, .id = id };
-    array_push(&unit->functions.by_name, &mapping);
 }
 
-// extract `name = fun (...) ret { ... }` from a top-level statement,
-// or return false if it's not that shape.
-bool
-lower_match_function_decl(
-    struct Tree_Statement* stmt, struct String* out_name, struct Tree_Expression** out_fn_expr)
+void
+lower_fill_type_body(struct Unit* unit, struct Type* type, struct Tree_Type* tree_type)
 {
-    if (stmt->kind != TREE_STATEMENT_EXPRESSION) return false;
-    struct Tree_Expression* expr = stmt->value.expression.inner;
-    if (!expr || expr->kind != TREE_EXPRESSION_BINARY_OPERATION) return false;
-    if (expr->value.binary_operator.operation != BINARY_ASSIGN) return false;
-
-    struct Tree_Expression* lhs = expr->value.binary_operator.left_operand;
-    struct Tree_Expression* rhs = expr->value.binary_operator.right_operand;
-    if (!lhs || lhs->kind != TREE_EXPRESSION_NAME) return false;
-    if (!rhs || rhs->kind != TREE_EXPRESSION_FUNCTION) return false;
-
-    *out_name = lhs->value.name.name;
-    *out_fn_expr = rhs;
-    return true;
+    switch (tree_type->type) {
+    case TREE_TYPE_NAME: {
+        type->kind = TYPE_ALIAS;
+        Type_Id target_id;
+        if (lower_type_lookup_by_name(unit, tree_type->value.name.name, &target_id)) {
+            type->value.alias.target_id = target_id;
+            // aliases always need their target's full definition.
+            array_push(&type->depends_on, &target_id);
+        } else {
+            lower_emit_error(
+                unit, tree_type->span,
+                string_concatenate(
+                    ARG_ASCII, "undefined type '", ARG_STRING, tree_type->value.name.name,
+                    ARG_ASCII, "'", ARG_END));
+        }
+        break;
+    }
+    case TREE_TYPE_STRUCTURE: {
+        type->kind = TYPE_STRUCTURE;
+        type->value.structure.fields = array_new(struct Field, 16);
+        FOR_EACH (struct Tree_Type*, tree_field, tree_type->value.structure.fields) {
+            struct Field f = {
+                .name = tree_field->value_name,
+                .type = lower_intern_type_ref(unit, tree_field),
+            };
+            array_push(&type->value.structure.fields, &f);
+            lower_add_dependency(type, f.type);
+        }
+        break;
+    }
+    case TREE_TYPE_VARIANT: {
+        type->kind = TYPE_VARIANT;
+        type->value.variant.cases = array_new(struct Variant_Case, 16);
+        uint32 next_tag = 0;
+        FOR_EACH (struct Tree_Type*, tree_case, tree_type->value.variant.variants) {
+            struct Variant_Case c = {
+                .name = tree_case->value_name,
+                .tag = next_tag++,
+                .has_payload = tree_case->type != TREE_TYPE_NONE,
+                .payload = { 0 },
+            };
+            if (c.has_payload) {
+                c.payload = lower_intern_type_ref(unit, tree_case);
+                lower_add_dependency(type, c.payload);
+            }
+            array_push(&type->value.variant.cases, &c);
+        }
+        break;
+    }
+    case TREE_TYPE_FUNCTION: {
+        type->kind = TYPE_FUNCTION;
+        struct Tree_Function_Header* header = &tree_type->value.function.header;
+        type->value.function.return_type = lower_intern_type_ref(unit, header->return_type);
+        lower_add_dependency(type, type->value.function.return_type);
+        type->value.function.params = array_new(struct Type_Ref, 16);
+
+        bool variadic = false;
+        FOR_EACH (struct Tree_Type*, param_type, header->parameters_type_and_name) {
+            struct Type_Ref ref = lower_intern_type_ref(unit, param_type);
+            array_push(&type->value.function.params, &ref);
+            lower_add_dependency(type, ref);
+            if (param_type->variadic) variadic = true;
+        }
+        type->value.function.variadic = variadic;
+        break;
+    }
+    case TREE_TYPE_CLASS:
+        lower_emit_error_c(unit, tree_type->span, "unimplemented: class types");
+        break;
+    default:
+        // structural compound forms (array, maybe, tuple, map) at top level
+        // belong to step 10 (structural canonicalization).
+        lower_emit_error_c(unit, tree_type->span, "unimplemented: this top-level type form");
+        break;
+    }
 }
 
-bool
-lower_match_type_decl(struct Tree_Statement* stmt)
+// lowering pass 1, sub-pass a
+// collection of every single declaration of a type of function,
+// alongside with initial registration of any referenced dependencies.
+void
+lower_pass_1_register_shells(struct Unit* unit, struct Tree* tree)
 {
-    if (stmt->kind != TREE_STATEMENT_EXPRESSION) return false;
-    struct Tree_Expression* expr = stmt->value.expression.inner;
-    if (!expr || expr->kind != TREE_EXPRESSION_BINARY_OPERATION) return false;
-    if (expr->value.binary_operator.operation != BINARY_ASSIGN) return false;
+    FOR_EACH (struct Tree_Statement*, stmt, tree->top_level_statements) {
+        struct String name;
 
-    struct Tree_Expression* lhs = expr->value.binary_operator.left_operand;
-    struct Tree_Expression* rhs = expr->value.binary_operator.right_operand;
-    if (!lhs || lhs->kind != TREE_EXPRESSION_NAME) return false;
-    if (!rhs || rhs->kind != TREE_EXPRESSION_TYPE) return false;
+        struct Tree_Expression* fn_expr;
+        if (lower_match_function_decl(stmt, &name, &fn_expr)) {
+            lower_register_function_shell(unit, name, stmt->span);
+            continue;
+        }
 
-    return true;
+        struct Tree_Type* tree_type;
+        if (lower_match_type_decl(stmt, &name, &tree_type)) {
+            lower_register_type_shell(unit, name, stmt->span);
+            continue;
+        }
+    }
 }
 
-// pass 1 walks every top-level statement and collects declarations into
-// the unit's tables. function bodies stay as raw ast for pass 2 to lower.
+// lowering pass 1, sub-pass b
+// walking over all top-level functions and types, fully filling out
+// their definitions.
+// now that sub-pass a has registered all top-level definitions, we can
+// finally build out the type reference dag within the translation unit.
 void
-lower_pass_1(struct Unit* unit, struct Tree* tree)
+lower_pass_1_fill_bodies(struct Unit* unit, struct Tree* tree)
 {
     FOR_EACH (struct Tree_Statement*, stmt, tree->top_level_statements) {
         struct String name;
+
         struct Tree_Expression* fn_expr;
         if (lower_match_function_decl(stmt, &name, &fn_expr)) {
-            lower_declare_function(unit, name, fn_expr, stmt->span);
+            Function_Id id;
+            // TODO: actually go into the body, only the signature for now.
+            if (lower_function_lookup_by_name(unit, name, &id)) {
+                struct Function* fn = *array_at(struct Function*, &unit->functions.entries, id);
+                if (!fn->ast_body) lower_fill_function_signature(unit, fn, fn_expr);
+            }
             continue;
         }
 
-        if (lower_match_type_decl(stmt)) {
-            lower_emit_error_c(unit, stmt->span, "unimplemented: type declarations");
+        struct Tree_Type* tree_type;
+        if (lower_match_type_decl(stmt, &name, &tree_type)) {
+            Type_Id id;
+            if (lower_type_lookup_by_name(unit, name, &id)) {
+                struct Type* type = *array_at(struct Type*, &unit->types.entries, id);
+                if (type->kind == TYPE_NONE) lower_fill_type_body(unit, type, tree_type);
+            }
             continue;
         }
 
@@ -222,6 +401,16 @@ lower_pass_1(struct Unit* unit, struct Tree* tree)
     }
 }
 
+// lowering pass 1
+// collects all top-level definitions into the translation unit's
+// tables and fully maps out the references between them.
+void
+lower_pass_1(struct Unit* unit, struct Tree* tree)
+{
+    lower_pass_1_register_shells(unit, tree);
+    lower_pass_1_fill_bodies(unit, tree);
+}
+
 void
 lower_tree(struct Tree* tree, struct Source_File source, struct Unit* unit)
 {