aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-03-18 23:47:45 +0100
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commit2323ca6c9ec3c8ee76b9acf13745b80b92952a6a (patch)
tree93013237dbcb0fa96ceb5f3c026fd040aff464cf
parent5a93c32a59775cd1be4b4f450e8230016b434366 (diff)
Add struct, import caching, binop ops etc
-rwxr-xr-xbuild.sh9
-rw-r--r--doc/DESIGN.md6
-rw-r--r--include/ast.h31
-rw-r--r--include/compiler.h13
-rw-r--r--include/defs.h1
-rw-r--r--include/std/buffer.h2
-rw-r--r--include/std/file.h4
-rw-r--r--include/std/log.h1
-rw-r--r--include/std/misc.h16
-rw-r--r--include/std/scoped_allocator.h2
-rw-r--r--include/std/thread.h3
-rw-r--r--include/tokenizer.h1
-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
-rw-r--r--tests/b.amal5
-rw-r--r--tests/io.amal2
-rw-r--r--tests/main.amal8
-rw-r--r--tests/main.c5
-rw-r--r--tests/sub/a.amal1
27 files changed, 556 insertions, 134 deletions
diff --git a/build.sh b/build.sh
index ab43d20..240b2e9 100755
--- a/build.sh
+++ b/build.sh
@@ -12,11 +12,18 @@ fi
CFLAGS=""
LIBS=""
+
if [ ! -z "$SANITIZE_ADDRESS" ]; then
CFLAGS+="-fsanitize=address "
+elif [ ! -z "$SANITIZE_THREAD" ]; then
+ CFLAGS+="-fsanitize=thread "
+fi
+
+if [ ! -z "$PEDANTIC" ]; then
+ CFLAGS+="-DAMAL_PEDANTIC -pedantic "
fi
-CFLAGS+="-Wall -Wextra -Werror -g -O0 -DDEBUG -std=c89 -pedantic -D_GNU_SOURCE"
+CFLAGS+="-Wall -Wextra -Werror -g -O0 -DDEBUG -std=c89 -D_GNU_SOURCE"
LIBS+="-pthread"
BUILD_ARGS="$source_files $CFLAGS $LIBS -shared -fpic -o libamalgam.so"
diff --git a/doc/DESIGN.md b/doc/DESIGN.md
index 37ca82a..7781cd1 100644
--- a/doc/DESIGN.md
+++ b/doc/DESIGN.md
@@ -92,7 +92,7 @@ const main = fn() !void {
## Structures and instances
```
-struct User {
+const User = struct {
name: str,
age: i32,
level = 1 // default value is 1 and type is i32
@@ -124,7 +124,7 @@ const main = fn {
## Named parameters
Functions call be called with arguments in position that matches the parameters or by using the names of the parameters.
```
-struct User {
+const User = struct {
name: str,
age: i32,
level: i32
@@ -165,7 +165,7 @@ doesn't change location after realloc, which would be the case for pointers.
```
const ArrayList = @import("std.array.ArrayList")
-struct User {
+const User = struct {
name: str,
level: i32
}
diff --git a/include/ast.h b/include/ast.h
index 6077d5d..e49ad08 100644
--- a/include/ast.h
+++ b/include/ast.h
@@ -18,6 +18,8 @@
typedef struct FunctionDecl FunctionDecl;
typedef struct FunctionCall FunctionCall;
+typedef struct StructDecl StructDecl;
+typedef struct StructField StructField;
typedef struct LhsExpr LhsExpr;
typedef struct Import Import;
typedef struct String String;
@@ -29,6 +31,8 @@ typedef union {
void *data;
FunctionDecl *func_decl;
FunctionCall *func_call;
+ StructDecl *struct_decl;
+ StructField *struct_field;
LhsExpr *lhs_expr;
Import *import;
String *string;
@@ -41,6 +45,8 @@ typedef enum {
AST_NONE,
AST_FUNCTION_DECL,
AST_FUNCTION_CALL,
+ AST_STRUCT_DECL,
+ AST_STRUCT_FIELD,
AST_LHS,
AST_IMPORT,
AST_STRING,
@@ -59,6 +65,7 @@ typedef struct {
AstValue value;
AstType type;
AstResolveStatus resolve_status;
+ StructDecl *resolved_type;
} Ast;
struct Scope {
@@ -67,6 +74,11 @@ struct Scope {
Scope *parent;
};
+struct FileScopeReference {
+ Scope *scope;
+ Buffer canonical_path;
+};
+
struct Variable {
BufferView name;
Ast resolved_variable;
@@ -82,15 +94,25 @@ struct FunctionCall {
Buffer/*Ast*/ args;
};
+struct StructDecl {
+ Scope body;
+};
+
+struct StructField {
+ BufferView name;
+ Variable type;
+};
+
struct LhsExpr {
- int is_const;
- BufferView type_name;
+ bool is_const;
BufferView var_name;
+ Variable type;
Ast rhs_expr;
};
struct Import {
BufferView path;
+ FileScopeReference *file_scope;
};
struct String {
@@ -125,7 +147,9 @@ BufferView ast_get_name(Ast *self);
CHECK_RESULT int funcdecl_init(FunctionDecl *self, Scope *parent, ScopedAllocator *allocator);
CHECK_RESULT int funccall_init(FunctionCall *self, BufferView name, ScopedAllocator *allocator);
-void lhsexpr_init(LhsExpr *self, int isConst, BufferView var_name);
+CHECK_RESULT int structdecl_init(StructDecl *self, Scope *parent, ScopedAllocator *allocator);
+void structfield_init(StructField *self, BufferView name, BufferView type_name);
+void lhsexpr_init(LhsExpr *self, bool is_const, BufferView var_name);
void import_init(Import *self, BufferView path);
CHECK_RESULT int string_init(String *self, BufferView str);
void number_init(Number *self, i64 value, bool is_integer);
@@ -133,6 +157,7 @@ void variable_init(Variable *self, BufferView name);
void binop_init(Binop *self);
CHECK_RESULT int scope_init(Scope *self, Scope *parent, ScopedAllocator *allocator);
+CHECK_RESULT int file_scope_reference_init(FileScopeReference *self, BufferView canonical_path, ScopedAllocator *allocator);
CHECK_RESULT int scope_add_child(Scope *self, Ast *child);
/* longjump to compiler env on failure */
void scope_resolve(Scope *self, AstCompilerContext *context);
diff --git a/include/compiler.h b/include/compiler.h
index 32a5eab..47d4471 100644
--- a/include/compiler.h
+++ b/include/compiler.h
@@ -7,6 +7,7 @@
#include "std/scoped_allocator.h"
#include "std/thread.h"
#include "defs.h"
+#include "ast.h"
#define AMAL_COMPILER_OK 0
/* General error */
@@ -14,9 +15,10 @@
struct amal_compiler {
ScopedAllocator allocator;
- ScopedAllocator main_thread_allocator;
+ Scope root_scope;
Buffer/*<Parser*>*/ parsers;
- Buffer queued_files;
+ Buffer/*<BufferView>*/ queued_files;
+ HashMap/*<BufferView, FileScopeReference*>*/ file_scopes;
ParserThreadData *threads;
int usable_thread_count;
bool started;
@@ -26,8 +28,11 @@ struct amal_compiler {
CHECK_RESULT int amal_compiler_init(amal_compiler *self);
CHECK_RESULT int amal_compiler_deinit(amal_compiler *self);
-/* Not thread-safe */
-CHECK_RESULT int amal_compiler_load_file(amal_compiler *self, BufferView filepath);
+/*
+This function creates a copy of @filepath in the same thread it's called from
+so it doesn't have to survive longer than this function call.
+*/
+CHECK_RESULT int amal_compiler_load_file(amal_compiler *self, const char *filepath, FileScopeReference **file_scope);
/* TODO: amal_compiler_unload_file */
#endif
diff --git a/include/defs.h b/include/defs.h
index 74f2411..76e5e0d 100644
--- a/include/defs.h
+++ b/include/defs.h
@@ -5,5 +5,6 @@ typedef struct ParserThreadData ParserThreadData;
typedef struct amal_compiler amal_compiler;
typedef struct Parser Parser;
typedef struct Scope Scope;
+typedef struct FileScopeReference FileScopeReference;
#endif
diff --git a/include/std/buffer.h b/include/std/buffer.h
index c961b6e..a1bfb01 100644
--- a/include/std/buffer.h
+++ b/include/std/buffer.h
@@ -21,7 +21,7 @@ CHECK_RESULT int buffer_append(Buffer *self, const void *data, usize size);
void* buffer_get(Buffer *self, usize index, usize type_size);
CHECK_RESULT int buffer_pop(Buffer *self, void *data, usize size);
void* buffer_start(Buffer *self);
-void *buffer_end(Buffer *self);
+void* buffer_end(Buffer *self);
usize __buffer_get_size(Buffer *self, usize type_size);
#define buffer_get_size(self, type) __buffer_get_size((self), sizeof(type))
diff --git a/include/std/file.h b/include/std/file.h
index 120e7bb..c6753fd 100644
--- a/include/std/file.h
+++ b/include/std/file.h
@@ -4,6 +4,7 @@
#include "misc.h"
#include "types.h"
#include "buffer_view.h"
+#include "buffer.h"
/* Return bool_true if you want to continue scanning, otherwise return bool_false */
typedef bool (*scan_dir_callback_func)(const char *filepath, int filepath_length, void *userdata);
@@ -31,7 +32,10 @@ CHECK_RESULT int mapped_file_init(MappedFile *self, const char *filepath, Mapped
CHECK_RESULT int mapped_file_deinit(MappedFile *self);
#endif
+/* @data will be allocated with am_malloc, should be deallocated with am_free */
CHECK_RESULT int read_whole_file(const char *filepath, char **data, usize *size);
+/* @result_path will be allocated with am_malloc, should be deallocated with am_free */
+CHECK_RESULT int file_get_canonical_path(const char *filepath, char **result_path, usize *result_path_size);
BufferView file_get_parent_directory(BufferView filepath);
BufferView file_get_name(BufferView filepath);
diff --git a/include/std/log.h b/include/std/log.h
index d13c5bf..cd376c6 100644
--- a/include/std/log.h
+++ b/include/std/log.h
@@ -8,6 +8,5 @@ void amal_log_debug(const char *fmt, ...);
void amal_log_error(const char *fmt, ...);
void amal_log_info(const char *fmt, ...);
void amal_log_warning(const char *fmt, ...);
-void amal_log_perror(const char *prefix);
#endif
diff --git a/include/std/misc.h b/include/std/misc.h
index b979d9c..e28ed48 100644
--- a/include/std/misc.h
+++ b/include/std/misc.h
@@ -1,15 +1,27 @@
#ifndef AMALGAM_MISC_H
#define AMALGAM_MISC_H
+#include <stdio.h>
+
+#ifdef AMAL_PEDANTIC
+ #define return_if_debug_msg do {} while(0)
+ #define cleanup_if_debug_msg do {} while(0)
+#else
+ #define return_if_debug_msg do { fprintf(stderr, "Return from %s:%d\n", __FUNCTION__, __LINE__); } while(0)
+ #define cleanup_if_debug_msg do { fprintf(stderr, "cleanup from %s:%d\n", __FUNCTION__, __LINE__); } while(0)
+#endif
+
#define return_if_error(result) \
do { \
int return_if_result; \
return_if_result = (result); \
- if((return_if_result) != 0) \
+ if((return_if_result) != 0) { \
+ return_if_debug_msg; \
return return_if_result; \
+ } \
} while(0)
-#define cleanup_if_error(result) do { if((result) != 0) goto cleanup; } while(0)
+#define cleanup_if_error(result) do { if((result) != 0) { cleanup_if_debug_msg; goto cleanup; } } while(0)
#if defined(__GNUC__) && __GNUC__ >= 4
#define CHECK_RESULT __attribute__ ((warn_unused_result))
diff --git a/include/std/scoped_allocator.h b/include/std/scoped_allocator.h
index 0de4f04..9090767 100644
--- a/include/std/scoped_allocator.h
+++ b/include/std/scoped_allocator.h
@@ -15,7 +15,7 @@ struct ScopedAllocatorNode {
struct ScopedAllocator {
ScopedAllocatorNode head;
ScopedAllocatorNode *current;
- Buffer buffers;
+ Buffer/*<Buffer*>*/ buffers;
};
CHECK_RESULT int scoped_allocator_node_init(ScopedAllocatorNode *self);
diff --git a/include/std/thread.h b/include/std/thread.h
index 915d6a9..6eedb08 100644
--- a/include/std/thread.h
+++ b/include/std/thread.h
@@ -29,6 +29,8 @@ typedef enum {
struct amal_mutex {
pthread_mutex_t mutex;
const char *lock_identifier;
+ bool locked;
+ pthread_t owner_thread;
};
CHECK_RESULT int amal_thread_create(amal_thread *self, amal_thread_type thread_type, const char *name, AmalThreadCallbackFunc callback_func, void *userdata);
@@ -40,6 +42,7 @@ CHECK_RESULT int amal_thread_join(amal_thread *self, void **result);
void amal_mutex_init(amal_mutex *self);
void amal_mutex_deinit(amal_mutex *self);
CHECK_RESULT int amal_mutex_lock(amal_mutex *self, const char *lock_identifier);
+/* Safe to call unlock when another thread owns the lock or if the lock is not locked */
CHECK_RESULT int amal_mutex_unlock(amal_mutex *self);
void amal_mutex_tryunlock(amal_mutex *self);
diff --git a/include/tokenizer.h b/include/tokenizer.h
index a06b689..c4c3725 100644
--- a/include/tokenizer.h
+++ b/include/tokenizer.h
@@ -19,6 +19,7 @@ typedef enum {
TOK_VAR,
TOK_STRING,
TOK_FN,
+ TOK_STRUCT,
TOK_EQUALS,
TOK_OPEN_PAREN,
TOK_CLOSING_PAREN,
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));
}
diff --git a/tests/b.amal b/tests/b.amal
new file mode 100644
index 0000000..c7d833d
--- /dev/null
+++ b/tests/b.amal
@@ -0,0 +1,5 @@
+const a = @import("io.amal");
+
+const nothing = fn {
+
+} \ No newline at end of file
diff --git a/tests/io.amal b/tests/io.amal
index bb7935b..5505d44 100644
--- a/tests/io.amal
+++ b/tests/io.amal
@@ -1,3 +1,5 @@
+const a = @import("sub/a.amal");
+
const puts = fn {
} \ No newline at end of file
diff --git a/tests/main.amal b/tests/main.amal
index ba6247b..d085501 100644
--- a/tests/main.amal
+++ b/tests/main.amal
@@ -1,5 +1,10 @@
const io = @import("io.amal");
+const User = struct {
+ name: str;
+ age: i32;
+}
+
const main = fn {
var hello = fn {
@@ -14,8 +19,9 @@ const main = fn {
/*
episfjpseifipesf
*/
+ //io.puts("lole");
}
const print = fn {
-}
+} \ No newline at end of file
diff --git a/tests/main.c b/tests/main.c
index 8e45ba4..2b57bcf 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -43,9 +43,8 @@ static CHECK_RESULT int test_hash_map() {
int main() {
amal_compiler compiler;
+ FileScopeReference *file_scope;
int result;
- const char *filepath;
- filepath = "tests/main.amal";
return_if_error(test_hash_map());
@@ -55,7 +54,7 @@ int main() {
return 1;
}
- result = amal_compiler_load_file(&compiler, create_buffer_view(filepath, strlen(filepath)));
+ result = amal_compiler_load_file(&compiler, "tests/main.amal", &file_scope);
if(result != AMAL_COMPILER_OK) {
fprintf(stderr, "Failed to load file, error code: %d\n", result);
return 1;
diff --git a/tests/sub/a.amal b/tests/sub/a.amal
new file mode 100644
index 0000000..1e9c8a4
--- /dev/null
+++ b/tests/sub/a.amal
@@ -0,0 +1 @@
+const b = @import("../b.amal"); \ No newline at end of file