diff options
| -rw-r--r-- | boot/catboot.c | 2 | ||||
| -rw-r--r-- | boot/lower.c | 186 | ||||
| -rw-r--r-- | boot/visit/ir.c | 135 |
3 files changed, 241 insertions, 82 deletions
diff --git a/boot/catboot.c b/boot/catboot.c index fe879a1..8fdad41 100644 --- a/boot/catboot.c +++ b/boot/catboot.c @@ -133,7 +133,7 @@ debug_lower_pass(struct Source_File source_file) lower_tree(&tree, source_file, &unit); printer(&unit); - return 0; + return unit.had_error ? 1 : 0; } integer diff --git a/boot/lower.c b/boot/lower.c index 1fd1a7e..e8bf8d0 100644 --- a/boot/lower.c +++ b/boot/lower.c @@ -26,6 +26,48 @@ #include "catboot.h" +void +lower_emit_error(struct Unit* unit, struct Span span, struct String message) +{ + struct Diagnostic d = { + .severity = DIAGNOSTIC_ERROR, + .span = span, + .message = message, + }; + array_push(&unit->diagnostics, &d); + unit->had_error = true; +} + +void +lower_emit_error_c(struct Unit* unit, struct Span span, const ascii* message) +{ + lower_emit_error(unit, span, string_from_c_string(message)); +} + +bool +lower_type_lookup_by_name(struct Unit* unit, struct String name, Type_Id* out_id) +{ + FOR_EACH_ARRAY(struct Type_Name_To_Id, mapping, &unit->types.by_name, { + if (string_equals(mapping.name, name)) { + *out_id = mapping.id; + return true; + } + }) + return false; +} + +bool +lower_function_lookup_by_name(struct Unit* unit, struct String name, Function_Id* out_id) +{ + FOR_EACH_ARRAY(struct Function_Name_To_Id, mapping, &unit->functions.by_name, { + if (string_equals(mapping.name, name)) { + *out_id = mapping.id; + return true; + } + }) + return false; +} + Type_Id lower_seed_primitive(struct Unit* unit, const ascii* name) { @@ -40,11 +82,149 @@ 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. +struct Type_Ref +lower_intern_type_ref(struct Unit* unit, struct Tree_Type* tree_type) +{ + struct Type_Ref ref = { + .type_id = unit->types.primitive_void_id, + .mods = array_new(enum Type_Modifier, 4), + }; + + if (!tree_type || tree_type->type == TREE_TYPE_NONE) return ref; + + switch (tree_type->type) { + case TREE_TYPE_NAME: { + Type_Id id; + if (lower_type_lookup_by_name(unit, tree_type->value.name.name, &id)) { + ref.type_id = 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)); + } + 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"); + return ref; + } +} + +void +lower_declare_function( + struct Unit* unit, struct String name, struct Tree_Expression* fn_expr, struct Span span) +{ + Function_Id existing; + if (lower_function_lookup_by_name(unit, name, &existing)) { + lower_emit_error( + unit, span, + string_concatenate( + ARG_ASCII, "duplicate function '", ARG_STRING, name, ARG_ASCII, "'", ARG_END)); + return; + } + + Function_Id id = array_length(&unit->functions.entries); + struct Function* fn = function_new(id, name); + + fn->is_main = string_equals_c_str(name, "main"); + fn->return_type = lower_intern_type_ref(unit, fn_expr->value.function.header.return_type); + fn->params = array_new(struct Param, 16); + + bool variadic = false; + FOR_EACH ( + struct Tree_Type*, param_type, fn_expr->value.function.header.parameters_type_and_name) { + struct Param p = { + .name = param_type->value_name, + .type = lower_intern_type_ref(unit, param_type), + }; + array_push(&fn->params, &p); + if (param_type->variadic) variadic = true; + } + fn->variadic = variadic; + 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) +{ + 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; +} + +bool +lower_match_type_decl(struct Tree_Statement* stmt) +{ + 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; + + return true; +} + +// 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. +void +lower_pass_1(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); + continue; + } + + if (lower_match_type_decl(stmt)) { + lower_emit_error_c(unit, stmt->span, "unimplemented: type declarations"); + continue; + } + + if (stmt->kind == TREE_STATEMENT_PRAGMA) { + lower_emit_error_c(unit, stmt->span, "unimplemented: top-level pragmas"); + continue; + } + + lower_emit_error_c(unit, stmt->span, "unsupported top-level statement"); + } +} + void lower_tree(struct Tree* tree, struct Source_File source, struct Unit* unit) { - // TODO: implement! - (void)tree; (void)source; unit->types.entries = array_new(struct Type*, 256); @@ -67,4 +247,6 @@ lower_tree(struct Tree* tree, struct Source_File source, struct Unit* unit) unit->types.primitive_byte_id = lower_seed_primitive(unit, "byte"); unit->types.primitive_ascii_id = lower_seed_primitive(unit, "ascii"); unit->types.primitive_void_id = lower_seed_primitive(unit, "void"); + + if (tree) lower_pass_1(unit, tree); } diff --git a/boot/visit/ir.c b/boot/visit/ir.c index b2239d5..3880b36 100644 --- a/boot/visit/ir.c +++ b/boot/visit/ir.c @@ -82,12 +82,10 @@ walk_unit(struct Visit* visit, struct Unit* unit) FOR_EACH_ARRAY(struct Function*, function, &unit->functions.entries, { VISIT(visit_function, function); }) - for (uint i = 0; i < array_length(&unit->imports); ++i) { - VISIT(visit_import, array_at(struct Import, &unit->imports, i)); - } - for (uint i = 0; i < array_length(&unit->diagnostics); ++i) { - VISIT(visit_diagnostic, array_at(struct Diagnostic, &unit->diagnostics, i)); - } + FOR_EACH_ARRAY(struct Import, import, &unit->imports, { VISIT(visit_import, &import); }) + FOR_EACH_ARRAY(struct Diagnostic, diagnostic, &unit->diagnostics, { + VISIT(visit_diagnostic, &diagnostic); + }) } void @@ -122,40 +120,33 @@ WALK_LEAF_FUNCTION(walk_type_alias, struct Type*); void walk_type_structure(struct Visit* visit, struct Type* type) { - for (uint i = 0; i < array_length(&type->value.structure.fields); ++i) { - struct Field* field = array_at(struct Field, &type->value.structure.fields, i); - VISIT(visit_type_ref, &field->type); - } + FOR_EACH_ARRAY(struct Field, field, &type->value.structure.fields, { + VISIT(visit_type_ref, &field.type); + }) } void walk_type_variant(struct Visit* visit, struct Type* type) { - for (uint i = 0; i < array_length(&type->value.variant.cases); ++i) { - struct Variant_Case* one_case = - array_at(struct Variant_Case, &type->value.variant.cases, i); - if (one_case->has_payload) VISIT(visit_type_ref, &one_case->payload); - } + FOR_EACH_ARRAY(struct Variant_Case, one_case, &type->value.variant.cases, { + if (one_case.has_payload) VISIT(visit_type_ref, &one_case.payload); + }) } void walk_type_function(struct Visit* visit, struct Type* type) { VISIT(visit_type_ref, &type->value.function.return_type); - for (uint i = 0; i < array_length(&type->value.function.params); ++i) { - struct Type_Ref* param = array_at(struct Type_Ref, &type->value.function.params, i); - VISIT(visit_type_ref, param); - } + FOR_EACH_ARRAY(struct Type_Ref, param, &type->value.function.params, { + VISIT(visit_type_ref, ¶m); + }) } void walk_function(struct Visit* visit, struct Function* function) { VISIT(visit_type_ref, &function->return_type); - for (uint i = 0; i < array_length(&function->params); ++i) { - struct Param* param = array_at(struct Param, &function->params, i); - VISIT(visit_type_ref, ¶m->type); - } + FOR_EACH_ARRAY(struct Param, param, &function->params, { VISIT(visit_type_ref, ¶m.type); }) VISIT_MAYBE(visit_block, function->body); } @@ -226,11 +217,10 @@ walk_statement_expression(struct Visit* visit, struct Statement* stmt) void walk_statement_conditional(struct Visit* visit, struct Statement* stmt) { - for (uint i = 0; i < array_length(&stmt->value.conditional.branches); ++i) { - struct If_Branch* branch = array_at(struct If_Branch, &stmt->value.conditional.branches, i); - VISIT_MAYBE(visit_expression, branch->condition); - VISIT(visit_block, branch->body); - } + FOR_EACH_ARRAY(struct If_Branch, branch, &stmt->value.conditional.branches, { + VISIT_MAYBE(visit_expression, branch.condition); + VISIT(visit_block, branch.body); + }) } void @@ -364,11 +354,9 @@ walk_expression_cast(struct Visit* visit, struct Expression* expr) void walk_expression_construct(struct Visit* visit, struct Expression* expr) { - for (uint i = 0; i < array_length(&expr->value.construct.fields); ++i) { - struct Construct_Field* field = - array_at(struct Construct_Field, &expr->value.construct.fields, i); - VISIT(visit_expression, field->value); - } + FOR_EACH_ARRAY(struct Construct_Field, field, &expr->value.construct.fields, { + VISIT(visit_expression, field.value); + }) } void walk_block(struct Visit* visit, struct Block* block){ FOR_EACH_ARRAY( @@ -514,10 +502,10 @@ printer_visit_unit(struct Visit* visit, struct Unit* unit) PRINT("\n"); printer_indent(p); PRINT("(imports"); - for (uint i = 0; i < array_length(&unit->imports); ++i) { + FOR_EACH_ARRAY(struct Import, import, &unit->imports, { PRINT(" "); - VISIT(visit_import, array_at(struct Import, &unit->imports, i)); - } + VISIT(visit_import, &import); + }) PRINT(")"); } @@ -525,9 +513,7 @@ printer_visit_unit(struct Visit* visit, struct Unit* unit) PRINT("\n"); printer_indent(p); PRINT("(emission_order"); - for (uint i = 0; i < array_length(&unit->type_emission_order); ++i) { - PRINT(" %lu", *array_at(Type_Id, &unit->type_emission_order, i)); - } + FOR_EACH_ARRAY(Type_Id, id, &unit->type_emission_order, { PRINT(" %lu", id); }) PRINT(")"); } @@ -536,11 +522,11 @@ printer_visit_unit(struct Visit* visit, struct Unit* unit) printer_indent(p); PRINT("(diagnostics"); p->indentation_level++; - for (uint i = 0; i < array_length(&unit->diagnostics); ++i) { + FOR_EACH_ARRAY(struct Diagnostic, diagnostic, &unit->diagnostics, { PRINT("\n"); printer_indent(p); - VISIT(visit_diagnostic, array_at(struct Diagnostic, &unit->diagnostics, i)); - } + VISIT(visit_diagnostic, &diagnostic); + }) p->indentation_level--; PRINT(")"); } @@ -555,10 +541,9 @@ printer_visit_type_ref(struct Visit* visit, struct Type_Ref* ref) PRINTER_PREAMBLE PRINT("(ref"); - for (uint i = 0; i < array_length(&ref->mods); ++i) { - enum Type_Modifier mod = *array_at(enum Type_Modifier, &ref->mods, i); + FOR_EACH_ARRAY(enum Type_Modifier, mod, &ref->mods, { if (mod == TYPE_MOD_REFERENCE) PRINT(" &"); - } + }) struct Type* target = printer_type_at(p, ref->type_id); if (target) PRINT(" %s", string_c_str(target->name)); @@ -604,12 +589,11 @@ printer_visit_type_structure(struct Visit* visit, struct Type* type) PRINTER_PREAMBLE PRINT(" structure"); - for (uint i = 0; i < array_length(&type->value.structure.fields); ++i) { - struct Field* field = array_at(struct Field, &type->value.structure.fields, i); - PRINT(" (field %s ", string_c_str(field->name)); - VISIT(visit_type_ref, &field->type); + FOR_EACH_ARRAY(struct Field, field, &type->value.structure.fields, { + PRINT(" (field %s ", string_c_str(field.name)); + VISIT(visit_type_ref, &field.type); PRINT(")"); - } + }) } void @@ -618,16 +602,14 @@ printer_visit_type_variant(struct Visit* visit, struct Type* type) PRINTER_PREAMBLE PRINT(" variant"); - for (uint i = 0; i < array_length(&type->value.variant.cases); ++i) { - struct Variant_Case* one_case = - array_at(struct Variant_Case, &type->value.variant.cases, i); - PRINT(" (case %s tag=%u", string_c_str(one_case->name), one_case->tag); - if (one_case->has_payload) { + FOR_EACH_ARRAY(struct Variant_Case, one_case, &type->value.variant.cases, { + PRINT(" (case %s tag=%u", string_c_str(one_case.name), one_case.tag); + if (one_case.has_payload) { PRINT(" "); - VISIT(visit_type_ref, &one_case->payload); + VISIT(visit_type_ref, &one_case.payload); } PRINT(")"); - } + }) } void @@ -638,12 +620,11 @@ printer_visit_type_function(struct Visit* visit, struct Type* type) PRINT(" function (returns "); VISIT(visit_type_ref, &type->value.function.return_type); PRINT(")"); - for (uint i = 0; i < array_length(&type->value.function.params); ++i) { - struct Type_Ref* param = array_at(struct Type_Ref, &type->value.function.params, i); + FOR_EACH_ARRAY(struct Type_Ref, param, &type->value.function.params, { PRINT(" (param "); - VISIT(visit_type_ref, param); + VISIT(visit_type_ref, ¶m); PRINT(")"); - } + }) if (type->value.function.variadic) PRINT(" variadic"); } @@ -658,12 +639,11 @@ printer_visit_function(struct Visit* visit, struct Function* function) PRINT(" (returns "); VISIT(visit_type_ref, &function->return_type); PRINT(")"); - for (uint i = 0; i < array_length(&function->params); ++i) { - struct Param* param = array_at(struct Param, &function->params, i); - PRINT(" (param %s ", string_c_str(param->name)); - VISIT(visit_type_ref, ¶m->type); + FOR_EACH_ARRAY(struct Param, param, &function->params, { + PRINT(" (param %s ", string_c_str(param.name)); + VISIT(visit_type_ref, ¶m.type); PRINT(")"); - } + }) if (function->variadic) PRINT(" variadic"); if (function->body) { @@ -724,18 +704,17 @@ printer_visit_statement_conditional(struct Visit* visit, struct Statement* stmt) PRINTER_PREAMBLE PRINT("(conditional"); - for (uint i = 0; i < array_length(&stmt->value.conditional.branches); ++i) { - struct If_Branch* branch = array_at(struct If_Branch, &stmt->value.conditional.branches, i); + FOR_EACH_ARRAY(struct If_Branch, branch, &stmt->value.conditional.branches, { PRINT(" "); - if (branch->condition) { + if (branch.condition) { PRINT("(when "); - VISIT(visit_expression, branch->condition); + VISIT(visit_expression, branch.condition); PRINT(") "); } else { PRINT("(else) "); } - VISIT(visit_block, branch->body); - } + VISIT(visit_block, branch.body); + }) PRINT(")"); } @@ -927,16 +906,14 @@ printer_visit_expression_construct(struct Visit* visit, struct Expression* expr) PRINT(" %s", string_c_str(type->name)); else PRINT(" #%lu", expr->value.construct.type_id); - for (uint i = 0; i < array_length(&expr->value.construct.fields); ++i) { - struct Construct_Field* field = - array_at(struct Construct_Field, &expr->value.construct.fields, i); - if (field->name.length > 0) - PRINT(" (field %s ", string_c_str(field->name)); + FOR_EACH_ARRAY(struct Construct_Field, field, &expr->value.construct.fields, { + if (field.name.length > 0) + PRINT(" (field %s ", string_c_str(field.name)); else PRINT(" (positional "); - VISIT(visit_expression, field->value); + VISIT(visit_expression, field.value); PRINT(")"); - } + }) PRINT(")"); } |
