diff options
Diffstat (limited to 'boot/lower.c')
| -rw-r--r-- | boot/lower.c | 186 |
1 files changed, 184 insertions, 2 deletions
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); } |
