aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-08-17 02:57:08 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commit81c5f8e750fcda6a2451fb54604130431434f88f (patch)
tree944fa06c781d57b1db90e9153080f411a1c34a55 /src
parent20662a1d203ffb9e05d6694347fd258115b41d0a (diff)
Implement more instructions, implement function parameters and arguments
Diffstat (limited to 'src')
-rw-r--r--src/ast.c494
-rw-r--r--src/bytecode/bytecode.c36
-rw-r--r--src/compiler.c34
-rw-r--r--src/parser.c232
-rw-r--r--src/program.c44
-rw-r--r--src/ssa/ssa.c239
-rw-r--r--src/std/arena_allocator.c9
-rw-r--r--src/std/buffer.c1
-rw-r--r--src/std/buffer_view.c5
-rw-r--r--src/tokenizer.c8
10 files changed, 824 insertions, 278 deletions
diff --git a/src/ast.c b/src/ast.c
index 0aa19d4..413f77e 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -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, &param->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(&reg, 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, &reg, 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, &reg));
- 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, &reg));
+ 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, &reg));
+ 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, &reg));
+ 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*), &reg));
}
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, &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, &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));
}