From c207014f81fe742675c6e869b59d09e916c69fc6 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sun, 24 Mar 2019 01:19:18 +0100 Subject: Resolve cross-file references (with mutex). Not done --- src/ast.c | 164 +++++++++++++++++++++++++++++++++++++++++-------------- src/compiler.c | 57 +++++++++---------- src/parser.c | 15 +++-- src/std/thread.c | 2 +- src/tokenizer.c | 2 + 5 files changed, 162 insertions(+), 78 deletions(-) (limited to 'src') diff --git a/src/ast.c b/src/ast.c index 7d25401..4ee0934 100644 --- a/src/ast.c +++ b/src/ast.c @@ -1,5 +1,6 @@ #include "../include/ast.h" #include "../include/parser.h" +#include "../include/compiler.h" #include "../include/std/log.h" #include "../include/std/hash.h" #include @@ -40,10 +41,12 @@ BufferView ast_get_name(Ast *self) { case AST_STRUCT_DECL: case AST_IMPORT: case AST_STRING: - case AST_NUMBER: case AST_BINOP: name = create_buffer_view_null(); break; + case AST_NUMBER: + name = self->value.number->code_ref; + break; case AST_LHS: name = self->value.lhs_expr->var_name; break; @@ -89,10 +92,7 @@ int lhsexpr_init(LhsExpr *self, bool is_pub, bool is_const, BufferView var_name, variable_init(&self->type, create_buffer_view_null()); self->var_name = var_name; self->rhs_expr = NULL; - if(is_pub && allocator) - return_if_error(scoped_allocator_create_mutex(allocator, &self->mutex)); - else - self->mutex = NULL; + return_if_error(scoped_allocator_create_mutex(allocator, &self->mutex)); return 0; } @@ -107,9 +107,10 @@ int string_init(String *self, BufferView str) { return 0; } -void number_init(Number *self, i64 value, bool is_integer) { +void number_init(Number *self, i64 value, bool is_integer, BufferView code_ref) { self->value.integer = value; self->is_integer = is_integer; + self->code_ref = code_ref; } void variable_init(Variable *self, BufferView name) { @@ -133,7 +134,7 @@ int scope_init(Scope *self, Scope *parent, ScopedAllocator *allocator) { int file_scope_reference_init(FileScopeReference *self, BufferView canonical_path, ScopedAllocator *allocator) { char null_terminator; null_terminator = '\0'; - self->scope = NULL; + self->parser = NULL; return_if_error(buffer_init(&self->canonical_path, allocator)); return_if_error(buffer_append(&self->canonical_path, canonical_path.data, canonical_path.size)); @@ -151,6 +152,7 @@ int scope_add_child(Scope *self, Ast *child) { if(child->type == AST_LHS) { BufferView var_name; var_name = child->value.lhs_expr->var_name; + assert(var_name.data); child_already_exists = hash_map_get(&self->named_objects, var_name, &existing_child); if(child_already_exists) return AST_ERR_DEF_DUP; @@ -177,7 +179,7 @@ void scope_resolve(Scope *self, AstCompilerContext *context) { context->scope = self->parent; } -static LhsExpr* scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name) { +static Ast* scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name) { Ast *result; bool exists; Scope *prev_scope; @@ -205,12 +207,12 @@ static LhsExpr* scope_get_resolved_variable(Scope *self, AstCompilerContext *con context->scope = prev_scope; assert(result->type == AST_LHS); - return result->value.lhs_expr; + return result; } static void variable_resolve(Variable *self, AstCompilerContext *context, AstResolveData *resolve_data) { if(!resolve_data->type) - resolve_data->type = scope_get_resolved_variable(context->scope, context, self->name); + resolve_data->type = scope_get_resolved_variable(context->scope, context, self->name)->resolve_data.type; } static LhsExpr* lhsexpr_resolve_rhs(Ast *ast, AstCompilerContext *context, LhsExpr *lhs_expr) { @@ -260,32 +262,32 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) { ast->resolve_data.type = rhs_resolve_type; } +static void import_resolve(Ast *ast, AstCompilerContext *context) { + Import *self; + 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); +} + /* LhsExpr has to be resolved before this is called */ -#if 0 static Scope* lhsexpr_get_scope(LhsExpr *self) { + AstValue value; + value = self->rhs_expr->value; switch(self->rhs_expr->type) { case AST_FUNCTION_DECL: - return &self->rhs_expr->value.func_decl->body; + return &value.func_decl->body; case AST_STRUCT_DECL: - return &self->rhs_expr->value.struct_decl->body; + return &value.struct_decl->body; case AST_IMPORT: - return self->rhs_expr->value.import->file_scope->scope; + return &value.import->file_scope->parser->struct_decl.body; default: break; } - assert(bool_false && "Expected lhsexpr_get_scope to only be called for function decl and struct decl"); + assert(bool_false && "Expected lhsexpr_get_scope to only be called for function decl, struct decl and import"); return NULL; } -#endif - -static void import_resolve(Ast *self, AstCompilerContext *context) { - Import *import; - import = self->value.import; - (void)import; - (void)self; - (void)context; - /* TODO: Convert all scopes to structs and set import->resolved_type to import->file_scope->scope; */ -} static void funcdecl_resolve(FunctionDecl *self, AstCompilerContext *context) { /* TODO: Implement parameters and return types */ @@ -320,45 +322,97 @@ static void structfield_resolve(Ast *self, AstCompilerContext *context) { variable_resolve(&struct_field->type, context, &self->resolve_data); } +static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) { + Binop *self; + Scope *lhs_scope; + Parser *callee_parser; + BufferView caller_code_ref; + + assert(ast->type == AST_BINOP); + self = ast->value.binop; + + if(self->lhs->type != AST_VARIABLE) { + /* TODO: Allow field access for numbers and string as well */ + BufferView code_ref; + code_ref = ast_get_code_reference(self->lhs); + tokenizer_print_error(&context->parser->tokenizer, + tokenizer_get_code_reference_index(&context->parser->tokenizer, code_ref.data), + "Accessing fields is only applicable for variables"); + 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; + self->rhs->resolve_data.status = AST_RESOLVED; + + /* + const io = @import("std.io"); + io.write ^ + ^-from this, to-' + */ + if(self->lhs->resolve_data.type->rhs_expr->type == AST_IMPORT) + callee_parser = self->lhs->resolve_data.type->rhs_expr->value.import->file_scope->parser; + + caller_code_ref = ast_get_code_reference(self->rhs); + callee_parser = context->parser; + + if(self->rhs->resolve_data.type->rhs_expr->type != AST_STRUCT_DECL) { + /* TODO: Add note where the referenced data was declared */ + tokenizer_print_error(&context->parser->tokenizer, + tokenizer_get_code_reference_index(&context->parser->tokenizer, caller_code_ref.data), + "Can only access field of struct's"); + throw(AST_ERR); + } + + if(!self->rhs->resolve_data.type->is_pub) { + tokenizer_print_error(&context->parser->tokenizer, + tokenizer_get_code_reference_index(&context->parser->tokenizer, 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 */ + tokenizer_print_error(&context->parser->tokenizer, + tokenizer_get_code_reference_index(&callee_parser->tokenizer, caller_code_ref.data), + "Type was declared non-public here"); + throw(AST_ERR); + } +} + static void binop_resolve(Ast *ast, AstCompilerContext *context) { Binop *self; assert(ast->type == AST_BINOP); self = ast->value.binop; ast_resolve(self->lhs, context); - #if 0 /* TODO: Readd this once mutex has been added for types */ if(self->type == BINOP_DOT && self->rhs->type == AST_VARIABLE) { - Scope *lhs_scope; - 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); + binop_resolve_dot_access(ast, context); self->rhs->resolve_data.status = AST_RESOLVED; ast->resolve_data.type = self->rhs->resolve_data.type; } else { ast_resolve(self->rhs, context); /* TODO: Convert types that can be safely converted */ - if(self->lhs->resolve_data.type != self->rhs->resolve_data.type) { + assert(self->lhs->resolve_data.type); + assert(self->rhs->resolve_data.type); + if(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. */ tokenizer_print_error(&context->parser->tokenizer, - tokenizer_get_code_reference_index(&context->parser->tokenizer, ast_get_code_reference(self->lhs).data), - "Right-hand side and left-hand side are different types"); + tokenizer_get_code_reference_index(&context->parser->tokenizer, 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); tokenizer_print_error(&context->parser->tokenizer, tokenizer_get_code_reference_index(&context->parser->tokenizer, ast_get_code_reference(self->lhs).data), "Left-hand side is of type %.*s", - self->rhs->resolve_data.type->var_name.size, - self->lhs->resolve_data.type->var_name.data); + self->lhs->resolve_data.type->var_name.size, self->lhs->resolve_data.type->var_name.data); tokenizer_print_error(&context->parser->tokenizer, tokenizer_get_code_reference_index(&context->parser->tokenizer, 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); + self->rhs->resolve_data.type->var_name.size, self->rhs->resolve_data.type->var_name.data); throw(AST_ERR); } + ast->resolve_data.type = self->lhs->resolve_data.type; } - #else - ast_resolve(self->rhs, context); - #endif } void ast_resolve(Ast *self, AstCompilerContext *context) { @@ -370,6 +424,10 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { 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. + */ if(self->resolve_data.status == AST_RESOLVED) { return; } else if(self->resolve_data.status == AST_RESOLVING) { @@ -379,11 +437,32 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { throw(AST_ERR); } + if(self->type == AST_LHS) { + throw_if_error(amal_mutex_lock(self->value.lhs_expr->mutex, "ast_resolve")); + if(self->resolve_data.status == AST_RESOLVED) { + amal_mutex_tryunlock(self->value.lhs_expr->mutex); + return; + } else if(self->resolve_data.status == AST_RESOLVING) { + amal_mutex_tryunlock(self->value.lhs_expr->mutex); + tokenizer_print_error(&context->parser->tokenizer, + tokenizer_get_code_reference_index(&context->parser->tokenizer, ast_get_code_reference(self).data), + "Found recursive dependency"); + throw(AST_ERR); + } + } + self->resolve_data.status = AST_RESOLVING; switch(self->type) { - case AST_NUMBER: - /* Nothing to resolve for numbers */ + case AST_NUMBER: { + Number *number; + number = self->value.number; + /* TODO: Support other number types */ + if(number->is_integer) + self->resolve_data.type = context->compiler->default_types.i64; + else + self->resolve_data.type = context->compiler->default_types.f64; break; + } case AST_FUNCTION_DECL: funcdecl_resolve(self->value.func_decl, context); break; @@ -405,6 +484,7 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { break; case AST_STRING: /* TODO: Convert special combinations. For example \n to newline */ + self->resolve_data.type = context->compiler->default_types.str; break; case AST_VARIABLE: variable_resolve(self->value.variable, context, &self->resolve_data); @@ -415,4 +495,6 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { } /* TODO: See comment at the top of this function */ self->resolve_data.status = AST_RESOLVED; + if(self->type == AST_LHS) + amal_mutex_tryunlock(self->value.lhs_expr->mutex); } diff --git a/src/compiler.c b/src/compiler.c index cc35d1e..1feaf86 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -13,11 +13,6 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) -typedef struct { - BufferView path; - Scope *file_scope; -} FileQueueItem; - static CHECK_RESULT int get_thread_count_env_var(int *thread_count) { char *threads; threads = getenv("THREADS"); @@ -38,39 +33,38 @@ static usize strnlen(const char *str, usize max_length) { } /* TODO: Allow to specify size and members? */ -static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char *name) { +static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char *name, LhsExpr **lhs_expr) { StructDecl *struct_decl; - LhsExpr *lhs_expr; Ast *expr; return_if_error(scoped_allocator_alloc(&compiler->allocator, sizeof(StructDecl), (void**)&struct_decl)); return_if_error(structdecl_init(struct_decl, &compiler->root_scope, &compiler->allocator)); - return_if_error(scoped_allocator_alloc(&compiler->allocator, sizeof(LhsExpr), (void**)&lhs_expr)); - return_if_error(lhsexpr_init(lhs_expr, bool_true, bool_true, + return_if_error(scoped_allocator_alloc(&compiler->allocator, sizeof(LhsExpr), (void**)lhs_expr)); + return_if_error(lhsexpr_init(*lhs_expr, bool_true, bool_true, create_buffer_view(name, strnlen(name, PATH_MAX)), - NULL)); - 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; + &compiler->allocator)); + 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.status = AST_RESOLVED; return scope_add_child(&compiler->root_scope, expr); } static CHECK_RESULT int init_default_types(amal_compiler *compiler) { - return_if_error(create_default_type(compiler, "i8")); - return_if_error(create_default_type(compiler, "i16")); - return_if_error(create_default_type(compiler, "i32")); - return_if_error(create_default_type(compiler, "i64")); - return_if_error(create_default_type(compiler, "u8")); - return_if_error(create_default_type(compiler, "u16")); - return_if_error(create_default_type(compiler, "u32")); - return_if_error(create_default_type(compiler, "u64")); - return_if_error(create_default_type(compiler, "isize")); - return_if_error(create_default_type(compiler, "usize")); - return_if_error(create_default_type(compiler, "f32")); - return_if_error(create_default_type(compiler, "f64")); - return_if_error(create_default_type(compiler, "str")); + return_if_error(create_default_type(compiler, "i8", &compiler->default_types.i8)); + return_if_error(create_default_type(compiler, "i16", &compiler->default_types.i16)); + return_if_error(create_default_type(compiler, "i32", &compiler->default_types.i32)); + return_if_error(create_default_type(compiler, "i64", &compiler->default_types.i64)); + return_if_error(create_default_type(compiler, "u8", &compiler->default_types.u8)); + return_if_error(create_default_type(compiler, "u16", &compiler->default_types.u16)); + return_if_error(create_default_type(compiler, "u32", &compiler->default_types.u32)); + return_if_error(create_default_type(compiler, "u64", &compiler->default_types.u64)); + return_if_error(create_default_type(compiler, "isize", &compiler->default_types.isize)); + return_if_error(create_default_type(compiler, "usize", &compiler->default_types.usize)); + return_if_error(create_default_type(compiler, "f32", &compiler->default_types.f32)); + return_if_error(create_default_type(compiler, "f64", &compiler->default_types.f64)); + return_if_error(create_default_type(compiler, "str", &compiler->default_types.str)); return 0; } @@ -169,7 +163,7 @@ static CHECK_RESULT int amal_compiler_load_in_this_thread(amal_compiler *self, F amal_log_info("Started parsing %.*s", filepath.size, filepath.data); return_if_error(scoped_allocator_alloc(allocator, sizeof(Parser), (void**)&parser)); return_if_error(parser_init(parser, self, allocator)); - file_scope->scope = &parser->scope; + file_scope->parser = parser; return_if_error(parser_parse_file(parser, filepath)); cleanup_if_error(amal_mutex_lock(&self->mutex, "amal_compiler_load_in_this_thread")); cleanup_if_error(buffer_append(&self->parsers, &parser, sizeof(parser))); @@ -211,15 +205,16 @@ static void* thread_callback_parse_file(void *userdata) { return result; } -static CHECK_RESULT int thread_resolve_ast(Parser *parser) { +static CHECK_RESULT int thread_resolve_ast(amal_compiler *compiler, Parser *parser) { AstCompilerContext compiler_context; int result; compiler_context.parser = parser; + compiler_context.compiler = compiler; compiler_context.scope = NULL; result = setjmp(compiler_context.env); if(result == 0) { amal_log_debug("Resolving AST for file: %.*s", parser->tokenizer.code_name.size, parser->tokenizer.code_name.data); - scope_resolve(&parser->scope, &compiler_context); + scope_resolve(&parser->struct_decl.body, &compiler_context); } return result; } @@ -232,7 +227,7 @@ static CHECK_RESULT int thread_generate_ssa(Parser *parser) { if(result == 0) { return_if_error(ssa_init(&compiler_context.ssa, parser->allocator)); amal_log_debug("Generating SSA for file: %.*s", parser->tokenizer.code_name.size, parser->tokenizer.code_name.data); - scope_generate_ssa(&parser->scope, &compiler_context); + scope_generate_ssa(&parser->struct_decl.body, &compiler_context); } return result; } @@ -256,7 +251,7 @@ static void* thread_callback_generic(void *userdata) { break; } case THREAD_WORK_RESOLVE_AST: - cleanup_if_error(thread_resolve_ast(parser)); + cleanup_if_error(thread_resolve_ast(compiler_userdata.compiler, parser)); break; case THREAD_WORK_GENERATE_SSA: cleanup_if_error(thread_generate_ssa(parser)); diff --git a/src/parser.c b/src/parser.c index 9e92ddd..7b69ee7 100644 --- a/src/parser.c +++ b/src/parser.c @@ -58,12 +58,15 @@ int parser_init(Parser *self, amal_compiler *compiler, ScopedAllocator *allocato self->error.index = 0; self->error.str = NULL; self->error_context = ERROR_CONTEXT_NONE; - self->current_scope = &self->scope; - return scope_init(&self->scope, &compiler->root_scope, self->allocator); + return_if_error(structdecl_init(&self->struct_decl, &compiler->root_scope, allocator)); + return_if_error(lhsexpr_init(&self->file_decl, bool_true, bool_true, create_buffer_view_null(), self->allocator)); + return_if_error(ast_create(self->allocator, &self->struct_decl, AST_STRUCT_DECL, &self->file_decl.rhs_expr)); + self->current_scope = &self->struct_decl.body; + return PARSER_OK; } static bool parser_is_global_scope(Parser *self) { - return self->current_scope == &self->scope; + return self->current_scope == &self->struct_decl.body; } /* @@ -345,7 +348,8 @@ static CHECK_RESULT Ast* parser_parse_number(Parser *self) { return result; throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(Number), (void**)&number)); - number_init(number, self->tokenizer.value.integer, self->tokenizer.number_is_integer); + number_init(number, self->tokenizer.value.integer, self->tokenizer.number_is_integer, + create_buffer_view(self->tokenizer.code.data + self->tokenizer.prev_index, self->tokenizer.index - self->tokenizer.prev_index)); throw_if_error(ast_create(self->allocator, number, AST_NUMBER, &result)); return result; } @@ -564,10 +568,11 @@ ROOT = BODY_LOOP */ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_name) { int result; + self->file_decl.var_name = buffer_name; throw_if_error(tokenizer_init(&self->tokenizer, self->allocator, code_buffer, buffer_name)); result = setjmp(self->parse_env); if(result == 0) - parser_parse_body_loop(self, &self->scope, TOK_END_OF_FILE); + parser_parse_body_loop(self, &self->struct_decl.body, TOK_END_OF_FILE); else if(self->error.str != NULL) { switch(self->error_context) { case ERROR_CONTEXT_NONE: diff --git a/src/std/thread.c b/src/std/thread.c index 350631a..fb0e9ab 100644 --- a/src/std/thread.c +++ b/src/std/thread.c @@ -95,7 +95,7 @@ int amal_thread_join(amal_thread *self, void **result) { } if((result_err = pthread_join(self->thread_id, result)) != 0) { - /* Joining a thread that is not joinable shouldn't be an error */ + /* Joining a thread that is not joinable (has already finished) shouldn't be an error */ if(result_err == ESRCH) goto cleanup; amal_log_error("amal_thread_join: failed to join thread %llu, error: %s", self->thread_id, strerror(result_err)); diff --git a/src/tokenizer.c b/src/tokenizer.c index c59f80c..d873b0e 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -663,5 +663,7 @@ int tokenizer_get_error_index(Tokenizer *self) { } int tokenizer_get_code_reference_index(Tokenizer *self, const char *ref) { + if(!ref) + return -1; return ref - self->code.data; } -- cgit v1.2.3