aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/catboot.c2
-rw-r--r--boot/lower.c186
-rw-r--r--boot/visit/ir.c135
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, &param);
+ })
}
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, &param->type);
- }
+ FOR_EACH_ARRAY(struct Param, param, &function->params, { VISIT(visit_type_ref, &param.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, &param);
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, &param->type);
+ FOR_EACH_ARRAY(struct Param, param, &function->params, {
+ PRINT(" (param %s ", string_c_str(param.name));
+ VISIT(visit_type_ref, &param.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(")");
}