aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md13
-rwxr-xr-xbuild.sh1
-rw-r--r--doc/Documentation.md9
-rw-r--r--executor/executor.h10
-rw-r--r--executor/interpreter/executor.c197
-rw-r--r--executor/x86_64/asm.c86
-rw-r--r--executor/x86_64/asm.h18
-rw-r--r--executor/x86_64/executor.c129
-rw-r--r--include/ast.h108
-rw-r--r--include/bytecode/bytecode.h2
-rw-r--r--include/compiler.h2
-rw-r--r--include/nullable.h2
-rw-r--r--include/program.h7
-rw-r--r--include/ssa/ssa.h4
-rw-r--r--include/std/buffer_view.h1
-rw-r--r--include/std/misc.h9
-rw-r--r--include/std/types.h9
-rw-r--r--include/tokenizer.h3
-rw-r--r--src/ast.c494
-rw-r--r--src/bytecode/bytecode.c36
-rw-r--r--src/compiler.c34
-rw-r--r--src/parser.c232
-rw-r--r--src/program.c44
-rw-r--r--src/ssa/ssa.c239
-rw-r--r--src/std/arena_allocator.c9
-rw-r--r--src/std/buffer.c1
-rw-r--r--src/std/buffer_view.c5
-rw-r--r--src/tokenizer.c8
-rw-r--r--tests/bytecode.amal6
-rw-r--r--tests/errors/closure_duplicate_param_name.amal1
-rw-r--r--tests/main.c1
31 files changed, 1360 insertions, 360 deletions
diff --git a/README.md b/README.md
index a96972f..ed64fd6 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,8 @@ Amalgam is not meant to be a replacement for any other language but rather a new
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.
+## Important
+Amalgam is not ready to be used yet.
# Fast compilation
Every stage of the compiler is multithreaded and data copy is kept to a minimal, for example tokenization
is done without storing tokens in a list. Almost all allocation is done using an arena allocator that
@@ -21,7 +23,16 @@ This could be done checking if an AST expression is referenced before evaluating
that compiles everything even if not referenced, since another user of the program/library may use the functions that are not used in your program
and they might have compile-issues.\
Align machine code to word boundary for the start of functions. No need to pad with NOP, as functions return before the padding.\
-Use const to cleanup ANSI C style variable declarations, since const allows you to declare and assign variables on the same line.
+Use const to cleanup ANSI C style variable declarations, since const allows you to declare and assign variables on the same line.\
+Make the bytecode work with big endian. On a big endian machine, the bytecode should be converted to little endian
+to make work on little endian as little as possible, meaning it would be a small penality to use big endian.\
+Verify all members of an extern struct are extern as well. Verify all parameters are of extern types for extern functions.\
+Verify all code execution paths in a function return a value, if the function excepts return values.\
+Show compile error if the result of a function call is ignored.\
+Show compile error if function result type and assigned to variable have different types.
+## Urgent
+Simplify src/compiler.c, it's pretty complex with the thread work done right now. The thread work should be put in a
+thread dispatch file that only handles thread job dispatching.
# Documents
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.
diff --git a/build.sh b/build.sh
index 07a4ee2..568c6c4 100755
--- a/build.sh
+++ b/build.sh
@@ -17,6 +17,7 @@ if [ "$cpu_arch" = "x86_64" ]; then
source_files="$source_files $(find "executor/x86_64" -name "*.c")"
else
echo "WARNING: There is no machine code implementation for your cpu architecture: $cpu_arch. An interpreter will be used instead"
+ source_files="$source_files $(find "executor/interpreter" -name "*.c")"
fi
if [ -z "$CC" ]; then
diff --git a/doc/Documentation.md b/doc/Documentation.md
index 602d0e1..3f9bd1a 100644
--- a/doc/Documentation.md
+++ b/doc/Documentation.md
@@ -25,6 +25,9 @@ in the next step. The last step is not done in parallel because the last step is
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
+The layout of the full bytecode is: Header (Intermediates Strings Functions Instructions)*
+
# Bytecode header
## Header layout
|Type|Field |Description |
@@ -63,6 +66,12 @@ The versions in the header only changes for every release, not every change.
|u16 |Size|The size of the string, in bytes. |
|u8* |Data|The data of the string, where the size is defined by @Size. Strings are null-terminated.|
+# Bytecode functions
+## Internal functions layout
+|Type|Field |Description |
+|----|-------------------|---------------------------------|
+|u16 |Number of functions|The number of internal functions.|
+
# Bytecode instructions
## Instructions layout
|Type |Field |Description |
diff --git a/executor/executor.h b/executor/executor.h
index fdf6e67..5be9abc 100644
--- a/executor/executor.h
+++ b/executor/executor.h
@@ -20,6 +20,10 @@ CHECK_RESULT int amal_executor_init(amal_executor **self);
void amal_executor_deinit(amal_executor *self);
CHECK_RESULT int amal_executor_run(amal_executor *self);
+/* These functions are called for every file in the program. Every file has its own list of strings, intermediates, functions and external functions */
+CHECK_RESULT int amal_executor_instructions_start(amal_executor *self, u16 num_functions);
+CHECK_RESULT int amal_executor_instructions_end(amal_executor *self);
+
CHECK_RESULT int amal_exec_nop(amal_executor *self);
CHECK_RESULT int amal_exec_setz(amal_executor *self, u8 dst_reg);
CHECK_RESULT int amal_exec_mov(amal_executor *self, u8 dst_reg, u8 src_reg);
@@ -34,12 +38,12 @@ CHECK_RESULT int amal_exec_div(amal_executor *self, u8 dst_reg, u8 src_reg1, u8
CHECK_RESULT int amal_exec_push(amal_executor *self, u8 reg);
CHECK_RESULT int amal_exec_pushi(amal_executor *self, i64 imm);
CHECK_RESULT int amal_exec_pushd(amal_executor *self, BufferView data);
-/*CHECK_RESULT int amal_exec_call(u8 dst_reg, BufferView data);
-CHECK_RESULT int amal_exec_callr(u8 dst_reg, BufferView data);*/
+CHECK_RESULT int amal_exec_call(amal_executor *self, u16 func_index, u8 num_args);
+/*CHECK_RESULT int amal_exec_callr(u8 dst_reg, BufferView data);*/
CHECK_RESULT int amal_exec_cmp(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2);
CHECK_RESULT int amal_exec_jz(amal_executor *self, u8 dst_reg, i16 offset);
CHECK_RESULT int amal_exec_jmp(amal_executor *self, i16 offset);
-CHECK_RESULT int amal_exec_ret(amal_executor *self);
+CHECK_RESULT int amal_exec_ret(amal_executor *self, u8 reg);
CHECK_RESULT int amal_exec_func_start(amal_executor *self, u16 num_regs);
CHECK_RESULT int amal_exec_func_end(amal_executor *self);
diff --git a/executor/interpreter/executor.c b/executor/interpreter/executor.c
new file mode 100644
index 0000000..0180b08
--- /dev/null
+++ b/executor/interpreter/executor.c
@@ -0,0 +1,197 @@
+#include "../executor.h"
+#include "../../include/program.h"
+#include "../../include/std/alloc.h"
+#include <assert.h>
+
+typedef struct {
+ usize *stack;
+ usize stack_size;
+ usize stack_index;
+ isize reg[AMAL_PROGRAM_NUM_REGISTERS];
+} amal_executor_impl;
+
+#define IMPL \
+ amal_executor_impl *impl; \
+ impl = (amal_executor_impl*)self;
+
+#define FOUR_MEGABYTES 1024*1024*4
+
+static int executor_ensure_stack_capacity(amal_executor_impl *self, usize bytes_to_add) {
+ const isize overflow = self->stack_size - self->stack_index * sizeof(isize) + bytes_to_add;
+ if(overflow > 0) {
+ void *new_data;
+ const usize new_stack_size = self->stack_size * 1.5;
+ if(new_stack_size > FOUR_MEGABYTES)
+ return AMAL_PROGRAM_INSTRUCTION_STACK_OVERFLOW;
+ return_if_error(am_realloc(self->stack, new_stack_size, &new_data));
+ self->stack = new_data;
+ self->stack_size = new_stack_size;
+ }
+ return 0;
+}
+
+int amal_executor_init(amal_executor **self) {
+ amal_executor_impl **impl;
+ impl = (amal_executor_impl**)self;
+ *impl = NULL;
+ return_if_error(am_malloc(sizeof(amal_executor_impl), (void**)impl));
+ (*impl)->stack_size = 4096;
+ return_if_error(am_malloc((*impl)->stack_size, (void**)&(*impl)->stack));
+ (*impl)->stack_index = 0;
+ return 0;
+}
+
+void amal_executor_deinit(amal_executor *self) {
+ IMPL
+ am_free(impl->stack);
+ am_free(impl);
+}
+
+int amal_executor_run(amal_executor *self) {
+ (void)self;
+ assert(bool_false && "TODO: Implement!");
+ return 0;
+}
+
+int amal_exec_nop(amal_executor *self) {
+ (void)self;
+ return 0;
+}
+
+int amal_exec_setz(amal_executor *self, u8 dst_reg) {
+ IMPL
+ impl->reg[dst_reg] = 0;
+ return 0;
+}
+
+int amal_exec_mov(amal_executor *self, u8 dst_reg, u8 src_reg) {
+ IMPL
+ impl->reg[dst_reg] = impl->reg[src_reg];
+ return 0;
+}
+
+int amal_exec_movi(amal_executor *self, u8 dst_reg, i64 imm) {
+ IMPL
+ impl->reg[dst_reg] = imm;
+ return 0;
+}
+
+int amal_exec_movd(amal_executor *self, u8 dst_reg, BufferView data) {
+ IMPL
+ impl->reg[dst_reg] = (uintptr_t)data.data;
+ return 0;
+}
+
+int amal_exec_add(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ IMPL
+ impl->reg[dst_reg] = impl->reg[src_reg1] + impl->reg[src_reg2];
+ return 0;
+}
+
+int amal_exec_sub(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ IMPL
+ impl->reg[dst_reg] = impl->reg[src_reg1] - impl->reg[src_reg2];
+ return 0;
+}
+
+int amal_exec_imul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ IMPL
+ impl->reg[dst_reg] = impl->reg[src_reg1] * impl->reg[src_reg2];
+ return 0;
+}
+
+int amal_exec_mul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ IMPL
+ impl->reg[dst_reg] = impl->reg[src_reg1] * impl->reg[src_reg2];
+ return 0;
+}
+
+int amal_exec_idiv(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ IMPL
+ impl->reg[dst_reg] = impl->reg[src_reg1] / impl->reg[src_reg2];
+ return 0;
+}
+
+int amal_exec_div(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ IMPL
+ impl->reg[dst_reg] = impl->reg[src_reg1] / impl->reg[src_reg2];
+ return 0;
+}
+
+int amal_exec_push(amal_executor *self, u8 reg) {
+ IMPL
+ return_if_error(executor_ensure_stack_capacity(impl, sizeof(isize)));
+ impl->stack[impl->stack_index++] = impl->reg[reg];
+ return 0;
+}
+
+int amal_exec_pushi(amal_executor *self, i64 imm) {
+ IMPL
+ return_if_error(executor_ensure_stack_capacity(impl, sizeof(isize)));
+ impl->stack[impl->stack_index++] = imm;
+ return 0;
+}
+
+int amal_exec_pushd(amal_executor *self, BufferView data) {
+ IMPL
+ return_if_error(executor_ensure_stack_capacity(impl, sizeof(isize)));
+ impl->stack[impl->stack_index++] = (uintptr_t)data.data;
+ return 0;
+}
+
+/*int amal_exec_call(u8 dst_reg, BufferView data);
+int amal_exec_callr(u8 dst_reg, BufferView data);*/
+int amal_exec_cmp(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
+ IMPL
+ impl->reg[dst_reg] = (impl->reg[src_reg1] == impl->reg[src_reg2]);
+ return 0;
+}
+
+int amal_exec_jz(amal_executor *self, u8 dst_reg, i16 offset) {
+ (void)self;
+ (void)dst_reg;
+ (void)offset;
+ /* TODO: Implement! */
+ assert(bool_false && "TODO: Implement!");
+ return 0;
+}
+
+int amal_exec_jmp(amal_executor *self, i16 offset) {
+ (void)self;
+ (void)offset;
+ /* TODO: Implement! */
+ assert(bool_false && "TODO: Implement!");
+ return 0;
+}
+
+int amal_exec_ret(amal_executor *self) {
+ (void)self;
+ /* TODO: Implement! */
+ assert(bool_false && "TODO: Implement RET. RET needs to restore the stack before returning");
+ return 0;
+}
+
+int amal_exec_func_start(amal_executor *self, u16 num_regs) {
+ /*
+ TODO: Validate stack size, or maybe remove all validation? do we really need validation?
+ If we need security, we could fork the process instead.
+ */
+
+ /*
+ Some registers need to be preserved before entering a function scope and these registers are different on different platforms.
+ 32-bit: EBX, ESI, EDI, EBP
+ 64-bit Windows: RBX, RSI, RDI, RBP, R12-R15, XMM6-XMM15
+ 64-bit Linux,BSD,Mac: RBX, RBP, R12-R15
+ */
+ /* TODO: Preserve registers and stack frame */
+ /*return executor_ensure_stack_capacity(impl, num_regs * sizeof(isize));*/
+ (void)self;
+ (void)num_regs;
+ return 0;
+}
+
+int amal_exec_func_end(amal_executor *self) {
+ (void)self;
+ /* TODO: Restore registers and stack frame and ret */
+ return 0;
+}
diff --git a/executor/x86_64/asm.c b/executor/x86_64/asm.c
index 73ae568..f032538 100644
--- a/executor/x86_64/asm.c
+++ b/executor/x86_64/asm.c
@@ -35,6 +35,10 @@ static void ins_start(Asm *self) {
asm_debug_str_buffer_index = 0;
}
+static int max(int a, int b) {
+ return a >= b ? a : b;
+}
+
static void ins_end(Asm *self, const char *fmt, ...) {
usize ins_end_offset;
usize i;
@@ -46,7 +50,7 @@ static void ins_end(Asm *self, const char *fmt, ...) {
fprintf(stderr, "%02x ", ((u8*)self->code)[i]);
}
/* Same padding for all instructions, no matter how long they are */
- for(i = 0; i < 35 - (ins_end_offset - ins_start_offset)*3; ++i) {
+ for(i = 0; i < (usize)max(0, 35 - (ins_end_offset - ins_start_offset)*3); ++i) {
putc(' ', stderr);
}
vfprintf(stderr, fmt, args);
@@ -160,9 +164,9 @@ void asm_ptr_init_index_disp(AsmPtr *self, Reg64 base, Reg64 index, i32 disp) {
}
int asm_init(Asm *self) {
- self->size = am_pagesize();
- amal_log_debug("asm: page size: %u", self->size);
- self->code = mmap(NULL, self->size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ self->allocated_size = am_pagesize();
+ amal_log_debug("asm: page size: %u", self->allocated_size);
+ self->code = mmap(NULL, self->allocated_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(self->code == MAP_FAILED)
return -errno;
self->code_it = self->code;
@@ -171,12 +175,17 @@ int asm_init(Asm *self) {
void asm_deinit(Asm *self) {
if(self->code)
- munmap(self->code, self->size);
+ munmap(self->code, self->allocated_size);
self->code = NULL;
self->code_it = NULL;
- self->size = 0;
+ self->allocated_size = 0;
+}
+
+usize asm_get_size(Asm *self) {
+ return self->code_it - (u8*)self->code;
}
+#if 0
static void asm_print_code_hex(Asm *self) {
u8 *ptr;
int off;
@@ -196,13 +205,14 @@ static void asm_print_code_hex(Asm *self) {
if(off != 0)
putc('\n', stdout);
}
+#endif
int asm_execute(Asm *self) {
void (*func)();
- if(mprotect(self->code, self->size, PROT_READ | PROT_EXEC) != 0)
+ if(mprotect(self->code, self->allocated_size, PROT_READ | PROT_EXEC) != 0)
return -errno;
- asm_print_code_hex(self);
+ /*asm_print_code_hex(self);*/
/* TODO: Verify if this is valid on all platforms. According to ISO C standard it isn't? */
*(void**)(&func) = self->code;
@@ -214,17 +224,17 @@ int asm_execute(Asm *self) {
static CHECK_RESULT int asm_ensure_capacity(Asm *self, usize size) {
usize current_offset;
current_offset = (u8*)self->code_it - (u8*)self->code;
- if(current_offset + size > self->size) {
+ if(current_offset + size > self->allocated_size) {
void *new_mem;
usize new_size;
- new_size = self->size + am_pagesize();
+ new_size = self->allocated_size + am_pagesize();
new_mem = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if(self->code == MAP_FAILED)
return -errno;
- am_memcpy(new_mem, self->code, self->size);
+ am_memcpy(new_mem, self->code, self->allocated_size);
self->code = new_mem;
- self->size = new_size;
+ self->allocated_size = new_size;
self->code_it = (u8*)self->code + current_offset;
}
return 0;
@@ -232,7 +242,7 @@ static CHECK_RESULT int asm_ensure_capacity(Asm *self, usize size) {
#ifdef DEBUG
static isize asm_get_capacity_left(Asm *self) {
- return (isize)self->size - (isize)((u8*)self->code_it - (u8*)self->code);
+ return (isize)self->allocated_size - (isize)((u8*)self->code_it - (u8*)self->code);
}
#endif
@@ -346,21 +356,20 @@ int asm_mov_rm(Asm *self, Reg64 dst, AsmPtr *src) {
return 0;
}
+/* Note: This shows as instruction movabs in intel assembly format */
int asm_mov_ri(Asm *self, Reg64 dst, i64 immediate) {
ins_start(self);
- /* 10 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
return_if_error(asm_ensure_capacity(self, 10));
*self->code_it++ = REX_W;
*self->code_it++ = 0xB8 + dst;
am_memcpy(self->code_it, &immediate, sizeof(immediate));
self->code_it += sizeof(immediate);
- ins_end(self, "mov %s, %ld", reg64_to_str(dst), immediate);
+ ins_end(self, "mov %s, 0x%x", reg64_to_str(dst), immediate);
return 0;
}
int asm_mov_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
- /* 3 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
return_if_error(asm_ensure_capacity(self, 3));
*self->code_it++ = REX_W;
*self->code_it++ = 0x89;
@@ -371,7 +380,6 @@ int asm_mov_rr(Asm *self, Reg64 dst, Reg64 src) {
int asm_add_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
- /* 3 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
return_if_error(asm_ensure_capacity(self, 3));
*self->code_it++ = REX_W;
*self->code_it++ = 0x01;
@@ -382,7 +390,6 @@ int asm_add_rr(Asm *self, Reg64 dst, Reg64 src) {
int asm_sub_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
- /* 3 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
return_if_error(asm_ensure_capacity(self, 3));
*self->code_it++ = REX_W;
*self->code_it++ = 0x29;
@@ -393,7 +400,6 @@ int asm_sub_rr(Asm *self, Reg64 dst, Reg64 src) {
int asm_imul_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
- /* 3 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
return_if_error(asm_ensure_capacity(self, 4));
*self->code_it++ = REX_W;
*self->code_it++ = 0x0F;
@@ -403,6 +409,25 @@ int asm_imul_rr(Asm *self, Reg64 dst, Reg64 src) {
return 0;
}
+int asm_cqo(Asm *self) {
+ ins_start(self);
+ return_if_error(asm_ensure_capacity(self, 2));
+ *self->code_it++ = REX_W;
+ *self->code_it++ = 0x99;
+ ins_end(self, "cqo");
+ return 0;
+}
+
+int asm_idiv_rr(Asm *self, Reg64 src) {
+ ins_start(self);
+ return_if_error(asm_ensure_capacity(self, 4));
+ *self->code_it++ = REX_W;
+ *self->code_it++ = 0xF7;
+ asm_rr(self, src, 0x7);
+ ins_end(self, "idiv %s", reg64_to_str(src));
+ return 0;
+}
+
int asm_pushr(Asm *self, Reg64 reg) {
ins_start(self);
return_if_error(asm_ensure_capacity(self, 1));
@@ -419,6 +444,26 @@ int asm_popr(Asm *self, Reg64 reg) {
return 0;
}
+/*
+ Note: This is sometimes called with @relative 0 (will print call -5), in which case it's most likely a dummy call until the relative position
+ is later changed with @asm_override_call_rel32. TODO: Update the ins_end debug print to take that into account somehow
+*/
+int asm_call_rel32(Asm *self, i32 relative) {
+ ins_start(self);
+ relative -= 5; /* In x86, the relative position starts from the next instruction */
+ return_if_error(asm_ensure_capacity(self, 5));
+ *self->code_it++ = 0xE8;
+ am_memcpy(self->code_it, &relative, sizeof(relative));
+ self->code_it += sizeof(relative);
+ ins_end(self, "call 0x%x", relative);
+ return 0;
+}
+
+void asm_override_call_rel32(Asm *self, u32 asm_index, i32 new_relative) {
+ new_relative -= 5; /* In x86, the relative position starts from the next instruction */
+ am_memcpy((u8*)self->code + asm_index + 1, &new_relative, sizeof(new_relative));
+}
+
/* TODO: Remove these !*/
/* /r */
@@ -559,11 +604,12 @@ int asm_ret(Asm *self, u16 bytes) {
if(bytes == 0) {
return_if_error(asm_ensure_capacity(self, 1));
*self->code_it++ = 0xC3;
+ ins_end(self, "ret");
} else {
return_if_error(asm_ensure_capacity(self, 3));
*self->code_it++ = 0xC2;
am_memcpy(self->code_it, &bytes, sizeof(bytes));
+ ins_end(self, "ret 0x%x", bytes);
}
- ins_end(self, "ret 0x%x", bytes);
return 0;
}
diff --git a/executor/x86_64/asm.h b/executor/x86_64/asm.h
index b374e44..7e5ac67 100644
--- a/executor/x86_64/asm.h
+++ b/executor/x86_64/asm.h
@@ -7,7 +7,7 @@
typedef struct {
void *code;
u8 *code_it;
- usize size;
+ usize allocated_size;
} Asm;
typedef enum {
@@ -47,6 +47,8 @@ void asm_ptr_init_index_disp(AsmPtr *self, Reg64 base, Reg64 index, i32 disp);
CHECK_RESULT int asm_init(Asm *self);
void asm_deinit(Asm *self);
+usize asm_get_size(Asm *self);
+
CHECK_RESULT int asm_execute(Asm *self);
CHECK_RESULT int asm_nop(Asm *self);
@@ -68,9 +70,23 @@ CHECK_RESULT int asm_mov_rr(Asm *self, Reg64 dst, Reg64 src);
CHECK_RESULT int asm_add_rr(Asm *self, Reg64 dst, Reg64 src);
CHECK_RESULT int asm_sub_rr(Asm *self, Reg64 dst, Reg64 src);
CHECK_RESULT int asm_imul_rr(Asm *self, Reg64 dst, Reg64 src);
+/* Sign extend RAX into RDX, this is needed for some operations, such as idiv */
+CHECK_RESULT int asm_cqo(Asm *self);
+/*
+ Divide RDX:RAX by @src. Store the quotient in RAX and the remainder in RDX.
+ @asm_cqo should be called before this, since RAX needs to be sign extended into RDX
+*/
+CHECK_RESULT int asm_idiv_rr(Asm *self, Reg64 src);
CHECK_RESULT int asm_pushr(Asm *self, Reg64 reg);
CHECK_RESULT int asm_popr(Asm *self, Reg64 reg);
+/*
+ In x86 assembly, the @relative position starts from the next instruction.
+ This offset shouldn't be calculated by the caller and is instead managed
+ by this asm library itself.
+*/
+CHECK_RESULT int asm_call_rel32(Asm *self, i32 relative);
+void asm_override_call_rel32(Asm *self, u32 asm_index, i32 new_relative);
diff --git a/executor/x86_64/executor.c b/executor/x86_64/executor.c
index b53ccea..b7aa91f 100644
--- a/executor/x86_64/executor.c
+++ b/executor/x86_64/executor.c
@@ -1,5 +1,6 @@
#include "../executor.h"
#include "../../include/std/alloc.h"
+#include "../../include/std/buffer.h"
#include "asm.h"
#include <assert.h>
@@ -11,7 +12,16 @@
*/
typedef struct {
+ u32 asm_index;
+ u16 func_index;
+} CallDefer;
+
+typedef struct {
Asm asm;
+ usize *function_indices;
+ u16 num_functions;
+ u16 func_counter;
+ Buffer/*CallDefer*/ call_defer;
} amal_executor_impl;
#define IMPL \
@@ -26,13 +36,20 @@ static i64 abs_i64(i64 value) {
int amal_executor_init(amal_executor **self) {
amal_executor_impl **impl;
- return_if_error(am_malloc(sizeof(amal_executor_impl), (void**)self));
impl = (amal_executor_impl**)self;
+ *impl = NULL;
+ return_if_error(am_malloc(sizeof(amal_executor_impl), (void**)impl));
+ (*impl)->function_indices = NULL;
+ (*impl)->num_functions = 0;
+ (*impl)->func_counter = 0;
+ ignore_result_int(buffer_init(&(*impl)->call_defer, NULL));
return asm_init(&(*impl)->asm);
}
void amal_executor_deinit(amal_executor *self) {
IMPL
+ buffer_deinit(&impl->call_defer);
+ am_free(impl->function_indices);
asm_deinit(&impl->asm);
am_free(impl);
}
@@ -42,6 +59,30 @@ int amal_executor_run(amal_executor *self) {
return asm_execute(&impl->asm);
}
+int amal_executor_instructions_start(amal_executor *self, u16 num_functions) {
+ void *new_data;
+ IMPL
+ return_if_error(am_realloc(impl->function_indices, num_functions * sizeof(*impl->function_indices), &new_data));
+ impl->function_indices = new_data;
+ impl->num_functions = num_functions;
+ impl->func_counter = 0;
+ buffer_clear(&impl->call_defer);
+ return 0;
+}
+
+int amal_executor_instructions_end(amal_executor *self) {
+ CallDefer *call_defer, *call_defer_end;
+ IMPL
+
+ call_defer = buffer_begin(&impl->call_defer);
+ call_defer_end = buffer_end(&impl->call_defer);
+ for(; call_defer != call_defer_end; ++call_defer) {
+ const isize func_offset = (isize)impl->function_indices[call_defer->func_index] - (isize)call_defer->asm_index;
+ asm_override_call_rel32(&impl->asm, call_defer->asm_index, func_offset);
+ }
+ return 0;
+}
+
int amal_exec_nop(amal_executor *self) {
IMPL
return asm_nop(&impl->asm);
@@ -158,16 +199,25 @@ int amal_exec_mul(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
return_if_error(asm_mul_rr(&self->asm, RAX, RCX));
return_if_error(asm_mov_mr(&self->asm, &dst, RAX));
#endif
+ assert(bool_false && "TODO: Implement!");
return 0;
}
int amal_exec_idiv(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
- (void)self;
- (void)dst_reg;
- (void)src_reg1;
- (void)src_reg2;
- /* TODO: Implement! */
- return 0;
+ AsmPtr dst;
+ AsmPtr reg1;
+ AsmPtr reg2;
+ IMPL
+
+ asm_ptr_init_disp(&dst, RBP, get_register_stack_offset(dst_reg));
+ asm_ptr_init_disp(&reg1, RBP, get_register_stack_offset(src_reg1));
+ asm_ptr_init_disp(&reg2, RBP, get_register_stack_offset(src_reg2));
+
+ return_if_error(asm_mov_rm(&impl->asm, RAX, &reg1));
+ return_if_error(asm_mov_rm(&impl->asm, RCX, &reg2));
+ return_if_error(asm_cqo(&impl->asm));
+ return_if_error(asm_idiv_rr(&impl->asm, RCX));
+ return asm_mov_mr(&impl->asm, &dst, RAX);
}
int amal_exec_div(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
@@ -176,20 +226,24 @@ int amal_exec_div(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
(void)src_reg1;
(void)src_reg2;
/* TODO: Implement! */
+ assert(bool_false && "TODO: Implement!");
return 0;
}
int amal_exec_push(amal_executor *self, u8 reg) {
- (void)self;
- (void)reg;
- /* TODO: Implement! */
- return 0;
+ AsmPtr reg_ptr;
+ IMPL
+
+ asm_ptr_init_disp(&reg_ptr, RBP, get_register_stack_offset(reg));
+ return_if_error(asm_mov_rm(&impl->asm, RAX, &reg_ptr));
+ return asm_pushr(&impl->asm, RAX);
}
int amal_exec_pushi(amal_executor *self, i64 imm) {
(void)self;
(void)imm;
/* TODO: Implement! */
+ assert(bool_false && "TODO: Implement!");
return 0;
}
@@ -197,17 +251,48 @@ int amal_exec_pushd(amal_executor *self, BufferView data) {
(void)self;
(void)data;
/* TODO: Implement! */
+ assert(bool_false && "TODO: Implement!");
+ return 0;
+}
+
+int amal_exec_call(amal_executor *self, u16 func_index, u8 num_args) {
+ isize asm_size;
+ IMPL
+ /* TODO: Preserve necessary registers before call? */
+ /* TODO: This assumes all arguments are isize */
+ asm_size = asm_get_size(&impl->asm);
+ if(func_index < impl->func_counter) {
+ return_if_error(asm_call_rel32(&impl->asm, (isize)impl->function_indices[func_index] - asm_size));
+ } else {
+ /*
+ The location of the function has not been defined yet. Use call instruction with dummy data and change
+ the location once the location to the function is known
+ */
+ CallDefer call_defer;
+ call_defer.asm_index = asm_size;
+ call_defer.func_index = func_index;
+ return_if_error(buffer_append(&impl->call_defer, &call_defer, sizeof(call_defer)));
+ return_if_error(asm_call_rel32(&impl->asm, 0));
+ }
+
+ if(num_args > 0)
+ return asm_add_rm64_imm(&impl->asm, RSP, num_args * sizeof(isize));
return 0;
}
-/*int amal_exec_call(u8 dst_reg, BufferView data);
-int amal_exec_callr(u8 dst_reg, BufferView data);*/
+/*
+int amal_exec_callr(u8 dst_reg, BufferView data) {
+
+}
+*/
+
int amal_exec_cmp(amal_executor *self, u8 dst_reg, u8 src_reg1, u8 src_reg2) {
(void)self;
(void)dst_reg;
(void)src_reg1;
(void)src_reg2;
/* TODO: Implement! */
+ assert(bool_false && "TODO: Implement!");
return 0;
}
@@ -216,6 +301,7 @@ int amal_exec_jz(amal_executor *self, u8 dst_reg, i16 offset) {
(void)dst_reg;
(void)offset;
/* TODO: Implement! */
+ assert(bool_false && "TODO: Implement!");
return 0;
}
@@ -223,14 +309,18 @@ int amal_exec_jmp(amal_executor *self, i16 offset) {
(void)self;
(void)offset;
/* TODO: Implement! */
+ assert(bool_false && "TODO: Implement!");
return 0;
}
-int amal_exec_ret(amal_executor *self) {
- (void)self;
- /* TODO: Implement! */
- assert(bool_false && "TODO: Implement RET. RET needs to restore the stack before returning");
- return 0;
+int amal_exec_ret(amal_executor *self, u8 reg) {
+ AsmPtr ret_reg;
+ IMPL
+
+ asm_ptr_init_disp(&ret_reg, RBP, get_register_stack_offset(reg));
+ /* Result is returned in RAX register. TODO: Make this work for larger data */
+ return_if_error(asm_mov_rm(&impl->asm, RAX, &ret_reg));
+ return amal_exec_func_end(self);
}
int amal_exec_func_start(amal_executor *self, u16 num_regs) {
@@ -246,10 +336,11 @@ int amal_exec_func_start(amal_executor *self, u16 num_regs) {
64-bit Linux,BSD,Mac: RBX, RBP, R12-R15
*/
IMPL
+ impl->function_indices[impl->func_counter++] = asm_get_size(&impl->asm);
return_if_error(asm_pushr(&impl->asm, RBX));
return_if_error(asm_pushr(&impl->asm, RBP));
return_if_error(asm_mov_rr(&impl->asm, RBP, RSP));
- return asm_sub_rm64_imm(&impl->asm, RSP, num_regs * sizeof(usize));
+ return asm_sub_rm64_imm(&impl->asm, RSP, num_regs * sizeof(isize));
}
int amal_exec_func_end(amal_executor *self) {
diff --git a/include/ast.h b/include/ast.h
index d89d099..1198a98 100644
--- a/include/ast.h
+++ b/include/ast.h
@@ -19,6 +19,7 @@
#define AST_ERR_DEF_DUP -20
typedef struct Ast Ast;
+typedef struct FunctionParameter FunctionParameter;
typedef struct FunctionSignature FunctionSignature;
typedef struct FunctionCall FunctionCall;
typedef struct StructDecl StructDecl;
@@ -33,6 +34,7 @@ typedef struct Binop Binop;
typedef struct IfStatement IfStatement;
typedef struct ElseIfStatement ElseIfStatement;
typedef struct WhileStatement WhileStatement;
+typedef struct ReturnExpr ReturnExpr;
/* TODO: Instead of using pointers, use the data directly */
typedef union {
@@ -50,6 +52,7 @@ typedef union {
Binop *binop;
IfStatement *if_stmt;
WhileStatement *while_stmt;
+ ReturnExpr *return_expr;
} AstValue;
typedef enum {
@@ -65,7 +68,8 @@ typedef enum {
AST_VARIABLE,
AST_BINOP,
AST_IF_STATEMENT,
- AST_WHILE_STATEMENT
+ AST_WHILE_STATEMENT,
+ AST_RETURN
} AstType;
typedef enum {
@@ -75,17 +79,50 @@ typedef enum {
AST_SSA_RESOLVED
} AstResolveStatus;
+typedef enum {
+ RESOLVED_TYPE_NONE,
+ RESOLVED_TYPE_LHS_EXPR,
+ RESOLVED_TYPE_FUNC_SIG
+} AstResolvedTypeEnum;
+
typedef struct {
- LhsExpr *type;
+ union {
+ LhsExpr *lhs_expr;
+ FunctionSignature *func_sig;
+ } value;
+ AstResolvedTypeEnum type;
+} AstResolvedType;
+
+typedef struct {
+ AstResolvedType type;
AstResolveStatus status;
- Parser *parser; /* Borrowed. This is the parser that is currently parsing the expression */
+ SsaRegister ssa_reg;
} AstResolveData;
+typedef enum {
+ NAMED_OBJECT_NONE,
+ NAMED_OBJECT_LHS_EXPR,
+ NAMED_OBJECT_FUNC_PARAM
+} ScopeNamedObjectType;
+
+typedef struct {
+ union {
+ LhsExpr *lhs_expr;
+ FunctionParameter *func_param;
+ } value;
+ ScopeNamedObjectType type;
+ AstResolveData *resolve_data; /* Borrowed from @value.func_param or from the Ast @value.lhs_expr belongs to */
+} ScopeNamedObject;
+
struct Ast {
AstValue value;
AstType type;
AstResolveData resolve_data;
- SsaRegister ssa_reg;
+ /*
+ Borrowed. This is the parser (thread) that is currently parsing the expression.
+ TODO: Move this to LhsExpr
+ */
+ Parser *parser;
};
struct Scope {
@@ -94,8 +131,22 @@ struct Scope {
Scope *parent;
/* Is null unless the scope is a file scope, in which case this is the parser that owns the scope */
Parser *parser;
+ FunctionSignature *function_signature; /* Borrowed from FunctionDecl. Only used if the scope belongs to FunctionDecl */
};
+typedef struct {
+ enum {
+ VARIABLE_TYPE_NONE,
+ VARIABLE_TYPE_VARIABLE,
+ VARIABLE_TYPE_SIGNATURE
+ } type;
+
+ union {
+ Variable *variable;
+ FunctionSignature *signature;
+ } value;
+} VariableType;
+
struct FileScopeReference {
Parser *parser;
Buffer canonical_path;
@@ -103,15 +154,30 @@ struct FileScopeReference {
struct Variable {
BufferView name;
- Ast *resolved_var; /* resolved_var will always be a LhsExpr after resolved */
+ ScopeNamedObject resolved_var;
+};
+
+struct FunctionParameter {
+ BufferView name;
+ VariableType type;
+ AstResolveData resolve_data;
};
+typedef struct {
+ VariableType type;
+ AstResolvedType resolved_type;
+} FunctionReturnType;
+
struct FunctionSignature {
- int params; /* TODO: Implement signatures */
+ Buffer/*FunctionParameter*/ parameters;
+ Buffer/*FunctionReturnType*/ return_types;
+ /* Borrowed from the FunctionDecl the function signature belongs to. This is used to access the scope the function signature belongs to */
+ FunctionDecl *func_decl;
bool resolved;
};
struct FunctionDecl {
+ LhsExpr *lhs_expr; /* Borrowed from the LhsExpr that owns this FunctionDecl if it exists, otherwise NULL */
FunctionSignature *signature;
Scope body;
SsaFuncIndex ssa_func_index;
@@ -128,22 +194,9 @@ struct StructDecl {
struct StructField {
BufferView name;
- Variable type;
+ VariableType type;
};
-typedef struct {
- enum {
- VARIABLE_TYPE_NONE,
- VARIABLE_TYPE_VARIABLE,
- VARIABLE_TYPE_SIGNATURE
- } type;
-
- union {
- Variable *variable;
- FunctionSignature *signature;
- } value;
-} VariableType;
-
typedef enum {
DECL_FLAG_NONE = 0,
DECL_FLAG_EXTERN = 1 << 0,
@@ -221,6 +274,10 @@ struct WhileStatement {
Scope body;
};
+struct ReturnExpr {
+ Ast *rhs_expr;
+};
+
typedef struct {
jmp_buf env;
amal_compiler *compiler; /* Borrowed */
@@ -235,12 +292,18 @@ typedef struct {
CHECK_RESULT int ast_create(ArenaAllocator *allocator, void *value, AstType type, Ast **result);
BufferView ast_get_name(Ast *self);
-void function_signature_init(FunctionSignature *self);
+CHECK_RESULT int function_signature_init(FunctionSignature *self, ArenaAllocator *allocator);
+/* Adds a copy of @param to the function signature parameter list */
+CHECK_RESULT int function_signature_add_parameter(FunctionSignature *self, const FunctionParameter *param);
+/* Adds a copy of @return_type to the function signature return type list */
+CHECK_RESULT int function_signature_add_return_type(FunctionSignature *self, const VariableType *return_type);
+void function_parameter_init(FunctionParameter *self);
CHECK_RESULT int funcdecl_init(FunctionDecl *self, FunctionSignature *signature, Scope *parent, ArenaAllocator *allocator);
CHECK_RESULT int funccall_init(FunctionCall *self, BufferView name, ArenaAllocator *allocator);
CHECK_RESULT int structdecl_init(StructDecl *self, Scope *parent, ArenaAllocator *allocator);
LhsExpr* structdecl_get_field_by_name(StructDecl *self, BufferView field_name);
-void structfield_init(StructField *self, BufferView name, BufferView type_name);
+/* Copies @type */
+void structfield_init(StructField *self, BufferView name, VariableType *type);
void lhsexpr_init(LhsExpr *self, DeclFlag decl_flag, BufferView var_name);
void assignmentexpr_init(AssignmentExpr *self, Ast *lhs_expr, Ast *rhs_expr);
void import_init(Import *self, BufferView path);
@@ -251,6 +314,7 @@ void binop_init(Binop *self);
CHECK_RESULT int if_statement_init(IfStatement *self, Scope *parent, ArenaAllocator *allocator);
CHECK_RESULT int else_if_statement_init(ElseIfStatement *self, Scope *parent, ArenaAllocator *allocator);
CHECK_RESULT int while_statement_init(WhileStatement *self, Scope *parent, ArenaAllocator *allocator);
+void return_expr_init(ReturnExpr *self, Ast *rhs_expr);
CHECK_RESULT int scope_init(Scope *self, Scope *parent, ArenaAllocator *allocator);
CHECK_RESULT int file_scope_reference_init(FileScopeReference *self, BufferView canonical_path, ArenaAllocator *allocator);
diff --git a/include/bytecode/bytecode.h b/include/bytecode/bytecode.h
index 4c54fbd..adad291 100644
--- a/include/bytecode/bytecode.h
+++ b/include/bytecode/bytecode.h
@@ -48,7 +48,7 @@ typedef enum {
AMAL_OP_CMP, /* cmp dst, reg1, reg2 - Set dst to 1 if reg1 equals reg2, otherwise set it to 0 */
AMAL_OP_JZ, /* jz reg, offset - Jump to offset if reg is zero. offset is i16 */
AMAL_OP_JMP, /* jmp offset - Unconditional jump to offset. offset is i16 */
- AMAL_OP_RET, /* ret - Return from the function */
+ AMAL_OP_RET, /* ret reg - Return from the function with reg result */
AMAL_OP_FUNC_START, /* func_start num_reg - Start of a function which has num_reg registers allocated. num_reg is a u16 */
AMAL_OP_FUNC_END /* func_end - End of a function. Implementation should do a ret here */
} AmalOpcode;
diff --git a/include/compiler.h b/include/compiler.h
index 83dde63..08b74b8 100644
--- a/include/compiler.h
+++ b/include/compiler.h
@@ -13,6 +13,7 @@
#define AMAL_COMPILER_OK 0
/* General error */
#define AMAL_COMPILER_ERR -1
+#define AMAL_COMPILER_WORK_FAIL_ABORT -2
#define NUM_ARITHMETIC_TYPES 10
@@ -53,6 +54,7 @@ struct amal_compiler {
ParserThreadData *threads;
int usable_thread_count;
bool started;
+ bool work_failed;
amal_mutex mutex;
int generic_work_object_index;
};
diff --git a/include/nullable.h b/include/nullable.h
index f38fb37..961b210 100644
--- a/include/nullable.h
+++ b/include/nullable.h
@@ -1,8 +1,6 @@
#ifndef AMAL_NULLABLE_H
#define AMAL_NULLABLE_H
-struct __nullable_type_dummy{ int _; };
-
/* Used by static analysis tool to find null-pointer dereference errors */
#define nullable __attribute__((annotate("nullable")))
diff --git a/include/program.h b/include/program.h
index b98c0a6..6603c63 100644
--- a/include/program.h
+++ b/include/program.h
@@ -6,7 +6,8 @@
#include "../executor/executor.h"
#define AMAL_PROGRAM_OK 0
-#define AMAL_PROGRAM_INVALID_HEADER -1
+#define AMAL_PROGRAM_ERR -1
+#define AMAL_PROGRAM_INVALID_HEADER -16
#define AMAL_PROGRAM_INVALID_MAGIC_NUMBER -2
#define AMAL_PROGRAM_INCOMPATIBLE -3
#define AMAL_PROGRAM_INVALID_INTERMEDIATES -4
@@ -21,6 +22,7 @@
#define AMAL_PROGRAM_INSTRUCTION_STACK_OVERFLOW -13
#define AMAL_PROGRAM_INSTRUCTION_STACK_OOM -14
#define AMAL_PROGRAM_INSTRUCTION_ILLEGAL_JUMP_TARGET -15
+#define AMAL_PROGRAM_INVALID_FUNCTIONS -16
#define AMAL_PROGRAM_MAGIC_NUMBER (u32)0xdec05eba
#define AMAL_PROGRAM_MAJOR_VERSION 1
@@ -36,8 +38,9 @@ typedef struct {
char *strings_start; /* Reference inside @data */
usize read_index;
- u16 num_strings;
u16 num_intermediates;
+ u16 num_strings;
+ u16 num_functions;
} amal_program;
CHECK_RESULT int amal_program_init(amal_program *self);
diff --git a/include/ssa/ssa.h b/include/ssa/ssa.h
index 016acc8..21d7f40 100644
--- a/include/ssa/ssa.h
+++ b/include/ssa/ssa.h
@@ -24,7 +24,8 @@ typedef enum {
SSA_PUSH,
SSA_CALL,
SSA_JUMP_ZERO,
- SSA_JUMP
+ SSA_JUMP,
+ SSA_RET
} SsaInstruction;
typedef enum {
@@ -75,6 +76,7 @@ typedef struct {
} SsaInsFuncStart;
typedef struct {
+ u8 num_args;
SsaRegister result;
FunctionDecl *func_decl;
} SsaInsFuncCall;
diff --git a/include/std/buffer_view.h b/include/std/buffer_view.h
index 4993dc2..59a97d3 100644
--- a/include/std/buffer_view.h
+++ b/include/std/buffer_view.h
@@ -10,5 +10,6 @@ typedef struct {
BufferView create_buffer_view_null();
BufferView create_buffer_view(const char *data, usize size);
+bool buffer_view_equals(const BufferView *self, const BufferView *other);
#endif
diff --git a/include/std/misc.h b/include/std/misc.h
index 2fbe92c..e89f1eb 100644
--- a/include/std/misc.h
+++ b/include/std/misc.h
@@ -41,13 +41,4 @@
#define ignore_result_int(expr) (void)((expr)+1)
-typedef enum {
- bool_false,
- bool_true
-} bool;
-
-#ifndef NULL
-#define NULL ((void*)0)
-#endif
-
#endif
diff --git a/include/std/types.h b/include/std/types.h
index a8a44fd..bbf31c5 100644
--- a/include/std/types.h
+++ b/include/std/types.h
@@ -20,4 +20,13 @@ typedef size_t usize;
typedef float f32;
typedef double f64;
+typedef enum {
+ bool_false,
+ bool_true
+} bool;
+
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
#endif
diff --git a/include/tokenizer.h b/include/tokenizer.h
index f624816..16123cc 100644
--- a/include/tokenizer.h
+++ b/include/tokenizer.h
@@ -37,7 +37,8 @@ typedef enum {
TOK_IF,
TOK_ELSE,
TOK_WHILE,
- TOK_EXTERN
+ TOK_EXTERN,
+ TOK_RETURN
} Token;
typedef struct {
diff --git a/src/ast.c b/src/ast.c
index 0aa19d4..413f77e 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -15,12 +15,118 @@
throw(return_if_result); \
} while(0)
+static void variable_type_resolve(VariableType *self, AstCompilerContext *context, AstResolvedType *resolved_type);
static void ast_resolve(Ast *self, AstCompilerContext *context);
+static BufferView variable_type_get_name(VariableType *self);
+
+static void scope_named_object_init(ScopeNamedObject *self) {
+ self->type = NAMED_OBJECT_NONE;
+ self->value.lhs_expr = NULL;
+ self->resolve_data = NULL;
+}
+
+/* TODO: Remove these? */
+#if 0
+static bool scope_named_object_equals(ScopeNamedObject *self, ScopeNamedObject *other) {
+ /* This should be fine, without checking ScopeNamedObject type, since they are only equal if the types are equal as well */
+ return self->value.lhs_expr == other->value.lhs_expr;
+}
+
+static BufferView scope_named_object_get_type_name(ScopeNamedObject *self) {
+ BufferView result;
+ switch(self->type) {
+ case NAMED_OBJECT_NONE:
+ result = create_buffer_view("", 0);
+ break;
+ case NAMED_OBJECT_LHS_EXPR:
+ result = variable_type_get_name(&self->value.lhs_expr->type);
+ break;
+ case NAMED_OBJECT_FUNC_PARAM:
+ result = variable_type_get_name(&self->value.func_param->type);
+ break;
+ }
+ return result;
+}
+#endif
+
+static BufferView ast_resolved_type_get_name(AstResolvedType *self) {
+ BufferView result;
+ switch(self->type) {
+ case RESOLVED_TYPE_NONE:
+ result = create_buffer_view("", 0);
+ break;
+ case RESOLVED_TYPE_LHS_EXPR:
+ result = variable_type_get_name(&self->value.lhs_expr->type);
+ break;
+ case RESOLVED_TYPE_FUNC_SIG: {
+ if(!self->value.func_sig->func_decl || !self->value.func_sig->func_decl->lhs_expr) {
+ /*
+ TODO: Use function signature string from the source file, which will also help with error reference.
+ Currently this would point to an invalid location in the source file
+ */
+ result = create_buffer_view("fn()", 0);
+ break;
+ }
+ result = self->value.func_sig->func_decl->lhs_expr->var_name;
+ break;
+ }
+ }
+ return result;
+}
+
+static void ast_resolved_type_init(AstResolvedType *self) {
+ self->value.lhs_expr = NULL;
+ self->type = RESOLVED_TYPE_NONE;
+}
+
+static bool ast_resolved_type_equals(AstResolvedType *self, AstResolvedType *other) {
+ /* This should be fine, without checking AstResolvedType type, since they are only equal if the types are equal as well */
+ return self->value.lhs_expr == other->value.lhs_expr;
+}
+
+static AstResolvedType lhs_expr_get_resolved_type(LhsExpr *self, AstCompilerContext *context) {
+ AstResolvedType result;
+ variable_type_resolve(&self->type, context, &result);
+ if(result.type != RESOLVED_TYPE_NONE)
+ return result;
+
+ assert(self->rhs_expr);
+ ast_resolve(self->rhs_expr, context);
+ return self->rhs_expr->resolve_data.type;
+}
+
+/* TODO: Detect recursive dependency? is it even possible for function parameters? (it would normally be a recursive dependency on the function declaration) */
+static void function_parameter_resolve(FunctionParameter *self, AstCompilerContext *context) {
+ if(self->resolve_data.status == AST_RESOLVED)
+ return;
+ self->resolve_data.status = AST_RESOLVING;
+ variable_type_resolve(&self->type, context, &self->resolve_data.type);
+ self->resolve_data.status = AST_RESOLVED;
+}
+
+static AstResolvedType scope_named_object_get_resolved_type(ScopeNamedObject *self, AstCompilerContext *context) {
+ AstResolvedType result;
+ switch(self->type) {
+ case NAMED_OBJECT_NONE:
+ /* Type not resolved */
+ assert(bool_false);
+ ast_resolved_type_init(&result);
+ return result;
+ case NAMED_OBJECT_LHS_EXPR:
+ result = lhs_expr_get_resolved_type(self->value.lhs_expr, context);
+ break;
+ case NAMED_OBJECT_FUNC_PARAM:
+ function_parameter_resolve(self->value.func_param, context);
+ result = self->value.func_param->resolve_data.type;
+ break;
+ }
+ return result;
+}
static void resolve_data_init(AstResolveData *self) {
self->status = AST_NOT_RESOLVED;
- self->parser = NULL;
- self->type = NULL;
+ ast_resolved_type_init(&self->type);
+ self->ssa_reg = 0;
}
int ast_create(ArenaAllocator *allocator, void *value, AstType type, Ast **result) {
@@ -28,7 +134,7 @@ int ast_create(ArenaAllocator *allocator, void *value, AstType type, Ast **resul
(*result)->value.data = value;
(*result)->type = type;
resolve_data_init(&(*result)->resolve_data);
- (*result)->ssa_reg = 0;
+ (*result)->parser = NULL;
return 0;
}
@@ -47,6 +153,7 @@ BufferView ast_get_name(Ast *self) {
case AST_BINOP:
case AST_IF_STATEMENT:
case AST_WHILE_STATEMENT:
+ case AST_RETURN:
name = create_buffer_view_null();
break;
case AST_NUMBER:
@@ -75,12 +182,47 @@ static BufferView ast_get_code_reference(Ast *self) {
return ast_get_name(self);
}
-void function_signature_init(FunctionSignature *self) {
- self->params = 0;
+int function_signature_init(FunctionSignature *self, ArenaAllocator *allocator) {
self->resolved = bool_false;
+ self->func_decl = NULL;
+ return_if_error(buffer_init(&self->parameters, allocator));
+ return buffer_init(&self->return_types, allocator);
+}
+
+static FunctionParameter* function_signature_get_parameter_by_name(FunctionSignature *self, BufferView name) {
+ FunctionParameter *param, *param_end;
+ param = buffer_begin(&self->parameters);
+ param_end = buffer_end(&self->parameters);
+ for(; param != param_end; ++param) {
+ if(buffer_view_equals(&name, &param->name))
+ return param;
+ }
+ return NULL;
+}
+
+int function_signature_add_parameter(FunctionSignature *self, const FunctionParameter *new_param) {
+ const FunctionParameter *existing_param = function_signature_get_parameter_by_name(self, new_param->name);
+ if(existing_param)
+ return AST_ERR_DEF_DUP;
+ return buffer_append(&self->parameters, new_param, sizeof(FunctionParameter));
+}
+
+int function_signature_add_return_type(FunctionSignature *self, const VariableType *var_type) {
+ FunctionReturnType return_type;
+ return_type.type = *var_type;
+ ast_resolved_type_init(&return_type.resolved_type);
+ return buffer_append(&self->return_types, &return_type, sizeof(return_type));
+}
+
+void function_parameter_init(FunctionParameter *self) {
+ self->name = create_buffer_view_null();
+ self->type.type = VARIABLE_TYPE_NONE;
+ self->type.value.variable = NULL;
+ resolve_data_init(&self->resolve_data);
}
int funcdecl_init(FunctionDecl *self, FunctionSignature *signature, Scope *parent, ArenaAllocator *allocator) {
+ self->lhs_expr = NULL;
self->signature = signature;
self->ssa_func_index = 0;
return scope_init(&self->body, parent, allocator);
@@ -102,9 +244,9 @@ LhsExpr* structdecl_get_field_by_name(StructDecl *self, BufferView field_name) {
return result->value.lhs_expr;
}
-void structfield_init(StructField *self, BufferView name, BufferView type_name) {
+void structfield_init(StructField *self, BufferView name, VariableType *type) {
self->name = name;
- variable_init(&self->type, type_name);
+ self->type = *type;
}
void lhsexpr_init(LhsExpr *self, DeclFlag decl_flag, BufferView var_name) {
@@ -140,7 +282,7 @@ void number_init(Number *self, i64 value, bool is_integer, BufferView code_ref)
void variable_init(Variable *self, BufferView name) {
self->name = name;
- self->resolved_var = NULL;
+ scope_named_object_init(&self->resolved_var);
}
void binop_init(Binop *self) {
@@ -167,11 +309,16 @@ int while_statement_init(WhileStatement *self, Scope *parent, ArenaAllocator *al
return scope_init(&self->body, parent, allocator);
}
+void return_expr_init(ReturnExpr *self, Ast *rhs_expr) {
+ self->rhs_expr = rhs_expr;
+}
+
int scope_init(Scope *self, Scope *parent, ArenaAllocator *allocator) {
return_if_error(buffer_init(&self->ast_objects, allocator));
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;
+ self->function_signature = NULL;
return 0;
}
@@ -192,7 +339,6 @@ int scope_add_child(Scope *self, Ast *child) {
Ast *existing_child;
bool child_already_exists;
- /* TODO: Implement for parameter */
if(child->type == AST_LHS) {
BufferView var_name;
var_name = child->value.lhs_expr->var_name;
@@ -213,14 +359,16 @@ int scope_add_child(Scope *self, Ast *child) {
void scope_resolve(Scope *self, AstCompilerContext *context) {
Ast **ast;
Ast **ast_end;
+ Scope *prev_scope;
ast = buffer_begin(&self->ast_objects);
ast_end = buffer_end(&self->ast_objects);
+ prev_scope = context->scope;
context->scope = self;
for(; ast != ast_end; ++ast) {
ast_resolve(*ast, context);
}
- context->scope = self->parent;
+ context->scope = prev_scope;
}
static Parser* scope_get_parser(Scope *scope) {
@@ -247,78 +395,114 @@ static void parser_print_error(Parser *parser, const char *ref, const char *fmt,
va_end(args);
}
-static Ast* __scope_get_resolved_variable(Scope *self, Scope *start, AstCompilerContext *context, BufferView name) {
- Ast *result;
+static void __scope_get_resolved_variable(Scope *self, Scope *start, AstCompilerContext *context, BufferView name, ScopeNamedObject *result) {
+ Ast *ast_result;
bool exists;
Scope *prev_scope;
assert(self);
- exists = hash_map_get(&self->named_objects, name, &result);
+ exists = hash_map_get(&self->named_objects, name, &ast_result);
if(!exists) {
- Parser *parser;
- if(self->parent)
- return __scope_get_resolved_variable(self->parent, start, context, name);
+ if(self->function_signature) {
+ FunctionParameter *func_param;
+ func_param = function_signature_get_parameter_by_name(self->function_signature, name);
+ if(func_param) {
+ prev_scope = context->scope;
+ context->scope = self;
+ function_parameter_resolve(func_param, context);
+ context->scope = prev_scope;
+
+ result->type = NAMED_OBJECT_FUNC_PARAM;
+ result->value.func_param = func_param;
+ result->resolve_data = &func_param->resolve_data;
+ return;
+ }
+ }
- parser = scope_get_parser(start);
- parser_print_error(parser, name.data, "Undefined reference to variable \"%.*s\"", name.size, name.data);
- throw(AST_ERR);
+ if(self->parent) {
+ __scope_get_resolved_variable(self->parent, start, context, name, result);
+ return;
+ }
+
+ {
+ Parser *parser;
+ parser = scope_get_parser(start);
+ parser_print_error(parser, name.data, "Undefined reference to variable \"%.*s\"", name.size, name.data);
+ throw(AST_ERR);
+ }
}
/*
- Need to change scope here because we are changing the visible scope
- and the ast object may be in another scope than the current
- resolving ast.
+ Need to change scope here because we are changing the visible scope
+ and the ast object may be in another scope than the current
+ resolving ast.
*/
prev_scope = context->scope;
context->scope = self;
- ast_resolve(result, context);
+ ast_resolve(ast_result, context);
context->scope = prev_scope;
- assert(result->type == AST_LHS);
- return result;
+ assert(ast_result->type == AST_LHS);
+ result->type = NAMED_OBJECT_LHS_EXPR;
+ result->value.lhs_expr = ast_result->value.lhs_expr;
+ result->resolve_data = &ast_result->resolve_data;
}
-static Ast* scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name) {
- return __scope_get_resolved_variable(self, self, context, name);
+static void scope_get_resolved_variable(Scope *self, AstCompilerContext *context, BufferView name, ScopeNamedObject *result) {
+ __scope_get_resolved_variable(self, self, context, name, result);
+}
+
+static void function_return_type_resolve(FunctionReturnType *self, AstCompilerContext *context) {
+ variable_type_resolve(&self->type, context, &self->resolved_type);
}
static void function_signature_resolve(FunctionSignature *self, AstCompilerContext *context) {
- /* TODO: Implement */
if(self->resolved)
return;
- self->resolved = bool_true;
- (void)self;
- (void)context;
-}
-static void variable_resolve(Variable *self, AstCompilerContext *context, AstResolveData *resolve_data) {
- if(!self->resolved_var)
- self->resolved_var = scope_get_resolved_variable(context->scope, context, self->name);
- resolve_data->type = self->resolved_var->resolve_data.type;
+ {
+ FunctionParameter *param, *param_end;
+ param = buffer_begin(&self->parameters);
+ param_end = buffer_end(&self->parameters);
+ for(; param != param_end; ++param) {
+ function_parameter_resolve(param, context);
+ }
+ }
+ {
+ FunctionReturnType *return_type, *return_type_end;
+ return_type = buffer_begin(&self->return_types);
+ return_type_end = buffer_end(&self->return_types);
+ for(; return_type != return_type_end; ++return_type) {
+ function_return_type_resolve(return_type, context);
+ }
+ }
+
+ self->resolved = bool_true;
}
-static LhsExpr* variable_get_resolved_type(Variable *self) {
- assert(self->resolved_var);
- return self->resolved_var->value.lhs_expr;
+static void variable_resolve(Variable *self, AstCompilerContext *context, AstResolvedType *resolved_type) {
+ if(self->resolved_var.type == NAMED_OBJECT_NONE)
+ scope_get_resolved_variable(context->scope, context, self->name, &self->resolved_var);
+ *resolved_type = scope_named_object_get_resolved_type(&self->resolved_var, context);
}
-static void variable_type_resolve(LhsExpr *self, AstCompilerContext *context, AstResolveData *resolve_data) {
- VariableType *lhs_expr_type;
- lhs_expr_type = &self->type;
- switch(lhs_expr_type->type) {
+void variable_type_resolve(VariableType *self, AstCompilerContext *context, AstResolvedType *resolved_type) {
+ switch(self->type) {
case VARIABLE_TYPE_NONE:
+ ast_resolved_type_init(resolved_type);
return;
case VARIABLE_TYPE_VARIABLE:
- variable_resolve(lhs_expr_type->value.variable, context, resolve_data);
+ variable_resolve(self->value.variable, context, resolved_type);
break;
case VARIABLE_TYPE_SIGNATURE:
- function_signature_resolve(lhs_expr_type->value.signature, context);
- resolve_data->type = self;
+ function_signature_resolve(self->value.signature, context);
+ resolved_type->type = RESOLVED_TYPE_FUNC_SIG;
+ resolved_type->value.func_sig = self->value.signature;
break;
}
}
-static BufferView variable_type_get_name(VariableType *self) {
+BufferView variable_type_get_name(VariableType *self) {
BufferView result;
switch(self->type) {
case VARIABLE_TYPE_NONE:
@@ -337,14 +521,14 @@ static BufferView variable_type_get_name(VariableType *self) {
return result;
}
-static LhsExpr* lhsexpr_resolve_rhs(LhsExpr *self, AstCompilerContext *context) {
- LhsExpr *rhs_resolved_type;
+static void lhsexpr_resolve_rhs(LhsExpr *self, AstCompilerContext *context, AstResolvedType *result) {
ast_resolve(self->rhs_expr, context);
- if(ast_is_decl(self->rhs_expr))
- rhs_resolved_type = self;
- else
- rhs_resolved_type = self->rhs_expr->resolve_data.type;
- return rhs_resolved_type;
+ if(ast_is_decl(self->rhs_expr)) {
+ result->type = RESOLVED_TYPE_LHS_EXPR;
+ result->value.lhs_expr = self;
+ } else {
+ *result = self->rhs_expr->resolve_data.type;
+ }
}
static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) {
@@ -353,7 +537,7 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) {
assert(ast->type == AST_LHS);
self = ast->value.lhs_expr;
- variable_type_resolve(self, context, &ast->resolve_data);
+ variable_type_resolve(&self->type, context, &ast->resolve_data.type);
/*
TODO: When parameters and return types are implemented, AST_RESOLVE_END should be set after
@@ -361,7 +545,7 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) {
be allowed but recursive function calls still require parameters and return types to be known.
*/
if(self->rhs_expr) {
- LhsExpr *rhs_resolve_type;
+ AstResolvedType rhs_resolve_type;
if(self->rhs_expr->type == AST_FUNCTION_DECL) {
/*
The function declaration itself always resolves the signature, but we also do it here because we
@@ -371,17 +555,19 @@ static void lhsexpr_resolve(Ast *ast, AstCompilerContext *context) {
function_signature_resolve(self->rhs_expr->value.func_decl->signature, context);
ast->resolve_data.status = AST_RESOLVED;
/*
- If rhs is a function declaration then there is no need to wait until it has been resolved before setting the type as the type
- is @self (the lhs). We still need to continue after this, so rhs can be resolved.
+ If rhs is a function declaration then there is no need to wait until it has been resolved before setting the type.
+ We still need to continue after this, so rhs can be resolved.
*/
- if(!ast->resolve_data.type)
- ast->resolve_data.type = self;
+ if(ast->resolve_data.type.type == RESOLVED_TYPE_NONE) {
+ ast->resolve_data.type.type = RESOLVED_TYPE_FUNC_SIG;
+ ast->resolve_data.type.value.func_sig = self->rhs_expr->value.func_decl->signature;
+ }
}
- rhs_resolve_type = lhsexpr_resolve_rhs(self, context);
+ lhsexpr_resolve_rhs(self, context, &rhs_resolve_type);
/* TODO: Add casting */
- if(ast->resolve_data.type && ast->resolve_data.type != rhs_resolve_type) {
+ if(ast->resolve_data.type.type == RESOLVED_TYPE_LHS_EXPR && !ast_resolved_type_equals(&ast->resolve_data.type, &rhs_resolve_type)) {
Parser *parser;
parser = scope_get_parser(context->scope);
/*
@@ -413,23 +599,29 @@ static LhsExpr* binop_get_lhs_expr(Binop *self) {
static void assignmentexpr_resolve(Ast *ast, AstCompilerContext *context) {
AssignmentExpr *self;
- LhsExpr *lhs_source;
+ bool is_lhs_const;
+ is_lhs_const = bool_false;
assert(ast->type == AST_ASSIGN);
self = ast->value.assign_expr;
- lhs_source = NULL;
ast_resolve(self->lhs_expr, context);
ast_resolve(self->rhs_expr, context);
- if(self->lhs_expr->type == AST_VARIABLE)
- lhs_source = variable_get_resolved_type(self->lhs_expr->value.variable);
- else if(self->lhs_expr->type == AST_BINOP)
- lhs_source = binop_get_lhs_expr(self->lhs_expr->value.binop);
+ if(self->lhs_expr->type == AST_VARIABLE) {
+ /* TODO: Allow non-const function param */
+ const ScopeNamedObject *resolved_var = &self->lhs_expr->value.variable->resolved_var;
+ if(resolved_var->type == NAMED_OBJECT_FUNC_PARAM || LHS_EXPR_IS_CONST(resolved_var->value.lhs_expr))
+ is_lhs_const = bool_true;
+ } else if(self->lhs_expr->type == AST_BINOP) {
+ LhsExpr *lhs_expr;
+ lhs_expr = binop_get_lhs_expr(self->lhs_expr->value.binop);
+ is_lhs_const = LHS_EXPR_IS_CONST(lhs_expr);
+ }
/* This also covers extern variables, since extern variables are always const */
/* TODO: var.field type expressions should also be checked */
- if(lhs_source && LHS_EXPR_IS_CONST(lhs_source)) {
+ if(is_lhs_const) {
Parser *parser;
parser = scope_get_parser(context->scope);
parser_print_error(parser, ast_get_code_reference(self->lhs_expr).data, "Can't assign to a const value");
@@ -437,14 +629,14 @@ static void assignmentexpr_resolve(Ast *ast, AstCompilerContext *context) {
}
/* TODO: Add casting */
- if(self->lhs_expr->resolve_data.type != self->rhs_expr->resolve_data.type) {
+ if(!ast_resolved_type_equals(&self->lhs_expr->resolve_data.type, &self->rhs_expr->resolve_data.type)) {
Parser *parser;
BufferView rhs_type_name;
BufferView lhs_type_name;
parser = scope_get_parser(context->scope);
- rhs_type_name = variable_type_get_name(&self->rhs_expr->resolve_data.type->type);
- lhs_type_name = variable_type_get_name(&self->lhs_expr->resolve_data.type->type);
+ rhs_type_name = ast_resolved_type_get_name(&self->rhs_expr->resolve_data.type);
+ lhs_type_name = ast_resolved_type_get_name(&self->lhs_expr->resolve_data.type);
/*
TODO: Instead of using self->var_name, using type name. This cant be done right now because
type can be function signature.
@@ -462,39 +654,54 @@ static void import_resolve(Ast *ast, AstCompilerContext *context) {
assert(ast->type == AST_IMPORT);
(void)context;
self = ast->value.import;
- ast->resolve_data.type = &self->file_scope->parser->file_decl;
- assert(ast->resolve_data.type);
+ ast->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR;
+ ast->resolve_data.type.value.lhs_expr = &self->file_scope->parser->file_decl;
}
static Scope* lhsexpr_get_scope(LhsExpr *self) {
AstValue value;
- if(self->rhs_expr) {
- value = self->rhs_expr->value;
- switch(self->rhs_expr->type) {
- case AST_FUNCTION_DECL:
- return &value.func_decl->body;
- case AST_STRUCT_DECL:
- return &value.struct_decl->body;
- case AST_IMPORT:
- return &value.import->file_scope->parser->struct_decl.body;
- default:
- break;
- }
+ value = self->rhs_expr->value;
+ switch(self->rhs_expr->type) {
+ case AST_FUNCTION_DECL:
+ return &value.func_decl->body;
+ case AST_STRUCT_DECL:
+ return &value.struct_decl->body;
+ case AST_IMPORT:
+ return &value.import->file_scope->parser->struct_decl.body;
+ default:
+ break;
}
assert(bool_false && "Expected lhsexpr_get_scope to only be called for non-extern function declaration, struct declaration and import");
return NULL;
}
+static Scope* ast_resolved_type_get_scope(AstResolvedType *self) {
+ switch(self->type) {
+ case RESOLVED_TYPE_NONE:
+ assert(bool_false && "Expected ast_resolved_type_get_scope to only be called for a resolved object");
+ return NULL;
+ case RESOLVED_TYPE_LHS_EXPR:
+ return lhsexpr_get_scope(self->value.lhs_expr);
+ case RESOLVED_TYPE_FUNC_SIG:
+ assert(self->value.func_sig->func_decl);
+ return &self->value.func_sig->func_decl->body;
+ }
+ return NULL;
+}
+
/* @self has to have a resolved type before calling this */
static Parser* get_resolved_type_parser(Ast *self) {
- assert(self->resolve_data.type);
- return scope_get_parser(lhsexpr_get_scope(self->resolve_data.type));
+ assert(self->resolve_data.type.type == RESOLVED_TYPE_LHS_EXPR && "Expected get_resolved_type_parser to only be called for non-extern function declaration, struct declaration and import");
+ return scope_get_parser(lhsexpr_get_scope(self->resolve_data.type.value.lhs_expr));
}
-static void funcdecl_resolve(FunctionDecl *self, AstCompilerContext *context) {
- /* TODO: Implement parameters and return types */
- function_signature_resolve(self->signature, context);
- scope_resolve(&self->body, context);
+static void funcdecl_resolve(Ast *self, AstCompilerContext *context) {
+ FunctionDecl *func_decl;
+ func_decl = self->value.func_decl;
+ function_signature_resolve(func_decl->signature, context);
+ scope_resolve(&func_decl->body, context);
+ self->resolve_data.type.type = RESOLVED_TYPE_FUNC_SIG;
+ self->resolve_data.type.value.func_sig = self->value.func_decl->signature;
}
/*
@@ -502,7 +709,10 @@ static void funcdecl_resolve(FunctionDecl *self, AstCompilerContext *context) {
Meaning the resolve status wont be set to solved but the resolve type will be set.
*/
bool resolved_type_is_func_decl(Ast *self) {
- const LhsExpr *resolved_type = self->resolve_data.type;
+ LhsExpr *resolved_type;
+ if(self->resolve_data.type.type != RESOLVED_TYPE_LHS_EXPR)
+ return bool_false;
+ resolved_type = self->resolve_data.type.value.lhs_expr;
return (resolved_type->rhs_expr && resolved_type->rhs_expr->type == AST_FUNCTION_DECL) ||
resolved_type->type.type == VARIABLE_TYPE_SIGNATURE;
}
@@ -513,16 +723,16 @@ static void funccall_resolve(Ast *self, AstCompilerContext *context) {
Ast **ast_end;
func_call = self->value.func_call;
- variable_resolve(&func_call->func, context, &self->resolve_data);
+ variable_resolve(&func_call->func, context, &self->resolve_data.type);
/* Attemping to use call syntax (variable_name ( ) ) with a variable that is not a function */
- if(!resolved_type_is_func_decl(self)) {
+ if(self->resolve_data.type.type != RESOLVED_TYPE_FUNC_SIG) {
Parser *caller_parser;
Parser *callee_parser;
BufferView callee_code_ref;
caller_parser = scope_get_parser(context->scope);
callee_parser = get_resolved_type_parser(self);
- callee_code_ref = self->resolve_data.type->var_name;
+ callee_code_ref = ast_resolved_type_get_name(&self->resolve_data.type);
parser_print_error(caller_parser, func_call->func.name.data,
"\"%.*s\" is not a function. Only functions can be called", func_call->func.name.size, func_call->func.name.data);
@@ -549,11 +759,14 @@ 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->resolve_data);
+ variable_type_resolve(&struct_field->type, context, &self->resolve_data.type);
}
static bool is_struct_decl(Ast *self) {
- const LhsExpr *resolved_type = self->resolve_data.type;
+ LhsExpr *resolved_type;
+ if(self->resolve_data.type.type != RESOLVED_TYPE_LHS_EXPR)
+ return bool_false;
+ resolved_type = self->resolve_data.type.value.lhs_expr;
assert(self->resolve_data.status == AST_RESOLVED);
return resolved_type->rhs_expr && resolved_type->rhs_expr->type == AST_STRUCT_DECL;
}
@@ -565,6 +778,7 @@ static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) {
Parser *callee_parser;
BufferView caller_code_ref;
BufferView callee_code_ref;
+ ScopeNamedObject rhs_resolved_var;
assert(ast->type == AST_BINOP);
self = ast->value.binop;
@@ -578,12 +792,13 @@ static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) {
throw(AST_ERR);
}
- lhs_scope = lhsexpr_get_scope(self->lhs->resolve_data.type);
- self->rhs->resolve_data.type = scope_get_resolved_variable(lhs_scope, context, self->rhs->value.variable->name)->resolve_data.type;
+ lhs_scope = ast_resolved_type_get_scope(&self->lhs->resolve_data.type);
+ scope_get_resolved_variable(lhs_scope, context, self->rhs->value.variable->name, &rhs_resolved_var);
+ self->rhs->resolve_data.type = scope_named_object_get_resolved_type(&rhs_resolved_var, context);
callee_parser = get_resolved_type_parser(self->rhs);
caller_code_ref = ast_get_code_reference(self->rhs);
- callee_code_ref = self->rhs->resolve_data.type->var_name;
+ callee_code_ref = ast_resolved_type_get_name(&self->rhs->resolve_data.type);
if(!is_struct_decl(self->lhs)) {
parser_print_error(caller_parser, caller_code_ref.data, "Can only access field of structs");
@@ -593,7 +808,7 @@ static void binop_resolve_dot_access(Ast *ast, AstCompilerContext *context) {
throw(AST_ERR);
}
- if(!LHS_EXPR_IS_PUB(self->rhs->resolve_data.type)) {
+ if(self->rhs->resolve_data.type.type != RESOLVED_TYPE_LHS_EXPR || !LHS_EXPR_IS_PUB(self->rhs->resolve_data.type.value.lhs_expr)) {
parser_print_error(caller_parser, caller_code_ref.data, "Can't access non-public field \"%.*s\"", caller_code_ref.size, caller_code_ref.data);
/* TODO: use tokenizer_print_note, once it has been added */
/* TODO: Print type */
@@ -612,7 +827,7 @@ static void binop_resolve(Ast *ast, AstCompilerContext *context) {
/* Only function call has extra data that needs to be resolved (args) */
if(self->rhs->type == AST_FUNCTION_CALL) {
Scope *prev_scope = context->scope;
- context->scope = lhsexpr_get_scope(self->lhs->resolve_data.type);
+ context->scope = ast_resolved_type_get_scope(&self->lhs->resolve_data.type);
ast_resolve(self->rhs, context);
context->scope = prev_scope;
}
@@ -621,35 +836,43 @@ static void binop_resolve(Ast *ast, AstCompilerContext *context) {
} else {
ast_resolve(self->rhs, context);
/* TODO: Convert types that can be safely converted */
- assert(self->lhs->resolve_data.type);
- assert(self->rhs->resolve_data.type);
- if(self->rhs->resolve_data.type != self->lhs->resolve_data.type) {
+ assert(self->lhs->resolve_data.type.type != RESOLVED_TYPE_NONE);
+ assert(self->rhs->resolve_data.type.type != RESOLVED_TYPE_NONE);
+ if(!ast_resolved_type_equals(&self->rhs->resolve_data.type, &self->lhs->resolve_data.type)) {
/*
TODO: For this first error, only print the line without a reference to code.
This requires change in tokenizer_print_error to be able to take a line as reference.
TODO: Use note for the additional information instead of error.
*/
+ BufferView lhs_type_name, rhs_type_name;
Parser *parser;
+
+ lhs_type_name = ast_resolved_type_get_name(&self->lhs->resolve_data.type);
+ rhs_type_name = ast_resolved_type_get_name(&self->rhs->resolve_data.type);
parser = scope_get_parser(context->scope);
parser_print_error(parser, ast_get_code_reference(self->rhs).data,
"Can't cast type \"%.*s\" to type \"%.*s\"",
- self->rhs->resolve_data.type->var_name.size, self->rhs->resolve_data.type->var_name.data,
- self->lhs->resolve_data.type->var_name.size, self->lhs->resolve_data.type->var_name.data);
+ rhs_type_name.size, rhs_type_name.data,
+ lhs_type_name.size, lhs_type_name.data);
parser_print_error(parser, ast_get_code_reference(self->lhs).data,
"Left-hand side is of type %.*s",
- self->lhs->resolve_data.type->var_name.size, self->lhs->resolve_data.type->var_name.data);
+ lhs_type_name.size, lhs_type_name.data);
parser_print_error(parser, ast_get_code_reference(self->rhs).data,
"Right-hand side is of type %.*s",
- self->rhs->resolve_data.type->var_name.size, self->rhs->resolve_data.type->var_name.data);
+ rhs_type_name.size, rhs_type_name.data);
throw(AST_ERR);
- } else if(!is_arithmetic_type(self->lhs->resolve_data.type, context->compiler)) { /* TODO: Optimize this? store arithmetic type in the LhsExpr itself? */
+ /* TODO: Optimize this? store arithmetic type in the LhsExpr itself? */
+ } else if(self->lhs->resolve_data.type.type != RESOLVED_TYPE_LHS_EXPR || !is_arithmetic_type(self->lhs->resolve_data.type.value.lhs_expr, context->compiler)) {
/* TODO: Point the error at the binop instead of LHS */
+ BufferView lhs_type_name;
Parser *parser;
+
+ lhs_type_name = ast_resolved_type_get_name(&self->lhs->resolve_data.type);
parser = scope_get_parser(context->scope);
parser_print_error(parser, ast_get_code_reference(self->lhs).data,
"Arithmetic operation can only be performed with the types i8, u8, i16, u16, i32, u32, i64, u64, isize and usize",
- self->lhs->resolve_data.type->var_name.size, self->lhs->resolve_data.type->var_name.data);
+ lhs_type_name.size, lhs_type_name.data);
throw(AST_ERR);
}
ast->resolve_data.type = self->lhs->resolve_data.type;
@@ -678,23 +901,36 @@ static void while_statement_resolve(WhileStatement *while_stmt, AstCompilerConte
scope_resolve(&while_stmt->body, context);
}
+static void return_expr_resolve(ReturnExpr *self, AstCompilerContext *context) {
+ ast_resolve(self->rhs_expr, context);
+}
+
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
- instead of the whole function declaration including the body
- because the body can have function call that calls functions that are resolving
- or even recursive function call, which should be allowed.
+ 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
+ instead of the whole function declaration including the body
+ because the body can have function call that calls functions that are resolving
+ or even recursive function call, which should be allowed.
*/
/*
- This check is outside lhs_expr mutex for optimization purpose as most times there wont be
- a race in multiple threads to resolve an AST expression.
+ This check is outside lhs_expr mutex for optimization purpose as most times there wont be
+ a race in multiple threads to resolve an AST expression.
*/
if(self->resolve_data.status == AST_RESOLVED) {
return;
- } else if(self->resolve_data.status == AST_RESOLVING && self->resolve_data.parser == context->parser) {
+ /*
+ NOTE!!!: There is a data race here and it's fine!
+ A recursive dependency can be missed here in one thread, but it's fine because that
+ can only happen if another thread is also resolving the same ast, in which case that thread will finally
+ report the recursive dependency. Note that this can mean that different files can report the
+ recursive dependency everytime you compile code with recursive dependency. However this can happen
+ even without a data race since files are compile in parallel so one file might resolve the ast with the issue
+ before another file has done it, and it can be different everytime the files are compiled.
+ */
+ } else if(self->resolve_data.status == AST_RESOLVING && self->parser == context->parser) {
Parser *parser;
parser = scope_get_parser(context->scope);
parser_print_error(parser, ast_get_code_reference(self).data, "Found recursive dependency");
@@ -702,20 +938,21 @@ void ast_resolve(Ast *self, AstCompilerContext *context) {
}
self->resolve_data.status = AST_RESOLVING;
- self->resolve_data.parser = context->parser;
+ self->parser = context->parser;
switch(self->type) {
case AST_NUMBER: {
Number *number;
number = self->value.number;
/* TODO: Support other number types */
+ self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR;
if(number->is_integer)
- self->resolve_data.type = (LhsExpr*)context->compiler->default_types.i64;
+ self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.i64;
else
- self->resolve_data.type = (LhsExpr*)context->compiler->default_types.f64;
+ self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.f64;
break;
}
case AST_FUNCTION_DECL:
- funcdecl_resolve(self->value.func_decl, context);
+ funcdecl_resolve(self, context);
break;
case AST_FUNCTION_CALL:
funccall_resolve(self, context);
@@ -738,10 +975,11 @@ void ast_resolve(Ast *self, AstCompilerContext *context) {
break;
case AST_STRING:
/* TODO: Convert special combinations. For example \n to newline */
- self->resolve_data.type = (LhsExpr*)context->compiler->default_types.str;
+ self->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR;
+ self->resolve_data.type.value.lhs_expr = (LhsExpr*)context->compiler->default_types.str;
break;
case AST_VARIABLE:
- variable_resolve(self->value.variable, context, &self->resolve_data);
+ variable_resolve(self->value.variable, context, &self->resolve_data.type);
break;
case AST_BINOP:
binop_resolve(self, context);
@@ -752,7 +990,11 @@ void ast_resolve(Ast *self, AstCompilerContext *context) {
case AST_WHILE_STATEMENT:
while_statement_resolve(self->value.while_stmt, context);
break;
+ case AST_RETURN:
+ return_expr_resolve(self->value.return_expr, context);
+ break;
}
/* TODO: See comment at the top of this function */
+ /*assert(self->resolve_data.type.type != RESOLVED_TYPE_NONE);*/
self->resolve_data.status = AST_RESOLVED;
}
diff --git a/src/bytecode/bytecode.c b/src/bytecode/bytecode.c
index 70a5cfa..8a41900 100644
--- a/src/bytecode/bytecode.c
+++ b/src/bytecode/bytecode.c
@@ -42,9 +42,10 @@ static CHECK_RESULT usize ssa_extract_func_start(u8 *instruction_data, SsaInsFun
}
static CHECK_RESULT usize ssa_extract_func_call(u8 *instruction_data, SsaInsFuncCall *result) {
- am_memcpy(&result->result, instruction_data, sizeof(result->result));
- am_memcpy(&result->func_decl, instruction_data + sizeof(result->result), sizeof(result->func_decl));
- return sizeof(result->result) + sizeof(result->func_decl);
+ result->num_args = instruction_data[0];
+ am_memcpy(&result->result, instruction_data + 1, sizeof(result->result));
+ am_memcpy(&result->func_decl, instruction_data + 1 + sizeof(result->result), sizeof(result->func_decl));
+ return sizeof(u8) + sizeof(result->result) + sizeof(result->func_decl);
}
static CHECK_RESULT usize ssa_extract_jump_zero(u8 *instruction_data, SsaInsJumpZero *result) {
@@ -94,7 +95,7 @@ static void add_intermediates(BytecodeCompilerContext *self) {
}
}
-void add_strings(BytecodeCompilerContext *self) {
+static void add_strings(BytecodeCompilerContext *self) {
/*doc(Bytecode strings)
# Strings layout
|Type |Field |Description |
@@ -144,6 +145,18 @@ void add_strings(BytecodeCompilerContext *self) {
}
}
+static void add_functions(BytecodeCompilerContext *self) {
+ /*doc(Bytecode functions)
+ # Internal functions layout
+ |Type|Field |Description |
+ |----|-------------------|---------------------------------|
+ |u16 |Number of functions|The number of internal functions.|
+ */
+
+ assert(sizeof(SsaFuncIndex) == sizeof(u16) && "Program decoder needs to be updated since size of func index has changed");
+ throw_if_error(buffer_append(&self->bytecode.data, &self->parser->ssa->func_counter, sizeof(u16)));
+}
+
static void add_ins1(BytecodeCompilerContext *self, AmalOpcode opcode, const char *fmt) {
throw_if_error(buffer_append(&self->bytecode.data, &opcode, sizeof(AmalOpcodeType)));
fprintf(stderr, fmt);
@@ -437,7 +450,7 @@ static void add_instructions(BytecodeCompilerContext *self) {
}
case SSA_ASSIGN_REG: {
instruction += ssa_extract_form1(instruction, &ssa_ins_form1);
- add_ins3(self, AMAL_OP_MOV, ssa_ins_form1.lhs, ssa_ins_form1.rhs, "mov r%d, d%d");
+ add_ins3(self, AMAL_OP_MOV, ssa_ins_form1.lhs, ssa_ins_form1.rhs, "mov r%d, r%d");
break;
}
case SSA_ADD: {
@@ -505,8 +518,8 @@ static void add_instructions(BytecodeCompilerContext *self) {
is defined as the size of all previous files' number of functions.
*/
instruction += ssa_extract_func_call(instruction, &ssa_ins_func_call);
- /* TODO: Replace 0 with the number of arguments */
- add_ins7(self, AMAL_OP_CALL, ssa_ins_func_call.func_decl->ssa_func_index, 0, "call %d, %d");
+ add_ins7(self, AMAL_OP_CALL, ssa_ins_func_call.func_decl->ssa_func_index, ssa_ins_func_call.num_args, "call %d, %d");
+ assert(bool_false && "TODO: Assign function result (RAX for x86_64) to ssa_ins_func_call.result reg");
break;
}
case SSA_JUMP_ZERO: {
@@ -519,6 +532,13 @@ static void add_instructions(BytecodeCompilerContext *self) {
add_ins4(self, AMAL_OP_JMP, ssa_ins_jump.jump_offset, "jmp %d");
break;
}
+ case SSA_RET: {
+ SsaRegister reg;
+ am_memcpy(&reg, instruction, sizeof(SsaRegister));
+ instruction += sizeof(SsaRegister);
+ add_ins2(self, AMAL_OP_RET, reg, "ret r%d");
+ break;
+ }
}
}
@@ -537,6 +557,6 @@ static void add_instructions(BytecodeCompilerContext *self) {
void generate_bytecode_from_ssa(BytecodeCompilerContext *self) {
add_intermediates(self);
add_strings(self);
- /* TODO: Also add strings in ssa, so we can index them */
+ add_functions(self);
add_instructions(self);
}
diff --git a/src/compiler.c b/src/compiler.c
index 39cbb00..228cb74 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -36,7 +36,8 @@ static CHECK_RESULT int create_default_type(amal_compiler *compiler, const char
lhsexpr_init(*lhs_expr, DECL_FLAG_EXTERN | DECL_FLAG_PUB | DECL_FLAG_CONST, 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;
+ expr->resolve_data.type.type = RESOLVED_TYPE_LHS_EXPR;
+ expr->resolve_data.type.value.lhs_expr = *lhs_expr;
expr->resolve_data.status = AST_RESOLVED;
return scope_add_child(&compiler->root_scope, expr);
}
@@ -123,6 +124,7 @@ static CHECK_RESULT int amal_compiler_init(amal_compiler *self, const amal_compi
amal_compiler_options_init(&self->options);
self->program = program;
self->started = bool_false;
+ self->work_failed = bool_false;
self->generic_work_object_index = 0;
amal_mutex_init(&self->mutex);
@@ -204,7 +206,6 @@ static CHECK_RESULT int amal_compiler_load_in_this_thread(amal_compiler *compile
return result;
}
-/* TODO: Handle errors (stop parsing in all other threads and report errors/warnings) */
static void* thread_callback_parse_file(void *userdata) {
FileScopeReference *file_scope;
CompilerParserThreadUserData compiler_parser_userdata;
@@ -217,6 +218,11 @@ static void* thread_callback_parse_file(void *userdata) {
result = (void*)AMAL_COMPILER_ERR;
for(;;) {
int has_next;
+ /* Abort job if another job failed */
+ if(compiler_parser_userdata.compiler->work_failed) {
+ result = (void*)AMAL_COMPILER_WORK_FAIL_ABORT;
+ goto cleanup;
+ }
cleanup_if_error(amal_compiler_load_in_this_thread(compiler_parser_userdata.compiler,
file_scope,
&compiler_parser_userdata.parser_thread_data->allocator));
@@ -324,6 +330,14 @@ static void* thread_callback_generic(void *userdata) {
cleanup_if_error(thread_generate_bytecode(parser));
break;
}
+
+ /* Abort job if another job failed */
+ if(compiler_userdata.compiler->work_failed) {
+ result = (void*)AMAL_COMPILER_WORK_FAIL_ABORT;
+ goto cleanup;
+ }
+
+ /* Find next job */
cleanup_if_error(amal_mutex_lock(&compiler_userdata.compiler->mutex, "thread_callback_generic"));
if(compiler_userdata.compiler->generic_work_object_index + 1 >= (int)buffer_get_size(&compiler_userdata.compiler->parsers, Parser*))
break;
@@ -339,7 +353,7 @@ static void* thread_callback_generic(void *userdata) {
and the other threads will stop when they are done with the work they are currently working on.
*/
if(result != NULL) {
- cleanup_if_error(amal_mutex_lock(&compiler_userdata.compiler->mutex, "thread_callback_generic"));
+ ignore_result_int(amal_mutex_lock(&compiler_userdata.compiler->mutex, "thread_callback_generic"));
compiler_userdata.compiler->generic_work_object_index = (int)buffer_get_size(&compiler_userdata.compiler->parsers, Parser*);
}
compiler_userdata.parser_thread_data->status = PARSER_THREAD_STATUS_IDLE;
@@ -424,11 +438,9 @@ static CHECK_RESULT int amal_compiler_load_file_join_threads(amal_compiler *self
int result;
void *thread_return_data;
ParserThreadData *parser_thread_data;
- bool work_failed;
assert(amal_thread_is_main());
thread_return_data = NULL;
- work_failed = bool_false;
for(;;) {
bool done;
/*
@@ -442,12 +454,11 @@ static CHECK_RESULT int amal_compiler_load_file_join_threads(amal_compiler *self
amal_mutex_tryunlock(&self->mutex);
if(result != 0)
goto cleanup;
- /* TODO: Cleanup remaining threads if join fails */
- cleanup_if_error(parser_thread_data_join(parser_thread_data, &thread_return_data));
+ /* TODO: What to do if join fails? */
+ ignore_result_int(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");
- work_failed = bool_true;
+ self->work_failed = bool_true;
}
}
@@ -458,7 +469,7 @@ static CHECK_RESULT int amal_compiler_load_file_join_threads(amal_compiler *self
result = AMAL_COMPILER_OK;
cleanup:
- if(work_failed)
+ if(self->work_failed)
result = AMAL_COMPILER_ERR;
return result;
}
@@ -545,6 +556,9 @@ int amal_compiler_internal_load_file(amal_compiler *self, const char *filepath,
bool main_job;
bool new_entry;
+ if(self->work_failed)
+ return AMAL_COMPILER_WORK_FAIL_ABORT;
+
return_if_error(try_create_file_scope(self, filepath, file_scope, &new_entry));
assert(file_scope && *file_scope && (*file_scope)->canonical_path.data);
if(!new_entry) {
diff --git a/src/parser.c b/src/parser.c
index e36790f..33e9146 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -24,6 +24,8 @@ static CHECK_RESULT Ast* parser_parse_rhs(Parser *self);
static CHECK_RESULT Ast* parser_parse_body(Parser *self);
static CHECK_RESULT Ast* parser_parse_struct_body(Parser *self);
static CHECK_RESULT Ast* parser_parse_rhs_binop(Parser *self);
+static void parser_parse_var_type(Parser *self, VariableType *result);
+static void parser_parse_var_type_def(Parser *self, VariableType *result);
static void parser_queue_file(Parser *self, BufferView path, FileScopeReference **file_scope);
int parser_thread_data_init(ParserThreadData *self) {
@@ -133,42 +135,119 @@ static void parser_parse_struct_body_loop(Parser *self, Scope *scope) {
}
/*
-TODO: Implement params and return types
-FUNC_SIGNATURE = 'fn' ('(' ')')?
+FUNC_PARAM = TOK_IDENTIFIER VAR_TYPE_DEF
+*/
+static CHECK_RESULT bool parser_parse_function_parameter(Parser *self, FunctionParameter *result) {
+ bool match;
+
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match));
+ if(!match)
+ return bool_false;
+
+ result->name = self->tokenizer.value.identifier;
+ parser_parse_var_type_def(self, &result->type);
+ if(result->type.type == VARIABLE_TYPE_NONE) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_error_index(&self->tokenizer),
+ "Expected ':' after parameter name");
+ throw(PARSER_UNEXPECTED_TOKEN);
+ }
+
+ return bool_true;
+}
+
+/*
+FUNC_PARAMS = FUNC_PARAM (',' FUNC_PARAM)*
+*/
+static void parser_parse_function_parameters(Parser *self, FunctionSignature *func_sig) {
+ for(;;) {
+ FunctionParameter func_param;
+ bool match;
+ int result;
+
+ function_parameter_init(&func_param);
+ if(!parser_parse_function_parameter(self, &func_param)) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_error_index(&self->tokenizer),
+ "Expected closure parameter");
+ throw(PARSER_UNEXPECTED_TOKEN);
+ }
+
+ result = function_signature_add_parameter(func_sig, &func_param);
+ if(result == AST_ERR_DEF_DUP) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_code_reference_index(&self->tokenizer, func_param.name.data),
+ "Parameter with name %.*s was defined twice for the function", func_param.name.size, func_param.name.data);
+ self->error_context = ERROR_CONTEXT_NONE;
+ throw(result);
+ } else if(result != 0) {
+ throw(result);
+ }
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COMMA, &match));
+ if(!match)
+ return;
+ }
+}
+
+/*
+FUNC_RETURN_TYPES = VAR_TYPE (',' VAR_TYPE)*
+*/
+static void parser_parse_function_return_types(Parser *self, FunctionSignature *func_sig) {
+ for(;;) {
+ VariableType var_type;
+ bool match;
+
+ parser_parse_var_type(self, &var_type);
+ if(var_type.type == VARIABLE_TYPE_NONE) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_error_index(&self->tokenizer),
+ "Expected closure return type");
+ throw(PARSER_UNEXPECTED_TOKEN);
+ }
+ throw_if_error(function_signature_add_return_type(func_sig, &var_type));
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COMMA, &match));
+ if(!match)
+ return;
+ }
+}
+
+/*
+FUNC_SIGNATURE = 'fn' ('(' FUNC_PARAMS? ') FUNC_RETURN_TYPES? ')?
*/
static CHECK_RESULT FunctionSignature* parser_parse_function_signature(Parser *self) {
FunctionSignature *signature;
bool match;
+ throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionSignature), (void**)&signature));
+ throw_if_error(function_signature_init(signature, self->allocator));
+
throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_FN, &match));
if(!match)
return NULL;
throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_OPEN_PAREN, &match));
if(match) {
- /* TODO: Parse parameters */
- throw_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN));
- /* TODO: Parse return types */
+ bool has_closing_paren;
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_CLOSING_PAREN, &has_closing_paren));
+ if(!has_closing_paren) {
+ parser_parse_function_parameters(self, signature);
+ throw_if_error(tokenizer_accept(&self->tokenizer, TOK_CLOSING_PAREN));
+ }
+ parser_parse_function_return_types(self, signature);
}
- throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionSignature), (void**)&signature));
- function_signature_init(signature);
return signature;
}
/*
-VAR_TYPE_DEF = ':' TOK_IDENTIFIER|FUNC_SIGNATURE
+VAR_TYPE = TOK_IDENTIFIER|FUNC_SIGNATURE
*/
-static void parser_parse_var_type_def(Parser *self, VariableType *result) {
+void parser_parse_var_type(Parser *self, VariableType *result) {
bool match;
result->type = VARIABLE_TYPE_NONE;
result->value.variable = NULL;
- throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COLON, &match));
- if(!match)
- return;
-
throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_IDENTIFIER, &match));
if(match) {
result->type = VARIABLE_TYPE_VARIABLE;
@@ -181,13 +260,29 @@ static void parser_parse_var_type_def(Parser *self, VariableType *result) {
result->value.signature = parser_parse_function_signature(self);
if(!result->value.signature) {
self->error = tokenizer_create_error(&self->tokenizer,
- tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data),
- "Expected type or closure signature");
+ tokenizer_get_code_reference_index(&self->tokenizer, self->tokenizer.value.identifier.data),
+ "Expected type or closure signature");
throw(PARSER_UNEXPECTED_TOKEN);
}
}
/*
+VAR_TYPE_DEF = ':' VAR_TYPE
+*/
+void parser_parse_var_type_def(Parser *self, VariableType *result) {
+ bool match;
+
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_COLON, &match));
+ if(!match) {
+ result->type = VARIABLE_TYPE_NONE;
+ result->value.variable = NULL;
+ return;
+ }
+
+ parser_parse_var_type(self, result);
+}
+
+/*
LHS_DECLARATION = 'extern'? 'pub'? 'const'|'var' TOK_IDENTIFIER VAR_TYPE_DEF?
*/
static CHECK_RESULT LhsExpr* parser_parse_declaration_lhs(Parser *self) {
@@ -263,6 +358,8 @@ static CHECK_RESULT FunctionDecl* parser_parse_closure(Parser *self) {
throw_if_error(arena_allocator_alloc(self->allocator, sizeof(FunctionDecl), (void**)&result));
throw_if_error(funcdecl_init(result, signature, self->current_scope, self->allocator));
+ signature->func_decl = result;
+ result->body.function_signature = signature;
self->current_scope = &result->body;
prev_has_func_parent = self->has_func_parent;
@@ -492,7 +589,7 @@ static CHECK_RESULT Ast* parser_parse_number(Parser *self) {
/*
RHS_S = STRING | NUMBER | FUNC_CALL_OR_VARIABLE
*/
-static Ast* parser_parse_rhs_single_expr(Parser *self) {
+static CHECK_RESULT Ast* parser_parse_rhs_single_expr(Parser *self) {
Ast *result;
bool match;
result = NULL;
@@ -634,21 +731,68 @@ static CHECK_RESULT Ast* parser_parse_conditional(Parser *self) {
}
/*
+LHS_RHS = CLOSURE|STRUCT|IMPORT
+*/
+static Ast* parser_parse_lhs_rhs(Parser *self, LhsExpr *lhs_expr) {
+ Ast *result;
+ FunctionDecl *func_decl;
+ StructDecl *struct_decl;
+ Import *import;
+
+ func_decl = parser_parse_closure(self);
+ if(func_decl) {
+ throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &result));
+ func_decl->lhs_expr = lhs_expr;
+ return result;
+ }
+
+ struct_decl = parser_parse_struct_decl(self);
+ if(struct_decl) {
+ throw_if_error(ast_create(self->allocator, struct_decl, AST_STRUCT_DECL, &result));
+ return result;
+ }
+
+ import = parser_parse_import(self);
+ if(import) {
+ parser_queue_file(self, import->path, &import->file_scope);
+ throw_if_error(ast_create(self->allocator, import, AST_IMPORT, &result));
+ parser_parse_body_semicolon(self, result);
+ return result;
+ }
+
+ return NULL;
+}
+
+/*
+RETURN = 'return' RHS
+*/
+static ReturnExpr* parser_parse_return(Parser *self) {
+ bool match;
+ ReturnExpr *result;
+
+ throw_if_error(tokenizer_consume_if(&self->tokenizer, TOK_RETURN, &match));
+ if(!match)
+ return NULL;
+
+ throw_if_error(arena_allocator_alloc(self->allocator, sizeof(ReturnExpr), (void**)&result));
+ return_expr_init(result, parser_parse_rhs(self));
+ return result;
+}
+
+/*
BODY = (LHS_DECLARATION ';') |
- (LHS_DECLARATION '=' CLOSURE|STRUCT|IMPORT|(RHS BODY_SEMICOLON)) |
+ (LHS_DECLARATION '=' LHS_RHS|(RHS BODY_SEMICOLON)) |
CONDITIONAL |
(RHS ';'|('=' RHS BODY_SEMICOLON))
*/
Ast* parser_parse_body(Parser *self) {
Ast *result;
LhsExpr *lhs_expr;
+ ReturnExpr *return_expr;
Ast *rhs_expr;
lhs_expr = parser_parse_declaration_lhs(self);
if(lhs_expr) {
- FunctionDecl *func_decl;
- StructDecl *struct_decl;
- Import *import;
bool match;
throw_if_error(ast_create(self->allocator, lhs_expr, AST_LHS, &result));
@@ -675,25 +819,9 @@ Ast* parser_parse_body(Parser *self) {
throw_if_error(tokenizer_accept(&self->tokenizer, TOK_EQUALS));
- func_decl = parser_parse_closure(self);
- if(func_decl) {
- throw_if_error(ast_create(self->allocator, func_decl, AST_FUNCTION_DECL, &lhs_expr->rhs_expr));
- return result;
- }
-
- struct_decl = parser_parse_struct_decl(self);
- if(struct_decl) {
- throw_if_error(ast_create(self->allocator, struct_decl, AST_STRUCT_DECL, &lhs_expr->rhs_expr));
- return result;
- }
-
- import = parser_parse_import(self);
- if(import) {
- parser_queue_file(self, import->path, &import->file_scope);
- throw_if_error(ast_create(self->allocator, import, AST_IMPORT, &lhs_expr->rhs_expr));
- parser_parse_body_semicolon(self, lhs_expr->rhs_expr);
+ lhs_expr->rhs_expr = parser_parse_lhs_rhs(self, lhs_expr);
+ if(lhs_expr->rhs_expr)
return result;
- }
} else {
self->error_context = ERROR_CONTEXT_NO_LHS;
result = parser_parse_conditional(self);
@@ -701,6 +829,15 @@ Ast* parser_parse_body(Parser *self) {
return result;
}
+ if(self->has_func_parent) {
+ return_expr = parser_parse_return(self);
+ if(return_expr) {
+ throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON));
+ throw_if_error(ast_create(self->allocator, return_expr, AST_RETURN, &result));
+ return result;
+ }
+ }
+
self->error_context = ERROR_CONTEXT_RHS_STANDALONE;
rhs_expr = parser_parse_rhs(self);
self->error_context = ERROR_CONTEXT_NONE;
@@ -731,22 +868,26 @@ Ast* parser_parse_body(Parser *self) {
}
/*
-STRUCT_BODY = TOK_IDENTIFIER ':' TOK_IDENTIFIER ';'
+STRUCT_BODY = TOK_IDENTIFIER VAR_TYPE_DEF ';'
*/
Ast* parser_parse_struct_body(Parser *self) {
Ast *result;
BufferView var_name;
- BufferView type_name;
+ VariableType var_type;
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;
+ parser_parse_var_type_def(self, &var_type);
+ if(var_type.type == VARIABLE_TYPE_NONE) {
+ self->error = tokenizer_create_error(&self->tokenizer,
+ tokenizer_get_error_index(&self->tokenizer),
+ "Expected ':' after struct field name");
+ throw(PARSER_UNEXPECTED_TOKEN);
+ }
throw_if_error(tokenizer_accept(&self->tokenizer, TOK_SEMICOLON));
throw_if_error(arena_allocator_alloc(self->allocator, sizeof(LhsExpr), (void**)&struct_field));
- structfield_init(struct_field, var_name, type_name);
+ structfield_init(struct_field, var_name, &var_type);
throw_if_error(ast_create(self->allocator, struct_field, AST_STRUCT_FIELD, &result));
return result;
}
@@ -767,7 +908,8 @@ int parser_parse_buffer(Parser *self, BufferView code_buffer, BufferView buffer_
tokenizer_print_error_object(&self->tokenizer, &self->error);
break;
case ERROR_CONTEXT_RHS_STANDALONE:
- tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, struct, function call or import");
+ /* TODO: Only show "return" in error message if @self->has_func_parent */
+ tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected string, variable, closure, struct, function call, import or return");
break;
case ERROR_CONTEXT_NO_LHS:
tokenizer_print_error(&self->tokenizer, self->tokenizer.prev_index, "Expected variable declaration, string, variable or function call");
diff --git a/src/program.c b/src/program.c
index 54709b1..a73cf72 100644
--- a/src/program.c
+++ b/src/program.c
@@ -25,6 +25,10 @@ typedef struct {
NumberUnion value;
} Number;
+/*doc(Bytecode)
+The layout of the full bytecode is: Header (Intermediates Strings Functions Instructions)*
+*/
+
static CHECK_RESULT int amal_program_append_header(amal_program *self) {
/*doc(Bytecode header)
# Header layout
@@ -57,8 +61,9 @@ int amal_program_init(amal_program *self) {
self->intermediates_start = NULL;
self->strings_start = NULL;
self->read_index = 0;
- self->num_strings = 0;
self->num_intermediates = 0;
+ self->num_strings = 0;
+ self->num_functions = 0;
cleanup_if_error(amal_program_append_header(self));
return 0;
@@ -189,6 +194,14 @@ static CHECK_RESULT int amal_program_read_strings(amal_program *self) {
return AMAL_PROGRAM_OK;
}
+static CHECK_RESULT int amal_program_read_functions(amal_program *self) {
+ if(bytes_left_to_read(self) < sizeof(u16))
+ return AMAL_PROGRAM_INVALID_FUNCTIONS;
+ am_memcpy(&self->num_functions, &self->data.data[self->read_index], sizeof(u16));
+ self->read_index += sizeof(u16);
+ return AMAL_PROGRAM_OK;
+}
+
static CHECK_RESULT int amal_program_get_intermediate_by_index(amal_program *self, u16 index, Number *result) {
if(index >= self->num_intermediates)
return AMAL_PROGRAM_INSTRUCTION_INVALID_INTERMEDIATE_INDEX;
@@ -216,10 +229,12 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_
u32 instructions_size;
u32 read_start;
u32 read_end;
+ u16 func_counter;
bool inside_func;
inside_func = bool_false;
(void)inside_func;
+ func_counter = 0;
if(bytes_left_to_read(self) < sizeof(instructions_size))
return AMAL_PROGRAM_INVALID_INSTRUCTIONS_SIZE;
@@ -235,12 +250,15 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_
on the type of the register.
*/
+ return_if_error(amal_executor_instructions_start(executor, self->num_functions));
+
read_start = self->read_index;
read_end = read_start + instructions_size;
while(self->read_index < read_end) {
AmalOpcode opcode;
opcode = self->data.data[self->read_index];
self->read_index += sizeof(AmalOpcodeType);
+ /* TODO: Check instruction length and check if that amount of bytes can be read */
switch(opcode) {
case AMAL_OP_NOP: {
return_if_error(amal_exec_nop(executor));
@@ -334,12 +352,17 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_
self->read_index += 2;
break;
}
- case AMAL_OP_CALL:
- /*assert(bool_false && "TODO: Implement CALL");*/
+ case AMAL_OP_CALL: {
+ u16 func_index;
+ u8 num_args;
+ am_memcpy(&func_index, self->data.data + self->read_index, sizeof(func_index));
+ am_memcpy(&num_args, self->data.data + self->read_index + sizeof(func_index), sizeof(num_args));
+ return_if_error(amal_exec_call(executor, func_index, num_args));
self->read_index += 3;
break;
+ }
case AMAL_OP_CALLR:
- /*assert(bool_false && "TODO: Implement CALLR");*/
+ assert(bool_false && "TODO: Implement CALLR");
self->read_index += 2;
break;
case AMAL_OP_CMP: {
@@ -361,13 +384,19 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_
self->read_index += 2;
break;
}
- case AMAL_OP_RET:
- return_if_error(amal_exec_ret(executor));
+ case AMAL_OP_RET: {
+ const u8 reg = self->data.data[self->read_index];
+ return_if_error(amal_exec_ret(executor, reg));
+ self->read_index += 1;
break;
+ }
case AMAL_OP_FUNC_START: {
u16 func_num_registers;
assert(!inside_func);
inside_func = bool_true;
+ assert(func_counter < self->num_functions);
+ ++func_counter;
+
am_memcpy(&func_num_registers, &self->data.data[self->read_index], sizeof(func_num_registers));
return_if_error(amal_exec_func_start(executor, func_num_registers));
self->read_index += 2;
@@ -383,6 +412,8 @@ static CHECK_RESULT int amal_program_read_instructions(amal_program *self, amal_
}
}
+ assert(self->read_index == read_end);
+ return_if_error(amal_executor_instructions_end(executor));
return AMAL_PROGRAM_OK;
}
@@ -396,6 +427,7 @@ int amal_program_run(amal_program *self) {
while(bytes_left_to_read(self) > 0) {
cleanup_if_error(amal_program_read_intermediates(self));
cleanup_if_error(amal_program_read_strings(self));
+ cleanup_if_error(amal_program_read_functions(self));
cleanup_if_error(amal_program_read_instructions(self, executor));
}
result = amal_executor_run(executor);
diff --git a/src/ssa/ssa.c b/src/ssa/ssa.c
index f3679aa..0aabc15 100644
--- a/src/ssa/ssa.c
+++ b/src/ssa/ssa.c
@@ -243,7 +243,7 @@ static CHECK_RESULT int ssa_ins_push(Ssa *self, SsaRegister reg) {
return 0;
}
-static CHECK_RESULT int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, SsaRegister *result) {
+static CHECK_RESULT int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, u8 num_args, SsaRegister *result) {
usize index;
index = self->instructions.size;
@@ -251,12 +251,13 @@ static CHECK_RESULT int ssa_ins_call(Ssa *self, FunctionDecl *func_decl, SsaRegi
if(self->reg_counter + 1 < self->reg_counter)
return -1;
- return_if_error(buffer_append_empty(&self->instructions, sizeof(u8) + sizeof(SsaRegister) + sizeof(func_decl)));
+ return_if_error(buffer_append_empty(&self->instructions, sizeof(u8) + sizeof(u8) + sizeof(SsaRegister) + sizeof(func_decl)));
*result = self->reg_counter++;
self->instructions.data[index + 0] = SSA_CALL;
- am_memcpy(self->instructions.data + index + 1, result, sizeof(SsaRegister));
- am_memcpy(self->instructions.data + index + 1 + sizeof(SsaRegister), &func_decl, sizeof(func_decl));
- amal_log_debug("r%u = CALL %p", *result, func_decl);
+ self->instructions.data[index + 1] = num_args;
+ am_memcpy(self->instructions.data + index + 2, result, sizeof(SsaRegister));
+ am_memcpy(self->instructions.data + index + 2 + sizeof(SsaRegister), &func_decl, sizeof(func_decl));
+ amal_log_debug("r%u = CALL %d, %p", *result, num_args, func_decl);
return 0;
}
@@ -286,6 +287,16 @@ static CHECK_RESULT int ssa_ins_jump(Ssa *self, JumpOffset jump_offset) {
return 0;
}
+static CHECK_RESULT int ssa_ins_return(Ssa *self, SsaRegister reg) {
+ usize index;
+ index = self->instructions.size;
+
+ return_if_error(buffer_append_empty(&self->instructions, sizeof(u8) + sizeof(SsaRegister)));
+ self->instructions.data[index + 0] = SSA_RET;
+ am_memcpy(self->instructions.data + index + 1, &reg, sizeof(SsaRegister));
+ return 0;
+}
+
static usize ssa_ins_get_index(Ssa *self) {
return self->instructions.size;
}
@@ -311,6 +322,28 @@ static CHECK_RESULT int ssa_ins_jump_set_target(Ssa *self, usize jump_ins_index)
}
static CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context);
+static CHECK_RESULT SsaRegister scope_named_object_generate_ssa(ScopeNamedObject *self, SsaCompilerContext *context);
+
+static bool ast_resolved_type_is_decl(AstResolvedType *self) {
+ /* TODO: Add more types as they are introduced */
+ LhsExpr *lhs_expr;
+ if(self->type != RESOLVED_TYPE_LHS_EXPR)
+ return bool_false;
+
+ lhs_expr = self->value.lhs_expr;
+ switch(lhs_expr->type.type) {
+ case VARIABLE_TYPE_NONE:
+ break;
+ case VARIABLE_TYPE_VARIABLE:
+ return bool_false;
+ case VARIABLE_TYPE_SIGNATURE:
+ /* TODO: This should return bool_false when it's possible to use signature in expressions */
+ return bool_true;
+ }
+
+ assert(lhs_expr->rhs_expr);
+ return lhs_expr->rhs_expr->type == AST_FUNCTION_DECL || lhs_expr->rhs_expr->type == AST_STRUCT_DECL;
+}
static CHECK_RESULT SsaRegister number_generate_ssa(Number *self, SsaCompilerContext *context) {
SsaRegister reg;
@@ -332,27 +365,23 @@ static CHECK_RESULT SsaRegister lhsexpr_extern_generate_ssa(LhsExpr *self, SsaCo
return 0;
}
-static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(Ast *self, SsaCompilerContext *context) {
- LhsExpr *lhs_expr;
+static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(LhsExpr *self, AstResolveData *resolve_data, SsaCompilerContext *context) {
SsaRegister reg;
- assert(self->type == AST_LHS);
- lhs_expr = self->value.lhs_expr;
-
- if(LHS_EXPR_IS_EXTERN(lhs_expr))
- return lhsexpr_extern_generate_ssa(lhs_expr, context);
+ if(LHS_EXPR_IS_EXTERN(self))
+ return lhsexpr_extern_generate_ssa(self, context);
- if(lhs_expr->rhs_expr) {
- Ast *rhs_expr = lhs_expr->rhs_expr;
+ if(self->rhs_expr) {
+ Ast *rhs_expr = self->rhs_expr;
SsaRegister rhs_reg;
rhs_reg = ast_generate_ssa(rhs_expr, context);
/*
- Declarations (struct and function declaration) resolves to itself and in that case this expression
- is just a compile-time name for the declaration.
- Import expression also has no meaning in SSA until it's used.
- TODO: Shouldn't lhsexpr that have struct/function declaration as rhs be different ast expression types?
+ Declarations (struct and function declaration) resolves to itself and in that case this expression
+ is just a compile-time name for the declaration.
+ Import expression also has no meaning in SSA until it's used.
+ TODO: Shouldn't lhsexpr that have struct/function declaration as rhs be different ast expression types?
*/
- if(self->resolve_data.type == lhs_expr || rhs_expr->type == AST_IMPORT) {
+ if(ast_resolved_type_is_decl(&resolve_data->type) || rhs_expr->type == AST_IMPORT) {
/*assert(bool_false);*/
return 0;
}
@@ -364,32 +393,55 @@ static CHECK_RESULT SsaRegister lhsexpr_generate_ssa(Ast *self, SsaCompilerConte
throw_if_error(ssa_ins_assign_reg(context->ssa, reg, rhs_reg));
} else {
/* TODO: Do not assign if we dont want default value */
- SsaNumber number;
- if(self->resolve_data.type == (LhsExpr*)context->compiler->default_types.i64)
- number = create_ssa_integer(0);
- else if(self->resolve_data.type == (LhsExpr*)context->compiler->default_types.f64)
- number = create_ssa_float(0.0);
- else
- assert(bool_false && "TODO: assign default value to reg depending on LhsExpr type");
- throw_if_error(ssa_get_unique_reg(context->ssa, &reg));
- throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number));
+ if(resolve_data->type.type == RESOLVED_TYPE_LHS_EXPR) {
+ SsaNumber number;
+ if(resolve_data->type.value.lhs_expr == (LhsExpr*)context->compiler->default_types.i64)
+ number = create_ssa_integer(0);
+ else if(resolve_data->type.value.lhs_expr == (LhsExpr*)context->compiler->default_types.f64)
+ number = create_ssa_float(0.0);
+ else
+ assert(bool_false && "TODO: assign default value to reg depending on LhsExpr type");
+ throw_if_error(ssa_get_unique_reg(context->ssa, &reg));
+ throw_if_error(ssa_ins_assign_inter(context->ssa, reg, number));
+ } else if(resolve_data->type.type == RESOLVED_TYPE_FUNC_SIG) {
+ assert(bool_false && "TODO: Implement this when variable can be null. Then the function pointer should be null");
+ } else {
+ assert(bool_false);
+ }
}
return reg;
}
-static CHECK_RESULT SsaRegister assignmentexpr_generate_ssa(Ast *ast, SsaCompilerContext *context) {
- AssignmentExpr *self;
+static CHECK_RESULT SsaRegister assignmentexpr_generate_ssa(AssignmentExpr *self, SsaCompilerContext *context) {
SsaRegister lhs_reg, rhs_reg;
-
- assert(ast->type == AST_ASSIGN);
- self = ast->value.assign_expr;
-
lhs_reg = ast_generate_ssa(self->lhs_expr, context);
rhs_reg = ast_generate_ssa(self->rhs_expr, context);
throw_if_error(ssa_ins_assign_reg(context->ssa, lhs_reg, rhs_reg));
return lhs_reg;
}
+static CHECK_RESULT SsaRegister function_parameter_generate_ssa(FunctionParameter *self, SsaCompilerContext *context) {
+ SsaRegister reg;
+ if(self->resolve_data.status == AST_SSA_RESOLVED)
+ return self->resolve_data.ssa_reg;
+
+ throw_if_error(ssa_get_unique_reg(context->ssa, &reg));
+ self->resolve_data.status = AST_SSA_RESOLVED;
+ self->resolve_data.ssa_reg = reg;
+ return reg;
+}
+
+static CHECK_RESULT void function_signature_generate_params_ssa(FunctionSignature *self, SsaCompilerContext *context) {
+ FunctionParameter *param, *param_end;
+ param = buffer_begin(&self->parameters);
+ param_end = buffer_end(&self->parameters);
+ for(; param != param_end; ++param) {
+ SsaRegister reg;
+ reg = function_parameter_generate_ssa(param, context);
+ (void)reg;
+ }
+}
+
/*
TODO: Each function declaration should be in separate SSA instances so ast can be converted into ssa
in any order.
@@ -402,49 +454,54 @@ static CHECK_RESULT SsaRegister funcdecl_generate_ssa(FunctionDecl *self, SsaCom
*/
SsaRegister prev_reg_counter;
usize func_metadata_index;
+ int num_params;
prev_reg_counter = context->ssa->reg_counter;
context->ssa->reg_counter = 0;
+ /*
+ Parameters need to have generated ssa so the first ssa registers belong to the function.
+ This way we can know if a register access is for a parameter or not by checking the number
+ */
+ function_signature_generate_params_ssa(self->signature, context);
+ num_params = buffer_get_size(&self->signature->parameters, FunctionParameter);
+
amal_log_debug("SSA funcdecl %p", self);
throw_if_error(ssa_ins_func_start(context->ssa, &self->ssa_func_index, &func_metadata_index));
scope_generate_ssa(&self->body, context);
throw_if_error(ssa_ins_func_end(context->ssa));
/* Add the number of registers used to the function metadata (FUNC_START) */
+ context->ssa->reg_counter -= num_params;
am_memcpy(&context->ssa->instructions.data[func_metadata_index], &context->ssa->reg_counter, sizeof(u16));
context->ssa->reg_counter = prev_reg_counter;
return 0;
}
-static CHECK_RESULT SsaRegister funccall_generate_ssa(Ast *self, SsaCompilerContext *context) {
- /* TODO: Implement */
- FunctionCall *func_call;
+static CHECK_RESULT SsaRegister funccall_generate_ssa(FunctionCall *self, AstResolveData *resolve_data, SsaCompilerContext *context) {
Ast **ast;
Ast **ast_end;
SsaRegister reg;
+ FunctionDecl *func_decl;
- assert(self->type == AST_FUNCTION_CALL);
- func_call = self->value.func_call;
- ast = buffer_begin(&func_call->args);
- ast_end = buffer_end(&func_call->args);
+ ast = buffer_begin(&self->args);
+ ast_end = buffer_end(&self->args);
for(; ast != ast_end; ++ast) {
SsaRegister arg_reg;
arg_reg = ast_generate_ssa(*ast, context);
throw_if_error(ssa_ins_push(context->ssa, arg_reg));
}
- assert((self->resolve_data.type->rhs_expr && self->resolve_data.type->rhs_expr->type == AST_FUNCTION_DECL) ||
- self->resolve_data.type->type.type == VARIABLE_TYPE_SIGNATURE);
- if(LHS_EXPR_IS_EXTERN(self->resolve_data.type)) {
- amal_log_error("TODO: Implement extern function call (extern function %.*s was called)", func_call->func.name.size, func_call->func.name.data);
+ func_decl = resolve_data->type.value.func_sig->func_decl;
+ assert(resolve_data->type.type == RESOLVED_TYPE_FUNC_SIG);
+ assert(func_decl && "TODO: Implement function call for anonymous closures");
+ if(func_decl && func_decl->lhs_expr && LHS_EXPR_IS_EXTERN(func_decl->lhs_expr)) {
+ amal_log_error("TODO: Implement extern function call (extern function %.*s was called)", self->func.name.size, self->func.name.data);
reg = 0;
assert(bool_false && "TODO: Implement extern function call!");
} else {
- FunctionDecl *func_to_call;
/* rhs wont be null here because only extern variable can't have rhs */
- func_to_call = self->resolve_data.type->rhs_expr->value.func_decl;
- amal_log_debug("SSA funccall %.*s, func index ptr: %p", func_call->func.name.size, func_call->func.name.data, func_to_call);
- throw_if_error(ssa_ins_call(context->ssa, func_to_call, &reg));
+ amal_log_debug("SSA funccall %.*s, func index ptr: %p", self->func.name.size, self->func.name.data, func_decl);
+ throw_if_error(ssa_ins_call(context->ssa, func_decl, buffer_get_size(&self->args, Ast*), &reg));
}
return reg;
@@ -475,8 +532,8 @@ static CHECK_RESULT SsaRegister string_generate_ssa(String *self, SsaCompilerCon
static CHECK_RESULT SsaRegister variable_generate_ssa(Variable *self, SsaCompilerContext *context) {
/* TODO: If resolved_var refers to a variable in another file, use a cross file reference that requires no locking (not yet implemented) */
/* This is not thread-safe:*/
- assert(self->resolved_var);
- return ast_generate_ssa(self->resolved_var, context);
+ assert(self->resolved_var.type != NAMED_OBJECT_NONE);
+ return scope_named_object_generate_ssa(&self->resolved_var, context);
}
static SsaInstruction binop_type_to_ssa_type(BinopType binop_type, amal_default_type *type) {
@@ -511,7 +568,8 @@ static CHECK_RESULT SsaRegister binop_generate_ssa(Binop *self, SsaCompilerConte
} else {
const SsaRegister lhs_reg = ast_generate_ssa(self->lhs, context);
const SsaRegister rhs_reg = ast_generate_ssa(self->rhs, context);
- throw_if_error(ssa_ins_binop(context->ssa, binop_type_to_ssa_type(self->type, (amal_default_type*)self->lhs->resolve_data.type), lhs_reg, rhs_reg, &reg));
+ assert(self->lhs->resolve_data.type.type == RESOLVED_TYPE_LHS_EXPR && "TODO: Implement binop_generate_ssa for function signature");
+ throw_if_error(ssa_ins_binop(context->ssa, binop_type_to_ssa_type(self->type, (amal_default_type*)self->lhs->resolve_data.type.value.lhs_expr), lhs_reg, rhs_reg, &reg));
}
return reg;
}
@@ -567,63 +625,88 @@ static void while_statement_generate_ssa(WhileStatement *while_stmt, SsaCompiler
throw_if_error(ssa_ins_jump_set_target(context->ssa, jump_condition_ins_index));
}
-static CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context) {
- assert(self);
- #ifdef DEBUG
- if(self->resolve_data.status != AST_RESOLVED && self->resolve_data.status != AST_SSA_RESOLVED) {
- amal_log_error("Ast type not resolved: %d", self->type);
- assert(bool_false);
- }
- #endif
+static void return_expr_generate_ssa(ReturnExpr *self, SsaCompilerContext *context) {
+ const SsaRegister reg = ast_generate_ssa(self->rhs_expr, context);
+ throw_if_error(ssa_ins_return(context->ssa, reg));
+}
- if(self->resolve_data.status == AST_SSA_RESOLVED)
- return self->ssa_reg;
+static CHECK_RESULT SsaRegister ast_generate_ssa_resolve_data(void *ast_data, AstType ast_type, AstResolveData *resolve_data, SsaCompilerContext *context) {
+ if(resolve_data->status == AST_SSA_RESOLVED)
+ return resolve_data->ssa_reg;
- switch(self->type) {
+ switch(ast_type) {
case AST_NUMBER:
- self->ssa_reg = number_generate_ssa(self->value.number, context);
+ resolve_data->ssa_reg = number_generate_ssa(ast_data, context);
break;
case AST_FUNCTION_DECL:
- self->ssa_reg = funcdecl_generate_ssa(self->value.func_decl, context);
+ resolve_data->ssa_reg = funcdecl_generate_ssa(ast_data, context);
break;
case AST_FUNCTION_CALL:
- self->ssa_reg = funccall_generate_ssa(self, context);
+ resolve_data->ssa_reg = funccall_generate_ssa(ast_data, resolve_data, context);
break;
case AST_STRUCT_DECL:
- self->ssa_reg = structdecl_generate_ssa(self->value.struct_decl, context);
+ resolve_data->ssa_reg = structdecl_generate_ssa(ast_data, context);
break;
case AST_STRUCT_FIELD:
- self->ssa_reg = structfield_generate_ssa(self->value.struct_field, context);
+ resolve_data->ssa_reg = structfield_generate_ssa(ast_data, context);
break;
case AST_LHS:
- self->ssa_reg = lhsexpr_generate_ssa(self, context);
+ resolve_data->ssa_reg = lhsexpr_generate_ssa(ast_data, resolve_data, context);
break;
case AST_ASSIGN:
- self->ssa_reg = assignmentexpr_generate_ssa(self, context);
+ resolve_data->ssa_reg = assignmentexpr_generate_ssa(ast_data, context);
break;
case AST_IMPORT:
/* TODO: Implement cross file references */
- self->ssa_reg = 0;
+ resolve_data->ssa_reg = 0;
break;
case AST_STRING:
- self->ssa_reg = string_generate_ssa(self->value.string, context);
+ resolve_data->ssa_reg = string_generate_ssa(ast_data, context);
break;
case AST_VARIABLE:
- self->ssa_reg = variable_generate_ssa(self->value.variable, context);
+ resolve_data->ssa_reg = variable_generate_ssa(ast_data, context);
break;
case AST_BINOP:
- self->ssa_reg = binop_generate_ssa(self->value.binop, context);
+ resolve_data->ssa_reg = binop_generate_ssa(ast_data, context);
break;
case AST_IF_STATEMENT:
- if_statement_generate_ssa(self->value.if_stmt, context);
+ if_statement_generate_ssa(ast_data, context);
break;
case AST_WHILE_STATEMENT:
- while_statement_generate_ssa(self->value.while_stmt, context);
+ while_statement_generate_ssa(ast_data, context);
+ break;
+ case AST_RETURN:
+ return_expr_generate_ssa(ast_data, context);
+ resolve_data->ssa_reg = 0;
break;
}
- self->resolve_data.status = AST_SSA_RESOLVED;
- return self->ssa_reg;
+ resolve_data->status = AST_SSA_RESOLVED;
+ return resolve_data->ssa_reg;
+}
+
+CHECK_RESULT SsaRegister ast_generate_ssa(Ast *self, SsaCompilerContext *context) {
+ assert(self);
+ #ifdef DEBUG
+ if(self->resolve_data.status != AST_RESOLVED && self->resolve_data.status != AST_SSA_RESOLVED) {
+ amal_log_error("Ast type not resolved: %d", self->type);
+ assert(bool_false);
+ }
+ #endif
+ return ast_generate_ssa_resolve_data(self->value.data, self->type, &self->resolve_data, context);
+}
+
+CHECK_RESULT SsaRegister scope_named_object_generate_ssa(ScopeNamedObject *self, SsaCompilerContext *context) {
+ switch(self->type) {
+ case NAMED_OBJECT_NONE:
+ assert(bool_false);
+ return 0;
+ case NAMED_OBJECT_LHS_EXPR:
+ return ast_generate_ssa_resolve_data(self->value.lhs_expr, AST_LHS, self->resolve_data, context);
+ case NAMED_OBJECT_FUNC_PARAM:
+ return function_parameter_generate_ssa(self->value.func_param, context);
+ }
+ return 0;
}
void scope_generate_ssa(Scope *self, SsaCompilerContext *context) {
diff --git a/src/std/arena_allocator.c b/src/std/arena_allocator.c
index 14787f1..73111dd 100644
--- a/src/std/arena_allocator.c
+++ b/src/std/arena_allocator.c
@@ -67,8 +67,7 @@ static CHECK_RESULT int arena_allocator_ensure_capacity_for(ArenaAllocator *self
}
static void* align_ptr_ceil(void *ptr, uintptr_t alignment) {
- uintptr_t ptrval;
- ptrval = (uintptr_t)ptr;
+ const uintptr_t ptrval = (uintptr_t)ptr;
return (void*)((ptrval + alignment + 1) & ~(alignment - 1));
}
@@ -76,7 +75,7 @@ static usize align_ptr_ceil_offset(void *ptr, uintptr_t alignment) {
return (uintptr_t)align_ptr_ceil(ptr, alignment) - (uintptr_t)ptr;
}
-#define SCOPED_ALLOC_ALIGNMENT 8
+#define ALLOC_ALIGNMENT 8
int arena_allocator_alloc(ArenaAllocator *self, usize size, void **mem) {
ArenaAllocatorNode *current;
@@ -89,14 +88,14 @@ int arena_allocator_alloc(ArenaAllocator *self, usize size, void **mem) {
return -1;
}
- alloc_size = size + align_ptr_ceil_offset(self->current->data + self->current->size, SCOPED_ALLOC_ALIGNMENT);
+ alloc_size = size + align_ptr_ceil_offset(self->current->data + self->current->size, ALLOC_ALIGNMENT);
return_if_error(arena_allocator_ensure_capacity_for(self, alloc_size));
/* Reallocated (new node created) */
if(self->current != current) {
*mem = self->current->data;
self->current->size += size;
} else {
- *mem = align_ptr_ceil(self->current->data + self->current->size, SCOPED_ALLOC_ALIGNMENT);
+ *mem = align_ptr_ceil(self->current->data + self->current->size, ALLOC_ALIGNMENT);
self->current->size += alloc_size;
}
return 0;
diff --git a/src/std/buffer.c b/src/std/buffer.c
index 0e4ca89..93e8558 100644
--- a/src/std/buffer.c
+++ b/src/std/buffer.c
@@ -48,6 +48,7 @@ static CHECK_RESULT int buffer_ensure_capacity(Buffer *self, usize new_capacity)
self->data = new_mem;
self->capacity = capacity;
+ /* Update list of buffers in the allocator with the new address of the buffer data */
if(self->allocator)
am_memcpy(self->allocator->mems.data + sizeof(void*) * self->allocator_index, &self->data, sizeof(void*));
return BUFFER_OK;
diff --git a/src/std/buffer_view.c b/src/std/buffer_view.c
index 8ddfab9..f2d79c0 100644
--- a/src/std/buffer_view.c
+++ b/src/std/buffer_view.c
@@ -1,4 +1,5 @@
#include "../../include/std/buffer_view.h"
+#include "../../include/std/mem.h"
BufferView create_buffer_view_null() {
BufferView buffer_view;
@@ -13,3 +14,7 @@ BufferView create_buffer_view(const char *data, usize size) {
buffer_view.size = size;
return buffer_view;
}
+
+bool buffer_view_equals(const BufferView *self, const BufferView *other) {
+ return self->size == other->size && am_memeql(self->data, other->data, self->size);
+}
diff --git a/src/tokenizer.c b/src/tokenizer.c
index 7620fc0..1586691 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -216,6 +216,9 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) {
} else if(am_memeql(self->value.identifier.data, "extern", 6)) {
*token = TOK_EXTERN;
return TOKENIZER_OK;
+ } else if(am_memeql(self->value.identifier.data, "return", 6)) {
+ *token = TOK_RETURN;
+ return TOKENIZER_OK;
}
break;
}
@@ -346,6 +349,8 @@ static CHECK_RESULT int __tokenizer_next(Tokenizer *self, Token *token) {
}
self->index += 6;
+ /* TODO: This should be moved to the parser */
+
result = tokenizer_next(self, &last_token);
if(result != 0 || last_token != TOK_OPEN_PAREN) {
err_msg = "Expected '(' after @import";
@@ -490,6 +495,9 @@ static BufferView tokenizer_expected_token_as_string(Token token) {
case TOK_EXTERN:
str = "extern";
break;
+ case TOK_RETURN:
+ str = "return";
+ break;
}
return create_buffer_view(str, strlen(str));
}
diff --git a/tests/bytecode.amal b/tests/bytecode.amal
index 799733b..dd9bd3d 100644
--- a/tests/bytecode.amal
+++ b/tests/bytecode.amal
@@ -7,8 +7,14 @@ const main = fn {
const value3 = 2 + 5 - 1 * 10 / 2;
const str_value = "hello, world";
//printf();
+ print();
+ const result = print_num(value3);
}
const print = fn {
+}
+
+const print_num = fn(num: i32) i32 {
+ return num;
} \ No newline at end of file
diff --git a/tests/errors/closure_duplicate_param_name.amal b/tests/errors/closure_duplicate_param_name.amal
new file mode 100644
index 0000000..4e74eec
--- /dev/null
+++ b/tests/errors/closure_duplicate_param_name.amal
@@ -0,0 +1 @@
+const func = fn(value: i32, value: i32) {} \ No newline at end of file
diff --git a/tests/main.c b/tests/main.c
index 63f72d5..3336c46 100644
--- a/tests/main.c
+++ b/tests/main.c
@@ -251,6 +251,7 @@ static void run_all_tests() {
" extern const a;\n"
" ^\n");
test_load_error("tests/errors/no_main_func.amal", NULL);
+ test_load_error("tests/errors/closure_duplicate_param_name.amal", "TODO: Add expected error here");
}
/* TODO: Restrict variables in global scope to const */