From 2323ca6c9ec3c8ee76b9acf13745b80b92952a6a Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 18 Mar 2019 23:47:45 +0100 Subject: Add struct, import caching, binop ops etc --- src/ast.c | 121 +++++++++++++++++++++++++------- src/compiler.c | 138 ++++++++++++++++++++++++++++++------- src/parser.c | 167 ++++++++++++++++++++++++++++++++++++--------- src/ssa/ssa.c | 30 +++++++- src/std/alloc.c | 9 ++- src/std/file.c | 22 +++++- src/std/log.c | 17 +++-- src/std/scoped_allocator.c | 23 ++++--- src/std/thread.c | 39 +++++++++-- src/tokenizer.c | 14 +++- 10 files changed, 466 insertions(+), 114 deletions(-) (limited to 'src') diff --git a/src/ast.c b/src/ast.c index 9605b57..8e19819 100644 --- a/src/ast.c +++ b/src/ast.c @@ -20,6 +20,7 @@ Ast ast_none() { ast.value.func_decl = NULL; ast.type = AST_NONE; ast.resolve_status = AST_NOT_RESOLVED; + ast.resolved_type = NULL; return ast; } @@ -27,6 +28,7 @@ void ast_init(Ast *self, void *value, AstType type) { self->value.data = value; self->type = type; self->resolve_status = AST_NOT_RESOLVED; + self->resolved_type = NULL; } BufferView ast_get_name(Ast *self) { @@ -34,6 +36,7 @@ BufferView ast_get_name(Ast *self) { switch(self->type) { case AST_NONE: case AST_FUNCTION_DECL: + case AST_STRUCT_DECL: case AST_IMPORT: case AST_STRING: case AST_NUMBER: @@ -49,6 +52,9 @@ BufferView ast_get_name(Ast *self) { case AST_VARIABLE: name = self->value.variable->name; break; + case AST_STRUCT_FIELD: + name = self->value.struct_field->name; + break; } return name; } @@ -67,15 +73,25 @@ int funccall_init(FunctionCall *self, BufferView name, ScopedAllocator *allocato return buffer_init(&self->args, allocator); } -void lhsexpr_init(LhsExpr *self, int isConst, BufferView var_name) { - self->is_const = isConst; - self->type_name = create_buffer_view_null(); +int structdecl_init(StructDecl *self, Scope *parent, ScopedAllocator *allocator) { + return scope_init(&self->body, parent, allocator); +} + +void structfield_init(StructField *self, BufferView name, BufferView type_name) { + self->name = name; + variable_init(&self->type, type_name); +} + +void lhsexpr_init(LhsExpr *self, bool is_const, BufferView var_name) { + self->is_const = is_const; + variable_init(&self->type, create_buffer_view_null()); self->var_name = var_name; self->rhs_expr = ast_none(); } void import_init(Import *self, BufferView path) { self->path = path; + self->file_scope = NULL; } int string_init(String *self, BufferView str) { @@ -108,6 +124,19 @@ int scope_init(Scope *self, Scope *parent, ScopedAllocator *allocator) { return 0; } +int file_scope_reference_init(FileScopeReference *self, BufferView canonical_path, ScopedAllocator *allocator) { + char null_terminator; + null_terminator = '\0'; + self->scope = NULL; + + return_if_error(buffer_init(&self->canonical_path, allocator)); + return_if_error(buffer_append(&self->canonical_path, canonical_path.data, canonical_path.size)); + return_if_error(buffer_append(&self->canonical_path, &null_terminator, 1)); + /* To exclude null-terminator character from size but not from data */ + self->canonical_path.size -= 1; + return 0; +} + int scope_add_child(Scope *self, Ast *child) { Ast existing_child; bool child_already_exists; @@ -132,16 +161,14 @@ int scope_add_child(Scope *self, Ast *child) { void scope_resolve(Scope *self, AstCompilerContext *context) { Ast *ast; Ast *ast_end; - Scope *prev_scope = context->scope; + ast = buffer_start(&self->ast_objects); ast_end = buffer_end(&self->ast_objects); - assert(self->parent == context->scope); context->scope = self; for(; ast != ast_end; ++ast) { ast_resolve(ast, context); } context->scope = self->parent; - assert(context->scope == prev_scope); } static Ast scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name) { @@ -155,7 +182,6 @@ static Ast scope_get_resolved_variable(Scope *self, AstCompilerContext *context, if(self->parent) return scope_get_resolved_variable(self->parent, context, name); - tokenizer_get_code_reference_index(&context->parser->tokenizer, name.data); tokenizer_print_error(&context->parser->tokenizer, tokenizer_get_code_reference_index(&context->parser->tokenizer, name.data), "Undefined reference to variable \"%.*s\"", name.size, name.data); @@ -176,23 +202,46 @@ static Ast scope_get_resolved_variable(Scope *self, AstCompilerContext *context, return result.value.lhs_expr->rhs_expr; } -static void variable_resolve(Variable *self, AstCompilerContext *context) { - /* TODO: Implement */ - amal_log_debug("variable resolve, var name: %.*s", self->name.size, self->name.data); +static void variable_resolve(Variable *self, AstCompilerContext *context, StructDecl **resolved_type) { self->resolved_variable = scope_get_resolved_variable(context->scope, context, self->name); + /* TODO: Implement */ + if(self->resolved_variable.type == AST_STRUCT_DECL) { + *resolved_type = self->resolved_variable.value.struct_decl; + } else if(self->resolved_variable.type == AST_FUNCTION_DECL) { + /* TODO: Set resolved type to function declaration return type */ + *resolved_type = NULL; + } else { + *resolved_type = NULL; + } } static void lhs_resolve(Ast *self, AstCompilerContext *context) { - /* TODO: Implement */ LhsExpr *lhs_expr; - assert(self->type == AST_LHS); lhs_expr = self->value.lhs_expr; - amal_log_debug("Lhs resolve %s name: %.*s, type: %.*s", - lhs_expr->is_const ? "const" : "var", - lhs_expr->var_name.size, lhs_expr->var_name.data, - lhs_expr->type_name.size, lhs_expr->type_name.data); + + if(lhs_expr->type.name.data) + variable_resolve(&lhs_expr->type, context, &self->resolved_type); self->resolve_status = AST_RESOLVED; ast_resolve(&lhs_expr->rhs_expr, context); + + if(self->resolved_type && + lhs_expr->rhs_expr.type != AST_NONE && + self->resolved_type != lhs_expr->rhs_expr.resolved_type) { + tokenizer_print_error(&context->parser->tokenizer, + tokenizer_get_code_reference_index(&context->parser->tokenizer, ast_get_code_reference(self).data), + "Variable type and variable assignment type (right-hand side) do not match"); + throw(AST_ERR); + } + self->resolved_type = lhs_expr->rhs_expr.resolved_type; +} + +static void import_resolve(Ast *self, AstCompilerContext *context) { + Import *import; + import = self->value.import; + (void)import; + (void)self; + (void)context; + /* TODO: Implement parser scope for import */ } static void funcdecl_resolve(Ast *self, AstCompilerContext *context) { @@ -200,26 +249,40 @@ static void funcdecl_resolve(Ast *self, AstCompilerContext *context) { FunctionDecl *func_decl; assert(self->type == AST_FUNCTION_DECL); func_decl = self->value.func_decl; - amal_log_debug("funcdecl resolve"); self->resolve_status = AST_RESOLVED; scope_resolve(&func_decl->body, context); } -static void funccall_resolve(FunctionCall *self, AstCompilerContext *context) { - /* TODO: Implement */ +static void funccall_resolve(Ast *self, AstCompilerContext *context) { + FunctionCall *func_call; Ast *ast; Ast *ast_end; - variable_resolve(&self->func, context); + func_call = self->value.func_call; + variable_resolve(&func_call->func, context, &self->resolved_type); - ast = buffer_start(&self->args); - ast_end = buffer_end(&self->args); - amal_log_debug("funccall resolve, func name: %.*s", self->func.name.size, self->func.name.data); + ast = buffer_start(&func_call->args); + ast_end = buffer_end(&func_call->args); for(; ast != ast_end; ++ast) { ast_resolve(ast, context); } } +static void structdecl_resolve(Ast *self, AstCompilerContext *context) { + /* TODO: Implement */ + StructDecl *struct_decl; + struct_decl = self->value.struct_decl; + self->resolved_type = struct_decl; + scope_resolve(&struct_decl->body, context); +} + +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->resolved_type); +} + static void binop_resolve(Binop *self, AstCompilerContext *context) { /* TODO: Implement */ ast_resolve(&self->lhs, context); @@ -237,7 +300,6 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { if(self->resolve_status == AST_RESOLVED) { return; } else if(self->resolve_status == AST_RESOLVING) { - tokenizer_get_code_reference_index(&context->parser->tokenizer, ast_get_code_reference(self).data); tokenizer_print_error(&context->parser->tokenizer, tokenizer_get_code_reference_index(&context->parser->tokenizer, ast_get_code_reference(self).data), "Found recursive dependency"); @@ -254,19 +316,26 @@ void ast_resolve(Ast *self, AstCompilerContext *context) { funcdecl_resolve(self, context); break; case AST_FUNCTION_CALL: - funccall_resolve(self->value.func_call, context); + funccall_resolve(self, context); + break; + case AST_STRUCT_DECL: + structdecl_resolve(self, context); + break; + case AST_STRUCT_FIELD: + structfield_resolve(self, context); break; case AST_LHS: lhs_resolve(self, context); break; case AST_IMPORT: /* TODO: When @import(...).data syntax is added, implement the resolve for it */ + import_resolve(self, context); break; case AST_STRING: /* TODO: Convert special combinations. For example \n to newline */ break; case AST_VARIABLE: - variable_resolve(self->value.variable, context); + variable_resolve(self->value.variable, context, &self->resolved_type); break; case AST_BINOP: binop_resolve(self->value.binop, context); diff --git a/src/compiler.c b/src/compiler.c index c56221b..f0e5928 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -3,13 +3,21 @@ #include "../include/ssa/ssa.h" #include "../include/std/log.h" #include "../include/std/mem.h" +#include "../include/std/hash.h" +#include "../include/std/file.h" #include "../include/std/alloc.h" #include #include +#include #include #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"); @@ -19,6 +27,49 @@ static CHECK_RESULT int get_thread_count_env_var(int *thread_count) { return 0; } +static usize strnlen(const char *str, usize max_length) { + usize len; + len = 0; + while(len < max_length && *str != '\0') { + ++len; + ++str; + } + return len; +} + +/* TODO: Allow to specify size and members? */ +static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char *name) { + 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)); + lhsexpr_init(lhs_expr, bool_true, create_buffer_view(name, strnlen(name, PATH_MAX))); + ast_init(&lhs_expr->rhs_expr, struct_decl, AST_STRUCT_DECL); + ast_init(&expr, lhs_expr, AST_LHS); + 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 0; +} + int amal_compiler_init(amal_compiler *self) { int i; int result; @@ -37,20 +88,22 @@ int amal_compiler_init(amal_compiler *self) { } am_memset(&self->allocator, 0, sizeof(self->allocator)); - am_memset(&self->main_thread_allocator, 0, sizeof(self->main_thread_allocator)); + am_memset(&self->root_scope, 0, sizeof(self->root_scope)); self->started = bool_false; self->generic_work_object_index = 0; amal_mutex_init(&self->mutex); return_if_error(scoped_allocator_init(&self->allocator)); - cleanup_if_error(scoped_allocator_init(&self->main_thread_allocator)); + cleanup_if_error(scope_init(&self->root_scope, NULL, &self->allocator)); cleanup_if_error(buffer_init(&self->parsers, &self->allocator)); cleanup_if_error(buffer_init(&self->queued_files, &self->allocator)); + cleanup_if_error(hash_map_init(&self->file_scopes, &self->allocator, sizeof(FileScopeReference*), hash_compare_string, amal_hash_string)); cleanup_if_error(scoped_allocator_alloc(&self->allocator, self->usable_thread_count * sizeof(ParserThreadData), (void**)&self->threads)); for(i = 0; i < self->usable_thread_count; ++i) cleanup_if_error(parser_thread_data_init(&self->threads[i])); + cleanup_if_error(init_default_types(self)); return AMAL_COMPILER_OK; cleanup: @@ -72,7 +125,6 @@ int amal_compiler_deinit(amal_compiler *self) { amal_mutex_deinit(&self->mutex); scoped_allocator_deinit(&self->allocator); - scoped_allocator_deinit(&self->main_thread_allocator); return result; } @@ -108,11 +160,13 @@ static CHECK_RESULT int amal_compiler_load_in_this_thread(amal_compiler *self, B int result; result = AMAL_COMPILER_ERR; - return_if_error(scoped_allocator_alloc(&self->allocator, sizeof(Parser), (void**)&parser)); + amal_log_info("Started parsing %.*s", (int)filepath.size, filepath.data); + return_if_error(scoped_allocator_alloc(allocator, sizeof(Parser), (void**)&parser)); return_if_error(parser_init(parser, self, allocator)); 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))); + amal_log_info("Finished parsing %.*s", (int)filepath.size, filepath.data); result = AMAL_COMPILER_OK; cleanup: @@ -156,13 +210,10 @@ static CHECK_RESULT int thread_resolve_ast(Parser *parser) { compiler_context.parser = parser; compiler_context.scope = NULL; result = setjmp(compiler_context.env); - assert(!parser->scope.parent); 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); } - if(result == 0) - assert(!compiler_context.scope); return result; } @@ -226,7 +277,7 @@ static CHECK_RESULT int amal_compiler_select_thread_for_work(amal_compiler *self void *thread_user_data; thread_user_data = NULL; *thread_selected = NULL; - result = AMAL_COMPILER_ERR; + result = AMAL_COMPILER_OK; cleanup_if_error(amal_mutex_lock(&self->mutex, "amal_compiler_select_thread_for_work")); for(i = 0; i < self->usable_thread_count; ++i) { @@ -270,23 +321,21 @@ static CHECK_RESULT int amal_compiler_select_thread_for_work(amal_compiler *self return result; } -static CHECK_RESULT int amal_compiler_check_all_threads_done(amal_compiler *self, bool *done) { +static CHECK_RESULT bool amal_compiler_check_all_threads_done(amal_compiler *self) { int i; - int result; - result = AMAL_COMPILER_OK; - *done = bool_false; + bool result; + result = bool_false; cleanup_if_error(amal_mutex_lock(&self->mutex, "amal_compiler_check_all_threads_done")); for(i = 0; i < self->usable_thread_count; ++i) { ParserThreadData *parser_thread_data; parser_thread_data = &self->threads[i]; if(parser_thread_data->status == PARSER_THREAD_STATUS_RUNNING) { - result = AMAL_COMPILER_ERR; goto cleanup; } } - *done = bool_true; + result = bool_true; cleanup: amal_mutex_tryunlock(&self->mutex); return result; @@ -316,7 +365,8 @@ static CHECK_RESULT int amal_compiler_load_file_join_threads(amal_compiler *self amal_mutex_tryunlock(&self->mutex); if(result != 0) goto cleanup; - ignore_result_int(parser_thread_data_join(parser_thread_data, &thread_return_data)); + /* TODO: Cleanup remaining threads if join fails */ + cleanup_if_error(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"); @@ -324,7 +374,7 @@ static CHECK_RESULT int amal_compiler_load_file_join_threads(amal_compiler *self } } - cleanup_if_error(amal_compiler_check_all_threads_done(self, &done)); + done = amal_compiler_check_all_threads_done(self); if(done) break; } @@ -355,19 +405,56 @@ static CHECK_RESULT int amal_compiler_dispatch_generic(amal_compiler *self, Thre return amal_compiler_load_file_join_threads(self); } +static CHECK_RESULT int try_create_file_scope(amal_compiler *compiler, const char *filepath, FileScopeReference **file_scope, bool *new_entry) { + int ret; + char *result_path; + usize result_path_size; + BufferView path_view; + + ret = -1; + result_path = NULL; + *new_entry = bool_false; + + return_if_error(file_get_canonical_path(filepath, &result_path, &result_path_size)); + path_view = create_buffer_view(result_path, result_path_size); + cleanup_if_error(amal_mutex_lock(&compiler->mutex, "try_create_file_scope")); + if(!hash_map_get(&compiler->file_scopes, path_view, file_scope)) { + cleanup_if_error(scoped_allocator_alloc(&compiler->allocator, sizeof(FileScopeReference), (void**)file_scope)); + /* @(*file_scope)->canonical_path won't change after this, so it's fine if allocator belongs to non-thread safe compiler instance */ + cleanup_if_error(file_scope_reference_init(*file_scope, path_view, &compiler->allocator)); + cleanup_if_error(hash_map_insert(&compiler->file_scopes, path_view, file_scope)); + *new_entry = bool_true; + } + + ret = 0; + cleanup: + amal_mutex_tryunlock(&compiler->mutex); + am_free(result_path); + return ret; +} + /* amal_compiler_load_file is called by the user for the first file to compile but also called by the parser when it sees @import */ -int amal_compiler_load_file(amal_compiler *self, BufferView filepath) { +int amal_compiler_load_file(amal_compiler *self, const char *filepath, FileScopeReference **file_scope) { int result; + BufferView filepath_view; ParserThreadData *parser_thread_data; ThreadWorkData thread_work_data; bool main_job; + bool new_entry; + + return_if_error(try_create_file_scope(self, filepath, file_scope, &new_entry)); + filepath_view = create_buffer_view((*file_scope)->canonical_path.data, (*file_scope)->canonical_path.size); + if(!new_entry) { + amal_log_info("amal_compiler_load_file: file already parsed: %.*s", filepath_view.size, filepath_view.data); + return 0; + } result = AMAL_COMPILER_ERR; thread_work_data.type = THREAD_WORK_PARSE; - thread_work_data.value.filepath = filepath; + thread_work_data.value.filepath = filepath_view; main_job = bool_false; /* The first time we get here, this will run single-threaded so this part doesn't need mutex */ @@ -379,13 +466,18 @@ int amal_compiler_load_file(amal_compiler *self, BufferView filepath) { return_if_error(amal_compiler_select_thread_for_work(self, thread_work_data, &parser_thread_data)); if(main_job) { - /*amal_log_info("Parsing %.*s using %d thread(s)", (int)filepath.size, filepath.data, self->usable_thread_count);*/ - /*return_if_error(amal_compiler_load_first_this_thread(self, filepath, &self->main_thread_allocator));*/ return_if_error(amal_compiler_load_file_join_threads(self)); - amal_log_debug("Finished parsing all files, resolving AST"); + assert(amal_compiler_check_all_threads_done(self)); + amal_log_info("Finished parsing all files, resolving AST"); + return_if_error(amal_compiler_dispatch_generic(self, THREAD_WORK_RESOLVE_AST)); - amal_log_debug("Finished resolving AST, generating SSA"); + assert(amal_compiler_check_all_threads_done(self)); + amal_log_info("Finished resolving AST, generating SSA"); + return_if_error(amal_compiler_dispatch_generic(self, THREAD_WORK_GENERATE_SSA)); + assert(amal_compiler_check_all_threads_done(self)); + amal_log_info("Finished generating SSA"); + return AMAL_COMPILER_OK; } @@ -393,7 +485,7 @@ int amal_compiler_load_file(amal_compiler *self, BufferView filepath) { return AMAL_COMPILER_OK; cleanup_if_error(amal_mutex_lock(&self->mutex, "amal_compiler_load_file")); - cleanup_if_error(buffer_append(&self->queued_files, &filepath, sizeof(filepath))); + cleanup_if_error(buffer_append(&self->queued_files, &filepath_view, sizeof(filepath_view))); result = AMAL_COMPILER_OK; cleanup: diff --git a/src/parser.c b/src/parser.c index 7c0280a..5396b72 100644 --- a/src/parser.c +++ b/src/parser.c @@ -11,6 +11,12 @@ #include #include +#ifdef AMAL_PEDANTIC + #define throw_debug_msg do {} while(0) +#else + #define throw_debug_msg do { amal_log_info("Throwing from %s:%d", __FUNCTION__, __LINE__); } while(0) +#endif + #define THROWABLE CHECK_RESULT int #define try(result) \ do { \ @@ -18,7 +24,7 @@ do { \ return_if_result = (result); \ (void)return_if_result; \ } while(0) -#define throw(result) do { longjmp(self->parse_env, (result)); } while(0) +#define throw(result) do { throw_debug_msg; longjmp(self->parse_env, (result)); } while(0) #define throw_if_error(result) \ do { \ int return_if_result; \ @@ -29,7 +35,8 @@ do { \ static THROWABLE parser_parse_rhs_start(Parser *self, Ast *rhs_expr); static THROWABLE parser_parse_body(Parser *self, Ast *ast); -static THROWABLE parser_queue_file(Parser *self, BufferView path); +static THROWABLE parser_parse_struct_body(Parser *self, Ast *ast); +static THROWABLE parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope); int parser_thread_data_init(ParserThreadData *self) { am_memset(&self->allocator, 0, sizeof(self->allocator)); @@ -39,8 +46,9 @@ int parser_thread_data_init(ParserThreadData *self) { } int parser_thread_data_deinit(ParserThreadData *self) { + ignore_result_int(amal_thread_deinit(&self->thread)); scoped_allocator_deinit(&self->allocator); - return amal_thread_deinit(&self->thread); + return 0; } int parser_thread_data_start(ParserThreadData *self, AmalThreadCallbackFunc callback_func, void *userdata) { @@ -63,9 +71,8 @@ int parser_init(Parser *self, amal_compiler *compiler, ScopedAllocator *allocato self->error.index = 0; self->error.str = NULL; self->error_context = ERROR_CONTEXT_NONE; - /* TODO: When resolving ast uses mutex, add compiler->scope as the parent of the parser scope */ self->current_scope = &self->scope; - return scope_init(&self->scope, NULL, self->allocator); + return scope_init(&self->scope, &compiler->root_scope, self->allocator); } /* @@ -100,6 +107,39 @@ static THROWABLE parser_parse_body_loop(Parser *self, Scope *scope, Token end_to return PARSER_OK; } +/* +STRUCT_BODY_LOOP = '{' STRUCT_BODY* '}' +*/ +static THROWABLE parser_parse_struct_body_loop(Parser *self, Scope *scope) { + int result; + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_OPEN_BRACE)); + for(;;) { + Ast body_obj; + bool is_end_token; + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CLOSING_BRACE, &is_end_token)); + if(is_end_token) + break; + + try(parser_parse_struct_body(self, &body_obj)); + result = scope_add_child(scope, &body_obj); + if(result == 0) { + continue; + } else if(result == AST_ERR_DEF_DUP) { + /* TODO: Convert ast type to string for error message */ + BufferView obj_name; + obj_name = ast_get_name(&body_obj); + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_code_reference_index(&self->tokenizer, obj_name.data), + "Variable with the name %.*s was declared twice in the struct", obj_name.size, obj_name.data); + self->error_context = ERROR_CONTEXT_NONE; + throw(result); + } else { + throw(result); + } + } + return PARSER_OK; +} + /* VAR_TYPE_DEF = ':' TOK_IDENTIFIER */ @@ -137,37 +177,44 @@ static THROWABLE parser_parse_lhs(Parser *self, LhsExpr **result, bool *assignme throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)result)); lhsexpr_init(*result, is_const, var_name); - try(parser_parse_var_type_def(self, &(*result)->type_name)); + try(parser_parse_var_type_def(self, &(*result)->type.name)); throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_EQUALS, &match)); if(match) { *assignment_or_rhs = bool_true; return PARSER_OK; } + + if(!(*result)->type.name.data) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_error_index(&self->tokenizer), + "Variable declaration requires type or right-hand side expression (Expected ':' or '=')"); + throw(PARSER_UNEXPECTED_TOKEN); + } *assignment_or_rhs = bool_false; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_SEMICOLON, &match)); if(match && is_const) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), - "const variable declaration requires assignment (expected '=', got ';')"); + "Const variable declaration requires assignment (expected '=', got ';')"); throw(PARSER_UNEXPECTED_TOKEN); } if(!match) { self->error = tokenizer_create_error(&self->tokenizer, tokenizer_get_error_index(&self->tokenizer), - "Expected '=' or ';'"); + "Expected ';'"); throw(PARSER_UNEXPECTED_TOKEN); } return PARSER_OK; } /* +TODO: Implement params CLOSURE = 'fn' ('(' PARAM* ')')? '{' BODY_LOOP '}' */ static THROWABLE parser_parse_function_decl(Parser *self, FunctionDecl **func_decl) { bool match; - Scope *prev_scope; *func_decl = NULL; throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_FN, &match)); @@ -186,11 +233,29 @@ static THROWABLE parser_parse_function_decl(Parser *self, FunctionDecl **func_de throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)func_decl)); throw_if_error(funcdecl_init(*func_decl, self->current_scope, self->allocator)); - prev_scope = self->current_scope; self->current_scope = &(*func_decl)->body; try(parser_parse_body_loop(self, self->current_scope, TOK_CLOSING_BRACE)); self->current_scope = (*func_decl)->body.parent; - assert(self->current_scope == prev_scope); + return PARSER_OK; +} + +/* +STRUCT = 'struct' '{' STRUCT_BODY_LOOP '}' +*/ +static THROWABLE parser_parse_struct_decl(Parser *self, StructDecl **struct_decl) { + bool match; + *struct_decl = NULL; + + throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_STRUCT, &match)); + if(!match) + return PARSER_OK; + + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(StructDecl), (void**)struct_decl)); + throw_if_error(structdecl_init(*struct_decl, self->current_scope, self->allocator)); + + self->current_scope = &(*struct_decl)->body; + try(parser_parse_struct_body_loop(self, self->current_scope)); + self->current_scope = (*struct_decl)->body.parent; return PARSER_OK; } @@ -381,6 +446,7 @@ RHS_START = CLOSURE | IMPORT | RHS */ int parser_parse_rhs_start(Parser *self, Ast *rhs_expr) { FunctionDecl *func_decl; + StructDecl *struct_decl; Import *import; try(parser_parse_function_decl(self, &func_decl)); @@ -389,10 +455,16 @@ int parser_parse_rhs_start(Parser *self, Ast *rhs_expr) { return PARSER_OK; } + try(parser_parse_struct_decl(self, &struct_decl)); + if(struct_decl) { + ast_init(rhs_expr, struct_decl, AST_STRUCT_DECL); + return PARSER_OK; + } + try(parser_parse_import(self, &import)); if(import) { + try(parser_queue_file(self, import->path, &import->file_scope)); ast_init(rhs_expr, import, AST_IMPORT); - try(parser_queue_file(self, import->path)); return PARSER_OK; } @@ -421,8 +493,8 @@ static THROWABLE parser_parse_body_semicolon(Parser *self, Ast *expr) { return PARSER_OK; } - /* TODO: Check for struct and tables */ - if(expr->type != AST_FUNCTION_DECL) + /* TODO: Check for tables */ + if(expr->type != AST_FUNCTION_DECL && expr->type != AST_STRUCT_DECL) throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); return PARSER_OK; @@ -457,6 +529,26 @@ int parser_parse_body(Parser *self, Ast *ast) { return PARSER_OK; } +/* +STRUCT_BODY = LHS ';' +*/ +int parser_parse_struct_body(Parser *self, Ast *ast) { + BufferView var_name; + BufferView type_name; + 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; + throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON)); + throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)&struct_field)); + structfield_init(struct_field, var_name, type_name); + ast_init(ast, struct_field, AST_STRUCT_FIELD); + return PARSER_OK; +} + /* ROOT = BODY_LOOP */ @@ -472,13 +564,16 @@ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_ tokenizer_print_error_object(&self->tokenizer, &self->error); break; case ERROR_CONTEXT_RHS_START: - tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, function call or import"); + tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, struct, function call or import"); break; default: assert(bool_false && "Error context handling not implemented"); break; } } + if(result != 0) { + amal_log_info("Failed, reason: %d", result); + } assert(self->current_scope == &self->scope); return result; } @@ -488,7 +583,6 @@ int parser_parse_file(Parser *self, BufferView filepath) { char *file_data; usize file_size; - amal_log_debug("Parsing %.*s", (int)filepath.size, filepath.data); assert(!self->started && "Parser can't be reused. Create a new parser."); self->started = bool_true; assert(filepath.size > 0 && filepath.data[filepath.size] == '\0'); @@ -498,13 +592,13 @@ int parser_parse_file(Parser *self, BufferView filepath) { return result; } -static CHECK_RESULT int file_path_join(BufferView directory, BufferView file, ScopedAllocator *allocator, Buffer *result) { - return_if_error(buffer_init(result, allocator)); - return_if_error(buffer_append(result, NULL, directory.size + 1 + file.size + 1)); - am_memcpy(result->data, directory.data, directory.size); - result->data[directory.size] = '/'; - am_memcpy(result->data + directory.size + 1, file.data, file.size); - result->data[directory.size + 1 + file.size] = '\0'; +static CHECK_RESULT int file_path_join(BufferView directory, BufferView file, char **result_path) { + /* '/' '\0' */ + return_if_error(am_malloc(directory.size + 1 + file.size + 1, (void**)result_path)); + am_memcpy(*result_path, directory.data, directory.size); + (*result_path)[directory.size] = '/'; + am_memcpy(*result_path + directory.size + 1, file.data, file.size); + (*result_path)[directory.size + 1 + file.size] = '\0'; return 0; } @@ -512,19 +606,24 @@ static CHECK_RESULT int file_path_join(BufferView directory, BufferView file, Sc Path can be path to included library path (or system library path) in which case the path separator is a dot, otherwise the path separator is forward slash '/' */ -int parser_queue_file(Parser *self, BufferView path) { - /* TODO: Do not load same path twice or the compile will fail (and it can have recursive import) also for performance reasons */ +int parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope) { /* TODO: Parse special path (to include library path with dots) */ BufferView file_directory; - BufferView filename; - Buffer file_to_parse; + char *path_relative; + int result; + file_directory = file_get_parent_directory(self->tokenizer.code_name); - filename = file_get_name(path); - return_if_error(file_path_join(file_directory, filename, self->allocator, &file_to_parse)); - /* - We want buffer to be null terminated but null terminated character - should not be included for the length. - */ - throw_if_error(amal_compiler_load_file(self->compiler, create_buffer_view(file_to_parse.data, file_to_parse.size - 1))); + return_if_error(file_path_join(file_directory, path, &path_relative)); + /* We want buffer to be null-terminated but null character should not be included for the size */ + result = amal_compiler_load_file(self->compiler, path_relative, file_scope); + if(result != 0) { + self->error = tokenizer_create_error(&self->tokenizer, + tokenizer_get_code_reference_index(&self->tokenizer, path.data), + "Failed to while parsing path %s (invalid path?)", path_relative); + self->error_context = ERROR_CONTEXT_NONE; + am_free(path_relative); + throw(result); + } + am_free(path_relative); return PARSER_OK; } diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c index f892531..b04e469 100644 --- a/src/ssa/ssa.c +++ b/src/ssa/ssa.c @@ -123,6 +123,17 @@ static CHECK_RESULT int ssa_add_ins_form1(Ssa *self, SsaInstructionType ins_type return 0; } +static const char* binop_type_to_string(SsaInstructionType binop_type) { + assert(binop_type >= SSA_ADD && binop_type <= SSA_DIV); + switch(binop_type) { + case SSA_ADD: return "+"; + case SSA_SUB: return "-"; + case SSA_MUL: return "*"; + case SSA_DIV: return "/"; + default: return ""; + } +} + static CHECK_RESULT int ssa_add_ins_form2(Ssa *self, SsaInstructionType ins_type, SsaRegister lhs, SsaRegister rhs, SsaRegister *result) { usize index; index = self->instructions.size; @@ -138,7 +149,7 @@ static CHECK_RESULT int ssa_add_ins_form2(Ssa *self, SsaInstructionType ins_type *(SsaRegister*)&self->instructions.data[index + 1] = *result; *(SsaRegister*)&self->instructions.data[index + 3] = lhs; *(SsaRegister*)&self->instructions.data[index + 5] = rhs; - amal_log_debug("r%u = r%u + r%u", *result, lhs, rhs); + amal_log_debug("r%u = r%u %s r%u", *result, lhs, binop_type_to_string(ins_type), rhs); return 0; } @@ -261,6 +272,19 @@ static CHECK_RESULT SsaRegister funccall_generate_ssa(FunctionCall *self, SsaCom return reg; } +static CHECK_RESULT SsaRegister structdecl_generate_ssa(StructDecl *self, SsaCompilerContext *context) { + /* TODO: Implement */ + scope_generate_ssa(&self->body, context); + return 0; +} + +static CHECK_RESULT SsaRegister structfield_generate_ssa(StructField *self, SsaCompilerContext *context) { + /* TODO: Implement */ + (void)self; + (void)context; + return 0; +} + static CHECK_RESULT SsaRegister lhs_generate_ssa(LhsExpr *self, SsaCompilerContext *context) { /* TODO: Implement */ SsaRegister rhs_reg; @@ -321,6 +345,10 @@ CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context return funcdecl_generate_ssa(self->value.func_decl, context); case AST_FUNCTION_CALL: return funccall_generate_ssa(self->value.func_call, context); + case AST_STRUCT_DECL: + return structdecl_generate_ssa(self->value.struct_decl, context); + case AST_STRUCT_FIELD: + return structfield_generate_ssa(self->value.struct_field, context); case AST_LHS: return lhs_generate_ssa(self->value.lhs_expr, context); case AST_IMPORT: diff --git a/src/std/alloc.c b/src/std/alloc.c index 93dcb98..d07e94f 100644 --- a/src/std/alloc.c +++ b/src/std/alloc.c @@ -1,10 +1,13 @@ #include "../../include/std/alloc.h" +#include "../../include/std/log.h" #include int am_malloc(usize size, void **mem) { void *allocated_data = malloc(size); - if(!allocated_data) + if(!allocated_data) { + amal_log_error("am_malloc: failed to allocate memory of size %lu", size); return ALLOC_FAIL; + } *mem = allocated_data; return ALLOC_OK; @@ -12,8 +15,10 @@ int am_malloc(usize size, void **mem) { int am_realloc(void *mem, usize new_size, void **new_mem) { void *new_allocated_data = realloc(mem, new_size); - if(!new_allocated_data) + if(!new_allocated_data) { + amal_log_error("am_malloc: failed to reallocate memory to size %lu", new_size); return ALLOC_FAIL; + } *new_mem = new_allocated_data; return ALLOC_OK; diff --git a/src/std/file.c b/src/std/file.c index 177ccbc..7de2b6c 100644 --- a/src/std/file.c +++ b/src/std/file.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include @@ -177,7 +179,7 @@ typedef enum { static CHECK_RESULT int file_get_type(const char *filepath, FileType *type) { struct stat file_stats; if(stat(filepath, &file_stats) == -1) { - amal_log_perror(filepath); + amal_log_error("file_get_type: %s failed: ", filepath, strerror(errno)); return -1; } @@ -202,12 +204,11 @@ int read_whole_file(const char *filepath, char **data, usize *size) { return -2; } - result = 0; file = fopen(filepath, "rb"); if(!file) { int error; error = errno; - amal_log_perror(filepath); + amal_log_error("read_whole_file: %s failed: ", filepath, strerror(error)); return error; } @@ -241,6 +242,21 @@ int read_whole_file(const char *filepath, char **data, usize *size) { return result; } +int file_get_canonical_path(const char *filepath, char **result_path, usize *result_path_size) { + char result_path_tmp[PATH_MAX]; + if(!realpath(filepath, result_path_tmp)) { + int ret; + ret = errno; + amal_log_error("file_get_canonical_path: realpath failed for path %s, error: %s", filepath, strerror(ret)); + return ret; + } + *result_path_size = strnlen(result_path_tmp, PATH_MAX); + return_if_error(am_malloc(*result_path_size + 1, (void**)result_path)); + am_memcpy(*result_path, result_path_tmp, *result_path_size); + (*result_path)[*result_path_size] = '\0'; + return 0; +} + BufferView file_get_parent_directory(BufferView filepath) { int i; for(i = filepath.size - 1; i >= 0; --i) { diff --git a/src/std/log.c b/src/std/log.c index 2563bdc..2059445 100644 --- a/src/std/log.c +++ b/src/std/log.c @@ -4,6 +4,8 @@ #include #include +/*#define LOG_DEBUG*/ + static amal_mutex mutex; static bool mutex_initialized = bool_false; @@ -21,6 +23,7 @@ amal_mutex* amal_log_get_mutex() { } void amal_log_debug(const char *fmt, ...) { +#ifdef LOG_DEBUG va_list args; mutex_init(); ignore_result_int(amal_mutex_lock(&mutex, NULL)); @@ -30,13 +33,16 @@ void amal_log_debug(const char *fmt, ...) { va_end(args); fprintf(stderr, "\n"); ignore_result_int(amal_mutex_unlock(&mutex)); +#else + (void)fmt; +#endif } void amal_log_error(const char *fmt, ...) { va_list args; mutex_init(); ignore_result_int(amal_mutex_lock(&mutex, NULL)); - fprintf(stderr, "Error: "); + fprintf(stderr, "\x1b[1;31mError:\x1b[0m "); va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); @@ -66,11 +72,4 @@ void amal_log_warning(const char *fmt, ...) { va_end(args); fprintf(stderr, "\n"); ignore_result_int(amal_mutex_unlock(&mutex)); -} - -void amal_log_perror(const char *prefix) { - mutex_init(); - ignore_result_int(amal_mutex_lock(&mutex, NULL)); - perror(prefix); - ignore_result_int(amal_mutex_unlock(&mutex)); -} +} \ No newline at end of file diff --git a/src/std/scoped_allocator.c b/src/std/scoped_allocator.c index dfed0b7..c66639a 100644 --- a/src/std/scoped_allocator.c +++ b/src/std/scoped_allocator.c @@ -1,5 +1,6 @@ #include "../../include/std/scoped_allocator.h" #include "../../include/std/alloc.h" +#include "../../include/std/log.h" #include #define ALLOC_NODE_SIZE 4096 @@ -36,18 +37,18 @@ static void buffer_deinit(Buffer *self) { } void scoped_allocator_deinit(ScopedAllocator *self) { - Buffer *buffer; - Buffer *buffer_end; + Buffer **buffer; + Buffer **buffers_end; - scoped_allocator_node_deinit(&self->head); self->current = NULL; - buffer = (Buffer*)self->buffers.data; - buffer_end = buffer + self->buffers.size / sizeof(Buffer); - while(buffer != buffer_end) { - buffer_deinit(buffer); + buffer = buffer_start(&self->buffers); + buffers_end = buffer_end(&self->buffers); + while(buffer != buffers_end) { + buffer_deinit(*buffer); ++buffer; } buffer_deinit(&self->buffers); + scoped_allocator_node_deinit(&self->head); } static CHECK_RESULT int scoped_allocator_ensure_capacity_for(ScopedAllocator *self, usize size) { @@ -82,9 +83,13 @@ int scoped_allocator_alloc(ScopedAllocator *self, usize size, void **mem) { ScopedAllocatorNode *current; usize alloc_size; assert(self->current); - assert(size <= ALLOC_NODE_SIZE); current = self->current; + if(size >= ALLOC_NODE_SIZE) { + amal_log_error("scoped_allocator_alloc: tried to alloc memory of size %lu. Max allowed alloc size is %lu", size, ALLOC_NODE_SIZE); + return -1; + } + alloc_size = size + align_ptr_ceil_offset(self->current->data + self->current->size, 16); return_if_error(scoped_allocator_ensure_capacity_for(self, alloc_size)); /* Reallocated (new node created) */ @@ -99,5 +104,5 @@ int scoped_allocator_alloc(ScopedAllocator *self, usize size, void **mem) { } int scoped_allocator_add_buffer(ScopedAllocator *self, Buffer *buffer) { - return buffer_append(&self->buffers, buffer, sizeof(Buffer)); + return buffer_append(&self->buffers, &buffer, sizeof(Buffer*)); } diff --git a/src/std/thread.c b/src/std/thread.c index 64a7f1b..ba67463 100644 --- a/src/std/thread.c +++ b/src/std/thread.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -48,12 +49,21 @@ int amal_thread_deinit(amal_thread *self) { if(self->cancellable) { r1 = pthread_cancel(self->thread_id); + /* thread deinit on a thread that has not been started shouldn't be an error */ + if(r1 == ESRCH) + r1 = 0; + if(r1 != 0) + amal_log_error("amal_thread_deinit: failed to cancel thread, error: %s", strerror(r1)); self->cancellable = bool_false; } + if(self->destroyable) { r2 = pthread_attr_destroy(&self->thread_attr); + if(r2 != 0) + amal_log_error("amal_thread_deinit: failed to destroy thread attributes, error: %s", strerror(r2)); self->destroyable = bool_false; } + return r1 != 0 ? r1 : r2; } @@ -73,20 +83,35 @@ int amal_thread_detach(amal_thread *self) { int amal_thread_join(amal_thread *self, void **result) { int result_err; int thread_type; - if((result_err = pthread_attr_getdetachstate(&self->thread_attr, &thread_type)) != 0) + + if((result_err = pthread_attr_getdetachstate(&self->thread_attr, &thread_type)) != 0) { + amal_log_error("amal_thread_join: failed to get detach state, error: %d", result_err); return result_err; - if(thread_type != PTHREAD_CREATE_JOINABLE) + } + + if(thread_type != PTHREAD_CREATE_JOINABLE) { + amal_log_error("amal_thread_join: thread not joinable type"); return AMAL_THREAD_NOT_JOINABLE; - if((result_err = pthread_join(self->thread_id, result)) != 0) + } + + if((result_err = pthread_join(self->thread_id, result)) != 0) { + /* Joining a thread that is not joinable 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)); return result_err == EINVAL ? AMAL_THREAD_NOT_JOINABLE : result_err; + } + + cleanup: self->cancellable = bool_false; return 0; } void amal_mutex_init(amal_mutex *self) { pthread_mutex_init(&self->mutex, NULL); - /* TODO: pthread_mutex_destroy in amal_mutex_deinit */ self->lock_identifier = NULL; + self->locked = bool_false; + self->owner_thread = 0; } void amal_mutex_deinit(amal_mutex *self) { @@ -105,6 +130,8 @@ int amal_mutex_lock(amal_mutex *self, const char *lock_identifier) { int result; result = pthread_mutex_lock(&self->mutex); self->lock_identifier = lock_identifier; + self->owner_thread = pthread_self(); + self->locked = bool_true; #ifdef AMAL_MUTEX_DEBUG if(result == 0 && self->lock_identifier) { amal_log_debug("amal_mutex_lock: mutex locked by thread %lu (%s), identification: %s", @@ -122,6 +149,10 @@ int amal_mutex_unlock(amal_mutex *self) { const char *identifier; identifier = self->lock_identifier; #endif + if(!self->locked || pthread_equal(self->owner_thread, pthread_self()) == 0) + return 0; + self->locked = bool_false; + self->owner_thread = 0; result = pthread_mutex_unlock(&self->mutex); #ifdef AMAL_MUTEX_DEBUG if(result == 0 && identifier) { diff --git a/src/tokenizer.c b/src/tokenizer.c index 5f6e59c..4403251 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -62,6 +62,7 @@ static Token tokenizer_skip_whitespace(Tokenizer *self) { ++self->index; } self->prev_index = self->index; + return TOK_NONE; } /* Returns -1 if end of string can't be found */ @@ -184,6 +185,13 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) { } break; } + case 6: { + if(am_memeql(self->value.identifier.data, "struct", 6)) { + *token = TOK_STRUCT; + return TOKENIZER_OK; + } + break; + } } *token = TOK_IDENTIFIER; } else if(c == '"') { @@ -397,6 +405,9 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_FN: str = "fn"; break; + case TOK_STRUCT: + str = "struct"; + break; case TOK_EQUALS: str = "="; break; @@ -431,9 +442,6 @@ static BufferView tokenizer_expected_token_as_string(Token token) { case TOK_COLON: str = ":"; break; - default: - str = "Unknown token"; - break; } return create_buffer_view(str, strlen(str)); } -- cgit v1.2.3