aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-03-24 01:19:18 +0100
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commitc207014f81fe742675c6e869b59d09e916c69fc6 (patch)
treedc4d82ea1d8ddabcf5fdc0d9eb3a16f5044063e9 /src
parentdc90305a767feaacc3430aaee0928b745a8e5b0f (diff)
Resolve cross-file references (with mutex). Not done
Diffstat (limited to 'src')
-rw-r--r--src/ast.c164
-rw-r--r--src/compiler.c57
-rw-r--r--src/parser.c15
-rw-r--r--src/std/thread.c2
-rw-r--r--src/tokenizer.c2
5 files changed, 162 insertions, 78 deletions
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 <assert.h>
@@ -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;
}