diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ast.c | 494 | ||||
-rw-r--r-- | src/bytecode/bytecode.c | 36 | ||||
-rw-r--r-- | src/compiler.c | 34 | ||||
-rw-r--r-- | src/parser.c | 232 | ||||
-rw-r--r-- | src/program.c | 44 | ||||
-rw-r--r-- | src/ssa/ssa.c | 239 | ||||
-rw-r--r-- | src/std/arena_allocator.c | 9 | ||||
-rw-r--r-- | src/std/buffer.c | 1 | ||||
-rw-r--r-- | src/std/buffer_view.c | 5 | ||||
-rw-r--r-- | src/tokenizer.c | 8 |
10 files changed, 824 insertions, 278 deletions
@@ -15,12 +15,118 @@ throw(return_if_result); \ } while(0) +static void variable_type_resolve(VariableType *self, AstCompilerContext *context, AstResolvedType *resolved_type); static void ast_resolve(Ast *self, AstCompilerContext *context); +static BufferView variable_type_get_name(VariableType *self); + +static void scope_named_object_init(ScopeNamedObject *self) { + self->type = NAMED_OBJECT_NONE; + self->value.lhs_expr = NULL; + self->resolve_data = NULL; +} + +/* TODO: Remove these? */ +#if 0 +static bool scope_named_object_equals(ScopeNamedObject *self, ScopeNamedObject *other) { + /* This should be fine, without checking ScopeNamedObject type, since they are only equal if the types are equal as well */ + return self->value.lhs_expr == other->value.lhs_expr; +} + +static BufferView scope_named_object_get_type_name(ScopeNamedObject *self) { + BufferView result; + switch(self->type) { + case NAMED_OBJECT_NONE: + result = create_buffer_view("", 0); + break; + case NAMED_OBJECT_LHS_EXPR: + result = variable_type_get_name(&self->value.lhs_expr->type); + break; + case NAMED_OBJECT_FUNC_PARAM: + result = variable_type_get_name(&self->value.func_param->type); + break; + } + return result; +} +#endif + +static BufferView ast_resolved_type_get_name(AstResolvedType *self) { + BufferView result; + switch(self->type) { + case RESOLVED_TYPE_NONE: + result = create_buffer_view("", 0); + break; + case RESOLVED_TYPE_LHS_EXPR: + result = variable_type_get_name(&self->value.lhs_expr->type); + break; + case RESOLVED_TYPE_FUNC_SIG: { + if(!self->value.func_sig->func_decl || !self->value.func_sig->func_decl->lhs_expr) { + /* + TODO: Use function signature string from the source file, which will also help with error reference. + Currently this would point to an invalid location in the source file + */ + result = create_buffer_view("fn()", 0); + break; + } + result = self->value.func_sig->func_decl->lhs_expr->var_name; + break; + } + } + return result; +} + +static void ast_resolved_type_init(AstResolvedType *self) { + self->value.lhs_expr = NULL; + self->type = RESOLVED_TYPE_NONE; +} + +static bool ast_resolved_type_equals(AstResolvedType *self, AstResolvedType *other) { + /* This should be fine, without checking AstResolvedType type, since they are only equal if the types are equal as well */ + return self->value.lhs_expr == other->value.lhs_expr; +} + +static AstResolvedType lhs_expr_get_resolved_type(LhsExpr *self, AstCompilerContext *context) { + AstResolvedType result; + variable_type_resolve(&self->type, context, &result); + if(result.type != RESOLVED_TYPE_NONE) + return result; + + assert(self->rhs_expr); + ast_resolve(self->rhs_expr, context); + return self->rhs_expr->resolve_data.type; +} + +/* TODO: Detect recursive dependency? is it even possible for function parameters? (it would normally be a recursive dependency on the function declaration) */ +static void function_parameter_resolve(FunctionParameter *self, AstCompilerContext *context) { + if(self->resolve_data.status == AST_RESOLVED) + return; + self->resolve_data.status = AST_RESOLVING; + variable_type_resolve(&self->type, context, &self->resolve_data.type); + self->resolve_data.status = AST_RESOLVED; +} + +static AstResolvedType scope_named_object_get_resolved_type(ScopeNamedObject *self, AstCompilerContext *context) { + AstResolvedType result; + switch(self->type) { + case NAMED_OBJECT_NONE: + /* Type not resolved */ + assert(bool_false); + ast_resolved_type_init(&result); + return result; + case NAMED_OBJECT_LHS_EXPR: + result = lhs_expr_get_resolved_type(self->value.lhs_expr, context); + break; + case NAMED_OBJECT_FUNC_PARAM: + function_parameter_resolve(self->value.func_param, context); + result = self->value.func_param->resolve_data.type; + break; + } + return result; +} static void resolve_data_init(AstResolveData *self) { self->status = AST_NOT_RESOLVED; - self->parser = NULL; - self->type = NULL; + ast_resolved_type_init(&self->type); + self->ssa_reg = 0; } int ast_create(ArenaAllocator *allocator, void *value, AstType type, Ast **result) { @@ -28,7 +134,7 @@ int ast_create(ArenaAllocator *allocator, void *value, AstType type, Ast **resul (*result)->value.data = value; (*result)->type = type; resolve_data_init(&(*result)->resolve_data); - (*result)->ssa_reg = 0; + (*result)->parser = NULL; return 0; } @@ -47,6 +153,7 @@ BufferView ast_get_name(Ast *self) { case AST_BINOP: case AST_IF_STATEMENT: case AST_WHILE_STATEMENT: + case AST_RETURN: name = create_buffer_view_null(); break; case AST_NUMBER: @@ -75,12 +182,47 @@ static BufferView ast_get_code_reference(Ast *self) { return ast_get_name(self); } -void function_signature_init(FunctionSignature *self) { - self->params = 0; +int function_signature_init(FunctionSignature *self, ArenaAllocator *allocator) { self->resolved = bool_false; + self->func_decl = NULL; + return_if_error(buffer_init(&self->parameters, allocator)); + return buffer_init(&self->return_types, allocator); +} + +static FunctionParameter* function_signature_get_parameter_by_name(FunctionSignature *self, BufferView name) { + FunctionParameter *param, *param_end; + param = buffer_begin(&self->parameters); + param_end = buffer_end(&self->parameters); + for(; param != param_end; ++param) { + if(buffer_view_equals(&name, ¶m->name)) + return param; + } + return NULL; +} + +int function_signature_add_parameter(FunctionSignature *self, const FunctionParameter *new_param) { + const FunctionParameter *existing_param = function_signature_get_parameter_by_name(self, new_param->name); + if(existing_param) + return AST_ERR_DEF_DUP; + return buffer_append(&self->parameters, new_param, sizeof(FunctionParameter)); +} + +int function_signature_add_return_type(FunctionSignature *self, const VariableType *var_type) { + FunctionReturnType return_type; + return_type.type = *var_type; + ast_resolved_type_init(&return_type.resolved_type); + return buffer_append(&self->return_types, &return_type, sizeof(return_type)); +} + +void function_parameter_init(FunctionParameter *self) { + self->name = create_buffer_view_null(); + self->type.type = VARIABLE_TYPE_NONE; + self->type.value.variable = NULL; + resolve_data_init(&self->resolve_data); } int funcdecl_init(FunctionDecl *self, FunctionSignature *signature, Scope *parent, ArenaAllocator *allocator) { + self->lhs_expr = NULL; self->signature = signature; self->ssa_func_index = 0; return scope_init(&self->body, parent, allocator); @@ -102,9 +244,9 @@ LhsExpr* structdecl_get_field_by_name(StructDecl *self, BufferView field_name) { return result->value.lhs_expr; } -void structfield_init(StructField *self, BufferView name, BufferView type_name) { +void structfield_init(StructField *self, BufferView name, VariableType *type) { self->name = name; - variable_init(&self->type, type_name); + self->type = *type; } void lhsexpr_init(LhsExpr *self, DeclFlag decl_flag, BufferView var_name) { @@ -140,7 +282,7 @@ void number_init(Number *self, i64 value, bool is_integer, BufferView code_ref) void variable_init(Variable *self, BufferView name) { self->name = name; - self->resolved_var = NULL; + scope_named_object_init(&self->resolved_var); } void binop_init(Binop *self) { @@ -167,11 +309,16 @@ int while_statement_init(WhileStatement *self, Scope *parent, ArenaAllocator *al return scope_init(&self->body, parent, allocator); } +void return_expr_init(ReturnExpr *self, Ast *rhs_expr) { + self->rhs_expr = rhs_expr; +} + int scope_init(Scope *self, Scope *parent, ArenaAllocator *allocator) { return_if_error(buffer_init(&self->ast_objects, allocator)); return_if_error(hash_map_init(&self->named_objects, allocator, sizeof(Ast*), hash_map_compare_string, amal_hash_string)); self->parent = parent; self->parser = NULL; + self->function_signature = NULL; return 0; } @@ -192,7 +339,6 @@ int scope_add_child(Scope *self, Ast *child) { Ast *existing_child; bool child_already_exists; - /* TODO: Implement for parameter */ if(child->type == AST_LHS) { BufferView var_name; var_name = child->value.lhs_expr->var_name; @@ -213,14 +359,16 @@ int scope_add_child(Scope *self, Ast *child) { void scope_resolve(Scope *self, AstCompilerContext *context) { Ast **ast; Ast **ast_end; + Scope *prev_scope; ast = buffer_begin(&self->ast_objects); ast_end = buffer_end(&self->ast_objects); + prev_scope = context->scope; context->scope = self; for(; ast != ast_end; ++ast) { ast_resolve(*ast, context); } - context->scope = self->parent; + context->scope = prev_scope; } static Parser* scope_get_parser(Scope *scope) { @@ -247,78 +395,114 @@ static void parser_print_error(Parser *parser, const char *ref, const char *fmt, va_end(args); } -static Ast* __scope_get_resolved_variable(Scope *self, Scope *start, AstCompilerContext *context, BufferView name) { - Ast *result; +static void __scope_get_resolved_variable(Scope *self, Scope *start, AstCompilerContext *context, BufferView name, ScopeNamedObject *result) { + Ast *ast_result; bool exists; Scope *prev_scope; assert(self); - exists = hash_map_get(&self->named_objects, name, &result); + exists = hash_map_get(&self->named_objects, name, &ast_result); if(!exists) { - Parser *parser; - if(self->parent) - return __scope_get_resolved_variable(self->parent, start, context, name); + if(self->function_signature) { + FunctionParameter *func_param; + func_param = function_signature_get_parameter_by_name(self->function_signature, name); + if(func_param) { + prev_scope = context->scope; + context->scope = self; + function_parameter_resolve(func_param, context); + context->scope = prev_scope; + + result->type = NAMED_OBJECT_FUNC_PARAM; + result->value.func_param = func_param; + result->resolve_data = &func_param->resolve_data; + return; + } + } - parser = scope_get_parser(start); - parser_print_error(parser, name.data, "Undefined reference to variable \"%.*s\"", name.size, name.data); - throw(AST_ERR); + if(self->parent) { + __scope_get_resolved_variable(self->parent, start, context, name, result); + return; + } + + { + Parser *parser; + parser = scope_get_parser(start); + parser_print_error(parser, name.data, "Undefined reference to variable \"%.*s\"", name.size, name.data); + throw(AST_ERR); + } } /* - Need to change scope here because we are changing the visible scope - and the ast object may be in another scope than the current - resolving ast. + Need to change scope here because we are changing the visible scope + and the ast object may be in another scope than the current + resolving ast. */ prev_scope = context->scope; context->scope = self; - ast_resolve(result, context); + ast_resolve(ast_result, context); context->scope = prev_scope; - assert(result->type == AST_LHS); - return result; + assert(ast_result->type == AST_LHS); + result->type = NAMED_OBJECT_LHS_EXPR; + result->value.lhs_expr = ast_result->value.lhs_expr; + result->resolve_data = &ast_result->resolve_data; } -static Ast* scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name) { - return __scope_get_resolved_variable(self, self, context, name); +static void scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name, ScopeNamedObject *result) { + __scope_get_resolved_variable(self, self, context, name, result); +} + +static void function_return_type_resolve(FunctionReturnType *self, AstCompilerContext *context) { + variable_type_resolve(&self->type, context, &self->resolved_type); } static void function_signature_resolve(FunctionSignature *self, AstCompilerContext *context) { - /* TODO: Implement */ if(self->resolved) return; - self->resolved = bool_true; - (void)self; - (void)context; -} -static void variable_resolve(Variable *self, AstCompilerContext *context, AstResolveData *resolve_data) { - if(!self->resolved_var) - self->resolved_var = scope_get_resolved_variable(context->scope, context, self->name); - resolve_data->type = self->resolved_var->resolve_data.type; + { + FunctionParameter *param, *param_end; + param = buffer_begin(&self->parameters); + param_end = buffer_end(&self->parameters); + for(; param != param_end; ++param) { + function_parameter_resolve(param, context); + } + } + { + FunctionReturnType *return_type, *return_type_end; + return_type = buffer_begin(&self->return_types); + return_type_end = buffer_end(&self->return_types); + for(; return_type != return_type_end; ++return_type) { + function_return_type_resolve(return_type, context); + } + } + + self->resolved = bool_true; } -static LhsExpr* variable_get_resolved_type(Variable *self) { - assert(self->resolved_var); - return self->resolved_var->value.lhs_expr; +static void variable_resolve(Variable *self, AstCompilerContext *context, AstResolvedType *resolved_type) { + if(self->resolved_var.type == NAMED_OBJECT_NONE) + scope_get_resolved_variable(context->scope, context, self->name, &self->resolved_var); + *resolved_type = scope_named_object_get_resolved_type(&self->resolved_var, context); } -static void variable_type_resolve(LhsExpr *self, AstCompilerContext *context, AstResolveData *resolve_data) { - VariableType *lhs_expr_type; - lhs_expr_type = &self->type; - switch(lhs_expr_type->type) { +void variable_type_resolve(VariableType *self, AstCompilerContext *context, AstResolvedType *resolved_type) { + switch(self->type) { case VARIABLE_TYPE_NONE: + ast_resolved_type_init(resolved_type); return; case VARIABLE_TYPE_VARIABLE: - variable_resolve(lhs_expr_type->value.variable, context, resolve_data); + variable_resolve(self->value.variable, context, resolved_type); break; case VARIABLE_TYPE_SIGNATURE: - function_signature_resolve(lhs_expr_type->value.signature, context); - resolve_data->type = self; + function_signature_resolve(self->value.signature, context); + resolved_type->type = RESOLVED_TYPE_FUNC_SIG; + resolved_type->value.func_sig = self->value.signature; break; } } -static BufferView variable_type_get_name(VariableType *self) { +BufferView variable_type_get_name(VariableType *self) { BufferView result; switch(self->type) { case VARIABLE_TYPE_NONE: @@ -337,14 +521,14 @@ static BufferView variable_type_get_name(VariableType *self) { return result; } -static LhsExpr* lhsexpr_resolve_rhs(LhsExpr *self, AstCompilerContext *context) { - LhsExpr *rhs_resolved_type; +static void lhsexpr_resolve_rhs(LhsExpr *self, AstCompilerContext *context, AstResolvedType *result) { ast_resolve(self->rhs_expr, context); - if(ast_is_decl(self->rhs_expr)) - rhs_resolved_type = self; - else - rhs_resolved_type = self->rhs_expr->resolve_data.type; - return rhs_resolved_type; + if(ast_is_decl(self->rhs_expr)) { + result->type = RESOLVED_TYPE_LHS_EXPR; + result->value.lhs_expr = self; + } else { + *result = self->rhs_expr->resolve_data.type; + } } static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) { @@ -353,7 +537,7 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) { assert(ast->type == AST_LHS); self = ast->value.lhs_expr; - variable_type_resolve(self, context, &ast->resolve_data); + variable_type_resolve(&self->type, context, &ast->resolve_data.type); /* TODO: When parameters and return types are implemented, AST_RESOLVE_END should be set after @@ -361,7 +545,7 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) { be allowed but recursive function calls still require parameters and return types to be known. */ if(self->rhs_expr) { - LhsExpr *rhs_resolve_type; + AstResolvedType rhs_resolve_type; if(self->rhs_expr->type == AST_FUNCTION_DECL) { /* The function declaration itself always resolves the signature, but we also do it here because we @@ -371,17 +555,19 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) { function_signature_resolve(self->rhs_expr->value.func_decl->signature, context); ast->resolve_data.status = AST_RESOLVED; /* - If rhs is a function declaration then there is no need to wait until it has been resolved before setting the type as the type - is @self (the lhs). We still need to continue after this, so rhs can be resolved. + If rhs is a function declaration then there is no need to wait until it has been resolved before setting the type. + We still need to continue after this, so rhs can be resolved. */ - if(!ast->resolve_data.type) - ast->resolve_data.type = self; + if(ast->resolve_data.type.type == RESOLVED_TYPE_NONE) { + ast->resolve_data.type.type = RESOLVED_TYPE_FUNC_SIG; + ast->resolve_data.type.value.func_sig = self->rhs_expr->value.func_decl->signature; + } } - rhs_resolve_type = lhsexpr_resolve_rhs(self, context); + lhsexpr_resolve_rhs(self, context, &rhs_resolve_type); /* TODO: Add casting */ - if(ast->resolve_data.type && ast->resolve_data.type != rhs_resolve_type) { + if(ast->resolve_data.type.type == RESOLVED_TYPE_LHS_EXPR && !ast_resolved_type_equals(&ast->resolve_data.type, &rhs_resolve_type)) { Parser *parser; parser = scope_get_parser(context->scope); /* @@ -413,23 +599,29 @@ static LhsExpr* binop_get_lhs_expr(Binop *self) { static void assignmentexpr_resolve(Ast *ast, AstCompilerContext *context) { AssignmentExpr *self; - LhsExpr *lhs_source; + bool is_lhs_const; + is_lhs_const = bool_false; assert(ast->type == AST_ASSIGN); self = ast->value.assign_expr; - lhs_source = NULL; ast_resolve(self->lhs_expr, context); ast_resolve(self->rhs_expr, context); - if(self->lhs_expr->type == AST_VARIABLE) - lhs_source = variable_get_resolved_type(self->lhs_expr->value.variable); - else if(self->lhs_expr->type == AST_BINOP) - lhs_source = binop_get_lhs_expr(self->lhs_expr->value.binop); + if(self->lhs_expr->type == AST_VARIABLE) { + /* TODO: Allow non-const function param */ + const ScopeNamedObject *resolved_var = &self->lhs_expr->value.variable->resolved_var; + if(resolved_var->type == NAMED_OBJECT_FUNC_PARAM || LHS_EXPR_IS_CONST(resolved_var->value.lhs_expr)) + is_lhs_const = bool_true; + } else if(self->lhs_expr->type == AST_BINOP) { + LhsExpr *lhs_expr; + lhs_expr = binop_get_lhs_expr(self->lhs_expr->value.binop); + is_lhs_const = LHS_EXPR_IS_CONST(lhs_expr); + } /* This also covers extern variables, since extern variables are always const */ /* TODO: var.field type expressions should also be checked */ - if(lhs_source && LHS_EXPR_IS_CONST(lhs_source)) { + if(is_lhs_const) { Parser *parser; parser = scope_get_parser(context->scope); parser_print_error(parser, ast_get_code_reference(self->lhs_expr).data, "Can't assign to a const value"); @@ -437,14 +629,14 @@ static void assignmentexpr_resolve(Ast *ast, AstCompilerContext *context) { } /* TODO: Add casting */ - if(self->lhs_expr->resolve_data.type != self->rhs_expr->resolve_data.type) { + if(!ast_resolved_type_equals(&self->lhs_expr->resolve_data.type, &self->rhs_expr->resolve_data.type)) { Parser *parser; BufferView rhs_type_name; BufferView lhs_type_name; parser = scope_get_parser(context->scope); - rhs_type_name = variable_type_get_name(&self->rhs_expr->resolve_data.type->type); - lhs_type_name = variable_type_get_name(&self->lhs_expr->resolve_data.type->type); + rhs_type_name = ast_resolved_type_get_name(&self->rhs_expr->resolve_data.type); + lhs_type_name = ast_resolved_type_get_name(&self->lhs_expr->resolve_data.type); /* TODO: Instead of using self->var_name, using type name. This cant be done right now because type can be function signature. @@ -462,39 +654,54 @@ static void import_resolve(Ast *ast, AstCompilerContext *context) { assert(ast->type == AST_IMPORT); (void)context; self = ast->value.import; - ast->resolve_data.type = &self->file_scope->parser->file_decl; - assert(ast->resolve_data.type); + ast->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR; + ast->resolve_data.type.value.lhs_expr = &self->file_scope->parser->file_decl; } static Scope* lhsexpr_get_scope(LhsExpr *self) { AstValue value; - if(self->rhs_expr) { - value = self->rhs_expr->value; - switch(self->rhs_expr->type) { - case AST_FUNCTION_DECL: - return &value.func_decl->body; - case AST_STRUCT_DECL: - return &value.struct_decl->body; - case AST_IMPORT: - return &value.import->file_scope->parser->struct_decl.body; - default: - break; - } + value = self->rhs_expr->value; + switch(self->rhs_expr->type) { + case AST_FUNCTION_DECL: + return &value.func_decl->body; + case AST_STRUCT_DECL: + return &value.struct_decl->body; + case AST_IMPORT: + return &value.import->file_scope->parser->struct_decl.body; + default: + break; } assert(bool_false && "Expected lhsexpr_get_scope to only be called for non-extern function declaration, struct declaration and import"); return NULL; } +static Scope* ast_resolved_type_get_scope(AstResolvedType *self) { + switch(self->type) { + case RESOLVED_TYPE_NONE: + assert(bool_false && "Expected ast_resolved_type_get_scope to only be called for a resolved object"); + return NULL; + case RESOLVED_TYPE_LHS_EXPR: + return lhsexpr_get_scope(self->value.lhs_expr); + case RESOLVED_TYPE_FUNC_SIG: + assert(self->value.func_sig->func_decl); + return &self->value.func_sig->func_decl->body; + } + return NULL; +} + /* @self has to have a resolved type before calling this */ static Parser* get_resolved_type_parser(Ast *self) { - assert(self->resolve_data.type); - return scope_get_parser(lhsexpr_get_scope(self->resolve_data.type)); + assert(self->resolve_data.type.type == RESOLVED_TYPE_LHS_EXPR && "Expected get_resolved_type_parser to only be called for non-extern function declaration, struct declaration and import"); + return scope_get_parser(lhsexpr_get_scope(self->resolve_data.type.value.lhs_expr)); } -static void funcdecl_resolve(FunctionDecl *self, AstCompilerContext *context) { - /* TODO: Implement parameters and return types */ - function_signature_resolve(self->signature, context); - scope_resolve(&self->body, context); +static void funcdecl_resolve(Ast *self, AstCompilerContext *context) { + FunctionDecl *func_decl; + func_decl = self->value.func_decl; + function_signature_resolve(func_decl->signature, context); + scope_resolve(&func_decl->body, context); + self->resolve_data.type.type = RESOLVED_TYPE_FUNC_SIG; + self->resolve_data.type.value.func_sig = self->value.func_decl->signature; } /* @@ -502,7 +709,10 @@ static void funcdecl_resolve(FunctionDecl *self, AstCompilerContext *context) { Meaning the resolve status wont be set to solved but the resolve type will be set. */ bool resolved_type_is_func_decl(Ast *self) { - const LhsExpr *resolved_type = self->resolve_data.type; + LhsExpr *resolved_type; + if(self->resolve_data.type.type != RESOLVED_TYPE_LHS_EXPR) + return bool_false; + resolved_type = self->resolve_data.type.value.lhs_expr; return (resolved_type->rhs_expr && resolved_type->rhs_expr->type == AST_FUNCTION_DECL) || resolved_type->type.type == VARIABLE_TYPE_SIGNATURE; } @@ -513,16 +723,16 @@ static void funccall_resolve(Ast *self, AstCompilerContext *context) { Ast **ast_end; func_call = self->value.func_call; - variable_resolve(&func_call->func, context, &self->resolve_data); + variable_resolve(&func_call->func, context, &self->resolve_data.type); /* Attemping to use call syntax (variable_name ( ) ) with a variable that is not a function */ - if(!resolved_type_is_func_decl(self)) { + if(self->resolve_data.type.type != RESOLVED_TYPE_FUNC_SIG) { Parser *caller_parser; Parser *callee_parser; BufferView callee_code_ref; caller_parser = scope_get_parser(context->scope); callee_parser = get_resolved_type_parser(self); - callee_code_ref = self->resolve_data.type->var_name; + callee_code_ref = ast_resolved_type_get_name(&self->resolve_data.type); parser_print_error(caller_parser, func_call->func.name.data, "\"%.*s\" is not a function. Only functions can be called", func_call->func.name.size, func_call->func.name.data); @@ -549,11 +759,14 @@ static void structfield_resolve(Ast *self, AstCompilerContext *context) { /* TODO: Implement */ StructField *struct_field; struct_field = self->value.struct_field; - variable_resolve(&struct_field->type, context, &self->resolve_data); + variable_type_resolve(&struct_field->type, context, &self->resolve_data.type); } static bool is_struct_decl(Ast *self) { - const LhsExpr *resolved_type = self->resolve_data.type; + LhsExpr *resolved_type; + if(self->resolve_data.type.type != RESOLVED_TYPE_LHS_EXPR) + return bool_false; + resolved_type = self->resolve_data.type.value.lhs_expr; assert(self->resolve_data.status == AST_RESOLVED); return resolved_type->rhs_expr && resolved_type->rhs_expr->type == AST_STRUCT_DECL; } @@ -565,6 +778,7 @@ static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) { Parser *callee_parser; BufferView caller_code_ref; BufferView callee_code_ref; + ScopeNamedObject rhs_resolved_var; assert(ast->type == AST_BINOP); self = ast->value.binop; @@ -578,12 +792,13 @@ static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) { throw(AST_ERR); } - lhs_scope = lhsexpr_get_scope(self->lhs->resolve_data.type); - self->rhs->resolve_data.type = scope_get_resolved_variable(lhs_scope, context, self->rhs->value.variable->name)->resolve_data.type; + lhs_scope = ast_resolved_type_get_scope(&self->lhs->resolve_data.type); + scope_get_resolved_variable(lhs_scope, context, self->rhs->value.variable->name, &rhs_resolved_var); + self->rhs->resolve_data.type = scope_named_object_get_resolved_type(&rhs_resolved_var, context); callee_parser = get_resolved_type_parser(self->rhs); caller_code_ref = ast_get_code_reference(self->rhs); - callee_code_ref = self->rhs->resolve_data.type->var_name; + callee_code_ref = ast_resolved_type_get_name(&self->rhs->resolve_data.type); if(!is_struct_decl(self->lhs)) { parser_print_error(caller_parser, caller_code_ref.data, "Can only access field of structs"); @@ -593,7 +808,7 @@ static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) { throw(AST_ERR); } - if(!LHS_EXPR_IS_PUB(self->rhs->resolve_data.type)) { + if(self->rhs->resolve_data.type.type != RESOLVED_TYPE_LHS_EXPR || !LHS_EXPR_IS_PUB(self->rhs->resolve_data.type.value.lhs_expr)) { parser_print_error(caller_parser, caller_code_ref.data, "Can't access non-public field \"%.*s\"", caller_code_ref.size, caller_code_ref.data); /* TODO: use tokenizer_print_note, once it has been added */ /* TODO: Print type */ @@ -612,7 +827,7 @@ static void binop_resolve(Ast *ast, AstCompilerContext *context) { /* Only function call has extra data that needs to be resolved (args) */ if(self->rhs->type == AST_FUNCTION_CALL) { Scope *prev_scope = context->scope; - context->scope = lhsexpr_get_scope(self->lhs->resolve_data.type); + context->scope = ast_resolved_type_get_scope(&self->lhs->resolve_data.type); ast_resolve(self->rhs, context); context->scope = prev_scope; } @@ -621,35 +836,43 @@ static void binop_resolve(Ast *ast, AstCompilerContext *context) { } else { ast_resolve(self->rhs, context); /* TODO: Convert types that can be safely converted */ - assert(self->lhs->resolve_data.type); - assert(self->rhs->resolve_data.type); - if(self->rhs->resolve_data.type != self->lhs->resolve_data.type) { + assert(self->lhs->resolve_data.type.type != RESOLVED_TYPE_NONE); + assert(self->rhs->resolve_data.type.type != RESOLVED_TYPE_NONE); + if(!ast_resolved_type_equals(&self->rhs->resolve_data.type, &self->lhs->resolve_data.type)) { /* TODO: For this first error, only print the line without a reference to code. This requires change in tokenizer_print_error to be able to take a line as reference. TODO: Use note for the additional information instead of error. */ + BufferView lhs_type_name, rhs_type_name; Parser *parser; + + lhs_type_name = ast_resolved_type_get_name(&self->lhs->resolve_data.type); + rhs_type_name = ast_resolved_type_get_name(&self->rhs->resolve_data.type); parser = scope_get_parser(context->scope); parser_print_error(parser, ast_get_code_reference(self->rhs).data, "Can't cast type \"%.*s\" to type \"%.*s\"", - self->rhs->resolve_data.type->var_name.size, self->rhs->resolve_data.type->var_name.data, - self->lhs->resolve_data.type->var_name.size, self->lhs->resolve_data.type->var_name.data); + rhs_type_name.size, rhs_type_name.data, + lhs_type_name.size, lhs_type_name.data); parser_print_error(parser, ast_get_code_reference(self->lhs).data, "Left-hand side is of type %.*s", - self->lhs->resolve_data.type->var_name.size, self->lhs->resolve_data.type->var_name.data); + lhs_type_name.size, lhs_type_name.data); parser_print_error(parser, ast_get_code_reference(self->rhs).data, "Right-hand side is of type %.*s", - self->rhs->resolve_data.type->var_name.size, self->rhs->resolve_data.type->var_name.data); + rhs_type_name.size, rhs_type_name.data); throw(AST_ERR); - } else if(!is_arithmetic_type(self->lhs->resolve_data.type, context->compiler)) { /* TODO: Optimize this? store arithmetic type in the LhsExpr itself? */ + /* TODO: Optimize this? store arithmetic type in the LhsExpr itself? */ + } else if(self->lhs->resolve_data.type.type != RESOLVED_TYPE_LHS_EXPR || !is_arithmetic_type(self->lhs->resolve_data.type.value.lhs_expr, context->compiler)) { /* TODO: Point the error at the binop instead of LHS */ + BufferView lhs_type_name; Parser *parser; + + lhs_type_name = ast_resolved_type_get_name(&self->lhs->resolve_data.type); parser = scope_get_parser(context->scope); parser_print_error(parser, ast_get_code_reference(self->lhs).data, "Arithmetic operation can only be performed with the types i8, u8, i16, u16, i32, u32, i64, u64, isize and usize", - self->lhs->resolve_data.type->var_name.size, self->lhs->resolve_data.type->var_name.data); + lhs_type_name.size, lhs_type_name.data); throw(AST_ERR); } ast->resolve_data.type = self->lhs->resolve_data.type; @@ -678,23 +901,36 @@ static void while_statement_resolve(WhileStatement *while_stmt, AstCompilerConte scope_resolve(&while_stmt->body, context); } +static void return_expr_resolve(ReturnExpr *self, AstCompilerContext *context) { + ast_resolve(self->rhs_expr, context); +} + void ast_resolve(Ast *self, AstCompilerContext *context) { assert(self); assert(context->parser); /* - TODO: Move these to the types that need checks for recursive dependency (function declaration, struct declaration) - For function declaration, it should be marked as resolved when the signature has been resolved - instead of the whole function declaration including the body - because the body can have function call that calls functions that are resolving - or even recursive function call, which should be allowed. + TODO: Move these to the types that need checks for recursive dependency (function declaration, struct declaration) + For function declaration, it should be marked as resolved when the signature has been resolved + instead of the whole function declaration including the body + because the body can have function call that calls functions that are resolving + or even recursive function call, which should be allowed. */ /* - This check is outside lhs_expr mutex for optimization purpose as most times there wont be - a race in multiple threads to resolve an AST expression. + This check is outside lhs_expr mutex for optimization purpose as most times there wont be + a race in multiple threads to resolve an AST expression. */ if(self->resolve_data.status == AST_RESOLVED) { return; - } else if(self->resolve_data.status == AST_RESOLVING && self->resolve_data.parser == context->parser) { + /* + NOTE!!!: There is a data race here and it's fine! + A recursive dependency can be missed here in one thread, but it's fine because that + can only happen if another thread is also resolving the same ast, in which case that thread will finally + report the recursive dependency. Note that this can mean that different files can report the + recursive dependency everytime you compile code with recursive dependency. However this can happen + even without a data race since files are compile in parallel so one file might resolve the ast with the issue + before another file has done it, and it can be different everytime the files are compiled. + */ + } else if(self->resolve_data.status == AST_RESOLVING && self->parser == context->parser) { Parser *parser; parser = scope_get_parser(context->scope); parser_print_error(parser, ast_get_code_reference(self).data, "Found recursive dependency"); @@ -702,20 +938,21 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { } self->resolve_data.status = AST_RESOLVING; - self->resolve_data.parser = context->parser; + self->parser = context->parser; switch(self->type) { case AST_NUMBER: { Number *number; number = self->value.number; /* TODO: Support other number types */ + self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR; if(number->is_integer) - self->resolve_data.type = (LhsExpr*)context->compiler->default_types.i64; + self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.i64; else - self->resolve_data.type = (LhsExpr*)context->compiler->default_types.f64; + self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.f64; break; } case AST_FUNCTION_DECL: - funcdecl_resolve(self->value.func_decl, context); + funcdecl_resolve(self, context); break; case AST_FUNCTION_CALL: funccall_resolve(self, context); @@ -738,10 +975,11 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { break; case AST_STRING: /* TODO: Convert special combinations. For example \n to newline */ - self->resolve_data.type = (LhsExpr*)context->compiler->default_types.str; + self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR; + self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.str; break; case AST_VARIABLE: - variable_resolve(self->value.variable, context, &self->resolve_data); + variable_resolve(self->value.variable, context, &self->resolve_data.type); break; case AST_BINOP: binop_resolve(self, context); @@ -752,7 +990,11 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { case AST_WHILE_STATEMENT: while_statement_resolve(self->value.while_stmt, context); break; + case AST_RETURN: + return_expr_resolve(self->value.return_expr, context); + break; } /* TODO: See comment at the top of this function */ + /*assert(self->resolve_data.type.type != RESOLVED_TYPE_NONE);*/ self->resolve_data.status = AST_RESOLVED; } diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c index 70a5cfa..8a41900 100644 --- a/src/bytecode/bytecode.c +++ b/src/bytecode/bytecode.c @@ -42,9 +42,10 @@ static CHECK_RESULT usize ssa_extract_func_start(u8 *instruction_data, SsaInsFun } static CHECK_RESULT usize ssa_extract_func_call(u8 *instruction_data, SsaInsFuncCall *result) { - am_memcpy(&result->result, instruction_data, sizeof(result->result)); - am_memcpy(&result->func_decl, instruction_data + sizeof(result->result), sizeof(result->func_decl)); - return sizeof(result->result) + sizeof(result->func_decl); + result->num_args = instruction_data[0]; + am_memcpy(&result->result, instruction_data + 1, sizeof(result->result)); + am_memcpy(&result->func_decl, instruction_data + 1 + sizeof(result->result), sizeof(result->func_decl)); + return sizeof(u8) + sizeof(result->result) + sizeof(result->func_decl); } static CHECK_RESULT usize ssa_extract_jump_zero(u8 *instruction_data, SsaInsJumpZero *result) { @@ -94,7 +95,7 @@ static void add_intermediates(BytecodeCompilerContext *self) { } } -void add_strings(BytecodeCompilerContext *self) { +static void add_strings(BytecodeCompilerContext *self) { /*doc(Bytecode strings) # Strings layout |Type |Field |Description | @@ -144,6 +145,18 @@ void add_strings(BytecodeCompilerContext *self) { } } +static void add_functions(BytecodeCompilerContext *self) { + /*doc(Bytecode functions) + # Internal functions layout + |Type|Field |Description | + |----|-------------------|---------------------------------| + |u16 |Number of functions|The number of internal functions.| + */ + + assert(sizeof(SsaFuncIndex) == sizeof(u16) && "Program decoder needs to be updated since size of func index has changed"); + throw_if_error(buffer_append(&self->bytecode.data, &self->parser->ssa->func_counter, sizeof(u16))); +} + static void add_ins1(BytecodeCompilerContext *self, AmalOpcode opcode, const char *fmt) { throw_if_error(buffer_append(&self->bytecode.data, &opcode, sizeof(AmalOpcodeType))); fprintf(stderr, fmt); @@ -437,7 +450,7 @@ static void add_instructions(BytecodeCompilerContext *self) { } case SSA_ASSIGN_REG: { instruction += ssa_extract_form1(instruction, &ssa_ins_form1); - add_ins3(self, AMAL_OP_MOV, ssa_ins_form1.lhs, ssa_ins_form1.rhs, "mov r%d, d%d"); + add_ins3(self, AMAL_OP_MOV, ssa_ins_form1.lhs, ssa_ins_form1.rhs, "mov r%d, r%d"); break; } case SSA_ADD: { @@ -505,8 +518,8 @@ static void add_instructions(BytecodeCompilerContext *self) { is defined as the size of all previous files' number of functions. */ instruction += ssa_extract_func_call(instruction, &ssa_ins_func_call); - /* TODO: Replace 0 with the number of arguments */ - add_ins7(self, AMAL_OP_CALL, ssa_ins_func_call.func_decl->ssa_func_index, 0, "call %d, %d"); + add_ins7(self, AMAL_OP_CALL, ssa_ins_func_call.func_decl->ssa_func_index, ssa_ins_func_call.num_args, "call %d, %d"); + assert(bool_false && "TODO: Assign function result (RAX for x86_64) to ssa_ins_func_call.result reg"); break; } case SSA_JUMP_ZERO: { @@ -519,6 +532,13 @@ static void add_instructions(BytecodeCompilerContext *self) { add_ins4(self, AMAL_OP_JMP, ssa_ins_jump.jump_offset, "jmp %d"); break; } + case SSA_RET: { + SsaRegister reg; + am_memcpy(®, instruction, sizeof(SsaRegister)); + instruction += sizeof(SsaRegister); + add_ins2(self, AMAL_OP_RET, reg, "ret r%d"); + break; + } } } @@ -537,6 +557,6 @@ static void add_instructions(BytecodeCompilerContext *self) { void generate_bytecode_from_ssa(BytecodeCompilerContext *self) { add_intermediates(self); add_strings(self); - /* TODO: Also add strings in ssa, so we can index them */ + add_functions(self); add_instructions(self); } diff --git a/src/compiler.c b/src/compiler.c index 39cbb00..228cb74 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -36,7 +36,8 @@ static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char lhsexpr_init(*lhs_expr, DECL_FLAG_EXTERN | DECL_FLAG_PUB | DECL_FLAG_CONST, create_buffer_view(name, strnlen(name, PATH_MAX))); return_if_error(ast_create(&compiler->allocator, struct_decl, AST_STRUCT_DECL, &(*lhs_expr)->rhs_expr)); return_if_error(ast_create(&compiler->allocator, *lhs_expr, AST_LHS, &expr)); - expr->resolve_data.type = *lhs_expr; + expr->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR; + expr->resolve_data.type.value.lhs_expr = *lhs_expr; expr->resolve_data.status = AST_RESOLVED; return scope_add_child(&compiler->root_scope, expr); } @@ -123,6 +124,7 @@ static CHECK_RESULT int amal_compiler_init(amal_compiler *self, const amal_compi amal_compiler_options_init(&self->options); self->program = program; self->started = bool_false; + self->work_failed = bool_false; self->generic_work_object_index = 0; amal_mutex_init(&self->mutex); @@ -204,7 +206,6 @@ static CHECK_RESULT int amal_compiler_load_in_this_thread(amal_compiler *compile return result; } -/* TODO: Handle errors (stop parsing in all other threads and report errors/warnings) */ static void* thread_callback_parse_file(void *userdata) { FileScopeReference *file_scope; CompilerParserThreadUserData compiler_parser_userdata; @@ -217,6 +218,11 @@ static void* thread_callback_parse_file(void *userdata) { result = (void*)AMAL_COMPILER_ERR; for(;;) { int has_next; + /* Abort job if another job failed */ + if(compiler_parser_userdata.compiler->work_failed) { + result = (void*)AMAL_COMPILER_WORK_FAIL_ABORT; + goto cleanup; + } cleanup_if_error(amal_compiler_load_in_this_thread(compiler_parser_userdata.compiler, file_scope, &compiler_parser_userdata.parser_thread_data->allocator)); @@ -324,6 +330,14 @@ static void* thread_callback_generic(void *userdata) { cleanup_if_error(thread_generate_bytecode(parser)); break; } + + /* Abort job if another job failed */ + if(compiler_userdata.compiler->work_failed) { + result = (void*)AMAL_COMPILER_WORK_FAIL_ABORT; + goto cleanup; + } + + /* Find next job */ cleanup_if_error(amal_mutex_lock(&compiler_userdata.compiler->mutex, "thread_callback_generic")); if(compiler_userdata.compiler->generic_work_object_index + 1 >= (int)buffer_get_size(&compiler_userdata.compiler->parsers, Parser*)) break; @@ -339,7 +353,7 @@ static void* thread_callback_generic(void *userdata) { and the other threads will stop when they are done with the work they are currently working on. */ if(result != NULL) { - cleanup_if_error(amal_mutex_lock(&compiler_userdata.compiler->mutex, "thread_callback_generic")); + ignore_result_int(amal_mutex_lock(&compiler_userdata.compiler->mutex, "thread_callback_generic")); compiler_userdata.compiler->generic_work_object_index = (int)buffer_get_size(&compiler_userdata.compiler->parsers, Parser*); } compiler_userdata.parser_thread_data->status = PARSER_THREAD_STATUS_IDLE; @@ -424,11 +438,9 @@ static CHECK_RESULT int amal_compiler_load_file_join_threads(amal_compiler *self int result; void *thread_return_data; ParserThreadData *parser_thread_data; - bool work_failed; assert(amal_thread_is_main()); thread_return_data = NULL; - work_failed = bool_false; for(;;) { bool done; /* @@ -442,12 +454,11 @@ static CHECK_RESULT int amal_compiler_load_file_join_threads(amal_compiler *self amal_mutex_tryunlock(&self->mutex); if(result != 0) goto cleanup; - /* TODO: Cleanup remaining threads if join fails */ - cleanup_if_error(parser_thread_data_join(parser_thread_data, &thread_return_data)); + /* TODO: What to do if join fails? */ + ignore_result_int(parser_thread_data_join(parser_thread_data, &thread_return_data)); if(thread_return_data != NULL) { - /* TODO: Somehow exit running jobs */ amal_log_error("Failed, waiting for jobs to finish"); - work_failed = bool_true; + self->work_failed = bool_true; } } @@ -458,7 +469,7 @@ static CHECK_RESULT int amal_compiler_load_file_join_threads(amal_compiler *self result = AMAL_COMPILER_OK; cleanup: - if(work_failed) + if(self->work_failed) result = AMAL_COMPILER_ERR; return result; } @@ -545,6 +556,9 @@ int amal_compiler_internal_load_file(amal_compiler *self, const char *filepath, bool main_job; bool new_entry; + if(self->work_failed) + return AMAL_COMPILER_WORK_FAIL_ABORT; + return_if_error(try_create_file_scope(self, filepath, file_scope, &new_entry)); assert(file_scope && *file_scope && (*file_scope)->canonical_path.data); if(!new_entry) { diff --git a/src/parser.c b/src/parser.c index e36790f..33e9146 100644 --- a/src/parser.c +++ b/src/parser.c @@ -24,6 +24,8 @@ static CHECK_RESULT Ast* parser_parse_rhs(Parser *self); static CHECK_RESULT Ast* parser_parse_body(Parser *self); static CHECK_RESULT Ast* parser_parse_struct_body(Parser *self); static CHECK_RESULT Ast* parser_parse_rhs_binop(Parser *self); +static void parser_parse_var_type(Parser *self, VariableType *result); +static void parser_parse_var_type_def(Parser *self, VariableType *result); static void parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope); int parser_thread_data_init(ParserThreadData *self) { @@ -133,42 +135,119 @@ static void parser_parse_struct_body_loop(Parser *self, Scope *scope) { } /* -TODO: Implement params and return types -FUNC_SIGNATURE = 'fn' ('(' ')')? +FUNC_PARAM = TOK_IDENTIFIER VAR_TYPE_DEF +*/ +static CHECK_RESULT bool parser_parse_function_parameter(Parser *self, FunctionParameter *result) { + bool match; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match)); + if(!match) + return bool_false; + + result->name = self->tokenizer.value.identifier; + parser_parse_var_type_def(self, &result->type); + if(result->type.type == VARIABLE_TYPE_NONE) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_error_index(&self->tokenizer), + "Expected ':' after parameter name"); + throw(PARSER_UNEXPECTED_TOKEN); + } + + return bool_true; +} + +/* +FUNC_PARAMS = FUNC_PARAM (',' FUNC_PARAM)* +*/ +static void parser_parse_function_parameters(Parser *self, FunctionSignature *func_sig) { + for(;;) { + FunctionParameter func_param; + bool match; + int result; + + function_parameter_init(&func_param); + if(!parser_parse_function_parameter(self, &func_param)) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_error_index(&self->tokenizer), + "Expected closure parameter"); + throw(PARSER_UNEXPECTED_TOKEN); + } + + result = function_signature_add_parameter(func_sig, &func_param); + if(result == AST_ERR_DEF_DUP) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_code_reference_index(&self->tokenizer, func_param.name.data), + "Parameter with name %.*s was defined twice for the function", func_param.name.size, func_param.name.data); + self->error_context = ERROR_CONTEXT_NONE; + throw(result); + } else if(result != 0) { + throw(result); + } + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COMMA, &match)); + if(!match) + return; + } +} + +/* +FUNC_RETURN_TYPES = VAR_TYPE (',' VAR_TYPE)* +*/ +static void parser_parse_function_return_types(Parser *self, FunctionSignature *func_sig) { + for(;;) { + VariableType var_type; + bool match; + + parser_parse_var_type(self, &var_type); + if(var_type.type == VARIABLE_TYPE_NONE) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_error_index(&self->tokenizer), + "Expected closure return type"); + throw(PARSER_UNEXPECTED_TOKEN); + } + throw_if_error(function_signature_add_return_type(func_sig, &var_type)); + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COMMA, &match)); + if(!match) + return; + } +} + +/* +FUNC_SIGNATURE = 'fn' ('(' FUNC_PARAMS? ') FUNC_RETURN_TYPES? ')? */ static CHECK_RESULT FunctionSignature* parser_parse_function_signature(Parser *self) { FunctionSignature *signature; bool match; + throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionSignature), (void**)&signature)); + throw_if_error(function_signature_init(signature, self->allocator)); + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_FN, &match)); if(!match) return NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_PAREN, &match)); if(match) { - /* TODO: Parse parameters */ - throw_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN)); - /* TODO: Parse return types */ + bool has_closing_paren; + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CLOSING_PAREN, &has_closing_paren)); + if(!has_closing_paren) { + parser_parse_function_parameters(self, signature); + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN)); + } + parser_parse_function_return_types(self, signature); } - throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionSignature), (void**)&signature)); - function_signature_init(signature); return signature; } /* -VAR_TYPE_DEF = ':' TOK_IDENTIFIER|FUNC_SIGNATURE +VAR_TYPE = TOK_IDENTIFIER|FUNC_SIGNATURE */ -static void parser_parse_var_type_def(Parser *self, VariableType *result) { +void parser_parse_var_type(Parser *self, VariableType *result) { bool match; result->type = VARIABLE_TYPE_NONE; result->value.variable = NULL; - throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COLON, &match)); - if(!match) - return; - throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match)); if(match) { result->type = VARIABLE_TYPE_VARIABLE; @@ -181,13 +260,29 @@ static void parser_parse_var_type_def(Parser *self, VariableType *result) { result->value.signature = parser_parse_function_signature(self); if(!result->value.signature) { self->error = tokenizer_create_error(&self->tokenizer, - tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data), - "Expected type or closure signature"); + tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data), + "Expected type or closure signature"); throw(PARSER_UNEXPECTED_TOKEN); } } /* +VAR_TYPE_DEF = ':' VAR_TYPE +*/ +void parser_parse_var_type_def(Parser *self, VariableType *result) { + bool match; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COLON, &match)); + if(!match) { + result->type = VARIABLE_TYPE_NONE; + result->value.variable = NULL; + return; + } + + parser_parse_var_type(self, result); +} + +/* LHS_DECLARATION = 'extern'? 'pub'? 'const'|'var' TOK_IDENTIFIER VAR_TYPE_DEF? */ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) { @@ -263,6 +358,8 @@ static CHECK_RESULT FunctionDecl* parser_parse_closure(Parser *self) { throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)&result)); throw_if_error(funcdecl_init(result, signature, self->current_scope, self->allocator)); + signature->func_decl = result; + result->body.function_signature = signature; self->current_scope = &result->body; prev_has_func_parent = self->has_func_parent; @@ -492,7 +589,7 @@ static CHECK_RESULT Ast* parser_parse_number(Parser *self) { /* RHS_S = STRING | NUMBER | FUNC_CALL_OR_VARIABLE */ -static Ast* parser_parse_rhs_single_expr(Parser *self) { +static CHECK_RESULT Ast* parser_parse_rhs_single_expr(Parser *self) { Ast *result; bool match; result = NULL; @@ -634,21 +731,68 @@ static CHECK_RESULT Ast* parser_parse_conditional(Parser *self) { } /* +LHS_RHS = CLOSURE|STRUCT|IMPORT +*/ +static Ast* parser_parse_lhs_rhs(Parser *self, LhsExpr *lhs_expr) { + Ast *result; + FunctionDecl *func_decl; + StructDecl *struct_decl; + Import *import; + + func_decl = parser_parse_closure(self); + if(func_decl) { + throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &result)); + func_decl->lhs_expr = lhs_expr; + return result; + } + + struct_decl = parser_parse_struct_decl(self); + if(struct_decl) { + throw_if_error(ast_create(self->allocator, struct_decl, AST_STRUCT_DECL, &result)); + return result; + } + + import = parser_parse_import(self); + if(import) { + parser_queue_file(self, import->path, &import->file_scope); + throw_if_error(ast_create(self->allocator, import, AST_IMPORT, &result)); + parser_parse_body_semicolon(self, result); + return result; + } + + return NULL; +} + +/* +RETURN = 'return' RHS +*/ +static ReturnExpr* parser_parse_return(Parser *self) { + bool match; + ReturnExpr *result; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_RETURN, &match)); + if(!match) + return NULL; + + throw_if_error(arena_allocator_alloc(self->allocator, sizeof(ReturnExpr), (void**)&result)); + return_expr_init(result, parser_parse_rhs(self)); + return result; +} + +/* BODY = (LHS_DECLARATION ';') | - (LHS_DECLARATION '=' CLOSURE|STRUCT|IMPORT|(RHS BODY_SEMICOLON)) | + (LHS_DECLARATION '=' LHS_RHS|(RHS BODY_SEMICOLON)) | CONDITIONAL | (RHS ';'|('=' RHS BODY_SEMICOLON)) */ Ast* parser_parse_body(Parser *self) { Ast *result; LhsExpr *lhs_expr; + ReturnExpr *return_expr; Ast *rhs_expr; lhs_expr = parser_parse_declaration_lhs(self); if(lhs_expr) { - FunctionDecl *func_decl; - StructDecl *struct_decl; - Import *import; bool match; throw_if_error(ast_create(self->allocator, lhs_expr, AST_LHS, &result)); @@ -675,25 +819,9 @@ Ast* parser_parse_body(Parser *self) { throw_if_error(tokenizer_accept(&self->tokenizer, TOK_EQUALS)); - func_decl = parser_parse_closure(self); - if(func_decl) { - throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &lhs_expr->rhs_expr)); - return result; - } - - struct_decl = parser_parse_struct_decl(self); - if(struct_decl) { - throw_if_error(ast_create(self->allocator, struct_decl, AST_STRUCT_DECL, &lhs_expr->rhs_expr)); - return result; - } - - import = parser_parse_import(self); - if(import) { - parser_queue_file(self, import->path, &import->file_scope); - throw_if_error(ast_create(self->allocator, import, AST_IMPORT, &lhs_expr->rhs_expr)); - parser_parse_body_semicolon(self, lhs_expr->rhs_expr); + lhs_expr->rhs_expr = parser_parse_lhs_rhs(self, lhs_expr); + if(lhs_expr->rhs_expr) return result; - } } else { self->error_context = ERROR_CONTEXT_NO_LHS; result = parser_parse_conditional(self); @@ -701,6 +829,15 @@ Ast* parser_parse_body(Parser *self) { return result; } + if(self->has_func_parent) { + return_expr = parser_parse_return(self); + if(return_expr) { + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); + throw_if_error(ast_create(self->allocator, return_expr, AST_RETURN, &result)); + return result; + } + } + self->error_context = ERROR_CONTEXT_RHS_STANDALONE; rhs_expr = parser_parse_rhs(self); self->error_context = ERROR_CONTEXT_NONE; @@ -731,22 +868,26 @@ Ast* parser_parse_body(Parser *self) { } /* -STRUCT_BODY = TOK_IDENTIFIER ':' TOK_IDENTIFIER ';' +STRUCT_BODY = TOK_IDENTIFIER VAR_TYPE_DEF ';' */ Ast* parser_parse_struct_body(Parser *self) { Ast *result; BufferView var_name; - BufferView type_name; + VariableType var_type; StructField *struct_field; throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); var_name = self->tokenizer.value.identifier; - throw_if_error(tokenizer_accept(&self->tokenizer, TOK_COLON)); - throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER)); - type_name = self->tokenizer.value.identifier; + parser_parse_var_type_def(self, &var_type); + if(var_type.type == VARIABLE_TYPE_NONE) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_error_index(&self->tokenizer), + "Expected ':' after struct field name"); + throw(PARSER_UNEXPECTED_TOKEN); + } throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); throw_if_error(arena_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)&struct_field)); - structfield_init(struct_field, var_name, type_name); + structfield_init(struct_field, var_name, &var_type); throw_if_error(ast_create(self->allocator, struct_field, AST_STRUCT_FIELD, &result)); return result; } @@ -767,7 +908,8 @@ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_ tokenizer_print_error_object(&self->tokenizer, &self->error); break; case ERROR_CONTEXT_RHS_STANDALONE: - tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, struct, function call or import"); + /* TODO: Only show "return" in error message if @self->has_func_parent */ + tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, struct, function call, import or return"); break; case ERROR_CONTEXT_NO_LHS: tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected variable declaration, string, variable or function call"); diff --git a/src/program.c b/src/program.c index 54709b1..a73cf72 100644 --- a/src/program.c +++ b/src/program.c @@ -25,6 +25,10 @@ typedef struct { NumberUnion value; } Number; +/*doc(Bytecode) +The layout of the full bytecode is: Header (Intermediates Strings Functions Instructions)* +*/ + static CHECK_RESULT int amal_program_append_header(amal_program *self) { /*doc(Bytecode header) # Header layout @@ -57,8 +61,9 @@ int amal_program_init(amal_program *self) { self->intermediates_start = NULL; self->strings_start = NULL; self->read_index = 0; - self->num_strings = 0; self->num_intermediates = 0; + self->num_strings = 0; + self->num_functions = 0; cleanup_if_error(amal_program_append_header(self)); return 0; @@ -189,6 +194,14 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) { return AMAL_PROGRAM_OK; } +static CHECK_RESULT int amal_program_read_functions(amal_program *self) { + if(bytes_left_to_read(self) < sizeof(u16)) + return AMAL_PROGRAM_INVALID_FUNCTIONS; + am_memcpy(&self->num_functions, &self->data.data[self->read_index], sizeof(u16)); + self->read_index += sizeof(u16); + return AMAL_PROGRAM_OK; +} + static CHECK_RESULT int amal_program_get_intermediate_by_index(amal_program *self, u16 index, Number *result) { if(index >= self->num_intermediates) return AMAL_PROGRAM_INSTRUCTION_INVALID_INTERMEDIATE_INDEX; @@ -216,10 +229,12 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ u32 instructions_size; u32 read_start; u32 read_end; + u16 func_counter; bool inside_func; inside_func = bool_false; (void)inside_func; + func_counter = 0; if(bytes_left_to_read(self) < sizeof(instructions_size)) return AMAL_PROGRAM_INVALID_INSTRUCTIONS_SIZE; @@ -235,12 +250,15 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ on the type of the register. */ + return_if_error(amal_executor_instructions_start(executor, self->num_functions)); + read_start = self->read_index; read_end = read_start + instructions_size; while(self->read_index < read_end) { AmalOpcode opcode; opcode = self->data.data[self->read_index]; self->read_index += sizeof(AmalOpcodeType); + /* TODO: Check instruction length and check if that amount of bytes can be read */ switch(opcode) { case AMAL_OP_NOP: { return_if_error(amal_exec_nop(executor)); @@ -334,12 +352,17 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ self->read_index += 2; break; } - case AMAL_OP_CALL: - /*assert(bool_false && "TODO: Implement CALL");*/ + case AMAL_OP_CALL: { + u16 func_index; + u8 num_args; + am_memcpy(&func_index, self->data.data + self->read_index, sizeof(func_index)); + am_memcpy(&num_args, self->data.data + self->read_index + sizeof(func_index), sizeof(num_args)); + return_if_error(amal_exec_call(executor, func_index, num_args)); self->read_index += 3; break; + } case AMAL_OP_CALLR: - /*assert(bool_false && "TODO: Implement CALLR");*/ + assert(bool_false && "TODO: Implement CALLR"); self->read_index += 2; break; case AMAL_OP_CMP: { @@ -361,13 +384,19 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ self->read_index += 2; break; } - case AMAL_OP_RET: - return_if_error(amal_exec_ret(executor)); + case AMAL_OP_RET: { + const u8 reg = self->data.data[self->read_index]; + return_if_error(amal_exec_ret(executor, reg)); + self->read_index += 1; break; + } case AMAL_OP_FUNC_START: { u16 func_num_registers; assert(!inside_func); inside_func = bool_true; + assert(func_counter < self->num_functions); + ++func_counter; + am_memcpy(&func_num_registers, &self->data.data[self->read_index], sizeof(func_num_registers)); return_if_error(amal_exec_func_start(executor, func_num_registers)); self->read_index += 2; @@ -383,6 +412,8 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_ } } + assert(self->read_index == read_end); + return_if_error(amal_executor_instructions_end(executor)); return AMAL_PROGRAM_OK; } @@ -396,6 +427,7 @@ int amal_program_run(amal_program *self) { while(bytes_left_to_read(self) > 0) { cleanup_if_error(amal_program_read_intermediates(self)); cleanup_if_error(amal_program_read_strings(self)); + cleanup_if_error(amal_program_read_functions(self)); cleanup_if_error(amal_program_read_instructions(self, executor)); } result = amal_executor_run(executor); diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c index f3679aa..0aabc15 100644 --- a/src/ssa/ssa.c +++ b/src/ssa/ssa.c @@ -243,7 +243,7 @@ static CHECK_RESULT int ssa_ins_push(Ssa *self, SsaRegister reg) { return 0; } -static CHECK_RESULT int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, SsaRegister *result) { +static CHECK_RESULT int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, u8 num_args, SsaRegister *result) { usize index; index = self->instructions.size; @@ -251,12 +251,13 @@ static CHECK_RESULT int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, SsaRegi if(self->reg_counter + 1 < self->reg_counter) return -1; - return_if_error(buffer_append_empty(&self->instructions, sizeof(u8) + sizeof(SsaRegister) + sizeof(func_decl))); + return_if_error(buffer_append_empty(&self->instructions, sizeof(u8) + sizeof(u8) + sizeof(SsaRegister) + sizeof(func_decl))); *result = self->reg_counter++; self->instructions.data[index + 0] = SSA_CALL; - am_memcpy(self->instructions.data + index + 1, result, sizeof(SsaRegister)); - am_memcpy(self->instructions.data + index + 1 + sizeof(SsaRegister), &func_decl, sizeof(func_decl)); - amal_log_debug("r%u = CALL %p", *result, func_decl); + self->instructions.data[index + 1] = num_args; + am_memcpy(self->instructions.data + index + 2, result, sizeof(SsaRegister)); + am_memcpy(self->instructions.data + index + 2 + sizeof(SsaRegister), &func_decl, sizeof(func_decl)); + amal_log_debug("r%u = CALL %d, %p", *result, num_args, func_decl); return 0; } @@ -286,6 +287,16 @@ static CHECK_RESULT int ssa_ins_jump(Ssa *self, JumpOffset jump_offset) { return 0; } +static CHECK_RESULT int ssa_ins_return(Ssa *self, SsaRegister reg) { + usize index; + index = self->instructions.size; + + return_if_error(buffer_append_empty(&self->instructions, sizeof(u8) + sizeof(SsaRegister))); + self->instructions.data[index + 0] = SSA_RET; + am_memcpy(self->instructions.data + index + 1, ®, sizeof(SsaRegister)); + return 0; +} + static usize ssa_ins_get_index(Ssa *self) { return self->instructions.size; } @@ -311,6 +322,28 @@ static CHECK_RESULT int ssa_ins_jump_set_target(Ssa *self, usize jump_ins_index) } static CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context); +static CHECK_RESULT SsaRegister scope_named_object_generate_ssa(ScopeNamedObject *self, SsaCompilerContext *context); + +static bool ast_resolved_type_is_decl(AstResolvedType *self) { + /* TODO: Add more types as they are introduced */ + LhsExpr *lhs_expr; + if(self->type != RESOLVED_TYPE_LHS_EXPR) + return bool_false; + + lhs_expr = self->value.lhs_expr; + switch(lhs_expr->type.type) { + case VARIABLE_TYPE_NONE: + break; + case VARIABLE_TYPE_VARIABLE: + return bool_false; + case VARIABLE_TYPE_SIGNATURE: + /* TODO: This should return bool_false when it's possible to use signature in expressions */ + return bool_true; + } + + assert(lhs_expr->rhs_expr); + return lhs_expr->rhs_expr->type == AST_FUNCTION_DECL || lhs_expr->rhs_expr->type == AST_STRUCT_DECL; +} static CHECK_RESULT SsaRegister number_generate_ssa(Number *self, SsaCompilerContext *context) { SsaRegister reg; @@ -332,27 +365,23 @@ static CHECK_RESULT SsaRegister lhsexpr_extern_generate_ssa(LhsExpr *self, SsaCo return 0; } -static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(Ast *self, SsaCompilerContext *context) { - LhsExpr *lhs_expr; +static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(LhsExpr *self, AstResolveData *resolve_data, SsaCompilerContext *context) { SsaRegister reg; - assert(self->type == AST_LHS); - lhs_expr = self->value.lhs_expr; - - if(LHS_EXPR_IS_EXTERN(lhs_expr)) - return lhsexpr_extern_generate_ssa(lhs_expr, context); + if(LHS_EXPR_IS_EXTERN(self)) + return lhsexpr_extern_generate_ssa(self, context); - if(lhs_expr->rhs_expr) { - Ast *rhs_expr = lhs_expr->rhs_expr; + if(self->rhs_expr) { + Ast *rhs_expr = self->rhs_expr; SsaRegister rhs_reg; rhs_reg = ast_generate_ssa(rhs_expr, context); /* - Declarations (struct and function declaration) resolves to itself and in that case this expression - is just a compile-time name for the declaration. - Import expression also has no meaning in SSA until it's used. - TODO: Shouldn't lhsexpr that have struct/function declaration as rhs be different ast expression types? + Declarations (struct and function declaration) resolves to itself and in that case this expression + is just a compile-time name for the declaration. + Import expression also has no meaning in SSA until it's used. + TODO: Shouldn't lhsexpr that have struct/function declaration as rhs be different ast expression types? */ - if(self->resolve_data.type == lhs_expr || rhs_expr->type == AST_IMPORT) { + if(ast_resolved_type_is_decl(&resolve_data->type) || rhs_expr->type == AST_IMPORT) { /*assert(bool_false);*/ return 0; } @@ -364,32 +393,55 @@ static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(Ast *self, SsaCompilerConte throw_if_error(ssa_ins_assign_reg(context->ssa, reg, rhs_reg)); } else { /* TODO: Do not assign if we dont want default value */ - SsaNumber number; - if(self->resolve_data.type == (LhsExpr*)context->compiler->default_types.i64) - number = create_ssa_integer(0); - else if(self->resolve_data.type == (LhsExpr*)context->compiler->default_types.f64) - number = create_ssa_float(0.0); - else - assert(bool_false && "TODO: assign default value to reg depending on LhsExpr type"); - throw_if_error(ssa_get_unique_reg(context->ssa, ®)); - throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number)); + if(resolve_data->type.type == RESOLVED_TYPE_LHS_EXPR) { + SsaNumber number; + if(resolve_data->type.value.lhs_expr == (LhsExpr*)context->compiler->default_types.i64) + number = create_ssa_integer(0); + else if(resolve_data->type.value.lhs_expr == (LhsExpr*)context->compiler->default_types.f64) + number = create_ssa_float(0.0); + else + assert(bool_false && "TODO: assign default value to reg depending on LhsExpr type"); + throw_if_error(ssa_get_unique_reg(context->ssa, ®)); + throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number)); + } else if(resolve_data->type.type == RESOLVED_TYPE_FUNC_SIG) { + assert(bool_false && "TODO: Implement this when variable can be null. Then the function pointer should be null"); + } else { + assert(bool_false); + } } return reg; } -static CHECK_RESULT SsaRegister assignmentexpr_generate_ssa(Ast *ast, SsaCompilerContext *context) { - AssignmentExpr *self; +static CHECK_RESULT SsaRegister assignmentexpr_generate_ssa(AssignmentExpr *self, SsaCompilerContext *context) { SsaRegister lhs_reg, rhs_reg; - - assert(ast->type == AST_ASSIGN); - self = ast->value.assign_expr; - lhs_reg = ast_generate_ssa(self->lhs_expr, context); rhs_reg = ast_generate_ssa(self->rhs_expr, context); throw_if_error(ssa_ins_assign_reg(context->ssa, lhs_reg, rhs_reg)); return lhs_reg; } +static CHECK_RESULT SsaRegister function_parameter_generate_ssa(FunctionParameter *self, SsaCompilerContext *context) { + SsaRegister reg; + if(self->resolve_data.status == AST_SSA_RESOLVED) + return self->resolve_data.ssa_reg; + + throw_if_error(ssa_get_unique_reg(context->ssa, ®)); + self->resolve_data.status = AST_SSA_RESOLVED; + self->resolve_data.ssa_reg = reg; + return reg; +} + +static CHECK_RESULT void function_signature_generate_params_ssa(FunctionSignature *self, SsaCompilerContext *context) { + FunctionParameter *param, *param_end; + param = buffer_begin(&self->parameters); + param_end = buffer_end(&self->parameters); + for(; param != param_end; ++param) { + SsaRegister reg; + reg = function_parameter_generate_ssa(param, context); + (void)reg; + } +} + /* TODO: Each function declaration should be in separate SSA instances so ast can be converted into ssa in any order. @@ -402,49 +454,54 @@ static CHECK_RESULT SsaRegister funcdecl_generate_ssa(FunctionDecl *self, SsaCom */ SsaRegister prev_reg_counter; usize func_metadata_index; + int num_params; prev_reg_counter = context->ssa->reg_counter; context->ssa->reg_counter = 0; + /* + Parameters need to have generated ssa so the first ssa registers belong to the function. + This way we can know if a register access is for a parameter or not by checking the number + */ + function_signature_generate_params_ssa(self->signature, context); + num_params = buffer_get_size(&self->signature->parameters, FunctionParameter); + amal_log_debug("SSA funcdecl %p", self); throw_if_error(ssa_ins_func_start(context->ssa, &self->ssa_func_index, &func_metadata_index)); scope_generate_ssa(&self->body, context); throw_if_error(ssa_ins_func_end(context->ssa)); /* Add the number of registers used to the function metadata (FUNC_START) */ + context->ssa->reg_counter -= num_params; am_memcpy(&context->ssa->instructions.data[func_metadata_index], &context->ssa->reg_counter, sizeof(u16)); context->ssa->reg_counter = prev_reg_counter; return 0; } -static CHECK_RESULT SsaRegister funccall_generate_ssa(Ast *self, SsaCompilerContext *context) { - /* TODO: Implement */ - FunctionCall *func_call; +static CHECK_RESULT SsaRegister funccall_generate_ssa(FunctionCall *self, AstResolveData *resolve_data, SsaCompilerContext *context) { Ast **ast; Ast **ast_end; SsaRegister reg; + FunctionDecl *func_decl; - assert(self->type == AST_FUNCTION_CALL); - func_call = self->value.func_call; - ast = buffer_begin(&func_call->args); - ast_end = buffer_end(&func_call->args); + ast = buffer_begin(&self->args); + ast_end = buffer_end(&self->args); for(; ast != ast_end; ++ast) { SsaRegister arg_reg; arg_reg = ast_generate_ssa(*ast, context); throw_if_error(ssa_ins_push(context->ssa, arg_reg)); } - assert((self->resolve_data.type->rhs_expr && self->resolve_data.type->rhs_expr->type == AST_FUNCTION_DECL) || - self->resolve_data.type->type.type == VARIABLE_TYPE_SIGNATURE); - if(LHS_EXPR_IS_EXTERN(self->resolve_data.type)) { - amal_log_error("TODO: Implement extern function call (extern function %.*s was called)", func_call->func.name.size, func_call->func.name.data); + func_decl = resolve_data->type.value.func_sig->func_decl; + assert(resolve_data->type.type == RESOLVED_TYPE_FUNC_SIG); + assert(func_decl && "TODO: Implement function call for anonymous closures"); + if(func_decl && func_decl->lhs_expr && LHS_EXPR_IS_EXTERN(func_decl->lhs_expr)) { + amal_log_error("TODO: Implement extern function call (extern function %.*s was called)", self->func.name.size, self->func.name.data); reg = 0; assert(bool_false && "TODO: Implement extern function call!"); } else { - FunctionDecl *func_to_call; /* rhs wont be null here because only extern variable can't have rhs */ - func_to_call = self->resolve_data.type->rhs_expr->value.func_decl; - amal_log_debug("SSA funccall %.*s, func index ptr: %p", func_call->func.name.size, func_call->func.name.data, func_to_call); - throw_if_error(ssa_ins_call(context->ssa, func_to_call, ®)); + amal_log_debug("SSA funccall %.*s, func index ptr: %p", self->func.name.size, self->func.name.data, func_decl); + throw_if_error(ssa_ins_call(context->ssa, func_decl, buffer_get_size(&self->args, Ast*), ®)); } return reg; @@ -475,8 +532,8 @@ static CHECK_RESULT SsaRegister string_generate_ssa(String *self, SsaCompilerCon static CHECK_RESULT SsaRegister variable_generate_ssa(Variable *self, SsaCompilerContext *context) { /* TODO: If resolved_var refers to a variable in another file, use a cross file reference that requires no locking (not yet implemented) */ /* This is not thread-safe:*/ - assert(self->resolved_var); - return ast_generate_ssa(self->resolved_var, context); + assert(self->resolved_var.type != NAMED_OBJECT_NONE); + return scope_named_object_generate_ssa(&self->resolved_var, context); } static SsaInstruction binop_type_to_ssa_type(BinopType binop_type, amal_default_type *type) { @@ -511,7 +568,8 @@ static CHECK_RESULT SsaRegister binop_generate_ssa(Binop *self, SsaCompilerConte } else { const SsaRegister lhs_reg = ast_generate_ssa(self->lhs, context); const SsaRegister rhs_reg = ast_generate_ssa(self->rhs, context); - throw_if_error(ssa_ins_binop(context->ssa, binop_type_to_ssa_type(self->type, (amal_default_type*)self->lhs->resolve_data.type), lhs_reg, rhs_reg, ®)); + assert(self->lhs->resolve_data.type.type == RESOLVED_TYPE_LHS_EXPR && "TODO: Implement binop_generate_ssa for function signature"); + throw_if_error(ssa_ins_binop(context->ssa, binop_type_to_ssa_type(self->type, (amal_default_type*)self->lhs->resolve_data.type.value.lhs_expr), lhs_reg, rhs_reg, ®)); } return reg; } @@ -567,63 +625,88 @@ static void while_statement_generate_ssa(WhileStatement *while_stmt, SsaCompiler throw_if_error(ssa_ins_jump_set_target(context->ssa, jump_condition_ins_index)); } -static CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context) { - assert(self); - #ifdef DEBUG - if(self->resolve_data.status != AST_RESOLVED && self->resolve_data.status != AST_SSA_RESOLVED) { - amal_log_error("Ast type not resolved: %d", self->type); - assert(bool_false); - } - #endif +static void return_expr_generate_ssa(ReturnExpr *self, SsaCompilerContext *context) { + const SsaRegister reg = ast_generate_ssa(self->rhs_expr, context); + throw_if_error(ssa_ins_return(context->ssa, reg)); +} - if(self->resolve_data.status == AST_SSA_RESOLVED) - return self->ssa_reg; +static CHECK_RESULT SsaRegister ast_generate_ssa_resolve_data(void *ast_data, AstType ast_type, AstResolveData *resolve_data, SsaCompilerContext *context) { + if(resolve_data->status == AST_SSA_RESOLVED) + return resolve_data->ssa_reg; - switch(self->type) { + switch(ast_type) { case AST_NUMBER: - self->ssa_reg = number_generate_ssa(self->value.number, context); + resolve_data->ssa_reg = number_generate_ssa(ast_data, context); break; case AST_FUNCTION_DECL: - self->ssa_reg = funcdecl_generate_ssa(self->value.func_decl, context); + resolve_data->ssa_reg = funcdecl_generate_ssa(ast_data, context); break; case AST_FUNCTION_CALL: - self->ssa_reg = funccall_generate_ssa(self, context); + resolve_data->ssa_reg = funccall_generate_ssa(ast_data, resolve_data, context); break; case AST_STRUCT_DECL: - self->ssa_reg = structdecl_generate_ssa(self->value.struct_decl, context); + resolve_data->ssa_reg = structdecl_generate_ssa(ast_data, context); break; case AST_STRUCT_FIELD: - self->ssa_reg = structfield_generate_ssa(self->value.struct_field, context); + resolve_data->ssa_reg = structfield_generate_ssa(ast_data, context); break; case AST_LHS: - self->ssa_reg = lhsexpr_generate_ssa(self, context); + resolve_data->ssa_reg = lhsexpr_generate_ssa(ast_data, resolve_data, context); break; case AST_ASSIGN: - self->ssa_reg = assignmentexpr_generate_ssa(self, context); + resolve_data->ssa_reg = assignmentexpr_generate_ssa(ast_data, context); break; case AST_IMPORT: /* TODO: Implement cross file references */ - self->ssa_reg = 0; + resolve_data->ssa_reg = 0; break; case AST_STRING: - self->ssa_reg = string_generate_ssa(self->value.string, context); + resolve_data->ssa_reg = string_generate_ssa(ast_data, context); break; case AST_VARIABLE: - self->ssa_reg = variable_generate_ssa(self->value.variable, context); + resolve_data->ssa_reg = variable_generate_ssa(ast_data, context); break; case AST_BINOP: - self->ssa_reg = binop_generate_ssa(self->value.binop, context); + resolve_data->ssa_reg = binop_generate_ssa(ast_data, context); break; case AST_IF_STATEMENT: - if_statement_generate_ssa(self->value.if_stmt, context); + if_statement_generate_ssa(ast_data, context); break; case AST_WHILE_STATEMENT: - while_statement_generate_ssa(self->value.while_stmt, context); + while_statement_generate_ssa(ast_data, context); + break; + case AST_RETURN: + return_expr_generate_ssa(ast_data, context); + resolve_data->ssa_reg = 0; break; } - self->resolve_data.status = AST_SSA_RESOLVED; - return self->ssa_reg; + resolve_data->status = AST_SSA_RESOLVED; + return resolve_data->ssa_reg; +} + +CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context) { + assert(self); + #ifdef DEBUG + if(self->resolve_data.status != AST_RESOLVED && self->resolve_data.status != AST_SSA_RESOLVED) { + amal_log_error("Ast type not resolved: %d", self->type); + assert(bool_false); + } + #endif + return ast_generate_ssa_resolve_data(self->value.data, self->type, &self->resolve_data, context); +} + +CHECK_RESULT SsaRegister scope_named_object_generate_ssa(ScopeNamedObject *self, SsaCompilerContext *context) { + switch(self->type) { + case NAMED_OBJECT_NONE: + assert(bool_false); + return 0; + case NAMED_OBJECT_LHS_EXPR: + return ast_generate_ssa_resolve_data(self->value.lhs_expr, AST_LHS, self->resolve_data, context); + case NAMED_OBJECT_FUNC_PARAM: + return function_parameter_generate_ssa(self->value.func_param, context); + } + return 0; } void scope_generate_ssa(Scope *self, SsaCompilerContext *context) { diff --git a/src/std/arena_allocator.c b/src/std/arena_allocator.c index 14787f1..73111dd 100644 --- a/src/std/arena_allocator.c +++ b/src/std/arena_allocator.c @@ -67,8 +67,7 @@ static CHECK_RESULT int arena_allocator_ensure_capacity_for(ArenaAllocator *self } static void* align_ptr_ceil(void *ptr, uintptr_t alignment) { - uintptr_t ptrval; - ptrval = (uintptr_t)ptr; + const uintptr_t ptrval = (uintptr_t)ptr; return (void*)((ptrval + alignment + 1) & ~(alignment - 1)); } @@ -76,7 +75,7 @@ static usize align_ptr_ceil_offset(void *ptr, uintptr_t alignment) { return (uintptr_t)align_ptr_ceil(ptr, alignment) - (uintptr_t)ptr; } -#define SCOPED_ALLOC_ALIGNMENT 8 +#define ALLOC_ALIGNMENT 8 int arena_allocator_alloc(ArenaAllocator *self, usize size, void **mem) { ArenaAllocatorNode *current; @@ -89,14 +88,14 @@ int arena_allocator_alloc(ArenaAllocator *self, usize size, void **mem) { return -1; } - alloc_size = size + align_ptr_ceil_offset(self->current->data + self->current->size, SCOPED_ALLOC_ALIGNMENT); + alloc_size = size + align_ptr_ceil_offset(self->current->data + self->current->size, ALLOC_ALIGNMENT); return_if_error(arena_allocator_ensure_capacity_for(self, alloc_size)); /* Reallocated (new node created) */ if(self->current != current) { *mem = self->current->data; self->current->size += size; } else { - *mem = align_ptr_ceil(self->current->data + self->current->size, SCOPED_ALLOC_ALIGNMENT); + *mem = align_ptr_ceil(self->current->data + self->current->size, ALLOC_ALIGNMENT); self->current->size += alloc_size; } return 0; diff --git a/src/std/buffer.c b/src/std/buffer.c index 0e4ca89..93e8558 100644 --- a/src/std/buffer.c +++ b/src/std/buffer.c @@ -48,6 +48,7 @@ static CHECK_RESULT int buffer_ensure_capacity(Buffer *self, usize new_capacity) self->data = new_mem; self->capacity = capacity; + /* Update list of buffers in the allocator with the new address of the buffer data */ if(self->allocator) am_memcpy(self->allocator->mems.data + sizeof(void*) * self->allocator_index, &self->data, sizeof(void*)); return BUFFER_OK; diff --git a/src/std/buffer_view.c b/src/std/buffer_view.c index 8ddfab9..f2d79c0 100644 --- a/src/std/buffer_view.c +++ b/src/std/buffer_view.c @@ -1,4 +1,5 @@ #include "../../include/std/buffer_view.h" +#include "../../include/std/mem.h" BufferView create_buffer_view_null() { BufferView buffer_view; @@ -13,3 +14,7 @@ BufferView create_buffer_view(const char *data, usize size) { buffer_view.size = size; return buffer_view; } + +bool buffer_view_equals(const BufferView *self, const BufferView *other) { + return self->size == other->size && am_memeql(self->data, other->data, self->size); +} diff --git a/src/tokenizer.c b/src/tokenizer.c index 7620fc0..1586691 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -216,6 +216,9 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { } else if(am_memeql(self->value.identifier.data, "extern", 6)) { *token = TOK_EXTERN; return TOKENIZER_OK; + } else if(am_memeql(self->value.identifier.data, "return", 6)) { + *token = TOK_RETURN; + return TOKENIZER_OK; } break; } @@ -346,6 +349,8 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { } self->index += 6; + /* TODO: This should be moved to the parser */ + result = tokenizer_next(self, &last_token); if(result != 0 || last_token != TOK_OPEN_PAREN) { err_msg = "Expected '(' after @import"; @@ -490,6 +495,9 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_EXTERN: str = "extern"; break; + case TOK_RETURN: + str = "return"; + break; } return create_buffer_view(str, strlen(str)); } |