aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast.c121
-rw-r--r--src/compiler.c138
-rw-r--r--src/parser.c167
-rw-r--r--src/ssa/ssa.c30
-rw-r--r--src/std/alloc.c9
-rw-r--r--src/std/file.c22
-rw-r--r--src/std/log.c17
-rw-r--r--src/std/scoped_allocator.c23
-rw-r--r--src/std/thread.c39
-rw-r--r--src/tokenizer.c14
10 files changed, 466 insertions, 114 deletions
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 <stdlib.h>
#include <stdio.h>
+#include <limits.h>
#include <assert.h>
#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 <stdlib.h>
#include <assert.h>
+#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);
}
/*
@@ -101,6 +108,39 @@ static THROWABLE parser_parse_body_loop(Parser *self, Scope *scope, Token end_to
}
/*
+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
*/
static THROWABLE parser_parse_var_type_def(Parser *self, BufferView *type_name) {
@@ -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;
@@ -458,6 +530,26 @@ int parser_parse_body(Parser *self, Ast *ast) {
}
/*
+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
*/
int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_name) {
@@ -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 <stdlib.h>
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 <errno.h>
#include <assert.h>
#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
@@ -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 <stdio.h>
#include <stdarg.h>
+/*#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 <assert.h>
#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 <stdio.h>
#include <assert.h>
#include <errno.h>
+#include <string.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/syscall.h>
@@ -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));
}