aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2019-07-17 19:23:16 +0200
committerdec05eba <dec05eba@protonmail.com>2020-07-25 14:36:46 +0200
commit84e65c63e7482590d535e86f7660a00ae8a0cecb (patch)
treec79de87b7136e96b977003db85d43e5e676bbfc1
parent85c654a102701958d3748e82ecac9c1bc4dbbcba (diff)
Start on amal program
Fix mutex issue in lhs expr which can cause a deadlock when a file has an error and throws and doesn't close the mutex and another thread waits for that mutex. The mutex can instead be removed and ignore race conditions which are uncommon. This should improve memory usage and performance.
-rw-r--r--README.md5
-rwxr-xr-xbuild.sh2
-rw-r--r--doc/BytecodeHeader.md8
-rw-r--r--doc/CompilerFlow.md6
-rw-r--r--doc/Documentation.md31
-rw-r--r--doc/Opcode.md14
-rwxr-xr-xdoc/doc_extract.py20
-rw-r--r--include/ast.h19
-rw-r--r--include/bytecode/bytecode.h5
-rw-r--r--include/compiler.h15
-rw-r--r--include/parser.h7
-rw-r--r--include/program.h18
-rw-r--r--include/std/buffer.h1
-rw-r--r--include/std/hash_map.h2
-rw-r--r--include/std/scoped_allocator.h4
-rw-r--r--src/ast.c27
-rw-r--r--src/bytecode/bytecode.c51
-rw-r--r--src/compiler.c81
-rw-r--r--src/parser.c12
-rw-r--r--src/program.c41
-rw-r--r--src/ssa/ssa.c4
-rw-r--r--src/std/buffer.c8
-rw-r--r--src/std/hash_map.c2
-rw-r--r--src/std/scoped_allocator.c30
-rw-r--r--src/tokenizer.c6
-rw-r--r--tests/main.c80
-rw-r--r--tests/utf8bom.amal1
27 files changed, 282 insertions, 218 deletions
diff --git a/README.md b/README.md
index 68a6dbd..c02a2d2 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,10 @@ which would allow you to compile amalgam with a compiler that generates smaller
Amalgam is not meant to be a replacement for any other language but rather a new unique language for programming
with gpu without writing an external gpu program (glsl/hlsl).
+
+Files have to be in utf-8 format and can optionally have utf-8 BOM.
# TODO
Build with -nostdlib and replace use of libc with syscalls (on linux)
# Documents
-Documents are located under doc. Some documents are generated using python3 scripts in the same directory. There is no need to run these unless you are changing the documentation in the source code. \ No newline at end of file
+Documents are located under doc. The file doc/Documentation.md is generated from source files by running doc/doc_extract.py
+but there is no need to run this script unless you are modifying documentation in the source. \ No newline at end of file
diff --git a/build.sh b/build.sh
index c8b7ab7..214769a 100755
--- a/build.sh
+++ b/build.sh
@@ -26,7 +26,7 @@ fi
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"
+BUILD_ARGS="$source_files $CFLAGS $LIBS -shared -fpic -o "$this_script_dir/libamalgam.so""
set -x
time $CC $BUILD_ARGS
if [ ! -z "$SCAN_BUILD" ]; then
diff --git a/doc/BytecodeHeader.md b/doc/BytecodeHeader.md
deleted file mode 100644
index 684a5ef..0000000
--- a/doc/BytecodeHeader.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# Header layout
-|Size|Name |Description |
-|----|-------------|----------------------------------------------------------------------------|
-|4 |Magic number |The magic number used to identify an amalgam bytecode file. |
-|1 |Major version|The major version of the bytecode. Updates in this is a breaking change. |
-|1 |Minor version|The minor version of the bytecode. Updates in this are backwards compatible.|
-|1 |Patch version|The patch version of the bytecode. Updates in this are only minor bug fixes.|
-The versions in the header only changes for every release, not every change. \ No newline at end of file
diff --git a/doc/CompilerFlow.md b/doc/CompilerFlow.md
deleted file mode 100644
index e42b94d..0000000
--- a/doc/CompilerFlow.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Compiler flow
-(Tokenize&parse -> Resolve AST -> Generate SSA -> Generate bytecode) -> Generate program\
-Each step except the last is done using multiple threads in parallel and the output of each step is used
-in the next step. The last step is not done in parallel because the last step is combining all bytecode
-and writing it to a file, which is an IO bottlenecked operation and it won't benefit from multithreading
-and may even lose performance because of it. \ No newline at end of file
diff --git a/doc/Documentation.md b/doc/Documentation.md
new file mode 100644
index 0000000..34d1b41
--- /dev/null
+++ b/doc/Documentation.md
@@ -0,0 +1,31 @@
+# Opcode
+Variable length opcodes. Sizes range from 1 to 4 bytes.
+## Instruction formats
+Instructions can be in 6 different formats:
+1. 1 byte: Opcode
+2. 2 bytes: Opcode + register
+3. 3 bytes: Opcode + register + register
+4. 3 bytes:\
+4.1 Opcode + intermediate\
+4.2 Opcode + data\
+4.3 Opcode + index\
+4.4 Opcode + offset
+5. 4 bytes: Opcode + register + register + register
+6. 4 bytes: Opcode + register + offset
+
+# Compiler flow
+(Tokenize&parse -> Resolve AST -> Generate SSA -> Generate bytecode) -> Generate program\
+Each step except the last is done using multiple threads in parallel and the output of each step is used
+in the next step. The last step is not done in parallel because the last step is combining all bytecode
+and writing it to a file, which is an IO bottlenecked operation and it won't benefit from multithreading
+and may even lose performance because of it.
+
+# Bytecode header
+## Header layout
+|Size|Name |Description |
+|----|-------------|----------------------------------------------------------------------------|
+|4 |Magic number |The magic number used to identify an amalgam bytecode file. |
+|1 |Major version|The major version of the bytecode. Updates in this is a breaking change. |
+|1 |Minor version|The minor version of the bytecode. Updates in this are backwards compatible.|
+|1 |Patch version|The patch version of the bytecode. Updates in this are only minor bug fixes.|
+The versions in the header only changes for every release, not every change. \ No newline at end of file
diff --git a/doc/Opcode.md b/doc/Opcode.md
deleted file mode 100644
index 37fa4e2..0000000
--- a/doc/Opcode.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Opcode
-Variable length opcodes. Sizes range from 1 to 4 bytes.
-# Instruction formats
-Instructions can be in 6 different formats:
-1. 1 byte: Opcode
-2. 2 bytes: Opcode + register
-3. 3 bytes: Opcode + register + register
-4. 3 bytes:\
-4.1 Opcode + intermediate\
-4.2 Opcode + data\
-4.3 Opcode + index\
-4.4 Opcode + offset
-5. 4 bytes: Opcode + register + register + register
-6. 4 bytes: Opcode + register + offset \ No newline at end of file
diff --git a/doc/doc_extract.py b/doc/doc_extract.py
index 66ec7e5..ce192cb 100755
--- a/doc/doc_extract.py
+++ b/doc/doc_extract.py
@@ -55,6 +55,14 @@ def extract_docs(filepath):
docs.append((doc_name, lstrip_lines(doc)))
return docs
+def convert_to_subsections(doc_str):
+ lines = []
+ for line in doc_str.splitlines():
+ if len(line) != 0 and line[0] == "#":
+ line = "##" + line[1:]
+ lines.append(line)
+ return "\n".join(lines)
+
def main():
script_path = os.path.realpath(sys.argv[0])
script_dir = os.path.dirname(script_path)
@@ -64,13 +72,17 @@ def main():
source_files = get_source_files_recursive(amalgam_includes)
source_files += get_source_files_recursive(amalgam_sources)
+ doc_data = []
for filepath in source_files:
docs = extract_docs(filepath)
for doc in docs:
- doc_path = os.path.join(script_dir, doc[0] + ".md")
- with open(doc_path, "w") as file:
- file.write(doc[1])
- print("Wrote doc for %s into file %s" % (doc[0], doc_path))
+ section = "# {}\n{}".format(doc[0], convert_to_subsections(doc[1]))
+ doc_data.append(section)
+ print("Added section %s to the documentation" % doc[0])
+
+ doc_path = os.path.join(script_dir, "Documentation.md")
+ with open(doc_path, "w") as file:
+ file.write("\n\n".join(doc_data))
if __name__ == "__main__":
main()
diff --git a/include/ast.h b/include/ast.h
index 34e62b8..2925f6c 100644
--- a/include/ast.h
+++ b/include/ast.h
@@ -76,6 +76,7 @@ typedef enum {
typedef struct {
LhsExpr *type;
AstResolveStatus status;
+ Parser *parser; /* Borrowed. This is the parser that is currently parsing the expression */
} AstResolveData;
typedef struct {
@@ -140,6 +141,11 @@ typedef struct {
} value;
} VariableType;
+/*
+ Note: When resolving AST, more than one thread can end up resolving the same expressions at the same time.
+ This is intentional. Instead of using mutex for every expression and locking/unlocking everytime
+ which uses more memory and affects performance, we assume such race conditions are rare and let them happen.
+*/
struct LhsExpr {
bool is_extern;
bool is_pub;
@@ -147,10 +153,6 @@ struct LhsExpr {
BufferView var_name;
VariableType type;
Ast *rhs_expr;
- /*
- TODO: Find a better way to store this. This most likely will use too much memory.
- */
- amal_mutex *mutex;
};
struct AssignmentExpr {
@@ -204,7 +206,12 @@ struct WhileStatement {
typedef struct {
jmp_buf env;
- amal_compiler *compiler;
+ amal_compiler *compiler; /* Borrowed */
+ Parser *parser; /* Borrowed. This is the parser that belongs to the thread */
+ /*
+ Borrowed. This is the current scope. Note that this scope can belong to another parser (and thread),
+ as such, @parser and scope_get_parser(@scope) parser may not be the same
+ */
Scope *scope;
} AstCompilerContext;
@@ -215,7 +222,7 @@ CHECK_RESULT int funcdecl_init(FunctionDecl *self, FunctionSignature *signature,
CHECK_RESULT int funccall_init(FunctionCall *self, BufferView name, ScopedAllocator *allocator);
CHECK_RESULT int structdecl_init(StructDecl *self, Scope *parent, ScopedAllocator *allocator);
void structfield_init(StructField *self, BufferView name, BufferView type_name);
-CHECK_RESULT int lhsexpr_init(LhsExpr *self, bool is_extern, bool is_pub, bool is_const, BufferView var_name, ScopedAllocator *allocator);
+void lhsexpr_init(LhsExpr *self, bool is_extern, bool is_pub, bool is_const, BufferView var_name);
void assignmentexpr_init(AssignmentExpr *self, Ast *lhs_expr, Ast *rhs_expr);
void import_init(Import *self, BufferView path);
CHECK_RESULT int string_init(String *self, BufferView str);
diff --git a/include/bytecode/bytecode.h b/include/bytecode/bytecode.h
index 600c9f2..739aa79 100644
--- a/include/bytecode/bytecode.h
+++ b/include/bytecode/bytecode.h
@@ -9,7 +9,6 @@
#include <setjmp.h>
/*doc(Opcode)
- # Opcode
Variable length opcodes. Sizes range from 1 to 4 bytes.
# Instruction formats
Instructions can be in 6 different formats:
@@ -50,12 +49,12 @@ typedef enum {
typedef u8 AmalOpcodeType;
typedef struct {
- Buffer/*<instruction data>*/ instructions;
+ Buffer/*<headers + instruction data>*/ data;
} Bytecode;
typedef struct {
jmp_buf env;
- Bytecode *bytecode;
+ Bytecode bytecode;
Parser *parser; /* borrowed */
} BytecodeCompilerContext;
diff --git a/include/compiler.h b/include/compiler.h
index 3a31ad9..0ae8532 100644
--- a/include/compiler.h
+++ b/include/compiler.h
@@ -8,6 +8,7 @@
#include "std/thread.h"
#include "compiler_options.h"
#include "ast.h"
+#include "program.h"
#define AMAL_COMPILER_OK 0
/* General error */
@@ -32,6 +33,7 @@ typedef struct {
struct amal_compiler {
amal_default_types default_types;
amal_compiler_options options;
+ amal_program *program;
ScopedAllocator allocator;
Scope root_scope;
Buffer/*<Parser*>*/ parsers;
@@ -40,22 +42,19 @@ struct amal_compiler {
ParserThreadData *threads;
int usable_thread_count;
bool started;
- bool used;
amal_mutex mutex;
int generic_work_object_index;
};
void amal_compiler_options_init(amal_compiler_options *self);
-/* If @options is NULL, then default values are used */
-CHECK_RESULT int amal_compiler_init(amal_compiler *self, const amal_compiler_options *options);
-CHECK_RESULT int amal_compiler_deinit(amal_compiler *self);
/*
-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.
+ If @options is NULL, then default values are used.
+ You should run @amal_program_deinit even @amal_compiler_load_file fails, to cleanup memory.
+ This function creates a copy of @filepath so it doesn't have to survive longer than this function call.
+ The file has to be in utf-8 format and it can optionally have utf-8 BOM.
*/
-CHECK_RESULT int amal_compiler_load_file(amal_compiler *self, const char *filepath);
+CHECK_RESULT int amal_compiler_load_file(amal_compiler_options *options, amal_program *program, const char *filepath);
CHECK_RESULT int amal_compiler_internal_load_file(amal_compiler *self, const char *filepath, FileScopeReference **file_scope);
-/* TODO: amal_compiler_unload_file */
#endif
diff --git a/include/parser.h b/include/parser.h
index 491815f..bc205f0 100644
--- a/include/parser.h
+++ b/include/parser.h
@@ -4,6 +4,7 @@
#include "std/buffer_view.h"
#include "std/scoped_allocator.h"
#include "std/thread.h"
+#include "bytecode/bytecode.h"
#include "tokenizer.h"
#include "ast.h"
#include "defs.h"
@@ -39,17 +40,19 @@ struct Parser {
LhsExpr file_decl;
Scope *current_scope;
bool has_func_parent;
- ScopedAllocator *allocator; /* borrowed. Copied from @compiler for faster access to allocator */
+ /* Borrowed from @compiler for faster access to allocator. The allocator is thread-specific */
+ ScopedAllocator *allocator;
amal_compiler *compiler;
Ssa *ssa;
bool started;
TokenizerError error;
ErrorContext error_context;
jmp_buf parse_env;
+ Bytecode bytecode;
};
CHECK_RESULT int parser_thread_data_init(ParserThreadData *self);
-CHECK_RESULT int parser_thread_data_deinit(ParserThreadData *self);
+void parser_thread_data_deinit(ParserThreadData *self);
CHECK_RESULT int parser_thread_data_start(ParserThreadData *self, AmalThreadCallbackFunc callback_func, void *userdata);
CHECK_RESULT int parser_thread_data_join(ParserThreadData *self, void **result);
diff --git a/include/program.h b/include/program.h
new file mode 100644
index 0000000..cbd9432
--- /dev/null
+++ b/include/program.h
@@ -0,0 +1,18 @@
+#ifndef AMAL_PROGRAM_H
+#define AMAL_PROGRAM_H
+
+#include "std/buffer.h"
+#include "bytecode/bytecode.h"
+
+typedef struct {
+ Buffer/*<...>*/ data;
+} amal_program;
+
+void amal_program_init(amal_program *self);
+void amal_program_deinit(amal_program *self);
+
+CHECK_RESULT int amal_program_append_bytecode(amal_program *self, Bytecode *bytecode);
+CHECK_RESULT int amal_program_run(amal_program *self);
+CHECK_RESULT int amal_program_save(amal_program *self, const char *filepath);
+
+#endif
diff --git a/include/std/buffer.h b/include/std/buffer.h
index af6b986..7a74d33 100644
--- a/include/std/buffer.h
+++ b/include/std/buffer.h
@@ -18,6 +18,7 @@ typedef struct {
} Buffer;
CHECK_RESULT int buffer_init(Buffer *self, struct ScopedAllocator *allocator);
+void buffer_deinit(Buffer *self);
/*
@data can't be null. Use @buffer_append_empty if you want to change the size
diff --git a/include/std/hash_map.h b/include/std/hash_map.h
index 97b0745..9b31213 100644
--- a/include/std/hash_map.h
+++ b/include/std/hash_map.h
@@ -33,6 +33,6 @@ Expected @value size to be @self->value_type_size.
*/
CHECK_RESULT bool hash_map_get(HashMap *self, BufferView key, void *value);
-int hash_compare_string(const void *a, const void *b);
+int hash_map_compare_string(const void *a, const void *b);
#endif
diff --git a/include/std/scoped_allocator.h b/include/std/scoped_allocator.h
index 60abcce..f037ea5 100644
--- a/include/std/scoped_allocator.h
+++ b/include/std/scoped_allocator.h
@@ -16,9 +16,6 @@ struct ScopedAllocator {
ScopedAllocatorNode head;
ScopedAllocatorNode *current;
Buffer/*<void*>*/ mems;
- /* TODO: Use linked-list instead, since the elements are dynamically allocated anyways */
- /* Find another way to store mutexes */
- Buffer/*<amal_mutex*>*/ mutexes;
};
CHECK_RESULT int scoped_allocator_node_init(ScopedAllocatorNode *self);
@@ -28,6 +25,5 @@ CHECK_RESULT int scoped_allocator_init(ScopedAllocator *self);
void scoped_allocator_deinit(ScopedAllocator *self);
CHECK_RESULT int scoped_allocator_alloc(ScopedAllocator *self, usize size, void **mem);
CHECK_RESULT int scoped_allocator_add_mem(ScopedAllocator *self, usize *result_index);
-CHECK_RESULT int scoped_allocator_create_mutex(ScopedAllocator *self, amal_mutex **mutex);
#endif
diff --git a/src/ast.c b/src/ast.c
index 4194737..69243f8 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -19,6 +19,7 @@ static void ast_resolve(Ast *self, AstCompilerContext *context);
static void resolve_data_init(AstResolveData *self) {
self->status = AST_NOT_RESOLVED;
+ self->parser = NULL;
self->type = NULL;
}
@@ -94,7 +95,7 @@ void structfield_init(StructField *self, BufferView name, BufferView type_name)
variable_init(&self->type, type_name);
}
-int lhsexpr_init(LhsExpr *self, bool is_extern, bool is_pub, bool is_const, BufferView var_name, ScopedAllocator *allocator) {
+void lhsexpr_init(LhsExpr *self, bool is_extern, bool is_pub, bool is_const, BufferView var_name) {
self->is_extern = is_extern;
self->is_pub = is_pub;
self->is_const = is_const;
@@ -102,8 +103,6 @@ int lhsexpr_init(LhsExpr *self, bool is_extern, bool is_pub, bool is_const, Buff
self->type.value.variable = NULL;
self->var_name = var_name;
self->rhs_expr = NULL;
- return_if_error(scoped_allocator_create_mutex(allocator, &self->mutex));
- return 0;
}
void assignmentexpr_init(AssignmentExpr *self, Ast *lhs_expr, Ast *rhs_expr) {
@@ -159,7 +158,7 @@ int while_statement_init(WhileStatement *self, Scope *parent, ScopedAllocator *a
int scope_init(Scope *self, Scope *parent, ScopedAllocator *allocator) {
return_if_error(buffer_init(&self->ast_objects, allocator));
- return_if_error(hash_map_init(&self->named_objects, allocator, sizeof(Ast*), hash_compare_string, amal_hash_string));
+ return_if_error(hash_map_init(&self->named_objects, allocator, sizeof(Ast*), hash_map_compare_string, amal_hash_string));
self->parent = parent;
self->parser = NULL;
return 0;
@@ -627,6 +626,7 @@ static void while_statement_resolve(WhileStatement *while_stmt, AstCompilerConte
void ast_resolve(Ast *self, AstCompilerContext *context) {
assert(self);
+ assert(context->parser);
/*
TODO: Move these to the types that need checks for recursive dependency (function declaration, struct declaration)
For function declaration, it should be marked as resolved when the signature has been resolved
@@ -640,28 +640,15 @@ void ast_resolve(Ast *self, AstCompilerContext *context) {
*/
if(self->resolve_data.status == AST_RESOLVED) {
return;
- } else if(self->resolve_data.status == AST_RESOLVING) {
+ } else if(self->resolve_data.status == AST_RESOLVING && self->resolve_data.parser == context->parser) {
Parser *parser;
parser = scope_get_parser(context->scope);
parser_print_error(parser, ast_get_code_reference(self).data, "Found recursive dependency");
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) {
- Parser *parser;
- parser = scope_get_parser(context->scope);
- amal_mutex_tryunlock(self->value.lhs_expr->mutex);
- parser_print_error(parser, ast_get_code_reference(self).data, "Found recursive dependency");
- throw(AST_ERR);
- }
- }
-
self->resolve_data.status = AST_RESOLVING;
+ self->resolve_data.parser = context->parser;
switch(self->type) {
case AST_NUMBER: {
Number *number;
@@ -714,6 +701,4 @@ 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/bytecode/bytecode.c b/src/bytecode/bytecode.c
index 29d99c0..7c4fe08 100644
--- a/src/bytecode/bytecode.c
+++ b/src/bytecode/bytecode.c
@@ -19,7 +19,7 @@
} while(0)
int bytecode_init(Bytecode *self, ScopedAllocator *allocator) {
- return buffer_init(&self->instructions, allocator);
+ return buffer_init(&self->data, allocator);
}
static CHECK_RESULT usize ssa_extract_form1(u8 *instruction_data, SsaInsForm1 *result) {
@@ -59,7 +59,7 @@ static CHECK_RESULT usize ssa_extract_jump(u8 *instruction_data, SsaInsJump *res
}
static void add_header(BytecodeCompilerContext *self) {
- /*doc(BytecodeHeader)
+ /*doc(Bytecode header)
# Header layout
|Size|Name |Description |
|----|-------------|----------------------------------------------------------------------------|
@@ -76,7 +76,7 @@ static void add_header(BytecodeCompilerContext *self) {
const u8 patch_version = 0;
Buffer *instructions;
- instructions = &self->bytecode->instructions;
+ instructions = &self->bytecode.data;
throw_if_error(buffer_append(instructions, &magic_number, 4));
throw_if_error(buffer_append(instructions, &major_version, 1));
throw_if_error(buffer_append(instructions, &minor_version, 1));
@@ -90,7 +90,7 @@ static void add_intermediates(BytecodeCompilerContext *self) {
SsaNumber *intermediates_end;
ssa = self->parser->ssa;
- instructions = &self->bytecode->instructions;
+ instructions = &self->bytecode.data;
intermediate = buffer_begin(&ssa->intermediates);
intermediates_end = buffer_end(&ssa->intermediates);
@@ -108,20 +108,21 @@ void add_strings(BytecodeCompilerContext *self) {
Buffer *instructions;
BufferView *string;
BufferView *strings_end;
+ u32 strings_size;
ssa = self->parser->ssa;
- instructions = &self->bytecode->instructions;
+ instructions = &self->bytecode.data;
string = buffer_begin(&ssa->strings);
strings_end = buffer_end(&ssa->strings);
+ strings_size = 0;
- /*
- The 8 here is a arbitrary chosen number since we don't know the actual
- size of all strings without counting. The logic is that the average
- size of all strings length would be 8.
- */
- throw_if_error(buffer_expand(instructions,
- sizeof(u16) + (sizeof(u16) + 8) * ssa->strings.size));
- throw_if_error(buffer_append(instructions, &ssa->strings.size, sizeof(u16)));
+ for(; string != strings_end; ++string) {
+ strings_size += sizeof(u16) + string->size;
+ }
+ string = buffer_begin(&ssa->strings);
+
+ throw_if_error(buffer_expand(instructions, sizeof(u32) + strings_size));
+ throw_if_error(buffer_append(instructions, &strings_size, sizeof(u32)));
for(; string != strings_end; ++string) {
throw_if_error(buffer_append(instructions, &string->size, sizeof(u16)));
throw_if_error(buffer_append(instructions, &string->data, string->size));
@@ -129,7 +130,7 @@ void add_strings(BytecodeCompilerContext *self) {
}
static void add_ins1(BytecodeCompilerContext *self, AmalOpcode opcode, const char *fmt) {
- throw_if_error(buffer_append(&self->bytecode->instructions, &opcode, sizeof(AmalOpcodeType)));
+ throw_if_error(buffer_append(&self->bytecode.data, &opcode, sizeof(AmalOpcodeType)));
fprintf(stderr, fmt);
fputc('\n', stderr);
}
@@ -138,7 +139,7 @@ static void add_ins1(BytecodeCompilerContext *self, AmalOpcode opcode, const cha
static void add_ins2(BytecodeCompilerContext *self, AmalOpcode opcode, u8 reg, const char *fmt) {
Buffer *instructions;
size_t index;
- instructions = &self->bytecode->instructions;
+ instructions = &self->bytecode.data;
index = instructions->size;
throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(reg)));
@@ -151,7 +152,7 @@ static void add_ins2(BytecodeCompilerContext *self, AmalOpcode opcode, u8 reg, c
static void add_ins3(BytecodeCompilerContext *self, AmalOpcode opcode, u8 dst_reg, u8 src_reg, const char *fmt) {
Buffer *instructions;
size_t index;
- instructions = &self->bytecode->instructions;
+ instructions = &self->bytecode.data;
index = instructions->size;
throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(dst_reg) + sizeof(src_reg)));
@@ -165,7 +166,7 @@ static void add_ins3(BytecodeCompilerContext *self, AmalOpcode opcode, u8 dst_re
static void add_ins4(BytecodeCompilerContext *self, AmalOpcode opcode, u16 data, const char *fmt) {
Buffer *instructions;
size_t index;
- instructions = &self->bytecode->instructions;
+ instructions = &self->bytecode.data;
index = instructions->size;
throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(data)));
@@ -178,7 +179,7 @@ static void add_ins4(BytecodeCompilerContext *self, AmalOpcode opcode, u16 data,
static void add_ins5(BytecodeCompilerContext *self, AmalOpcode opcode, u8 dst_reg, u8 reg1, u8 reg2, const char *fmt) {
Buffer *instructions;
size_t index;
- instructions = &self->bytecode->instructions;
+ instructions = &self->bytecode.data;
index = instructions->size;
throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(dst_reg) + sizeof(reg1) + sizeof(reg2)));
@@ -193,7 +194,7 @@ static void add_ins5(BytecodeCompilerContext *self, AmalOpcode opcode, u8 dst_re
static void add_ins6(BytecodeCompilerContext *self, AmalOpcode opcode, u8 dst_reg, u16 data, const char *fmt) {
Buffer *instructions;
size_t index;
- instructions = &self->bytecode->instructions;
+ instructions = &self->bytecode.data;
index = instructions->size;
throw_if_error(buffer_append_empty(instructions, sizeof(AmalOpcodeType) + sizeof(dst_reg) + sizeof(data)));
@@ -237,6 +238,10 @@ static void add_instructions(BytecodeCompilerContext *self) {
FILE *file;
char *filename;
+
+ u32 num_instructions_index;
+ num_instructions_index = self->bytecode.data.size;
+ throw_if_error(buffer_append_empty(&self->bytecode.data, sizeof(num_instructions_index)));
#ifdef COMPILE_TO_C
LhsExpr *reg_types[NUM_MAX_REGS]; /* TODO: Remove this. Encode this data in the register itself */
SsaRegister func_arg_stack[NUM_MAX_FUNC_ARGS]; /* TODO: Remove this? */
@@ -470,6 +475,14 @@ static void add_instructions(BytecodeCompilerContext *self) {
assert(bool_false && "Instruction not yet implemented");
}
}
+
+ /* Prepend instructions with its size */
+ {
+ u32 instructions_size;
+ instructions_size = self->bytecode.data.size - num_instructions_index;
+ am_memcpy(&self->bytecode.data.data[num_instructions_index], &instructions_size, sizeof(instructions_size));
+ }
+
#endif /* COMPILE_TO_C */
fclose(file);
}
diff --git a/src/compiler.c b/src/compiler.c
index e7b242b..1af9537 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -12,6 +12,8 @@
#include <limits.h>
#include <assert.h>
+static void amal_compiler_deinit(amal_compiler *self);
+
static usize strnlen(const char *str, usize max_length) {
usize len;
len = 0;
@@ -31,9 +33,7 @@ static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char
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, bool_true,
- create_buffer_view(name, strnlen(name, PATH_MAX)),
- &compiler->allocator));
+ lhsexpr_init(*lhs_expr, bool_true, bool_true, bool_true, create_buffer_view(name, strnlen(name, PATH_MAX)));
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;
@@ -64,7 +64,7 @@ void amal_compiler_options_init(amal_compiler_options *self) {
self->num_threads = 0;
}
-int amal_compiler_init(amal_compiler *self, const amal_compiler_options *options) {
+static CHECK_RESULT int amal_compiler_init(amal_compiler *self, const amal_compiler_options *options, amal_program *program) {
int i;
self->usable_thread_count = options ? options->num_threads : 0;
@@ -72,19 +72,20 @@ int amal_compiler_init(amal_compiler *self, const amal_compiler_options *options
self->usable_thread_count = amal_get_usable_thread_count();
if(self->usable_thread_count == 0) {
amal_log_warning("Unable to get the number of threads available on the system, using 1 thread.");
- amal_log_warning("You can override the number of threads using by setting compiler option for number of thread to use.");
+ amal_log_warning("You can override the number of threads used by setting compiler option for number of thread to use.");
self->usable_thread_count = 1;
}
}
+ amal_log_info("Using %d threads", self->usable_thread_count);
am_memset(&self->allocator, 0, sizeof(self->allocator));
am_memset(&self->root_scope, 0, sizeof(self->root_scope));
if(options)
- am_memcpy(&self->options, options, sizeof(self->options));
+ self->options = *options;
else
- am_memset(&self->options, 0, sizeof(self->options));
+ amal_compiler_options_init(&self->options);
+ self->program = program;
self->started = bool_false;
- self->used = bool_false;
self->generic_work_object_index = 0;
amal_mutex_init(&self->mutex);
@@ -92,7 +93,7 @@ int amal_compiler_init(amal_compiler *self, const amal_compiler_options *options
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(hash_map_init(&self->file_scopes, &self->allocator, sizeof(FileScopeReference*), hash_map_compare_string, amal_hash_string));
cleanup_if_error(scoped_allocator_alloc(&self->allocator,
self->usable_thread_count * sizeof(ParserThreadData),
(void**)&self->threads));
@@ -102,25 +103,18 @@ int amal_compiler_init(amal_compiler *self, const amal_compiler_options *options
return AMAL_COMPILER_OK;
cleanup:
- ignore_result_int(amal_compiler_deinit(self));
+ amal_compiler_deinit(self);
return AMAL_COMPILER_ERR;
}
-int amal_compiler_deinit(amal_compiler *self) {
+void amal_compiler_deinit(amal_compiler *self) {
int i;
- int result;
- result = AMAL_COMPILER_OK;
-
for(i = 0; i < self->usable_thread_count; ++i) {
- int r;
- r = parser_thread_data_deinit(&self->threads[i]);
- if(r != 0)
- result = r;
+ parser_thread_data_deinit(&self->threads[i]);
}
amal_mutex_deinit(&self->mutex);
scoped_allocator_deinit(&self->allocator);
- return result;
}
typedef enum {
@@ -199,13 +193,19 @@ static void* thread_callback_parse_file(void *userdata) {
cleanup:
/*
- To stop all other parsers from working cleanly, we simply clear the file queue,
- and the other threads will stop when they are done with the file they are currently parsing.
+ To stop all other parsers from working cleanly, we simply clear the file queue,
+ and the other threads will stop when they are done with the file they are currently parsing.
*/
if(result != NULL) {
ignore_result_int(amal_mutex_lock(&compiler_parser_userdata.compiler->mutex, "thread_callback_parse_file"));
buffer_clear(&compiler_parser_userdata.compiler->queued_files);
}
+ /*
+ There can be a data race between writing to this and when this is read in @amal_compiler_load_file_join_threads.
+ This is intentional and it's ok, because the join_threads function checks against this status is guaranteed at this point
+ to not be a certain value that it checks for. Writing to an int is atomic.
+ TODO: Verify this is ok on all platforms.
+ */
compiler_parser_userdata.parser_thread_data->status = PARSER_THREAD_STATUS_IDLE;
amal_mutex_tryunlock(&compiler_parser_userdata.compiler->mutex);
return result;
@@ -215,6 +215,7 @@ static CHECK_RESULT int thread_resolve_ast(amal_compiler *compiler, Parser *pars
AstCompilerContext compiler_context;
int result;
compiler_context.compiler = compiler;
+ compiler_context.parser = parser;
compiler_context.scope = NULL;
result = setjmp(compiler_context.env);
if(result == 0) {
@@ -245,14 +246,15 @@ static CHECK_RESULT int thread_generate_bytecode(Parser *parser) {
BytecodeCompilerContext compiler_context;
int result;
- return_if_error(scoped_allocator_alloc(parser->allocator, sizeof(Bytecode), (void**)&compiler_context.bytecode));
- return_if_error(bytecode_init(compiler_context.bytecode, parser->allocator));
+ return_if_error(bytecode_init(&compiler_context.bytecode, parser->allocator));
compiler_context.parser = parser;
amal_log_debug("Generating bytecode for file: %.*s", parser->tokenizer.code_name.size, parser->tokenizer.code_name.data);
result = setjmp(compiler_context.env);
- if(result == 0)
+ if(result == 0) {
generate_bytecode_from_ssa(&compiler_context);
+ parser->bytecode = compiler_context.bytecode;
+ }
return result;
}
@@ -443,6 +445,17 @@ static CHECK_RESULT int amal_compiler_dispatch_generic(amal_compiler *self, Thre
return amal_compiler_load_file_join_threads(self);
}
+static CHECK_RESULT int amal_compiler_generate_program(amal_compiler *self) {
+ Parser **parser;
+ Parser **parser_end;
+ parser = buffer_begin(&self->parsers);
+ parser_end = buffer_end(&self->parsers);
+ for(; parser != parser_end; ++parser) {
+ return_if_error(amal_program_append_bytecode(self->program, &(*parser)->bytecode));
+ }
+ return 0;
+}
+
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;
@@ -472,12 +485,16 @@ static CHECK_RESULT int try_create_file_scope(amal_compiler *compiler, const cha
return ret;
}
-int amal_compiler_load_file(amal_compiler *self, const char *filepath) {
+int amal_compiler_load_file(amal_compiler_options *options, amal_program *program, const char *filepath) {
+ assert(program);
+ assert(filepath);
+ amal_compiler compiler;
FileScopeReference *file_scope;
- if(self->used)
- return AMAL_COMPILER_ERR;
- self->used = bool_true;
- return amal_compiler_internal_load_file(self, filepath, &file_scope);
+ int result;
+ return_if_error(amal_compiler_init(&compiler, options, program));
+ result = amal_compiler_internal_load_file(&compiler, filepath, &file_scope);
+ amal_compiler_deinit(&compiler);
+ return result;
}
int amal_compiler_internal_load_file(amal_compiler *self, const char *filepath, FileScopeReference **file_scope) {
@@ -510,8 +527,7 @@ int amal_compiler_internal_load_file(amal_compiler *self, const char *filepath,
return_if_error(amal_compiler_select_thread_for_work(self, thread_work_data, &parser_thread_data));
if(main_job) {
- /*doc(CompilerFlow)
- # Compiler flow
+ /*doc(Compiler flow)
(Tokenize&parse -> Resolve AST -> Generate SSA -> Generate bytecode) -> Generate program\
Each step except the last is done using multiple threads in parallel and the output of each step is used
in the next step. The last step is not done in parallel because the last step is combining all bytecode
@@ -535,6 +551,9 @@ int amal_compiler_internal_load_file(amal_compiler *self, const char *filepath,
assert(amal_compiler_check_all_threads_done(self));
amal_log_info("Finished generating bytecode");
+ return_if_error(amal_compiler_generate_program(self));
+ amal_log_info("Finished generating program");
+
return AMAL_COMPILER_OK;
}
diff --git a/src/parser.c b/src/parser.c
index 68d0648..b61a968 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -33,10 +33,9 @@ int parser_thread_data_init(ParserThreadData *self) {
return scoped_allocator_init(&self->allocator);
}
-int parser_thread_data_deinit(ParserThreadData *self) {
+void parser_thread_data_deinit(ParserThreadData *self) {
ignore_result_int(amal_thread_deinit(&self->thread));
scoped_allocator_deinit(&self->allocator);
- return 0;
}
int parser_thread_data_start(ParserThreadData *self, AmalThreadCallbackFunc callback_func, void *userdata) {
@@ -62,10 +61,11 @@ int parser_init(Parser *self, amal_compiler *compiler, ScopedAllocator *allocato
self->error_context = ERROR_CONTEXT_NONE;
return_if_error(structdecl_init(&self->struct_decl, &compiler->root_scope, allocator));
self->struct_decl.body.parser = self;
- return_if_error(lhsexpr_init(&self->file_decl, bool_true, bool_true, bool_true, create_buffer_view_null(), self->allocator));
+ lhsexpr_init(&self->file_decl, bool_true, bool_true, bool_true, create_buffer_view_null());
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;
self->has_func_parent = bool_false;
+ am_memset(&self->bytecode, 0, sizeof(self->bytecode));
return PARSER_OK;
}
@@ -202,7 +202,7 @@ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) {
if(is_extern && self->has_func_parent) {
self->error = tokenizer_create_error(&self->tokenizer,
tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data),
- "Only declarations in structs can be extern");
+ "Only declarations in global structs can be extern");
throw(PARSER_UNEXPECTED_TOKEN);
}
@@ -210,7 +210,7 @@ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) {
if(is_pub && self->has_func_parent) {
self->error = tokenizer_create_error(&self->tokenizer,
tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data),
- "Only declarations in structs can be public");
+ "Only declarations in global structs can be public");
throw(PARSER_UNEXPECTED_TOKEN);
}
@@ -233,7 +233,7 @@ static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) {
throw_if_error(tokenizer_accept(&self->tokenizer, TOK_IDENTIFIER));
var_name = self->tokenizer.value.identifier;
throw_if_error(scoped_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)&result));
- throw_if_error(lhsexpr_init(result, is_extern, is_pub, is_const, var_name, self->allocator));
+ lhsexpr_init(result, is_extern, is_pub, is_const, var_name);
ignore_result_int(parser_parse_var_type_def(self, &result->type));
return result;
diff --git a/src/program.c b/src/program.c
new file mode 100644
index 0000000..aa39a4c
--- /dev/null
+++ b/src/program.c
@@ -0,0 +1,41 @@
+#include "../include/program.h"
+#include <stdio.h>
+#include <errno.h>
+
+void amal_program_init(amal_program *self) {
+ ignore_result_int(buffer_init(&self->data, NULL));
+}
+
+void amal_program_deinit(amal_program *self) {
+ buffer_deinit(&self->data);
+}
+
+int amal_program_append_bytecode(amal_program *self, Bytecode *bytecode) {
+ return buffer_append(&self->data, bytecode->data.data, bytecode->data.size);
+}
+
+int amal_program_run(amal_program *self) {
+ /* TODO: Implement */
+ (void)self;
+ return 0;
+}
+
+int amal_program_save(amal_program *self, const char *filepath) {
+ FILE *file;
+ file = fopen(filepath, "wb");
+ if(!file) {
+ int err;
+ err = errno;
+ perror(filepath);
+ return -err;
+ }
+ if(fwrite(self->data.data, 1, self->data.size, file) != self->data.size) {
+ int err;
+ err = errno;
+ perror(filepath);
+ return -err;
+ }
+ fclose(file);
+ return 0;
+}
+
diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c
index 34e3e3e..6e61bf1 100644
--- a/src/ssa/ssa.c
+++ b/src/ssa/ssa.c
@@ -17,7 +17,7 @@ do { \
} while(0)
/* Max length of a string that fits in u16 */
-#define MAX_STRING_LENGTH ((2 << 16) - 1)
+#define MAX_STRING_LENGTH UINT16_MAX
static int compare_number(const void *a, const void *b) {
const SsaNumber *lhs;
@@ -54,7 +54,7 @@ int ssa_init(Ssa *self, ScopedAllocator *allocator) {
return_if_error(buffer_init(&self->instructions, allocator));
return_if_error(hash_map_init(&self->intermediates_map, allocator, sizeof(SsaIntermediateIndex), compare_number, hash_number));
return_if_error(buffer_init(&self->intermediates, allocator));
- return_if_error(hash_map_init(&self->strings_map, allocator, sizeof(SsaStringIndex), hash_compare_string, amal_hash_string));
+ return_if_error(hash_map_init(&self->strings_map, allocator, sizeof(SsaStringIndex), hash_map_compare_string, amal_hash_string));
return_if_error(buffer_init(&self->strings, allocator));
self->intermediate_counter = 0;
self->string_counter = 0;
diff --git a/src/std/buffer.c b/src/std/buffer.c
index 3676cee..f4e93e5 100644
--- a/src/std/buffer.c
+++ b/src/std/buffer.c
@@ -17,6 +17,14 @@ int buffer_init(Buffer *self, struct ScopedAllocator *allocator) {
}
}
+void buffer_deinit(Buffer *self) {
+ assert(!self->allocator && "Can't deinit buffer if it has an allocator");
+ am_free(self->data);
+ self->data = NULL;
+ self->size = 0;
+ self->capacity = 0;
+}
+
static CHECK_RESULT int buffer_ensure_capacity(Buffer *self, usize new_capacity) {
usize capacity;
void *new_mem;
diff --git a/src/std/hash_map.c b/src/std/hash_map.c
index d13bf3d..1ad0dea 100644
--- a/src/std/hash_map.c
+++ b/src/std/hash_map.c
@@ -202,7 +202,7 @@ bool hash_map_get(HashMap *self, BufferView key, void *value) {
#define MIN(a, b) ((a) < (b) ? (a) : (b))
-int hash_compare_string(const void *a, const void *b) {
+int hash_map_compare_string(const void *a, const void *b) {
const BufferView *lhs;
const BufferView *rhs;
int mem_diff;
diff --git a/src/std/scoped_allocator.c b/src/std/scoped_allocator.c
index 84ec920..d8acbf6 100644
--- a/src/std/scoped_allocator.c
+++ b/src/std/scoped_allocator.c
@@ -27,15 +27,7 @@ void scoped_allocator_node_deinit(ScopedAllocatorNode *self) {
int scoped_allocator_init(ScopedAllocator *self) {
return_if_error(scoped_allocator_node_init(&self->head));
self->current = &self->head;
- return_if_error(buffer_init(&self->mems, NULL));
- return buffer_init(&self->mutexes, NULL);
-}
-
-static void buffer_deinit(Buffer *self) {
- am_free(self->data);
- self->data = NULL;
- self->size = 0;
- self->capacity = 0;
+ return buffer_init(&self->mems, NULL);
}
static void scoped_allocator_deinit_buffers(ScopedAllocator *self) {
@@ -50,22 +42,9 @@ static void scoped_allocator_deinit_buffers(ScopedAllocator *self) {
buffer_deinit(&self->mems);
}
-static void scoped_allocator_deinit_mutexes(ScopedAllocator *self) {
- amal_mutex **mutex;
- amal_mutex **mutexes_end;
- mutex = buffer_begin(&self->mutexes);
- mutexes_end = buffer_end(&self->mutexes);
- while(mutex != mutexes_end) {
- amal_mutex_deinit(*mutex);
- ++mutex;
- }
- buffer_deinit(&self->mutexes);
-}
-
void scoped_allocator_deinit(ScopedAllocator *self) {
self->current = NULL;
scoped_allocator_deinit_buffers(self);
- scoped_allocator_deinit_mutexes(self);
scoped_allocator_node_deinit(&self->head);
}
@@ -129,10 +108,3 @@ int scoped_allocator_add_mem(ScopedAllocator *self, usize *result_index) {
*result_index = buffer_get_size(&self->mems, sizeof(void*));
return buffer_append(&self->mems, &null_data, sizeof(void*));
}
-
-int scoped_allocator_create_mutex(ScopedAllocator *self, amal_mutex **mutex) {
- *mutex = NULL;
- return_if_error(scoped_allocator_alloc(self, sizeof(amal_mutex), (void**)mutex));
- amal_mutex_init(*mutex);
- return buffer_append(&self->mutexes, mutex, sizeof(amal_mutex*));
-}
diff --git a/src/tokenizer.c b/src/tokenizer.c
index bd57af7..bce386d 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -28,6 +28,11 @@ static int tokenizer_get_end_of_multiline_comment(Tokenizer *self, int index);
int tokenizer_init(Tokenizer *self, ScopedAllocator *allocator, BufferView code, BufferView code_name, const amal_compiler_options *compiler_options) {
assert(code.size <= INT_MAX);
assert(compiler_options);
+ /* Skip UTF-8 BOM */
+ if(code.size >= 3 && (u8)code.data[0] == 0xEF && (u8)code.data[1] == 0xBB && (u8)code.data[2] == 0xBF) {
+ code.data += 3;
+ code.size -= 3;
+ }
self->code = code;
self->index = 0;
self->prev_index = 0;
@@ -86,6 +91,7 @@ static CHECK_RESULT int find_end_of_string(BufferView buf, int index) {
}
/* TODO: Optimize string to integer and string to float */
+
#define I64_OVERFLOW_ERROR -1
static CHECK_RESULT int string_to_integer_unchecked(BufferView str, i64 *result) {
int i;
diff --git a/tests/main.c b/tests/main.c
index 94f2d32..9b1e40a 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -8,6 +8,7 @@
#include <stdlib.h>
#include <unistd.h>
+static int num_threads = 0;
static int num_successful_tests = 0;
static int num_tests_run = 0;
@@ -26,7 +27,7 @@ static CHECK_RESULT int test_hash_map() {
unsigned char i;
return_if_error(scoped_allocator_init(&scoped_allocator));
- cleanup_if_error(hash_map_init(&hash_map, &scoped_allocator, sizeof(int), hash_compare_string, amal_hash_string));
+ cleanup_if_error(hash_map_init(&hash_map, &scoped_allocator, sizeof(int), hash_map_compare_string, amal_hash_string));
value = 34;
return_if_error(hash_map_insert(&hash_map, create_buffer_view("hello", 5), &value));
@@ -131,48 +132,29 @@ static CHECK_RESULT int get_thread_count_env_var(int *thread_count) {
}
static void test_load(const char *filepath) {
- amal_compiler compiler;
amal_compiler_options options;
+ amal_program program;
char *full_path;
int result;
- int num_threads;
-
- result = get_thread_count_env_var(&num_threads);
- if(result != 0)
- num_threads = 0;
-
- if(num_threads < 0) {
- amal_log_error("Environment variable THREADS contains invalid number for threads. THREADS has to be at least 0 (0 = use the number of available threads on the system)");
- exit(1);
- }
amal_compiler_options_init(&options);
options.num_threads = num_threads;
++num_tests_run;
full_path = get_full_path(filepath);
- /*
- options.error_callback = error_callback;
- ErrorUnexpectedData data;
- data.filepath = get_full_path(filepath);
- options.error_callback_userdata = &data;
- */
-
- result = amal_compiler_init(&compiler, &options);
- if(result != AMAL_COMPILER_OK) {
- fprintf(stderr, "Failed to initialize compiler to test %s, error code: %d\n", full_path, result);
- FAIL_TEST(full_path);
- }
- result = amal_compiler_load_file(&compiler, filepath);
+ amal_program_init(&program);
+ result = amal_compiler_load_file(&options, &program, filepath);
if(result != AMAL_COMPILER_OK) {
fprintf(stderr, "Failed to load file %s, result: %d\n", full_path, result);
FAIL_TEST(full_path);
}
- if(amal_compiler_deinit(&compiler) != 0) {
- fprintf(stderr, "Failed to deinitialize compiler for test %s\n", full_path);
+ result = amal_program_run(&program);
+ if(result != 0) {
+ fprintf(stderr, "Failed to run the program %s, result: %d\n", full_path, result);
FAIL_TEST(full_path);
}
+ amal_program_deinit(&program);
fprintf(stderr, "Test succeeded as expected: %s\n", full_path);
++num_successful_tests;
@@ -180,20 +162,10 @@ static void test_load(const char *filepath) {
}
static void test_load_error(const char *filepath, const char *expected_error) {
- amal_compiler compiler;
amal_compiler_options options;
+ amal_program program;
ErrorExpectedData expected_data;
int result;
- int num_threads;
-
- result = get_thread_count_env_var(&num_threads);
- if(result != 0)
- num_threads = 0;
-
- if(num_threads < 0) {
- amal_log_error("Environment variable THREADS contains invalid number for threads. THREADS has to be at least 0 (0 = use the number of available threads on the system)");
- exit(1);
- }
amal_compiler_options_init(&options);
options.num_threads = num_threads;
@@ -205,22 +177,13 @@ static void test_load_error(const char *filepath, const char *expected_error) {
expected_data.got_expected_error = bool_false;
options.error_callback_userdata = &expected_data;
- result = amal_compiler_init(&compiler, &options);
- if(result != AMAL_COMPILER_OK) {
- fprintf(stderr, "Failed to initialize compiler, error code: %d\n", result);
- FAIL_TEST(expected_data.filepath);
- }
-
- result = amal_compiler_load_file(&compiler, filepath);
+ amal_program_init(&program);
+ result = amal_compiler_load_file(&options, &program, filepath);
if(result == AMAL_COMPILER_OK) {
fprintf(stderr, "Expected to fail loading file\n");
FAIL_TEST(expected_data.filepath);
}
-
- if(amal_compiler_deinit(&compiler) != 0) {
- fprintf(stderr, "Failed to deinitialize compiler.\n");
- FAIL_TEST(expected_data.filepath);
- }
+ amal_program_deinit(&program);
if(!expected_data.got_expected_error) {
fprintf(stderr, "Didn't get expected error message:\n%s\n", expected_error);
@@ -235,17 +198,30 @@ static void test_load_error(const char *filepath, const char *expected_error) {
/* TODO: Restrict variables in global scope to const */
int main(int argc, char **argv) {
+ int result;
+ result = get_thread_count_env_var(&num_threads);
+ if(result != 0)
+ num_threads = 0;
+
+ if(num_threads < 0) {
+ amal_log_error("Environment variable THREADS contains invalid number for threads. THREADS has to be at least 0 (0 = use the number of available threads on the system)");
+ exit(1);
+ }
+
return_if_error(test_hash_map());
/* Run all tests */
if(argc == 1) {
test_load("tests/main.amal");
+ test_load("tests/utf8bom.amal");
+ test_load("tests/bytecode.amal");
+
test_load_error("tests/errors/duplicate_declaration.amal",
"2:7: error: Variable with the name main was declared twice in the same scope\n"
"const main = fn {}\n"
" ^\n");
test_load_error("tests/errors/pub_in_closure.amal",
- "2:5: error: Only declarations in structs can be public\n"
+ "2:5: error: Only declarations in global structs can be public\n"
" pub const num = 45;\n"
" ^\n");
test_load_error("tests/errors/closure_no_lhs.amal",
@@ -262,10 +238,12 @@ int main(int argc, char **argv) {
fprintf(stderr, "##### %d/%d tests succeeded #####\n", num_successful_tests, num_tests_run);
} else {
fprintf(stderr, "usage: test [test-file-path]\n");
+ fprintf(stderr, "If you run test without any files then all tests will run.\n");
fprintf(stderr, "examples:\n");
fprintf(stderr, " test\n");
fprintf(stderr, " test tests/main.amal\n");
exit(1);
}
+
return 0;
} \ No newline at end of file
diff --git a/tests/utf8bom.amal b/tests/utf8bom.amal
new file mode 100644
index 0000000..673e77f
--- /dev/null
+++ b/tests/utf8bom.amal
@@ -0,0 +1 @@
+// This file has utf-8 BOM