diff options
-rw-r--r-- | bench/fib.bad | 12 | ||||
-rw-r--r-- | bench/fib.py | 5 | ||||
-rw-r--r-- | bench/life.bad | 77 | ||||
-rw-r--r-- | bench/rule110.bad | 45 | ||||
-rw-r--r-- | bench/rule110.py | 29 | ||||
-rw-r--r-- | src/badlib.h | 6 | ||||
-rw-r--r-- | src/compiler.c | 974 | ||||
-rw-r--r-- | src/main.c | 49 | ||||
-rw-r--r-- | src/parser.c | 2 | ||||
-rw-r--r-- | src/semantic.c | 181 | ||||
-rw-r--r-- | src/vm.c | 225 | ||||
-rw-r--r-- | tests/compilation.bad | 38 | ||||
-rw-r--r-- | tests/conditionals.bad | 4 | ||||
-rw-r--r-- | tests/semantics.bad | 7 |
14 files changed, 1431 insertions, 223 deletions
diff --git a/bench/fib.bad b/bench/fib.bad new file mode 100644 index 0000000..ae7c729 --- /dev/null +++ b/bench/fib.bad | |||
@@ -0,0 +1,12 @@ | |||
1 | let n = 1000 | ||
2 | let a = 0.0 | ||
3 | let b = 1.0 | ||
4 | let i = 0 | ||
5 | while i < n { | ||
6 | let tmp = a + b | ||
7 | set a = b | ||
8 | set b = tmp | ||
9 | set i = i + 1 | ||
10 | } | ||
11 | |||
12 | a | ||
diff --git a/bench/fib.py b/bench/fib.py new file mode 100644 index 0000000..ceb7406 --- /dev/null +++ b/bench/fib.py | |||
@@ -0,0 +1,5 @@ | |||
1 | n = 1000 | ||
2 | a, b = 0.0, 1.0 | ||
3 | for i in range(0, n): | ||
4 | a, b = b, a + b | ||
5 | print(a) | ||
diff --git a/bench/life.bad b/bench/life.bad new file mode 100644 index 0000000..d05038f --- /dev/null +++ b/bench/life.bad | |||
@@ -0,0 +1,77 @@ | |||
1 | ; This board is 8 * 8 = 64 cells. | ||
2 | ; let n_cells = 64 | ||
3 | ; let board_a: int[64] | ||
4 | ; let board_b: int[64] | ||
5 | ; let stride = 8 | ||
6 | |||
7 | ; This board is 32 * 32 = 1024 cells. | ||
8 | let n_cells = 1024 | ||
9 | let board_a: int[1024] | ||
10 | let board_b: int[1024] | ||
11 | let stride = 32 | ||
12 | |||
13 | ; Storing pointers to the front/backbuffer to avoid copying the board over. | ||
14 | let front = board_a | ||
15 | let back = board_b | ||
16 | |||
17 | ; Initialize glider | ||
18 | set front[stride * 2 + 5] = 1 | ||
19 | set front[stride * 3 + 6] = 1 | ||
20 | set front[stride * 4 + 4] = 1 | ||
21 | set front[stride * 4 + 5] = 1 | ||
22 | set front[stride * 4 + 6] = 1 | ||
23 | |||
24 | fun print_board(board: @int): nil { | ||
25 | let i = 0 | ||
26 | while i < n_cells { | ||
27 | if i % stride == 0 { | ||
28 | println("") | ||
29 | } | ||
30 | if board[i] == 1 print("■ ") | ||
31 | else print("· ") | ||
32 | set i = i + 1 | ||
33 | } | ||
34 | println("") | ||
35 | } | ||
36 | |||
37 | fun update_board(): nil { | ||
38 | let i = 0 | ||
39 | while i < n_cells { | ||
40 | let left = if i > 0 front[i - 1] else 0 | ||
41 | let right = if i < n_cells - 1 front[i + 1] else 0 | ||
42 | let top = if i >= stride front[i - stride] else 0 | ||
43 | let topleft = if i >= stride front[i - stride - 1] else 0 | ||
44 | let topright = if i >= stride front[i - stride + 1] else 0 | ||
45 | let bot = if i <= n_cells - stride front[i + stride] else 0 | ||
46 | let botleft = if i <= n_cells - stride front[i + stride - 1] else 0 | ||
47 | let botright = if i <= n_cells - stride front[i + stride + 1] else 0 | ||
48 | |||
49 | let neig = left | ||
50 | + right | ||
51 | + top | ||
52 | + bot | ||
53 | + topleft | ||
54 | + topright | ||
55 | + botleft | ||
56 | + botright | ||
57 | |||
58 | cond { | ||
59 | front[i] == 0 && neig == 3 = set back[i] = 1 | ||
60 | front[i] == 1 && (neig == 2 || neig == 3) = set back[i] = 1 | ||
61 | else = set back[i] = 0 | ||
62 | } | ||
63 | |||
64 | set i = i + 1 | ||
65 | } | ||
66 | |||
67 | let tmp = front | ||
68 | set front = back | ||
69 | set back = tmp | ||
70 | } | ||
71 | |||
72 | let n_iter = 1000 | ||
73 | while n_iter > 0 { | ||
74 | set n_iter = n_iter - 1 | ||
75 | print_board(front) | ||
76 | update_board() | ||
77 | } | ||
diff --git a/bench/rule110.bad b/bench/rule110.bad new file mode 100644 index 0000000..d925e18 --- /dev/null +++ b/bench/rule110.bad | |||
@@ -0,0 +1,45 @@ | |||
1 | ; Parameters. | ||
2 | let line = 0b00000000000000000000000000000001 | ||
3 | let max_iter = 30000 | ||
4 | |||
5 | ; Print current line. | ||
6 | fun print_line(line: int): nil { | ||
7 | let i = 0 | ||
8 | while i < 64 { | ||
9 | let val = line >> 63 - i & 0b1 | ||
10 | if val == 0b1 { | ||
11 | print("▀ ") | ||
12 | } else { | ||
13 | print(" ") | ||
14 | } | ||
15 | set i = i + 1 | ||
16 | } | ||
17 | println("") | ||
18 | } | ||
19 | |||
20 | ; Get the next line. | ||
21 | fun next_line(line: int): int { | ||
22 | let next = 0 | ||
23 | let j = 0 | ||
24 | while j < 61 { | ||
25 | let val = line >> 60 - j & 0b111 | ||
26 | set val = cond { | ||
27 | val == 1 = 1 | ||
28 | val == 2 = 1 | ||
29 | val == 3 = 1 | ||
30 | val == 5 = 1 | ||
31 | val == 6 = 1 | ||
32 | else = 0 | ||
33 | } | ||
34 | set next = next | val << 61 - j | ||
35 | set j = j + 1 | ||
36 | } | ||
37 | next | 1 | ||
38 | } | ||
39 | |||
40 | let iter = 0 | ||
41 | while iter < max_iter { | ||
42 | print_line(line) | ||
43 | set line = next_line(line) | ||
44 | set iter = iter + 1 | ||
45 | } | ||
diff --git a/bench/rule110.py b/bench/rule110.py new file mode 100644 index 0000000..a4fed06 --- /dev/null +++ b/bench/rule110.py | |||
@@ -0,0 +1,29 @@ | |||
1 | line = 0b00000000000000000000000000000001 | ||
2 | max_iter = 30000 | ||
3 | |||
4 | for iter in range(0, max_iter): | ||
5 | for i in range(0, 64): | ||
6 | val = line >> (63 - i) & 0b1 | ||
7 | if val == 0b1: | ||
8 | print("▀", end=" ") | ||
9 | else: | ||
10 | print(".", end=" ") | ||
11 | print("") | ||
12 | |||
13 | next = 0 | ||
14 | for j in range(0, 61): | ||
15 | val = (line >> (61 - j - 1)) & 0b111 | ||
16 | if val == 1: | ||
17 | val = 1 | ||
18 | elif val == 2: | ||
19 | val = 1 | ||
20 | elif val == 3: | ||
21 | val = 1 | ||
22 | elif val == 5: | ||
23 | val = 1 | ||
24 | elif val == 6: | ||
25 | val = 1 | ||
26 | else: | ||
27 | val = 0 | ||
28 | next = next | (val << (61 - j)) | ||
29 | line = next | 1 | ||
diff --git a/src/badlib.h b/src/badlib.h index 73e03db..c73b57f 100644 --- a/src/badlib.h +++ b/src/badlib.h | |||
@@ -347,7 +347,9 @@ str_split_fn(Str *a, StrSplitFn split_fn) { | |||
347 | void | 347 | void |
348 | str_replace(Str *a, Str from, Str to) { | 348 | str_replace(Str *a, Str from, Str to) { |
349 | assert(a != NULL); | 349 | assert(a != NULL); |
350 | assert(from.size == to.size); | 350 | if (from.size != to.size) { |
351 | return; | ||
352 | } | ||
351 | SearchResult res = array_find_next(*a, from); | 353 | SearchResult res = array_find_next(*a, from); |
352 | if (res.found) { | 354 | if (res.found) { |
353 | memcpy(a->mem + res.pos, to.mem, res.matched); | 355 | memcpy(a->mem + res.pos, to.mem, res.matched); |
@@ -1498,7 +1500,7 @@ void | |||
1498 | log_func_memory(Logger *l, void *in) { | 1500 | log_func_memory(Logger *l, void *in) { |
1499 | assert(l); | 1501 | assert(l); |
1500 | Array *val = in; | 1502 | Array *val = in; |
1501 | for (sz i = 0; i < MIN(64, val->size); i++) { | 1503 | for (sz i = 0; i < MIN(256, val->size); i++) { |
1502 | Arena scratch = l->storage; | 1504 | Arena scratch = l->storage; |
1503 | log_str(l, str_from_hex(val->mem[i], 2, &scratch)); | 1505 | log_str(l, str_from_hex(val->mem[i], 2, &scratch)); |
1504 | if ((i + 1) % 16 == 0) { | 1506 | if ((i + 1) % 16 == 0) { |
diff --git a/src/compiler.c b/src/compiler.c index 5bddcb6..ee2c06b 100644 --- a/src/compiler.c +++ b/src/compiler.c | |||
@@ -25,7 +25,7 @@ typedef struct Instruction { | |||
25 | typedef union Constant { | 25 | typedef union Constant { |
26 | s64 i; | 26 | s64 i; |
27 | u64 u; | 27 | u64 u; |
28 | double f; | 28 | f64 f; |
29 | ptrsize ptr; | 29 | ptrsize ptr; |
30 | } Constant; | 30 | } Constant; |
31 | 31 | ||
@@ -34,9 +34,24 @@ typedef struct LineCol { | |||
34 | sz col; | 34 | sz col; |
35 | } LineCol; | 35 | } LineCol; |
36 | 36 | ||
37 | typedef struct Function { | ||
38 | Str name; | ||
39 | sz param_arity; | ||
40 | sz return_arity; | ||
41 | sz index; | ||
42 | } Function; | ||
43 | |||
44 | MAPDEF(FunctionMap, funcmap, Str, Function, str_hash, str_eq) | ||
45 | |||
37 | typedef struct Chunk { | 46 | typedef struct Chunk { |
38 | sz id; | 47 | sz id; |
48 | Str name; | ||
49 | struct Chunk *parent; | ||
50 | |||
39 | Instruction *code; | 51 | Instruction *code; |
52 | IntIntMap *labels; // label -> chunk_index | ||
53 | IntIntMap *labels_rev; // chunk_index -> label | ||
54 | sz labels_idx; | ||
40 | 55 | ||
41 | // Constant values that fit in 64 bits. | 56 | // Constant values that fit in 64 bits. |
42 | Constant *constants; | 57 | Constant *constants; |
@@ -52,10 +67,16 @@ typedef struct Chunk { | |||
52 | Variable *vars; | 67 | Variable *vars; |
53 | StrVarMap *varmap; | 68 | StrVarMap *varmap; |
54 | sz var_off; | 69 | sz var_off; |
70 | sz param_off; | ||
55 | 71 | ||
56 | // Number of registers currently used in this chunk. | 72 | // Number of registers currently used in this chunk. |
57 | sz reg_idx; | 73 | sz reg_idx; |
58 | 74 | ||
75 | // Number of functions currently used in this chunk. | ||
76 | struct Chunk **functions; | ||
77 | FunctionMap *funmap; | ||
78 | sz fun_idx; | ||
79 | |||
59 | // Debugging. | 80 | // Debugging. |
60 | Str file_name; | 81 | Str file_name; |
61 | Arena *storage; | 82 | Arena *storage; |
@@ -66,31 +87,52 @@ typedef enum OpCode { | |||
66 | // OP DST A B | 87 | // OP DST A B |
67 | // --------------------------------------------------------------- | 88 | // --------------------------------------------------------------- |
68 | // VM/high level instructions. | 89 | // VM/high level instructions. |
69 | OP_HALT, // halt | 90 | OP_HALT, // halt |
91 | // NOTE: LDGVAR/STGVAR* could be obtained in terms of LDGADDR. | ||
70 | OP_STGVARI, // stgvari vx, ca | 92 | OP_STGVARI, // stgvari vx, ca |
71 | OP_STGVAR, // stgvar vx, ra | 93 | OP_STGVAR, // stgvar vx, ra |
72 | OP_LDGVAR, // ldgvar rx, vx | 94 | OP_LDGVAR, // ldgvar rx, va |
95 | OP_LDGADDR, // ldgaddr rx, va | ||
96 | OP_STLVARI, // stlvari vx, ca | ||
97 | OP_STLVAR, // stlvar vx, ra | ||
98 | OP_LDLVAR, // ldlvar rx, va | ||
99 | OP_LDLADDR, // ldladdr rx, va | ||
100 | // Functions. | ||
101 | OP_CALL, // call fx ; Bumps the stack pointer by cx | ||
102 | OP_RET, // ret ; Returns from current function | ||
103 | OP_RESERVE, // reserve cx ; Increments the stack pointer by cx bytes | ||
104 | OP_POP, // pop rx ; Pops the last value of the stack into rx. | ||
105 | OP_PUSH, // push rx ; Push the rx value to the stack. | ||
106 | OP_PUSHI, // pushi cx ; Push the cx value to the stack. | ||
107 | OP_PUTRET, // putret rx ; Put rx into the return value memory. | ||
108 | OP_PUTRETI, // putreti cx ; Put cx into the return value memory. | ||
109 | // Printing values with builtin print/println functions. | ||
110 | OP_PRINTSTR, // p rx | ||
111 | OP_PRINTS64, // p rx | ||
112 | OP_PRINTF64, // p rx | ||
113 | OP_PRINTS64I, // p cx | ||
114 | OP_PRINTF64I, // p cx | ||
73 | // Load/Store instructions. | 115 | // Load/Store instructions. |
74 | OP_LD8K, // ld8k rx, ca -> u8 rx = ca | 116 | OP_LD8K, // ld8k rx, ca -> u8 rx = ca |
75 | OP_LD16K, // ld16k rx, ca -> u16 rx = ca | 117 | OP_LD16K, // ld16k rx, ca -> u16 rx = ca |
76 | OP_LD32K, // ld32k rx, ca -> u32 rx = ca | 118 | OP_LD32K, // ld32k rx, ca -> u32 rx = ca |
77 | OP_LD64K, // ld64k rx, ca -> u64 rx = ca | 119 | OP_LD64K, // ld64k rx, ca -> u64 rx = ca |
78 | OP_LD8I, // ld8i rx, ra, cb -> u8 *p; rx = p[ra + cb] | 120 | OP_LD8I, // ld8i rx, ra, cb -> u8 *p = ra; rx = p[cb] |
79 | OP_LD16I, // ld16i rx, ra, cb -> u16 *p; rx = p[ra + cb] | 121 | OP_LD16I, // ld16i rx, ra, cb -> u16 *p = ra; rx = p[cb] |
80 | OP_LD32I, // ld32i rx, ra, cb -> u32 *p; rx = p[ra + cb] | 122 | OP_LD32I, // ld32i rx, ra, cb -> u32 *p = ra; rx = p[cb] |
81 | OP_LD64I, // ld64i rx, ra, cb -> u64 *p; rx = p[ra + cb] | 123 | OP_LD64I, // ld64i rx, ra, cb -> u64 *p = ra; rx = p[cb] |
82 | OP_LD8, // ld8 rx, ra, rb -> u8 *p; rx = p[ra + rb] | 124 | OP_LD8, // ld8 rx, ra, rb -> u8 *p = ra; rx = p[rb] |
83 | OP_LD16, // ld16 rx, ra, rb -> u16 *p; rx = p[ra + rb] | 125 | OP_LD16, // ld16 rx, ra, rb -> u16 *p = ra; rx = p[rb] |
84 | OP_LD32, // ld32 rx, ra, rb -> u32 *p; rx = p[ra + rb] | 126 | OP_LD32, // ld32 rx, ra, rb -> u32 *p = ra; rx = p[rb] |
85 | OP_LD64, // ld64 rx, ra, rb -> u64 *p; rx = p[ra + rb] | 127 | OP_LD64, // ld64 rx, ra, rb -> u64 *p = ra; rx = p[rb] |
86 | OP_ST8I, // st8i rx, ra, cb -> u8 *p; p[ra + cb] = rx | 128 | OP_ST8I, // st8i rx, ra, cb -> u8 *p = ra; p[cb] = rx |
87 | OP_ST16I, // st16i rx, ra, cb -> u16 *p; p[ra + cb] = rx | 129 | OP_ST16I, // st16i rx, ra, cb -> u16 *p = ra; p[cb] = rx |
88 | OP_ST32I, // st32i rx, ra, cb -> u32 *p; p[ra + cb] = rx | 130 | OP_ST32I, // st32i rx, ra, cb -> u32 *p = ra; p[cb] = rx |
89 | OP_ST64I, // st64i rx, ra, cb -> u64 *p; p[ra + cb] = rx | 131 | OP_ST64I, // st64i rx, ra, cb -> u64 *p = ra; p[cb] = rx |
90 | OP_ST8, // st8 rx, ra, rb -> u8 *p; p[ra + rb] = rx | 132 | OP_ST8, // st8 rx, ra, rb -> u8 *p = ra; p[rb] = rx |
91 | OP_ST16, // st16 rx, ra, rb -> u16 *p; p[ra + rb] = rx | 133 | OP_ST16, // st16 rx, ra, rb -> u16 *p = ra; p[rb] = rx |
92 | OP_ST32, // st32 rx, ra, rb -> u32 *p; p[ra + rb] = rx | 134 | OP_ST32, // st32 rx, ra, rb -> u32 *p = ra; p[rb] = rx |
93 | OP_ST64, // st64 rx, ra, rb -> u64 *p; p[ra + rb] = rx | 135 | OP_ST64, // st64 rx, ra, rb -> u64 *p = ra; p[rb] = rx |
94 | // Integer arithmetic (only int/s64 for now). | 136 | // Integer arithmetic (only int/s64 for now). |
95 | OP_ADDI, // addk rx, ra, cb | 137 | OP_ADDI, // addk rx, ra, cb |
96 | OP_SUBI, // subk rx, ra, cb | 138 | OP_SUBI, // subk rx, ra, cb |
@@ -149,19 +191,38 @@ typedef enum OpCode { | |||
149 | OP_BITOR, // bor rx, ra, rb | 191 | OP_BITOR, // bor rx, ra, rb |
150 | OP_BITNOT, // bnot rx, ra | 192 | OP_BITNOT, // bnot rx, ra |
151 | // Jump instructions. | 193 | // Jump instructions. |
152 | OP_JMPI, // jmp cx ; cx := signed offset | 194 | OP_JMP, // jmp lx ; jmp to label lx |
153 | OP_JMPFI, // jmpf cx, ca ; rx := condition, ca := offset | 195 | OP_JMPF, // jmpf lx, rx ; jmp to label lx if rx is false |
154 | OP_JMPTI, // jmpt cx, ca ; rx := condition, ca := offset | 196 | OP_JMPT, // jmpt lx, rx ; jmp to label lx if rx is true |
155 | OP_JMP, // jmp rx ; rx := signed offset | 197 | OP_JMPFI, // jmpf lx, cx ; jmp to label lx if rx is false |
156 | OP_JMPF, // jmpf rx, ca ; rx := condition, ca := offset | 198 | OP_JMPTI, // jmpt lx, cx ; jmp to label lx if rx is true |
157 | OP_JMPT, // jmpt rx, ca ; rx := condition, ca := offset | ||
158 | } OpCode; | 199 | } OpCode; |
159 | 200 | ||
160 | Str op_str[] = { | 201 | Str op_str[] = { |
202 | // High level ops. | ||
161 | [OP_HALT] = cstr("HALT "), | 203 | [OP_HALT] = cstr("HALT "), |
162 | [OP_STGVAR] = cstr("STGVAR "), | 204 | [OP_STGVAR] = cstr("STGVAR "), |
163 | [OP_STGVARI] = cstr("STGVARI "), | 205 | [OP_STGVARI] = cstr("STGVARI "), |
164 | [OP_LDGVAR] = cstr("LDGVAR "), | 206 | [OP_LDGVAR] = cstr("LDGVAR "), |
207 | [OP_LDGADDR] = cstr("LDGADDR "), | ||
208 | [OP_STLVAR] = cstr("STLVAR "), | ||
209 | [OP_STLVARI] = cstr("STLVARI "), | ||
210 | [OP_LDLVAR] = cstr("LDLVAR "), | ||
211 | [OP_LDLADDR] = cstr("LDLADDR "), | ||
212 | [OP_PRINTSTR] = cstr("PRNTSTR "), | ||
213 | [OP_PRINTS64] = cstr("PRNTS64 "), | ||
214 | [OP_PRINTF64] = cstr("PRNTF64 "), | ||
215 | [OP_PRINTS64I] = cstr("PRNTS64I"), | ||
216 | [OP_PRINTF64I] = cstr("PRNTF64I"), | ||
217 | [OP_PUTRET] = cstr("PUTRET "), | ||
218 | [OP_PUTRETI] = cstr("PUTRETI "), | ||
219 | // Functions. | ||
220 | [OP_CALL] = cstr("CALL "), | ||
221 | [OP_RET] = cstr("RET "), | ||
222 | [OP_RESERVE] = cstr("RESERVE "), | ||
223 | [OP_POP] = cstr("POP "), | ||
224 | [OP_PUSH] = cstr("PUSH "), | ||
225 | [OP_PUSHI] = cstr("PUSHI "), | ||
165 | // Load ops. | 226 | // Load ops. |
166 | [OP_LD8K] = cstr("LD8K "), | 227 | [OP_LD8K] = cstr("LD8K "), |
167 | [OP_LD16K] = cstr("LD16K "), | 228 | [OP_LD16K] = cstr("LD16K "), |
@@ -241,12 +302,11 @@ Str op_str[] = { | |||
241 | [OP_BITOR] = cstr("BOR "), | 302 | [OP_BITOR] = cstr("BOR "), |
242 | [OP_BITNOT] = cstr("BNOT "), | 303 | [OP_BITNOT] = cstr("BNOT "), |
243 | // Jump instructions. | 304 | // Jump instructions. |
244 | [OP_JMPI] = cstr("JMPI "), | ||
245 | [OP_JMPFI] = cstr("JMPFI "), | ||
246 | [OP_JMPTI] = cstr("JMPTI "), | ||
247 | [OP_JMP] = cstr("JMP "), | 305 | [OP_JMP] = cstr("JMP "), |
248 | [OP_JMPF] = cstr("JMPF "), | 306 | [OP_JMPF] = cstr("JMPF "), |
249 | [OP_JMPT] = cstr("JMPT "), | 307 | [OP_JMPT] = cstr("JMPT "), |
308 | [OP_JMPFI] = cstr("JMPFI "), | ||
309 | [OP_JMPTI] = cstr("JMPTI "), | ||
250 | }; | 310 | }; |
251 | 311 | ||
252 | typedef enum { | 312 | typedef enum { |
@@ -254,6 +314,7 @@ typedef enum { | |||
254 | COMP_CONST, | 314 | COMP_CONST, |
255 | COMP_STRING, | 315 | COMP_STRING, |
256 | COMP_REG, | 316 | COMP_REG, |
317 | COMP_RET, | ||
257 | COMP_ERR, | 318 | COMP_ERR, |
258 | } CompResultType; | 319 | } CompResultType; |
259 | 320 | ||
@@ -262,23 +323,27 @@ typedef struct CompResult { | |||
262 | CompResultType type; | 323 | CompResultType type; |
263 | } CompResult; | 324 | } CompResult; |
264 | 325 | ||
265 | CompResult compile_expr(Chunk *chunk, Node *node); | 326 | CompResult compile_expr(Chunk *chunk, Node *node, sz lab_pre, sz lab_post); |
266 | 327 | ||
267 | #define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ | 328 | #define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ |
268 | do { \ | 329 | do { \ |
269 | Instruction inst = (Instruction){ \ | 330 | Instruction inst = (Instruction){ \ |
270 | .op = (OP), \ | 331 | .op = (OP), \ |
271 | .dst = (DST), \ | 332 | .dst = (DST), \ |
272 | .a = (A), \ | 333 | .a = (A), \ |
273 | .b = (B), \ | 334 | .b = (B), \ |
274 | }; \ | 335 | }; \ |
275 | array_push((CHUNK)->code, inst, (CHUNK)->storage); \ | 336 | array_push((CHUNK)->code, inst, (CHUNK)->storage); \ |
276 | LineCol linecol = (LineCol){.line = (NODE)->line, .col = (NODE)->col}; \ | 337 | LineCol linecol = (LineCol){0}; \ |
277 | array_push((CHUNK)->linecol, linecol, (CHUNK)->storage); \ | 338 | if (NODE) { \ |
339 | Node *_node = (NODE); \ | ||
340 | linecol = (LineCol){.line = _node->line, .col = _node->col}; \ | ||
341 | } \ | ||
342 | array_push((CHUNK)->linecol, linecol, (CHUNK)->storage); \ | ||
278 | } while (0) | 343 | } while (0) |
279 | 344 | ||
280 | CompResult | 345 | CompResult |
281 | compile_binary(Chunk *chunk, Node *node) { | 346 | compile_binary(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { |
282 | OpCode op = OP_HALT; | 347 | OpCode op = OP_HALT; |
283 | OpCode opi = OP_HALT; | 348 | OpCode opi = OP_HALT; |
284 | OpCode ldop = OP_LD64K; | 349 | OpCode ldop = OP_LD64K; |
@@ -381,8 +446,8 @@ compile_binary(Chunk *chunk, Node *node) { | |||
381 | } break; | 446 | } break; |
382 | default: break; | 447 | default: break; |
383 | } | 448 | } |
384 | CompResult comp_a = compile_expr(chunk, node->left); | 449 | CompResult comp_a = compile_expr(chunk, node->left, lab_pre, lab_post); |
385 | CompResult comp_b = compile_expr(chunk, node->right); | 450 | CompResult comp_b = compile_expr(chunk, node->right, lab_pre, lab_post); |
386 | sz reg_a; | 451 | sz reg_a; |
387 | sz reg_b; | 452 | sz reg_b; |
388 | switch (comp_a.type) { | 453 | switch (comp_a.type) { |
@@ -415,7 +480,7 @@ compile_binary(Chunk *chunk, Node *node) { | |||
415 | } | 480 | } |
416 | 481 | ||
417 | CompResult | 482 | CompResult |
418 | compile_unary(Chunk *chunk, Node *node) { | 483 | compile_unary(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { |
419 | OpCode op = OP_HALT; | 484 | OpCode op = OP_HALT; |
420 | OpCode opi = OP_HALT; | 485 | OpCode opi = OP_HALT; |
421 | switch (node->kind) { | 486 | switch (node->kind) { |
@@ -429,7 +494,7 @@ compile_unary(Chunk *chunk, Node *node) { | |||
429 | } break; | 494 | } break; |
430 | default: break; | 495 | default: break; |
431 | } | 496 | } |
432 | CompResult comp_a = compile_expr(chunk, node->left); | 497 | CompResult comp_a = compile_expr(chunk, node->left, lab_pre, lab_post); |
433 | sz reg_a; | 498 | sz reg_a; |
434 | switch (comp_a.type) { | 499 | switch (comp_a.type) { |
435 | case COMP_CONST: { | 500 | case COMP_CONST: { |
@@ -461,9 +526,52 @@ add_constant(Chunk *chunk, sz value) { | |||
461 | return map->val; | 526 | return map->val; |
462 | } | 527 | } |
463 | 528 | ||
529 | sz | ||
530 | add_string(Chunk *chunk, Str string) { | ||
531 | // Make sure we don't have duplicated string. | ||
532 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
533 | if (!map) { | ||
534 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
535 | chunk->storage); | ||
536 | array_push(chunk->strings, string, chunk->storage); | ||
537 | } | ||
538 | return map->val; | ||
539 | } | ||
540 | |||
541 | sz | ||
542 | add_variable(Chunk *chunk, Str name, Str type, sz arr_size) { | ||
543 | sz idx = array_size(chunk->vars); | ||
544 | sz size = 8; | ||
545 | // TODO: get type storage from a table to consider all the basic | ||
546 | // types as well as user defined ones. | ||
547 | if (str_eq(type, cstr("str"))) { | ||
548 | size = 16; | ||
549 | } | ||
550 | if (arr_size) { | ||
551 | // TODO: get the proper storage size for the multiplication. | ||
552 | size *= arr_size; | ||
553 | // FIXME: this should be done on the static analysis, plus, | ||
554 | // we shouldn't be checking all these types by hand, but | ||
555 | // using the symbol tables. | ||
556 | type = str_remove_prefix(type, cstr("@")); | ||
557 | type = str_concat(cstr("[]"), type, chunk->storage); | ||
558 | } | ||
559 | Variable var = (Variable){ | ||
560 | .name = name, | ||
561 | .type = type, | ||
562 | .size = size, | ||
563 | .offset = chunk->var_off, | ||
564 | .idx = idx, | ||
565 | }; | ||
566 | varmap_insert(&chunk->varmap, name, var, chunk->storage); | ||
567 | array_push(chunk->vars, var, chunk->storage); | ||
568 | chunk->var_off += size; | ||
569 | return idx; | ||
570 | } | ||
571 | |||
464 | CompResult | 572 | CompResult |
465 | compile_if(Chunk *chunk, Node *node) { | 573 | compile_if(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { |
466 | CompResult cond = compile_expr(chunk, node->cond_if); | 574 | CompResult cond = compile_expr(chunk, node->cond_if, lab_pre, lab_post); |
467 | OpCode jmpop; | 575 | OpCode jmpop; |
468 | switch (cond.type) { | 576 | switch (cond.type) { |
469 | case COMP_CONST: { | 577 | case COMP_CONST: { |
@@ -476,19 +584,19 @@ compile_if(Chunk *chunk, Node *node) { | |||
476 | return (CompResult){.type = COMP_ERR}; | 584 | return (CompResult){.type = COMP_ERR}; |
477 | } break; | 585 | } break; |
478 | } | 586 | } |
479 | sz jump_a = array_size(chunk->code); | ||
480 | sz reg_dst = 255; | ||
481 | bool has_value = !str_eq(node->type, cstr("nil")); | ||
482 | if (has_value) { | ||
483 | reg_dst = chunk->reg_idx++; | ||
484 | } | ||
485 | 587 | ||
486 | // Jump to the `false` branch. | 588 | if (!str_eq(node->type, cstr("nil")) && |
487 | EMIT_OP(jmpop, cond.idx, 0xff, 0, node->cond_if, chunk); | 589 | !str_has_prefix(node->type, cstr("ret:")) && |
590 | !str_has_prefix(node->type, cstr("flow:"))) { | ||
591 | sz reg_dst = chunk->reg_idx++; | ||
488 | 592 | ||
489 | // Condition is true. | 593 | // Jump to the `false` branch. |
490 | CompResult then_expr = compile_expr(chunk, node->cond_expr); | 594 | sz lab0 = chunk->labels_idx++; |
491 | if (has_value) { | 595 | EMIT_OP(jmpop, lab0, cond.idx, 0, node->cond_if, chunk); |
596 | |||
597 | // Condition is true. | ||
598 | CompResult then_expr = | ||
599 | compile_expr(chunk, node->cond_expr, lab_pre, lab_post); | ||
492 | switch (then_expr.type) { | 600 | switch (then_expr.type) { |
493 | case COMP_CONST: { | 601 | case COMP_CONST: { |
494 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, node->cond_if, | 602 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, node->cond_if, |
@@ -498,59 +606,457 @@ compile_if(Chunk *chunk, Node *node) { | |||
498 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, node->cond_if, | 606 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, node->cond_if, |
499 | chunk); | 607 | chunk); |
500 | } break; | 608 | } break; |
609 | case COMP_RET: break; | ||
501 | default: { | 610 | default: { |
502 | return (CompResult){.type = COMP_ERR}; | 611 | return (CompResult){.type = COMP_ERR}; |
503 | } break; | 612 | } break; |
504 | } | 613 | } |
505 | } | ||
506 | 614 | ||
507 | // Jump to the end of the expression. | 615 | // Jump to the end of the expression. |
508 | sz jump_b = array_size(chunk->code); | 616 | sz pos0 = array_size(chunk->code); |
509 | EMIT_OP(OP_JMPI, 0xff, 0, 0, node->cond_if, chunk); | 617 | sz lab1 = chunk->labels_idx++; |
618 | EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk); | ||
510 | 619 | ||
511 | // Else expression. | 620 | // Else expression. |
512 | CompResult else_expr = compile_expr(chunk, node->cond_else); | 621 | CompResult else_expr = |
513 | if (has_value) { | 622 | compile_expr(chunk, node->cond_else, lab_pre, lab_post); |
514 | switch (else_expr.type) { | 623 | switch (else_expr.type) { |
515 | case COMP_CONST: { | 624 | case COMP_CONST: { |
516 | EMIT_OP(OP_LD64K, reg_dst, else_expr.idx, 0, node->cond_if, | 625 | EMIT_OP(OP_LD64K, reg_dst, else_expr.idx, 0, node->cond_else, |
517 | chunk); | 626 | chunk); |
518 | } break; | 627 | } break; |
519 | case COMP_REG: { | 628 | case COMP_REG: { |
520 | EMIT_OP(OP_MOV64, reg_dst, else_expr.idx, 0, node->cond_if, | 629 | EMIT_OP(OP_MOV64, reg_dst, else_expr.idx, 0, node->cond_else, |
521 | chunk); | 630 | chunk); |
522 | } break; | 631 | } break; |
632 | case COMP_RET: break; | ||
523 | default: { | 633 | default: { |
524 | return (CompResult){.type = COMP_ERR}; | 634 | return (CompResult){.type = COMP_ERR}; |
525 | } break; | 635 | } break; |
526 | } | 636 | } |
637 | sz pos1 = array_size(chunk->code); | ||
638 | |||
639 | // Update labels. | ||
640 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
641 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
642 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage); | ||
643 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
644 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
645 | } | ||
646 | |||
647 | // Jump to the `false` branch. | ||
648 | sz lab0 = chunk->labels_idx++; | ||
649 | EMIT_OP(jmpop, lab0, cond.idx, 0, node->cond_if, chunk); | ||
650 | |||
651 | // Condition is true. | ||
652 | compile_expr(chunk, node->cond_expr, lab_pre, lab_post); | ||
653 | |||
654 | // Jump to the end of the expression. | ||
655 | sz pos0 = array_size(chunk->code); | ||
656 | sz lab1 = chunk->labels_idx++; | ||
657 | EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk); | ||
658 | |||
659 | // Else expression. | ||
660 | if (node->cond_else) { | ||
661 | compile_expr(chunk, node->cond_else, lab_pre, lab_post); | ||
662 | } | ||
663 | sz pos1 = array_size(chunk->code); | ||
664 | |||
665 | // Update labels. | ||
666 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
667 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage); | ||
668 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
669 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
670 | |||
671 | return (CompResult){.type = COMP_NIL}; | ||
672 | } | ||
673 | |||
674 | CompResult | ||
675 | compile_cond(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { | ||
676 | if (str_eq(node->type, cstr("nil"))) { | ||
677 | sz lab1 = chunk->labels_idx++; | ||
678 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
679 | // condition = expression | ||
680 | Node *expr = node->match_cases[i]; | ||
681 | if (expr->case_value) { | ||
682 | CompResult cond = | ||
683 | compile_expr(chunk, expr->case_value, lab_pre, lab_post); | ||
684 | OpCode jmpop; | ||
685 | switch (cond.type) { | ||
686 | case COMP_CONST: { | ||
687 | jmpop = OP_JMPFI; | ||
688 | } break; | ||
689 | case COMP_REG: { | ||
690 | jmpop = OP_JMPF; | ||
691 | } break; | ||
692 | default: { | ||
693 | return (CompResult){.type = COMP_ERR}; | ||
694 | } break; | ||
695 | } | ||
696 | // Jump to the `next` branch. | ||
697 | sz lab0 = chunk->labels_idx++; | ||
698 | EMIT_OP(jmpop, lab0, cond.idx, 0, expr->case_expr, chunk); | ||
699 | |||
700 | // Condition is true. | ||
701 | compile_expr(chunk, expr->case_expr, lab_pre, lab_post); | ||
702 | if (i != array_size(node->match_cases) - 1) { | ||
703 | // Jump to the end of the expression. | ||
704 | sz pos0 = array_size(chunk->code); | ||
705 | EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk); | ||
706 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, | ||
707 | chunk->storage); | ||
708 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, | ||
709 | chunk->storage); | ||
710 | } | ||
711 | } else { | ||
712 | compile_expr(chunk, expr->case_expr, lab_pre, lab_post); | ||
713 | break; | ||
714 | } | ||
715 | } | ||
716 | sz pos1 = array_size(chunk->code); | ||
717 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
718 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
719 | return (CompResult){.type = COMP_NIL}; | ||
720 | } | ||
721 | |||
722 | sz reg_dst = chunk->reg_idx++; | ||
723 | sz lab1 = chunk->labels_idx++; | ||
724 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
725 | // condition = expression | ||
726 | Node *expr = node->match_cases[i]; | ||
727 | if (expr->case_value) { | ||
728 | CompResult cond = | ||
729 | compile_expr(chunk, expr->case_value, lab_pre, lab_post); | ||
730 | OpCode jmpop; | ||
731 | switch (cond.type) { | ||
732 | case COMP_CONST: { | ||
733 | jmpop = OP_JMPFI; | ||
734 | } break; | ||
735 | case COMP_REG: { | ||
736 | jmpop = OP_JMPF; | ||
737 | } break; | ||
738 | default: { | ||
739 | return (CompResult){.type = COMP_ERR}; | ||
740 | } break; | ||
741 | } | ||
742 | // Jump to the `next` branch. | ||
743 | sz lab0 = chunk->labels_idx++; | ||
744 | EMIT_OP(jmpop, lab0, cond.idx, 0, expr->case_expr, chunk); | ||
745 | |||
746 | // Condition is true. | ||
747 | CompResult then_expr = | ||
748 | compile_expr(chunk, expr->case_expr, lab_pre, lab_post); | ||
749 | switch (then_expr.type) { | ||
750 | case COMP_CONST: { | ||
751 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, | ||
752 | expr->case_expr, chunk); | ||
753 | } break; | ||
754 | case COMP_REG: { | ||
755 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, | ||
756 | expr->case_expr, chunk); | ||
757 | } break; | ||
758 | case COMP_RET: break; | ||
759 | default: { | ||
760 | return (CompResult){.type = COMP_ERR}; | ||
761 | } break; | ||
762 | } | ||
763 | if (i != array_size(node->match_cases) - 1) { | ||
764 | // Jump to the end of the expression. | ||
765 | sz pos0 = array_size(chunk->code); | ||
766 | EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk); | ||
767 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, | ||
768 | chunk->storage); | ||
769 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, | ||
770 | chunk->storage); | ||
771 | } | ||
772 | } else { | ||
773 | CompResult then_expr = | ||
774 | compile_expr(chunk, expr->case_expr, lab_pre, lab_post); | ||
775 | switch (then_expr.type) { | ||
776 | case COMP_CONST: { | ||
777 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, | ||
778 | expr->case_expr, chunk); | ||
779 | } break; | ||
780 | case COMP_REG: { | ||
781 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, | ||
782 | expr->case_expr, chunk); | ||
783 | } break; | ||
784 | case COMP_RET: break; | ||
785 | default: { | ||
786 | return (CompResult){.type = COMP_ERR}; | ||
787 | } break; | ||
788 | } | ||
789 | break; | ||
790 | } | ||
791 | } | ||
792 | sz pos1 = array_size(chunk->code); | ||
793 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
794 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
795 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
796 | } | ||
797 | |||
798 | CompResult | ||
799 | compile_break(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { | ||
800 | (void)lab_pre; | ||
801 | EMIT_OP(OP_JMP, lab_post, 0, 0, node, chunk); | ||
802 | return (CompResult){.type = COMP_NIL}; | ||
803 | } | ||
804 | |||
805 | CompResult | ||
806 | compile_continue(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { | ||
807 | (void)lab_post; | ||
808 | EMIT_OP(OP_JMP, lab_pre, 0, 0, node, chunk); | ||
809 | return (CompResult){.type = COMP_NIL}; | ||
810 | } | ||
811 | |||
812 | CompResult | ||
813 | compile_while(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { | ||
814 | sz lab0 = chunk->labels_idx++; | ||
815 | sz lab1 = chunk->labels_idx++; | ||
816 | sz pos1 = array_size(chunk->code); | ||
817 | CompResult cond = compile_expr(chunk, node->while_cond, lab_pre, lab_post); | ||
818 | OpCode jmpop; | ||
819 | switch (cond.type) { | ||
820 | case COMP_CONST: { | ||
821 | jmpop = OP_JMPFI; | ||
822 | } break; | ||
823 | case COMP_REG: { | ||
824 | jmpop = OP_JMPF; | ||
825 | } break; | ||
826 | default: { | ||
827 | return (CompResult){.type = COMP_ERR}; | ||
828 | } break; | ||
527 | } | 829 | } |
528 | sz end_expr = array_size(chunk->code); | ||
529 | 830 | ||
530 | // Backpatch jumps. | 831 | // Jump to the `end of the loop` branch. |
531 | sz const_a = add_constant(chunk, jump_b + 1 - jump_a); | 832 | EMIT_OP(jmpop, lab0, cond.idx, 0, node->while_cond, chunk); |
532 | sz const_b = add_constant(chunk, end_expr - jump_b); | 833 | |
533 | chunk->code[jump_a].a = const_a; | 834 | // Condition is true. |
534 | chunk->code[jump_b].dst = const_b; | 835 | compile_expr(chunk, node->while_expr, lab1, lab0); |
535 | // TODO: does it has an else or not? Moreover, should we enforce on the | 836 | sz pos0 = array_size(chunk->code); |
536 | // semantic level that if the `if` expression returns a value we must add an | 837 | EMIT_OP(OP_JMP, lab1, 0, 0, node, chunk); |
537 | // else? | 838 | |
839 | // Update labels. | ||
840 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
841 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage); | ||
842 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
843 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
538 | 844 | ||
539 | // Return. | 845 | // Return. |
540 | if (has_value) { | 846 | return (CompResult){.type = COMP_NIL}; |
847 | } | ||
848 | |||
849 | CompResult | ||
850 | compile_funcall(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { | ||
851 | Str name = node->value.str; | ||
852 | |||
853 | // Builtins. | ||
854 | if (str_eq(name, cstr("print")) || str_eq(name, cstr("println"))) { | ||
855 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
856 | Node *expr = node->elements[i]; | ||
857 | CompResult result = compile_expr(chunk, expr, lab_pre, lab_post); | ||
858 | if (str_eq(expr->type, cstr("int"))) { | ||
859 | switch (result.type) { | ||
860 | case COMP_CONST: { | ||
861 | EMIT_OP(OP_PRINTS64I, result.idx, 0, 0, expr, chunk); | ||
862 | } break; | ||
863 | case COMP_REG: { | ||
864 | EMIT_OP(OP_PRINTS64, result.idx, 0, 0, expr, chunk); | ||
865 | } break; | ||
866 | default: { | ||
867 | return (CompResult){.type = COMP_ERR}; | ||
868 | } break; | ||
869 | } | ||
870 | } else if (str_eq(expr->type, cstr("f64"))) { | ||
871 | switch (result.type) { | ||
872 | case COMP_CONST: { | ||
873 | EMIT_OP(OP_PRINTF64I, result.idx, 0, 0, expr, chunk); | ||
874 | } break; | ||
875 | case COMP_REG: { | ||
876 | EMIT_OP(OP_PRINTF64, result.idx, 0, 0, expr, chunk); | ||
877 | } break; | ||
878 | default: { | ||
879 | return (CompResult){.type = COMP_ERR}; | ||
880 | } break; | ||
881 | } | ||
882 | } else if (str_eq(expr->type, cstr("str"))) { | ||
883 | switch (result.type) { | ||
884 | case COMP_STRING: { | ||
885 | EMIT_OP(OP_PRINTSTR, result.idx, 0, 0, expr, chunk); | ||
886 | } break; | ||
887 | default: { | ||
888 | return (CompResult){.type = COMP_ERR}; | ||
889 | } break; | ||
890 | } | ||
891 | } | ||
892 | } | ||
893 | if (str_eq(name, cstr("println"))) { | ||
894 | sz idx = add_string(chunk, cstr("\n")); | ||
895 | EMIT_OP(OP_PRINTSTR, idx, 0, 0, node, chunk); | ||
896 | } | ||
897 | return (CompResult){.type = COMP_NIL}; | ||
898 | } | ||
899 | |||
900 | // TODO: need to find this on the parents, not just in the current chunk. | ||
901 | FunctionMap *map = funcmap_lookup(&chunk->funmap, node->unique_name); | ||
902 | if (!map) { | ||
903 | println("how come?"); | ||
904 | exit(EXIT_FAILURE); | ||
905 | } | ||
906 | Function fun = map->val; | ||
907 | |||
908 | // Reserve space for the return value if needed. | ||
909 | if (fun.return_arity > 0) { | ||
910 | // Put the return data into a register | ||
911 | sz ret_size = add_constant(chunk, 8); | ||
912 | EMIT_OP(OP_RESERVE, ret_size, 0, 0, node, chunk); | ||
913 | } | ||
914 | |||
915 | // Send parameters to the stack. | ||
916 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
917 | Node *expr = node->elements[i]; | ||
918 | CompResult result = compile_expr(chunk, expr, lab_pre, lab_post); | ||
919 | // TODO: Assuming all values are 8 bytes... again. | ||
920 | switch (result.type) { | ||
921 | case COMP_CONST: { | ||
922 | EMIT_OP(OP_PUSHI, result.idx, 0, 0, expr, chunk); | ||
923 | } break; | ||
924 | case COMP_REG: { | ||
925 | EMIT_OP(OP_PUSH, result.idx, 0, 0, expr, chunk); | ||
926 | } break; | ||
927 | default: { | ||
928 | return (CompResult){.type = COMP_ERR}; | ||
929 | } break; | ||
930 | } | ||
931 | } | ||
932 | |||
933 | EMIT_OP(OP_CALL, fun.index, 0, 0, node, chunk); | ||
934 | |||
935 | // Only one return parameter for now. | ||
936 | if (fun.return_arity > 0) { | ||
937 | // Put the return data into a register | ||
938 | sz reg_dst = chunk->reg_idx++; | ||
939 | EMIT_OP(OP_POP, reg_dst, 0, 0, node, chunk); | ||
541 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 940 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
542 | } | 941 | } |
942 | |||
543 | return (CompResult){.type = COMP_NIL}; | 943 | return (CompResult){.type = COMP_NIL}; |
544 | } | 944 | } |
545 | 945 | ||
546 | CompResult | 946 | CompResult |
547 | compile_expr(Chunk *chunk, Node *node) { | 947 | compile_return(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { |
948 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
949 | Node *expr = node->elements[i]; | ||
950 | CompResult res = compile_expr(chunk, expr, lab_pre, lab_post); | ||
951 | |||
952 | // TODO: Only one return field for now, but this is the idea. | ||
953 | // Put return values into memory. | ||
954 | switch (res.type) { | ||
955 | case COMP_CONST: { | ||
956 | EMIT_OP(OP_PUTRETI, res.idx, 0, 0, node, chunk); | ||
957 | } break; | ||
958 | case COMP_REG: { | ||
959 | EMIT_OP(OP_PUTRET, res.idx, 0, 0, node, chunk); | ||
960 | } break; | ||
961 | default: { | ||
962 | return (CompResult){.type = COMP_ERR}; | ||
963 | } break; | ||
964 | } | ||
965 | break; | ||
966 | } | ||
967 | |||
968 | EMIT_OP(OP_RET, 0, 0, 0, node, chunk); | ||
969 | return (CompResult){.type = COMP_RET}; | ||
970 | } | ||
971 | |||
972 | Chunk * | ||
973 | chunk_alloc(Chunk *parent) { | ||
974 | static sz chunk_idx = 1; | ||
975 | Chunk *chunk = arena_calloc((sz)sizeof(Chunk), parent->storage); | ||
976 | chunk->parent = parent; | ||
977 | chunk->id = chunk_idx++; | ||
978 | chunk->storage = parent->storage; | ||
979 | chunk->file_name = parent->file_name; | ||
980 | return chunk; | ||
981 | } | ||
982 | |||
983 | CompResult | ||
984 | compile_function(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { | ||
985 | // The current activation record procedure for the VM is as follows: | ||
986 | // | ||
987 | // [caller][callee ] | ||
988 | // [ .... ][ RET VAL ][ PARAMS ][ LOCALS ][ REGISTERS ][ RET META ] | ||
989 | // ^ | ||
990 | // frame pointer | ||
991 | // | ||
992 | // The caller is responsible for allocating the return memory and the | ||
993 | // parameter memory and filling the param data before OP_CALL. | ||
994 | // | ||
995 | Chunk *func = chunk_alloc(chunk); | ||
996 | func->name = node->unique_name; | ||
997 | Function fun = (Function){ | ||
998 | .name = func->name, | ||
999 | .index = chunk->fun_idx++, | ||
1000 | .param_arity = array_size(node->func_params), | ||
1001 | .return_arity = array_size(node->func_ret), | ||
1002 | }; | ||
1003 | funcmap_insert(&chunk->funmap, func->name, fun, chunk->storage); | ||
1004 | array_push(chunk->functions, func, chunk->storage); | ||
1005 | |||
1006 | // Push arguments as locals. | ||
1007 | for (sz i = 0; i < array_size(node->func_params); i++) { | ||
1008 | Node *param = node->func_params[i]; | ||
1009 | Str name = param->unique_name; | ||
1010 | Str type = param->type; | ||
1011 | sz arr_size = 0; | ||
1012 | if (str_has_prefix(type, cstr("@"))) { | ||
1013 | if (param->var_type && param->var_type->kind == NODE_ARR_TYPE && | ||
1014 | param->var_type->arr_size->value.i > 0) { | ||
1015 | arr_size = param->var_type->arr_size->value.i; | ||
1016 | } | ||
1017 | } | ||
1018 | add_variable(func, name, type, arr_size); | ||
1019 | } | ||
1020 | func->param_off = func->var_off; | ||
1021 | |||
1022 | // Compiling the body. | ||
1023 | CompResult res = compile_expr(func, node->func_body, lab_pre, lab_post); | ||
1024 | |||
1025 | // Put return values into memory. | ||
1026 | switch (res.type) { | ||
1027 | case COMP_CONST: { | ||
1028 | EMIT_OP(OP_PUTRETI, res.idx, 0, 0, node, func); | ||
1029 | } break; | ||
1030 | case COMP_REG: { | ||
1031 | EMIT_OP(OP_PUTRET, res.idx, 0, 0, node, func); | ||
1032 | } break; | ||
1033 | default: break; | ||
1034 | } | ||
1035 | |||
1036 | // TODO: handle captured locals/globals? | ||
1037 | EMIT_OP(OP_RET, 0, 0, 0, node, func); | ||
1038 | return (CompResult){.type = COMP_NIL}; | ||
1039 | } | ||
1040 | |||
1041 | CompResult | ||
1042 | compile_expr(Chunk *chunk, Node *node, sz lab_pre, sz lab_post) { | ||
548 | switch (node->kind) { | 1043 | switch (node->kind) { |
549 | case NODE_IF: return compile_if(chunk, node); | 1044 | case NODE_BREAK: return compile_break(chunk, node, lab_pre, lab_post); |
1045 | case NODE_CONTINUE: | ||
1046 | return compile_continue(chunk, node, lab_pre, lab_post); | ||
1047 | case NODE_RETURN: return compile_return(chunk, node, lab_pre, lab_post); | ||
1048 | case NODE_FUN: return compile_function(chunk, node, lab_pre, lab_post); | ||
1049 | case NODE_FUNCALL: | ||
1050 | return compile_funcall(chunk, node, lab_pre, lab_post); | ||
1051 | case NODE_WHILE: return compile_while(chunk, node, lab_pre, lab_post); | ||
1052 | case NODE_IF: return compile_if(chunk, node, lab_pre, lab_post); | ||
1053 | case NODE_COND: return compile_cond(chunk, node, lab_pre, lab_post); | ||
550 | // Logic. | 1054 | // Logic. |
551 | // case NODE_XOR: | 1055 | // case NODE_XOR: |
552 | case NODE_BITNOT: | 1056 | case NODE_BITNOT: |
553 | case NODE_NOT: return compile_unary(chunk, node); break; | 1057 | case NODE_NOT: |
1058 | return compile_unary(chunk, node, lab_pre, lab_post); | ||
1059 | break; | ||
554 | case NODE_AND: | 1060 | case NODE_AND: |
555 | case NODE_OR: | 1061 | case NODE_OR: |
556 | case NODE_EQ: | 1062 | case NODE_EQ: |
@@ -569,7 +1075,9 @@ compile_expr(Chunk *chunk, Node *node) { | |||
569 | case NODE_SUB: | 1075 | case NODE_SUB: |
570 | case NODE_MUL: | 1076 | case NODE_MUL: |
571 | case NODE_DIV: | 1077 | case NODE_DIV: |
572 | case NODE_MOD: return compile_binary(chunk, node); break; | 1078 | case NODE_MOD: |
1079 | return compile_binary(chunk, node, lab_pre, lab_post); | ||
1080 | break; | ||
573 | case NODE_TRUE: | 1081 | case NODE_TRUE: |
574 | case NODE_FALSE: | 1082 | case NODE_FALSE: |
575 | case NODE_NUM_FLOAT: | 1083 | case NODE_NUM_FLOAT: |
@@ -584,49 +1092,42 @@ compile_expr(Chunk *chunk, Node *node) { | |||
584 | } break; | 1092 | } break; |
585 | case NODE_STRING: { | 1093 | case NODE_STRING: { |
586 | Str string = node->value.str; | 1094 | Str string = node->value.str; |
587 | // Make sure we don't have duplicated strings. | 1095 | sz str_idx = add_string(chunk, string); |
588 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
589 | if (!map) { | ||
590 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
591 | chunk->storage); | ||
592 | array_push(chunk->strings, string, chunk->storage); | ||
593 | } | ||
594 | return (CompResult){ | 1096 | return (CompResult){ |
595 | .type = COMP_STRING, | 1097 | .type = COMP_STRING, |
596 | .idx = map->val, | 1098 | .idx = str_idx, |
597 | }; | 1099 | }; |
598 | } break; | 1100 | } break; |
599 | case NODE_LET: { | 1101 | case NODE_LET: { |
600 | sz idx = array_size(chunk->vars); | 1102 | u8 op_stvari = OP_STGVARI; |
1103 | u8 op_stvar = OP_STGVAR; | ||
1104 | if (!str_eq(chunk->name, cstr(".main"))) { | ||
1105 | op_stvari = OP_STLVARI; | ||
1106 | op_stvar = OP_STLVAR; | ||
1107 | } | ||
601 | Str name = node->unique_name; | 1108 | Str name = node->unique_name; |
602 | Str type = node->var_name->type; | 1109 | Str type = node->var_name->type; |
603 | sz size = 8; | 1110 | sz arr_size = 0; |
604 | // TODO: get type storage from a table to consider all the basic | 1111 | if (str_has_prefix(type, cstr("@"))) { |
605 | // types as well as user defined ones. | 1112 | if (node->var_type && node->var_type->kind == NODE_ARR_TYPE && |
606 | if (str_eq(type, cstr("str"))) { | 1113 | node->var_type->arr_size->value.i > 0) { |
607 | size = 16; | 1114 | arr_size = node->var_type->arr_size->value.i; |
1115 | } | ||
608 | } | 1116 | } |
609 | Variable var = (Variable){ | 1117 | |
610 | .name = name, | 1118 | sz idx = add_variable(chunk, name, type, arr_size); |
611 | .type = type, | ||
612 | .size = size, | ||
613 | .offset = chunk->var_off, | ||
614 | .idx = idx, | ||
615 | }; | ||
616 | varmap_insert(&chunk->varmap, name, var, chunk->storage); | ||
617 | array_push(chunk->vars, var, chunk->storage); | ||
618 | chunk->var_off += size; | ||
619 | 1119 | ||
620 | // Value. | 1120 | // Value. |
621 | if (node->var_val) { | 1121 | if (node->var_val) { |
622 | CompResult res = compile_expr(chunk, node->var_val); | 1122 | CompResult res = |
1123 | compile_expr(chunk, node->var_val, lab_pre, lab_post); | ||
623 | switch (res.type) { | 1124 | switch (res.type) { |
624 | case COMP_CONST: { | 1125 | case COMP_CONST: { |
625 | EMIT_OP(OP_STGVARI, idx, res.idx, 0, node->var_val, | 1126 | EMIT_OP(op_stvari, idx, res.idx, 0, node->var_val, |
626 | chunk); | 1127 | chunk); |
627 | } break; | 1128 | } break; |
628 | case COMP_REG: { | 1129 | case COMP_REG: { |
629 | EMIT_OP(OP_STGVAR, idx, res.idx, 0, node->var_val, | 1130 | EMIT_OP(op_stvar, idx, res.idx, 0, node->var_val, |
630 | chunk); | 1131 | chunk); |
631 | } break; | 1132 | } break; |
632 | default: { | 1133 | default: { |
@@ -637,23 +1138,182 @@ compile_expr(Chunk *chunk, Node *node) { | |||
637 | 1138 | ||
638 | return (CompResult){.type = COMP_NIL}; | 1139 | return (CompResult){.type = COMP_NIL}; |
639 | } break; | 1140 | } break; |
1141 | case NODE_SET: { | ||
1142 | Str name = node->unique_name; | ||
1143 | StrVarMap *map = NULL; | ||
1144 | Chunk *next = chunk; | ||
1145 | while (next) { | ||
1146 | map = varmap_lookup(&next->varmap, name); | ||
1147 | if (map) { | ||
1148 | break; | ||
1149 | } | ||
1150 | next = chunk->parent; | ||
1151 | } | ||
1152 | if (!map) { | ||
1153 | println("error: unreachable symbol name: %s", name); | ||
1154 | exit(EXIT_FAILURE); | ||
1155 | } | ||
1156 | u8 op_ldaddr = OP_LDGADDR; | ||
1157 | u8 op_ldvar = OP_LDGVAR; | ||
1158 | u8 op_stvari = OP_STGVARI; | ||
1159 | u8 op_stvar = OP_STGVAR; | ||
1160 | if (!str_eq(next->name, cstr(".main"))) { | ||
1161 | op_ldaddr = OP_LDLADDR; | ||
1162 | op_ldvar = OP_LDLVAR; | ||
1163 | op_stvari = OP_STLVARI; | ||
1164 | op_stvar = OP_STLVAR; | ||
1165 | } | ||
1166 | CompResult res = | ||
1167 | compile_expr(chunk, node->var_val, lab_pre, lab_post); | ||
1168 | if (node->var_name->kind == NODE_SYMBOL_IDX) { | ||
1169 | // Value. | ||
1170 | sz reg_val; | ||
1171 | switch (res.type) { | ||
1172 | case COMP_CONST: { | ||
1173 | reg_val = chunk->reg_idx++; | ||
1174 | EMIT_OP(OP_LD64K, reg_val, res.idx, 0, node, chunk); | ||
1175 | } break; | ||
1176 | case COMP_REG: { | ||
1177 | reg_val = res.idx; | ||
1178 | } break; | ||
1179 | default: { | ||
1180 | return (CompResult){.type = COMP_ERR}; | ||
1181 | } break; | ||
1182 | } | ||
1183 | |||
1184 | // Address. | ||
1185 | sz reg_addr = chunk->reg_idx++; | ||
1186 | // Is this a pointer access or an array access? | ||
1187 | if (str_has_prefix(map->val.type, cstr("[]"))) { | ||
1188 | EMIT_OP(op_ldaddr, reg_addr, map->val.idx, 0, node->var_val, | ||
1189 | chunk); | ||
1190 | } else { | ||
1191 | EMIT_OP(op_ldvar, reg_addr, map->val.idx, 0, node->var_val, | ||
1192 | chunk); | ||
1193 | } | ||
1194 | |||
1195 | // Index. | ||
1196 | CompResult idx = compile_expr(chunk, node->var_name->arr_size, | ||
1197 | lab_pre, lab_post); | ||
1198 | switch (idx.type) { | ||
1199 | case COMP_CONST: { | ||
1200 | EMIT_OP(OP_ST64I, reg_val, reg_addr, idx.idx, node, | ||
1201 | chunk); | ||
1202 | } break; | ||
1203 | case COMP_REG: { | ||
1204 | EMIT_OP(OP_ST64, reg_val, reg_addr, idx.idx, node, | ||
1205 | chunk); | ||
1206 | } break; | ||
1207 | default: { | ||
1208 | return (CompResult){.type = COMP_ERR}; | ||
1209 | } break; | ||
1210 | } | ||
1211 | // TODO: offset should be in bytes, in this case we are assuming | ||
1212 | // 64bit types, hence ST64 | ||
1213 | return (CompResult){.type = COMP_NIL}; | ||
1214 | } | ||
1215 | switch (res.type) { | ||
1216 | case COMP_CONST: { | ||
1217 | EMIT_OP(op_stvari, map->val.idx, res.idx, 0, node->var_val, | ||
1218 | chunk); | ||
1219 | } break; | ||
1220 | case COMP_REG: { | ||
1221 | EMIT_OP(op_stvar, map->val.idx, res.idx, 0, node->var_val, | ||
1222 | chunk); | ||
1223 | } break; | ||
1224 | default: { | ||
1225 | return (CompResult){.type = COMP_ERR}; | ||
1226 | } break; | ||
1227 | } | ||
1228 | return (CompResult){.type = COMP_NIL}; | ||
1229 | } break; | ||
640 | case NODE_SYMBOL: { | 1230 | case NODE_SYMBOL: { |
641 | Str name = node->unique_name; | 1231 | Str name = node->unique_name; |
642 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); | 1232 | StrVarMap *map = NULL; |
1233 | Chunk *next = chunk; | ||
1234 | while (next) { | ||
1235 | map = varmap_lookup(&next->varmap, name); | ||
1236 | if (map) { | ||
1237 | break; | ||
1238 | } | ||
1239 | next = chunk->parent; | ||
1240 | } | ||
643 | if (!map) { | 1241 | if (!map) { |
644 | println("error: unreachable... name: %s", name); | 1242 | println("error: unreachable symbol name: %s", name); |
645 | exit(EXIT_FAILURE); | 1243 | exit(EXIT_FAILURE); |
646 | } | 1244 | } |
1245 | u8 op_ldaddr = OP_LDGADDR; | ||
1246 | u8 op_ldvar = OP_LDGVAR; | ||
1247 | if (!str_eq(next->name, cstr(".main"))) { | ||
1248 | op_ldaddr = OP_LDLADDR; | ||
1249 | op_ldvar = OP_LDLVAR; | ||
1250 | } | ||
647 | Variable var = map->val; | 1251 | Variable var = map->val; |
648 | u8 reg_dst = chunk->reg_idx++; | 1252 | u8 reg_dst = chunk->reg_idx++; |
649 | EMIT_OP(OP_LDGVAR, reg_dst, var.idx, 0, node, chunk); | 1253 | if (node->is_ptr || str_has_prefix(var.type, cstr("[]"))) { |
1254 | EMIT_OP(op_ldaddr, reg_dst, var.idx, 0, node, chunk); | ||
1255 | } else { | ||
1256 | EMIT_OP(op_ldvar, reg_dst, var.idx, 0, node, chunk); | ||
1257 | } | ||
1258 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
1259 | } break; | ||
1260 | case NODE_SYMBOL_IDX: { | ||
1261 | Str name = node->unique_name; | ||
1262 | StrVarMap *map = NULL; | ||
1263 | Chunk *next = chunk; | ||
1264 | while (next) { | ||
1265 | map = varmap_lookup(&next->varmap, name); | ||
1266 | if (map) { | ||
1267 | break; | ||
1268 | } | ||
1269 | next = chunk->parent; | ||
1270 | } | ||
1271 | if (!map) { | ||
1272 | println("error: unreachable symbol name: %s", name); | ||
1273 | exit(EXIT_FAILURE); | ||
1274 | } | ||
1275 | u8 op_ldaddr = OP_LDGADDR; | ||
1276 | u8 op_ldvar = OP_LDGVAR; | ||
1277 | if (!str_eq(next->name, cstr(".main"))) { | ||
1278 | op_ldaddr = OP_LDLADDR; | ||
1279 | op_ldvar = OP_LDLVAR; | ||
1280 | } | ||
1281 | |||
1282 | // Destination. | ||
1283 | u8 reg_dst = chunk->reg_idx++; | ||
1284 | |||
1285 | // Address. | ||
1286 | sz reg_addr = chunk->reg_idx++; | ||
1287 | if (str_has_prefix(map->val.type, cstr("[]"))) { | ||
1288 | EMIT_OP(op_ldaddr, reg_addr, map->val.idx, 0, node->var_val, | ||
1289 | chunk); | ||
1290 | } else { | ||
1291 | EMIT_OP(op_ldvar, reg_addr, map->val.idx, 0, node->var_val, | ||
1292 | chunk); | ||
1293 | } | ||
1294 | |||
1295 | // Index. | ||
1296 | CompResult idx = | ||
1297 | compile_expr(chunk, node->arr_size, lab_pre, lab_post); | ||
1298 | switch (idx.type) { | ||
1299 | case COMP_CONST: { | ||
1300 | EMIT_OP(OP_LD64I, reg_dst, reg_addr, idx.idx, node, chunk); | ||
1301 | } break; | ||
1302 | case COMP_REG: { | ||
1303 | EMIT_OP(OP_LD64, reg_dst, reg_addr, idx.idx, node, chunk); | ||
1304 | } break; | ||
1305 | default: { | ||
1306 | return (CompResult){.type = COMP_ERR}; | ||
1307 | } break; | ||
1308 | } | ||
1309 | // TODO: hardcoding the type size for now (LD64/LD64I). | ||
650 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 1310 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
651 | } break; | 1311 | } break; |
652 | case NODE_BLOCK: { | 1312 | case NODE_BLOCK: { |
653 | CompResult res; | 1313 | CompResult res; |
654 | for (sz i = 0; i < array_size(node->elements); i++) { | 1314 | for (sz i = 0; i < array_size(node->elements); i++) { |
655 | Node *root = node->elements[i]; | 1315 | Node *root = node->elements[i]; |
656 | res = compile_expr(chunk, root); | 1316 | res = compile_expr(chunk, root, lab_pre, lab_post); |
657 | } | 1317 | } |
658 | return res; | 1318 | return res; |
659 | } break; | 1319 | } break; |
@@ -669,6 +1329,10 @@ compile_expr(Chunk *chunk, Node *node) { | |||
669 | void | 1329 | void |
670 | disassemble_instruction(Instruction instruction) { | 1330 | disassemble_instruction(Instruction instruction) { |
671 | switch (instruction.op) { | 1331 | switch (instruction.op) { |
1332 | case OP_CALL: | ||
1333 | println("%s f%d", op_str[instruction.op], instruction.dst, | ||
1334 | instruction.a, instruction.b); | ||
1335 | break; | ||
672 | case OP_MOV8: | 1336 | case OP_MOV8: |
673 | case OP_MOV16: | 1337 | case OP_MOV16: |
674 | case OP_MOV32: | 1338 | case OP_MOV32: |
@@ -676,12 +1340,15 @@ disassemble_instruction(Instruction instruction) { | |||
676 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | 1340 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, |
677 | instruction.a, instruction.b); | 1341 | instruction.a, instruction.b); |
678 | break; | 1342 | break; |
1343 | case OP_JMPF: | ||
1344 | case OP_JMPT: | ||
1345 | println("%s l%d, r%d", op_str[instruction.op], instruction.dst, | ||
1346 | instruction.a, instruction.b); | ||
1347 | break; | ||
679 | case OP_LD8K: | 1348 | case OP_LD8K: |
680 | case OP_LD16K: | 1349 | case OP_LD16K: |
681 | case OP_LD32K: | 1350 | case OP_LD32K: |
682 | case OP_LD64K: | 1351 | case OP_LD64K: |
683 | case OP_JMPF: | ||
684 | case OP_JMPT: | ||
685 | println("%s r%d, c%d", op_str[instruction.op], instruction.dst, | 1352 | println("%s r%d, c%d", op_str[instruction.op], instruction.dst, |
686 | instruction.a, instruction.b); | 1353 | instruction.a, instruction.b); |
687 | break; | 1354 | break; |
@@ -752,14 +1419,19 @@ disassemble_instruction(Instruction instruction) { | |||
752 | instruction.a, instruction.b); | 1419 | instruction.a, instruction.b); |
753 | break; | 1420 | break; |
754 | case OP_LDGVAR: | 1421 | case OP_LDGVAR: |
1422 | case OP_LDGADDR: | ||
1423 | case OP_LDLVAR: | ||
1424 | case OP_LDLADDR: | ||
755 | println("%s r%d, v%d", op_str[instruction.op], instruction.dst, | 1425 | println("%s r%d, v%d", op_str[instruction.op], instruction.dst, |
756 | instruction.a, instruction.b); | 1426 | instruction.a, instruction.b); |
757 | break; | 1427 | break; |
758 | case OP_STGVAR: | 1428 | case OP_STGVAR: |
1429 | case OP_STLVAR: | ||
759 | println("%s v%d, r%d", op_str[instruction.op], instruction.dst, | 1430 | println("%s v%d, r%d", op_str[instruction.op], instruction.dst, |
760 | instruction.a, instruction.b); | 1431 | instruction.a, instruction.b); |
761 | break; | 1432 | break; |
762 | case OP_STGVARI: | 1433 | case OP_STGVARI: |
1434 | case OP_STLVARI: | ||
763 | println("%s v%d, c%d", op_str[instruction.op], instruction.dst, | 1435 | println("%s v%d, c%d", op_str[instruction.op], instruction.dst, |
764 | instruction.a, instruction.b); | 1436 | instruction.a, instruction.b); |
765 | break; | 1437 | break; |
@@ -773,19 +1445,33 @@ disassemble_instruction(Instruction instruction) { | |||
773 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | 1445 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, |
774 | instruction.a, instruction.b); | 1446 | instruction.a, instruction.b); |
775 | break; | 1447 | break; |
776 | case OP_JMPI: | ||
777 | println("%s c%d", op_str[instruction.op], instruction.dst, | ||
778 | instruction.a, instruction.b); | ||
779 | break; | ||
780 | case OP_JMP: | 1448 | case OP_JMP: |
781 | println("%s r%d", op_str[instruction.op], instruction.dst, | 1449 | println("%s l%d", op_str[instruction.op], instruction.dst, |
782 | instruction.a, instruction.b); | 1450 | instruction.a, instruction.b); |
783 | break; | 1451 | break; |
784 | case OP_JMPFI: | 1452 | case OP_JMPFI: |
785 | case OP_JMPTI: | 1453 | case OP_JMPTI: |
786 | println("%s c%d, c%d", op_str[instruction.op], instruction.dst, | 1454 | println("%s l%d, c%d", op_str[instruction.op], instruction.dst, |
787 | instruction.a, instruction.b); | 1455 | instruction.a, instruction.b); |
788 | break; | 1456 | break; |
1457 | case OP_PRINTS64: | ||
1458 | case OP_PRINTF64: | ||
1459 | case OP_PRINTSTR: | ||
1460 | case OP_PUSH: | ||
1461 | case OP_POP: | ||
1462 | case OP_PUTRET: | ||
1463 | println("%s r%d", op_str[instruction.op], instruction.dst, | ||
1464 | instruction.a, instruction.b); | ||
1465 | break; | ||
1466 | case OP_PRINTS64I: | ||
1467 | case OP_PRINTF64I: | ||
1468 | case OP_RESERVE: | ||
1469 | case OP_PUSHI: | ||
1470 | case OP_PUTRETI: | ||
1471 | println("%s c%d", op_str[instruction.op], instruction.dst, | ||
1472 | instruction.a, instruction.b); | ||
1473 | break; | ||
1474 | case OP_RET: | ||
789 | case OP_HALT: println("%s", op_str[instruction.op]); break; | 1475 | case OP_HALT: println("%s", op_str[instruction.op]); break; |
790 | default: println("Unknown opcode %d", instruction.op); break; | 1476 | default: println("Unknown opcode %d", instruction.op); break; |
791 | } | 1477 | } |
@@ -793,36 +1479,56 @@ disassemble_instruction(Instruction instruction) { | |||
793 | 1479 | ||
794 | void | 1480 | void |
795 | disassemble_chunk(Chunk chunk) { | 1481 | disassemble_chunk(Chunk chunk) { |
796 | println("%s: ============== code ==============", chunk.file_name); | 1482 | println("CHUNK %d: %s%s", chunk.id, chunk.file_name, chunk.name); |
1483 | println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", | ||
1484 | chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, | ||
1485 | chunk.const_idx); | ||
1486 | println("================== code =================="); | ||
1487 | println(" LINE:COL INUM LABELS OP OPERANDS "); | ||
1488 | println("------------------------------------------"); | ||
797 | for (sz i = 0; i < array_size(chunk.code); i++) { | 1489 | for (sz i = 0; i < array_size(chunk.code); i++) { |
798 | print("%s: %x{4}: ", chunk.file_name, i); | 1490 | printf(" %.4ld:%.4ld %.4lx ", chunk.linecol[i].line, |
1491 | chunk.linecol[i].col, i); | ||
1492 | IntIntMap *label = intintmap_lookup(&chunk.labels_rev, i); | ||
1493 | if (label) { | ||
1494 | printf(".L%.2ld ", label->val); | ||
1495 | } else { | ||
1496 | printf(" %2s ", ""); | ||
1497 | } | ||
799 | disassemble_instruction(chunk.code[i]); | 1498 | disassemble_instruction(chunk.code[i]); |
800 | } | 1499 | } |
801 | if (array_size(chunk.constants) > 0) { | 1500 | if (array_size(chunk.constants) > 0) { |
802 | println("%s: ============ constants ===========", chunk.file_name); | 1501 | println("================ constants ===============", chunk.file_name); |
803 | for (sz i = 0; i < array_size(chunk.constants); i++) { | 1502 | for (sz i = 0; i < array_size(chunk.constants); i++) { |
804 | println("%s: %x{2}: %x{8}", chunk.file_name, i, | 1503 | println(" %x{2}: %x{8}", i, chunk.constants[i]); |
805 | chunk.constants[i]); | ||
806 | } | 1504 | } |
807 | } | 1505 | } |
808 | if (array_size(chunk.strings) > 0) { | 1506 | if (array_size(chunk.strings) > 0) { |
809 | println("%s: ============= strings ============", chunk.file_name); | 1507 | println("================= strings ================", chunk.file_name); |
810 | for (sz i = 0; i < array_size(chunk.strings); i++) { | 1508 | for (sz i = 0; i < array_size(chunk.strings); i++) { |
811 | println("%s: %x{2}: %s", chunk.file_name, i, chunk.strings[i]); | 1509 | println(" %x{2}: %s", i, chunk.strings[i]); |
812 | } | 1510 | } |
813 | } | 1511 | } |
814 | if (array_size(chunk.vars) > 0) { | 1512 | if (array_size(chunk.vars) > 0) { |
815 | println("%s: ============ variables ===========", chunk.file_name); | 1513 | println("================ variables ===============", chunk.file_name); |
816 | for (sz i = 0; i < array_size(chunk.vars); i++) { | 1514 | for (sz i = 0; i < array_size(chunk.vars); i++) { |
817 | println("%s: %x{2}: [%x{4}:%x{4}] %s: %s", chunk.file_name, i, | 1515 | println(" %x{2}: [%x{4}:%x{4}] %s: %s", i, chunk.vars[i].offset, |
818 | chunk.vars[i].offset, | ||
819 | chunk.vars[i].offset + chunk.vars[i].size, | 1516 | chunk.vars[i].offset + chunk.vars[i].size, |
820 | chunk.vars[i].name, chunk.vars[i].type); | 1517 | chunk.vars[i].name, chunk.vars[i].type); |
821 | } | 1518 | } |
822 | } | 1519 | } |
823 | println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", | 1520 | if (array_size(chunk.functions) > 0) { |
824 | chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, | 1521 | println("================ functions ===============", chunk.file_name); |
825 | chunk.const_idx); | 1522 | for (sz i = 0; i < array_size(chunk.functions); i++) { |
1523 | Chunk *func = chunk.functions[i]; | ||
1524 | println(" %x{2}: func%d: %s", i, func->id, func->name); | ||
1525 | } | ||
1526 | } | ||
1527 | println("=========================================="); | ||
1528 | for (sz i = 0; i < array_size(chunk.functions); i++) { | ||
1529 | Chunk *func = chunk.functions[i]; | ||
1530 | disassemble_chunk(*func); | ||
1531 | } | ||
826 | } | 1532 | } |
827 | 1533 | ||
828 | #endif // COMPILER_C | 1534 | #endif // COMPILER_C |
@@ -12,7 +12,29 @@ | |||
12 | // TODO: unions | 12 | // TODO: unions |
13 | // TODO: embed (binary file) and include (source file) | 13 | // TODO: embed (binary file) and include (source file) |
14 | // TODO: revisit ast parsing for pointers and arrays (I think I'm missing corner | 14 | // TODO: revisit ast parsing for pointers and arrays (I think I'm missing corner |
15 | // cases). | 15 | // cases). |
16 | // TODO: for loops? | ||
17 | // for i = 0 , i < 10 , i++ { | ||
18 | // ... | ||
19 | // } | ||
20 | // TODO: fix semantics for the following expression: | ||
21 | // let b = int | ||
22 | // a type shouldn't be a valid symbol name | ||
23 | // TODO: consider making all tye types PascalCase: Int F64 Str... | ||
24 | // TODO: add a `const` keyword that can only take literals or constexpr values. | ||
25 | // TODO: add assignment operators instead of `set` :=, +=, -= | ||
26 | // TODO: add mifix operators ++ and -- | ||
27 | // TODO: locals should probably be zero-initialized when we are performing | ||
28 | // a function call to avoid issues. | ||
29 | // TODO: nested function compilation | ||
30 | // TODO: "first class functions" via function pointers | ||
31 | // TODO: convenient function calls per data type instead of methods: | ||
32 | // fun add(a: int, b: int): int a + b | ||
33 | // add(12, 34) == 12.add(34) | ||
34 | // concat(str, str): str | ||
35 | // "hello ".concat("world") ; "hello world" | ||
36 | // TODO: refactor the parser to more clearly show the different node type, data | ||
37 | // fields. | ||
16 | 38 | ||
17 | typedef enum ExecMode { | 39 | typedef enum ExecMode { |
18 | RUN_NORMAL, | 40 | RUN_NORMAL, |
@@ -167,7 +189,11 @@ process_file(Str path) { | |||
167 | 189 | ||
168 | // Compile roots. | 190 | // Compile roots. |
169 | Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); | 191 | Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); |
170 | Chunk chunk = {.file_name = path, .storage = &bytecode_arena}; | 192 | Chunk chunk = { |
193 | .file_name = path, | ||
194 | .storage = &bytecode_arena, | ||
195 | .name = cstr(".main"), | ||
196 | }; | ||
171 | array_zero(chunk.constants, 256, &bytecode_arena); | 197 | array_zero(chunk.constants, 256, &bytecode_arena); |
172 | array_zero(chunk.code, 0xffff, &bytecode_arena); | 198 | array_zero(chunk.code, 0xffff, &bytecode_arena); |
173 | sz n_roots = array_size(parser.nodes); | 199 | sz n_roots = array_size(parser.nodes); |
@@ -175,13 +201,15 @@ process_file(Str path) { | |||
175 | for (sz i = 0; i < n_roots; i++) { | 201 | for (sz i = 0; i < n_roots; i++) { |
176 | // The parser stores the root nodes as a stack. | 202 | // The parser stores the root nodes as a stack. |
177 | Node *root = parser.nodes[i]; | 203 | Node *root = parser.nodes[i]; |
178 | res = compile_expr(&chunk, root); | 204 | res = compile_expr(&chunk, root, -1, -1); |
179 | if (res.type == COMP_ERR) { | 205 | if (res.type == COMP_ERR) { |
180 | eprintln("compilation error..."); | 206 | eprintln("compilation error..."); |
181 | exit(EXIT_FAILURE); | 207 | exit(EXIT_FAILURE); |
182 | } | 208 | } |
183 | } | 209 | } |
210 | // Make sure the last result is on r0. | ||
184 | sz res_reg = 0; | 211 | sz res_reg = 0; |
212 | bool is_nil = false; | ||
185 | switch (res.type) { | 213 | switch (res.type) { |
186 | case COMP_CONST: { | 214 | case COMP_CONST: { |
187 | res_reg = chunk.reg_idx++; | 215 | res_reg = chunk.reg_idx++; |
@@ -192,11 +220,12 @@ process_file(Str path) { | |||
192 | case COMP_REG: { | 220 | case COMP_REG: { |
193 | res_reg = res.idx; | 221 | res_reg = res.idx; |
194 | } break; | 222 | } break; |
223 | case COMP_NIL: { | ||
224 | is_nil = true; | ||
225 | } break; | ||
195 | default: break; | 226 | default: break; |
196 | } | 227 | } |
197 | // After we are done move the last result to r0 for printing. | 228 | EMIT_OP(OP_HALT, res_reg, !is_nil, 0, NULL, &chunk); |
198 | Instruction halt = (Instruction){.op = OP_HALT, .dst = res_reg}; | ||
199 | array_push(chunk.code, halt, &bytecode_arena); | ||
200 | 229 | ||
201 | if (chunk.const_idx >= 256) { | 230 | if (chunk.const_idx >= 256) { |
202 | eprintln("too many constants on chunk %s", chunk.id); | 231 | eprintln("too many constants on chunk %s", chunk.id); |
@@ -219,10 +248,10 @@ process_file(Str path) { | |||
219 | // println("VM REGISTERS BEFORE:\n%{Mem}", | 248 | // println("VM REGISTERS BEFORE:\n%{Mem}", |
220 | // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); | 249 | // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); |
221 | vm_run(&vm); | 250 | vm_run(&vm); |
222 | // println("VM REGISTERS AFTER:\n%{Mem}", | 251 | #if DEBUG == 1 |
223 | // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); | 252 | println("MEMORY:\n%{Mem}", |
224 | // println("VM MEMORY AFTER:\n%{Mem}", | 253 | &(Array){.mem = (u8 *)&vm.stack, sizeof(vm.stack)}); |
225 | // &(Array){.mem = (u8 *)&vm.stack, sizeof(vm.stack)}); | 254 | #endif |
226 | 255 | ||
227 | #if DEBUG == 1 | 256 | #if DEBUG == 1 |
228 | println("Space used: %{Arena}", &lexer_arena); | 257 | println("Space used: %{Arena}", &lexer_arena); |
diff --git a/src/parser.c b/src/parser.c index 90adaf3..ace7160 100644 --- a/src/parser.c +++ b/src/parser.c | |||
@@ -132,6 +132,7 @@ typedef struct Node { | |||
132 | sz id; | 132 | sz id; |
133 | sz line; | 133 | sz line; |
134 | sz col; | 134 | sz col; |
135 | struct Node *parent; | ||
135 | 136 | ||
136 | NodeKind kind; | 137 | NodeKind kind; |
137 | union { | 138 | union { |
@@ -193,7 +194,6 @@ typedef struct Node { | |||
193 | }; | 194 | }; |
194 | }; | 195 | }; |
195 | bool is_ptr; | 196 | bool is_ptr; |
196 | struct Scope *scope; | ||
197 | Str type; | 197 | Str type; |
198 | Str fun_params; | 198 | Str fun_params; |
199 | Str fun_return; | 199 | Str fun_return; |
diff --git a/src/semantic.c b/src/semantic.c index d782cac..386d143 100644 --- a/src/semantic.c +++ b/src/semantic.c | |||
@@ -390,11 +390,12 @@ typecheck_returns(Analyzer *a, Node *node, Str expected) { | |||
390 | } | 390 | } |
391 | } break; | 391 | } break; |
392 | case NODE_RETURN: { | 392 | case NODE_RETURN: { |
393 | bool err = !str_eq(node->type, expected); | 393 | Str type = str_remove_prefix(node->type, cstr("ret:")); |
394 | bool err = !str_eq(type, expected); | ||
394 | if (err) { | 395 | if (err) { |
395 | eprintln( | 396 | eprintln( |
396 | "%s:%d:%d: error: mismatched return type %s, expected %s", | 397 | "%s:%d:%d: error: mismatched return type %s, expected %s", |
397 | a->file_name, node->line, node->col, node->type, expected); | 398 | a->file_name, node->line, node->col, type, expected); |
398 | a->err = true; | 399 | a->err = true; |
399 | } | 400 | } |
400 | } break; | 401 | } break; |
@@ -452,13 +453,14 @@ Str | |||
452 | type_inference(Analyzer *a, Node *node, Scope *scope) { | 453 | type_inference(Analyzer *a, Node *node, Scope *scope) { |
453 | assert(a); | 454 | assert(a); |
454 | assert(scope); | 455 | assert(scope); |
455 | if (!node) { | 456 | if (!node || a->err) { |
456 | return cstr(""); | 457 | return cstr(""); |
457 | } | 458 | } |
458 | // NOTE: For now we are not going to do implicit numeric conversions. | 459 | // NOTE: For now we are not going to do implicit numeric conversions. |
459 | switch (node->kind) { | 460 | switch (node->kind) { |
460 | case NODE_LET: { | 461 | case NODE_LET: { |
461 | node->type = cstr("nil"); | 462 | node->type = cstr("nil"); |
463 | node->var_name->parent = node; | ||
462 | Str symbol = node->var_name->value.str; | 464 | Str symbol = node->var_name->value.str; |
463 | if (symmap_lookup(&scope->symbols, symbol)) { | 465 | if (symmap_lookup(&scope->symbols, symbol)) { |
464 | eprintln( | 466 | eprintln( |
@@ -470,6 +472,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
470 | return cstr(""); | 472 | return cstr(""); |
471 | } | 473 | } |
472 | if (node->var_type) { | 474 | if (node->var_type) { |
475 | node->var_type->parent = node; | ||
473 | Str type_name = node->var_type->value.str; | 476 | Str type_name = node->var_type->value.str; |
474 | SymbolMap *type = find_type(scope, type_name); | 477 | SymbolMap *type = find_type(scope, type_name); |
475 | if (type == NULL) { | 478 | if (type == NULL) { |
@@ -484,10 +487,17 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
484 | } | 487 | } |
485 | if (node->var_type->kind == NODE_ARR_TYPE) { | 488 | if (node->var_type->kind == NODE_ARR_TYPE) { |
486 | type_name = str_concat(cstr("@"), type_name, a->storage); | 489 | type_name = str_concat(cstr("@"), type_name, a->storage); |
487 | // TODO: typecheck size | 490 | if (node->var_type->arr_size->value.i == 0) { |
491 | eprintln("%s:%d:%d: error: zero sized array '%s'", | ||
492 | a->file_name, node->var_type->line, | ||
493 | node->var_type->col, symbol); | ||
494 | a->err = true; | ||
495 | return cstr(""); | ||
496 | } | ||
488 | // TODO: register array in scope | 497 | // TODO: register array in scope |
489 | } | 498 | } |
490 | if (node->var_val) { | 499 | if (node->var_val) { |
500 | node->var_val->parent = node; | ||
491 | Str type = type_inference(a, node->var_val, scope); | 501 | Str type = type_inference(a, node->var_val, scope); |
492 | if (!type.size) { | 502 | if (!type.size) { |
493 | eprintln( | 503 | eprintln( |
@@ -521,17 +531,29 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
521 | .kind = SYM_VAR, | 531 | .kind = SYM_VAR, |
522 | }, | 532 | }, |
523 | a->storage); | 533 | a->storage); |
534 | node->var_name->type = type_name; | ||
535 | symbol = str_concat(cstr("."), symbol, a->storage); | ||
536 | symbol = str_concat(symbol, str_from_int(scope->id, a->storage), | ||
537 | a->storage); | ||
538 | node->unique_name = symbol; | ||
524 | return node->type; | 539 | return node->type; |
525 | } | 540 | } |
526 | 541 | ||
527 | // We don't know the type for this symbol, perform inference. | 542 | // We don't know the type for this symbol, perform inference. |
543 | node->var_val->parent = node; | ||
528 | Str type = type_inference(a, node->var_val, scope); | 544 | Str type = type_inference(a, node->var_val, scope); |
529 | if (type.size) { | 545 | if (!type.size || str_eq(type, cstr("nil")) || |
530 | symmap_insert(&scope->symbols, symbol, | 546 | str_has_prefix(type, cstr("ret:"))) { |
531 | (Symbol){.name = type, .kind = SYM_VAR}, | 547 | eprintln( |
532 | a->storage); | 548 | "%s:%d:%d: error: can't bind `nil` to variable " |
533 | node->var_name->type = type; | 549 | "'%s'", |
550 | a->file_name, node->line, node->col, symbol); | ||
551 | a->err = true; | ||
552 | return cstr(""); | ||
534 | } | 553 | } |
554 | symmap_insert(&scope->symbols, symbol, | ||
555 | (Symbol){.name = type, .kind = SYM_VAR}, a->storage); | ||
556 | node->var_name->type = type; | ||
535 | symbol = str_concat(cstr("."), symbol, a->storage); | 557 | symbol = str_concat(cstr("."), symbol, a->storage); |
536 | symbol = str_concat(symbol, str_from_int(scope->id, a->storage), | 558 | symbol = str_concat(symbol, str_from_int(scope->id, a->storage), |
537 | a->storage); | 559 | a->storage); |
@@ -539,6 +561,8 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
539 | return node->type; | 561 | return node->type; |
540 | } break; | 562 | } break; |
541 | case NODE_SET: { | 563 | case NODE_SET: { |
564 | node->var_name->parent = node; | ||
565 | node->var_val->parent = node; | ||
542 | Str name = type_inference(a, node->var_name, scope); | 566 | Str name = type_inference(a, node->var_name, scope); |
543 | Str val = type_inference(a, node->var_val, scope); | 567 | Str val = type_inference(a, node->var_val, scope); |
544 | if (!str_eq(name, val)) { | 568 | if (!str_eq(name, val)) { |
@@ -550,6 +574,12 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
550 | a->err = true; | 574 | a->err = true; |
551 | return cstr(""); | 575 | return cstr(""); |
552 | } | 576 | } |
577 | Str symbol = node->var_name->value.str; | ||
578 | FindSymbolResult sym = find_symbol(scope, symbol); | ||
579 | node->unique_name = str_concat(cstr("."), symbol, a->storage); | ||
580 | node->unique_name = | ||
581 | str_concat(node->unique_name, | ||
582 | str_from_int(sym.scope->id, a->storage), a->storage); | ||
553 | node->type = cstr("nil"); | 583 | node->type = cstr("nil"); |
554 | return node->type; | 584 | return node->type; |
555 | } break; | 585 | } break; |
@@ -568,6 +598,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
568 | a->storage); | 598 | a->storage); |
569 | for (sz i = 0; i < array_size(node->struct_field); i++) { | 599 | for (sz i = 0; i < array_size(node->struct_field); i++) { |
570 | Node *field = node->struct_field[i]; | 600 | Node *field = node->struct_field[i]; |
601 | field->parent = node; | ||
571 | typecheck_field(a, field, scope, symbol); | 602 | typecheck_field(a, field, scope, symbol); |
572 | } | 603 | } |
573 | symmap_insert(&scope->symbols, symbol, | 604 | symmap_insert(&scope->symbols, symbol, |
@@ -594,6 +625,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
594 | a->storage); | 625 | a->storage); |
595 | for (sz i = 0; i < array_size(node->struct_field); i++) { | 626 | for (sz i = 0; i < array_size(node->struct_field); i++) { |
596 | Node *field = node->struct_field[i]; | 627 | Node *field = node->struct_field[i]; |
628 | field->parent = node; | ||
597 | Str field_name = str_concat(symbol, cstr("."), a->storage); | 629 | Str field_name = str_concat(symbol, cstr("."), a->storage); |
598 | field_name = | 630 | field_name = |
599 | str_concat(field_name, field->value.str, a->storage); | 631 | str_concat(field_name, field->value.str, a->storage); |
@@ -626,6 +658,8 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
626 | return node->type; | 658 | return node->type; |
627 | } break; | 659 | } break; |
628 | case NODE_IF: { | 660 | case NODE_IF: { |
661 | node->cond_if->parent = node; | ||
662 | node->cond_expr->parent = node; | ||
629 | Str cond_type = type_inference(a, node->cond_if, scope); | 663 | Str cond_type = type_inference(a, node->cond_if, scope); |
630 | if (!str_eq(cond_type, cstr("bool"))) { | 664 | if (!str_eq(cond_type, cstr("bool"))) { |
631 | emit_semantic_error( | 665 | emit_semantic_error( |
@@ -640,6 +674,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
640 | node->type = type_inference(a, node->cond_expr, next); | 674 | node->type = type_inference(a, node->cond_expr, next); |
641 | } | 675 | } |
642 | if (node->cond_else) { | 676 | if (node->cond_else) { |
677 | node->cond_else->parent = node; | ||
643 | Str else_type; | 678 | Str else_type; |
644 | if (node->cond_else->kind == NODE_BLOCK) { | 679 | if (node->cond_else->kind == NODE_BLOCK) { |
645 | else_type = type_inference(a, node->cond_else, scope); | 680 | else_type = type_inference(a, node->cond_else, scope); |
@@ -647,19 +682,34 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
647 | Scope *next = typescope_alloc(a, scope); | 682 | Scope *next = typescope_alloc(a, scope); |
648 | else_type = type_inference(a, node->cond_else, next); | 683 | else_type = type_inference(a, node->cond_else, next); |
649 | } | 684 | } |
650 | if (!str_eq(node->type, else_type)) { | 685 | if (!str_eq(node->type, else_type) && |
686 | !str_has_prefix(node->type, cstr("ret:")) && | ||
687 | !str_has_prefix(node->type, cstr("flow:"))) { | ||
651 | emit_semantic_error( | 688 | emit_semantic_error( |
652 | a, node, cstr("mismatch types for if/else branches")); | 689 | a, node, cstr("mismatch types for if/else branches")); |
653 | return cstr(""); | 690 | return cstr(""); |
654 | } | 691 | } |
655 | } | 692 | } |
693 | // If it returns a value, verify it contains an `else` statement. | ||
694 | if (!str_eq(node->type, cstr("nil")) && | ||
695 | !str_has_prefix(node->type, cstr("ret:")) && | ||
696 | !str_has_prefix(node->type, cstr("flow:"))) { | ||
697 | if (!node->cond_else) { | ||
698 | emit_semantic_error( | ||
699 | a, node, | ||
700 | cstr("missing else statement in if expression")); | ||
701 | return cstr(""); | ||
702 | } | ||
703 | } | ||
656 | return node->type; | 704 | return node->type; |
657 | } break; | 705 | } break; |
658 | case NODE_WHILE: { | 706 | case NODE_WHILE: { |
707 | node->while_cond->parent = node; | ||
708 | node->while_expr->parent = node; | ||
659 | Str cond_type = type_inference(a, node->while_cond, scope); | 709 | Str cond_type = type_inference(a, node->while_cond, scope); |
660 | if (!str_eq(cond_type, cstr("bool"))) { | 710 | if (!str_eq(cond_type, cstr("bool"))) { |
661 | emit_semantic_error( | 711 | emit_semantic_error( |
662 | a, node->cond_if, | 712 | a, node->while_cond, |
663 | cstr("non boolean expression on while condition")); | 713 | cstr("non boolean expression on while condition")); |
664 | return cstr(""); | 714 | return cstr(""); |
665 | } | 715 | } |
@@ -672,8 +722,10 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
672 | } break; | 722 | } break; |
673 | case NODE_COND: { | 723 | case NODE_COND: { |
674 | Str previous = cstr(""); | 724 | Str previous = cstr(""); |
725 | bool has_else = false; | ||
675 | for (sz i = 0; i < array_size(node->match_cases); i++) { | 726 | for (sz i = 0; i < array_size(node->match_cases); i++) { |
676 | Node *expr = node->match_cases[i]; | 727 | Node *expr = node->match_cases[i]; |
728 | expr->parent = node; | ||
677 | Str next = type_inference(a, expr, scope); | 729 | Str next = type_inference(a, expr, scope); |
678 | if (i != 0 && !str_eq(next, previous)) { | 730 | if (i != 0 && !str_eq(next, previous)) { |
679 | emit_semantic_error( | 731 | emit_semantic_error( |
@@ -681,17 +733,33 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
681 | cstr("non-matching types for cond expressions")); | 733 | cstr("non-matching types for cond expressions")); |
682 | return cstr(""); | 734 | return cstr(""); |
683 | } | 735 | } |
736 | if (!expr->case_value) { | ||
737 | has_else = true; | ||
738 | } | ||
684 | previous = next; | 739 | previous = next; |
685 | } | 740 | } |
741 | // If it returns a value, verify it contains an `else` statement. | ||
742 | if (!str_eq(node->type, cstr("nil")) && | ||
743 | !str_has_prefix(node->type, cstr("ret:")) && | ||
744 | !str_has_prefix(node->type, cstr("flow:"))) { | ||
745 | if (!has_else) { | ||
746 | emit_semantic_error( | ||
747 | a, node, | ||
748 | cstr("missing else statement in cond expression")); | ||
749 | return cstr(""); | ||
750 | } | ||
751 | } | ||
686 | node->type = previous; | 752 | node->type = previous; |
687 | return node->type; | 753 | return node->type; |
688 | } break; | 754 | } break; |
689 | case NODE_MATCH: { | 755 | case NODE_MATCH: { |
756 | node->match_expr->parent = node; | ||
690 | Str e = type_inference(a, node->match_expr, scope); | 757 | Str e = type_inference(a, node->match_expr, scope); |
691 | if (str_eq(e, cstr("int"))) { | 758 | if (str_eq(e, cstr("int"))) { |
692 | // Integer matching. | 759 | // Integer matching. |
693 | for (sz i = 0; i < array_size(node->match_cases); i++) { | 760 | for (sz i = 0; i < array_size(node->match_cases); i++) { |
694 | Node *field = node->match_cases[i]; | 761 | Node *field = node->match_cases[i]; |
762 | field->parent = node; | ||
695 | if (field->case_value) { | 763 | if (field->case_value) { |
696 | if (field->case_value->kind != NODE_NUM_INT && | 764 | if (field->case_value->kind != NODE_NUM_INT && |
697 | field->case_value->kind != NODE_NUM_UINT) { | 765 | field->case_value->kind != NODE_NUM_UINT) { |
@@ -709,6 +777,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
709 | str_concat(res.map->val.name, cstr("."), a->storage); | 777 | str_concat(res.map->val.name, cstr("."), a->storage); |
710 | for (sz i = 0; i < array_size(node->match_cases); i++) { | 778 | for (sz i = 0; i < array_size(node->match_cases); i++) { |
711 | Node *field = node->match_cases[i]; | 779 | Node *field = node->match_cases[i]; |
780 | field->parent = node; | ||
712 | if (field->case_value) { | 781 | if (field->case_value) { |
713 | Str field_name = str_concat( | 782 | Str field_name = str_concat( |
714 | enum_prefix, field->case_value->value.str, | 783 | enum_prefix, field->case_value->value.str, |
@@ -725,6 +794,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
725 | Str previous = cstr(""); | 794 | Str previous = cstr(""); |
726 | for (sz i = 0; i < array_size(node->match_cases); i++) { | 795 | for (sz i = 0; i < array_size(node->match_cases); i++) { |
727 | Node *expr = node->match_cases[i]; | 796 | Node *expr = node->match_cases[i]; |
797 | expr->parent = node; | ||
728 | Str next = type_inference(a, expr, scope); | 798 | Str next = type_inference(a, expr, scope); |
729 | if (i != 0 && !str_eq(next, previous)) { | 799 | if (i != 0 && !str_eq(next, previous)) { |
730 | emit_semantic_error( | 800 | emit_semantic_error( |
@@ -741,11 +811,14 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
741 | if (node->case_expr->kind != NODE_BLOCK) { | 811 | if (node->case_expr->kind != NODE_BLOCK) { |
742 | scope = typescope_alloc(a, scope); | 812 | scope = typescope_alloc(a, scope); |
743 | } | 813 | } |
814 | node->case_expr->parent = node; | ||
744 | node->type = type_inference(a, node->case_expr, scope); | 815 | node->type = type_inference(a, node->case_expr, scope); |
745 | return node->type; | 816 | return node->type; |
746 | } break; | 817 | } break; |
747 | case NODE_CASE_COND: { | 818 | case NODE_CASE_COND: { |
819 | node->case_expr->parent = node; | ||
748 | if (node->case_value) { | 820 | if (node->case_value) { |
821 | node->case_value->parent = node; | ||
749 | Str cond = type_inference(a, node->case_value, scope); | 822 | Str cond = type_inference(a, node->case_value, scope); |
750 | if (!str_eq(cond, cstr("bool"))) { | 823 | if (!str_eq(cond, cstr("bool"))) { |
751 | emit_semantic_error(a, node, | 824 | emit_semantic_error(a, node, |
@@ -770,6 +843,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
770 | case NODE_NOT: | 843 | case NODE_NOT: |
771 | case NODE_AND: | 844 | case NODE_AND: |
772 | case NODE_OR: { | 845 | case NODE_OR: { |
846 | node->left->parent = node; | ||
773 | Str left = type_inference(a, node->left, scope); | 847 | Str left = type_inference(a, node->left, scope); |
774 | if (!str_eq(left, cstr("bool"))) { | 848 | if (!str_eq(left, cstr("bool"))) { |
775 | emit_semantic_error(a, node, | 849 | emit_semantic_error(a, node, |
@@ -777,6 +851,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
777 | return cstr(""); | 851 | return cstr(""); |
778 | } | 852 | } |
779 | if (node->right) { | 853 | if (node->right) { |
854 | node->right->parent = node; | ||
780 | Str right = type_inference(a, node->right, scope); | 855 | Str right = type_inference(a, node->right, scope); |
781 | if (!str_eq(right, cstr("bool"))) { | 856 | if (!str_eq(right, cstr("bool"))) { |
782 | emit_semantic_error( | 857 | emit_semantic_error( |
@@ -793,6 +868,8 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
793 | case NODE_GT: | 868 | case NODE_GT: |
794 | case NODE_LE: | 869 | case NODE_LE: |
795 | case NODE_GE: { | 870 | case NODE_GE: { |
871 | node->left->parent = node; | ||
872 | node->right->parent = node; | ||
796 | Str left = type_inference(a, node->left, scope); | 873 | Str left = type_inference(a, node->left, scope); |
797 | Str right = type_inference(a, node->right, scope); | 874 | Str right = type_inference(a, node->right, scope); |
798 | if (!str_eq(left, right)) { | 875 | if (!str_eq(left, right)) { |
@@ -804,6 +881,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
804 | return node->type; | 881 | return node->type; |
805 | } break; | 882 | } break; |
806 | case NODE_BITNOT: { | 883 | case NODE_BITNOT: { |
884 | node->left->parent = node; | ||
807 | Str left = type_inference(a, node->left, scope); | 885 | Str left = type_inference(a, node->left, scope); |
808 | if (!strset_lookup(&a->integer_types, left)) { | 886 | if (!strset_lookup(&a->integer_types, left)) { |
809 | emit_semantic_error( | 887 | emit_semantic_error( |
@@ -817,6 +895,8 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
817 | case NODE_BITOR: | 895 | case NODE_BITOR: |
818 | case NODE_BITLSHIFT: | 896 | case NODE_BITLSHIFT: |
819 | case NODE_BITRSHIFT: { | 897 | case NODE_BITRSHIFT: { |
898 | node->left->parent = node; | ||
899 | node->right->parent = node; | ||
820 | Str left = type_inference(a, node->left, scope); | 900 | Str left = type_inference(a, node->left, scope); |
821 | Str right = type_inference(a, node->right, scope); | 901 | Str right = type_inference(a, node->right, scope); |
822 | if (!strset_lookup(&a->integer_types, left) || | 902 | if (!strset_lookup(&a->integer_types, left) || |
@@ -833,6 +913,8 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
833 | case NODE_DIV: | 913 | case NODE_DIV: |
834 | case NODE_MUL: | 914 | case NODE_MUL: |
835 | case NODE_MOD: { | 915 | case NODE_MOD: { |
916 | node->left->parent = node; | ||
917 | node->right->parent = node; | ||
836 | Str left = type_inference(a, node->left, scope); | 918 | Str left = type_inference(a, node->left, scope); |
837 | Str right = type_inference(a, node->right, scope); | 919 | Str right = type_inference(a, node->right, scope); |
838 | if (!strset_lookup(&a->numeric_types, left) || | 920 | if (!strset_lookup(&a->numeric_types, left) || |
@@ -850,7 +932,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
850 | return node->type; | 932 | return node->type; |
851 | } break; | 933 | } break; |
852 | case NODE_NUM_UINT: { | 934 | case NODE_NUM_UINT: { |
853 | node->type = cstr("uint"); | 935 | node->type = cstr("int"); |
854 | return node->type; | 936 | return node->type; |
855 | } break; | 937 | } break; |
856 | case NODE_NUM_INT: { | 938 | case NODE_NUM_INT: { |
@@ -894,6 +976,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
894 | 976 | ||
895 | Str type_name = type->val.name; | 977 | Str type_name = type->val.name; |
896 | if (node->kind == NODE_SYMBOL_IDX) { | 978 | if (node->kind == NODE_SYMBOL_IDX) { |
979 | node->arr_size->parent = node; | ||
897 | Str idx_type = type_inference(a, node->arr_size, scope); | 980 | Str idx_type = type_inference(a, node->arr_size, scope); |
898 | if (!strset_lookup(&a->integer_types, idx_type)) { | 981 | if (!strset_lookup(&a->integer_types, idx_type)) { |
899 | emit_semantic_error( | 982 | emit_semantic_error( |
@@ -964,6 +1047,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
964 | } | 1047 | } |
965 | Str field_type = field->val.type; | 1048 | Str field_type = field->val.type; |
966 | if (next->kind == NODE_SYMBOL_IDX) { | 1049 | if (next->kind == NODE_SYMBOL_IDX) { |
1050 | node->arr_size->parent = node; | ||
967 | Str idx_type = | 1051 | Str idx_type = |
968 | type_inference(a, next->arr_size, scope); | 1052 | type_inference(a, next->arr_size, scope); |
969 | if (!strset_lookup(&a->integer_types, idx_type)) { | 1053 | if (!strset_lookup(&a->integer_types, idx_type)) { |
@@ -996,6 +1080,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
996 | StrSet *set = NULL; | 1080 | StrSet *set = NULL; |
997 | for (sz i = 0; i < array_size(node->elements); i++) { | 1081 | for (sz i = 0; i < array_size(node->elements); i++) { |
998 | Node *next = node->elements[i]; | 1082 | Node *next = node->elements[i]; |
1083 | next->parent = node; | ||
999 | Str field_name = str_concat(name, cstr("."), a->storage); | 1084 | Str field_name = str_concat(name, cstr("."), a->storage); |
1000 | field_name = | 1085 | field_name = |
1001 | str_concat(field_name, next->value.str, a->storage); | 1086 | str_concat(field_name, next->value.str, a->storage); |
@@ -1025,10 +1110,17 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1025 | a->err = true; | 1110 | a->err = true; |
1026 | return cstr(""); | 1111 | return cstr(""); |
1027 | } | 1112 | } |
1113 | FindSymbolResult sym = find_symbol(scope, symbol); | ||
1114 | node->unique_name = str_concat(cstr("."), symbol, a->storage); | ||
1115 | node->unique_name = | ||
1116 | str_concat(node->unique_name, | ||
1117 | str_from_int(sym.scope->id, a->storage), a->storage); | ||
1118 | |||
1028 | // Check that actual parameters typecheck | 1119 | // Check that actual parameters typecheck |
1029 | Str args = cstr(""); | 1120 | Str args = cstr(""); |
1030 | for (sz i = 0; i < array_size(node->elements); i++) { | 1121 | for (sz i = 0; i < array_size(node->elements); i++) { |
1031 | Node *expr = node->elements[i]; | 1122 | Node *expr = node->elements[i]; |
1123 | expr->parent = node; | ||
1032 | Str type = type_inference(a, expr, scope); | 1124 | Str type = type_inference(a, expr, scope); |
1033 | args = str_concat(args, type, a->storage); | 1125 | args = str_concat(args, type, a->storage); |
1034 | if (i != array_size(node->elements) - 1) { | 1126 | if (i != array_size(node->elements) - 1) { |
@@ -1054,15 +1146,27 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1054 | Str type; | 1146 | Str type; |
1055 | for (sz i = 0; i < array_size(node->elements); i++) { | 1147 | for (sz i = 0; i < array_size(node->elements); i++) { |
1056 | Node *expr = node->elements[i]; | 1148 | Node *expr = node->elements[i]; |
1149 | expr->parent = node; | ||
1057 | type = type_inference(a, expr, scope); | 1150 | type = type_inference(a, expr, scope); |
1151 | if (str_has_prefix(type, cstr("ret:")) || | ||
1152 | str_has_prefix(type, cstr("flow:"))) { | ||
1153 | break; | ||
1154 | } | ||
1058 | } | 1155 | } |
1059 | node->type = type; | 1156 | node->type = type; |
1060 | return node->type; | 1157 | return node->type; |
1061 | } break; | 1158 | } break; |
1062 | case NODE_RETURN: { | 1159 | case NODE_RETURN: { |
1063 | Str ret_type = cstr(""); | 1160 | if (!scope->name.size) { |
1161 | emit_semantic_error( | ||
1162 | a, node, cstr("return statement outside a function")); | ||
1163 | a->err = true; | ||
1164 | return cstr(""); | ||
1165 | } | ||
1166 | Str ret_type = cstr("ret:"); | ||
1064 | for (sz i = 0; i < array_size(node->elements); i++) { | 1167 | for (sz i = 0; i < array_size(node->elements); i++) { |
1065 | Node *expr = node->elements[i]; | 1168 | Node *expr = node->elements[i]; |
1169 | expr->parent = node; | ||
1066 | Str type = type_inference(a, expr, scope); | 1170 | Str type = type_inference(a, expr, scope); |
1067 | ret_type = str_concat(ret_type, type, a->storage); | 1171 | ret_type = str_concat(ret_type, type, a->storage); |
1068 | if (i != array_size(node->elements) - 1) { | 1172 | if (i != array_size(node->elements) - 1) { |
@@ -1075,6 +1179,28 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1075 | node->type = ret_type; | 1179 | node->type = ret_type; |
1076 | return node->type; | 1180 | return node->type; |
1077 | } break; | 1181 | } break; |
1182 | case NODE_CONTINUE: | ||
1183 | case NODE_BREAK: { | ||
1184 | // Check if we are inside a loop. | ||
1185 | Node *parent = node->parent; | ||
1186 | bool inside_loop = false; | ||
1187 | while (parent != NULL) { | ||
1188 | if (parent->kind == NODE_WHILE) { | ||
1189 | inside_loop = true; | ||
1190 | break; | ||
1191 | } | ||
1192 | parent = parent->parent; | ||
1193 | } | ||
1194 | if (!inside_loop) { | ||
1195 | eprintln( | ||
1196 | "%s:%d:%d: error: control flow statement outside a loop", | ||
1197 | a->file_name, node->line, node->col); | ||
1198 | a->err = true; | ||
1199 | return cstr(""); | ||
1200 | } | ||
1201 | node->type = cstr("flow:"); | ||
1202 | return node->type; | ||
1203 | } break; | ||
1078 | case NODE_FUN: { | 1204 | case NODE_FUN: { |
1079 | node->type = cstr("nil"); | 1205 | node->type = cstr("nil"); |
1080 | Scope *prev_scope = scope; | 1206 | Scope *prev_scope = scope; |
@@ -1082,6 +1208,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1082 | Str param_type = cstr(""); | 1208 | Str param_type = cstr(""); |
1083 | for (sz i = 0; i < array_size(node->func_params); i++) { | 1209 | for (sz i = 0; i < array_size(node->func_params); i++) { |
1084 | Node *param = node->func_params[i]; | 1210 | Node *param = node->func_params[i]; |
1211 | param->parent = node; | ||
1085 | Str symbol = param->param_name->value.str; | 1212 | Str symbol = param->param_name->value.str; |
1086 | Str type = param->param_type->value.str; | 1213 | Str type = param->param_type->value.str; |
1087 | if (param->param_type->is_ptr) { | 1214 | if (param->param_type->is_ptr) { |
@@ -1100,6 +1227,10 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1100 | if (i != array_size(node->func_params) - 1) { | 1227 | if (i != array_size(node->func_params) - 1) { |
1101 | param_type = str_concat(param_type, cstr(","), a->storage); | 1228 | param_type = str_concat(param_type, cstr(","), a->storage); |
1102 | } | 1229 | } |
1230 | symbol = str_concat(cstr("."), symbol, a->storage); | ||
1231 | symbol = str_concat(symbol, str_from_int(scope->id, a->storage), | ||
1232 | a->storage); | ||
1233 | param->unique_name = symbol; | ||
1103 | } | 1234 | } |
1104 | if (!param_type.size) { | 1235 | if (!param_type.size) { |
1105 | param_type = cstr("nil"); | 1236 | param_type = cstr("nil"); |
@@ -1109,6 +1240,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1109 | Str ret_type = cstr(""); | 1240 | Str ret_type = cstr(""); |
1110 | for (sz i = 0; i < array_size(node->func_ret); i++) { | 1241 | for (sz i = 0; i < array_size(node->func_ret); i++) { |
1111 | Node *expr = node->func_ret[i]; | 1242 | Node *expr = node->func_ret[i]; |
1243 | expr->parent = node; | ||
1112 | Str type = type_inference(a, expr, scope); | 1244 | Str type = type_inference(a, expr, scope); |
1113 | if (expr->is_ptr) { | 1245 | if (expr->is_ptr) { |
1114 | type = str_concat(cstr("@"), type, a->storage); | 1246 | type = str_concat(cstr("@"), type, a->storage); |
@@ -1148,11 +1280,17 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1148 | .param_type = param_type, | 1280 | .param_type = param_type, |
1149 | .return_type = ret_type}, | 1281 | .return_type = ret_type}, |
1150 | a->storage); | 1282 | a->storage); |
1283 | symbol = str_concat(cstr("."), symbol, a->storage); | ||
1284 | symbol = str_concat( | ||
1285 | symbol, str_from_int(prev_scope->id, a->storage), a->storage); | ||
1286 | node->unique_name = symbol; | ||
1151 | 1287 | ||
1152 | if (node->func_body->kind == NODE_BLOCK) { | 1288 | if (node->func_body->kind == NODE_BLOCK) { |
1153 | Str type; | 1289 | Str type; |
1154 | for (sz i = 0; i < array_size(node->func_body->elements); i++) { | 1290 | for (sz i = 0; i < array_size(node->func_body->elements); i++) { |
1155 | Node *expr = node->func_body->elements[i]; | 1291 | Node *expr = node->func_body->elements[i]; |
1292 | expr->parent = node; | ||
1293 | // TODO: block early return. | ||
1156 | type = type_inference(a, expr, scope); | 1294 | type = type_inference(a, expr, scope); |
1157 | } | 1295 | } |
1158 | if (!type.size) { | 1296 | if (!type.size) { |
@@ -1160,15 +1298,17 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1160 | } | 1298 | } |
1161 | node->func_body->type = type; | 1299 | node->func_body->type = type; |
1162 | } else { | 1300 | } else { |
1301 | node->func_body->parent = node; | ||
1163 | type_inference(a, node->func_body, scope); | 1302 | type_inference(a, node->func_body, scope); |
1164 | } | 1303 | } |
1165 | 1304 | ||
1166 | // Ensure main body return matches the prototype. | 1305 | // Ensure main body return matches the prototype. |
1167 | if (!str_eq(node->func_body->type, ret_type)) { | 1306 | Str type = str_remove_prefix(node->func_body->type, cstr("ret:")); |
1307 | node->func_body->type = type; | ||
1308 | if (!str_eq(type, ret_type)) { | ||
1168 | eprintln( | 1309 | eprintln( |
1169 | "%s:%d:%d: error: mismatched return type %s, expected %s", | 1310 | "%s:%d:%d: error: mismatched return type %s, expected %s", |
1170 | a->file_name, node->line, node->col, node->func_body->type, | 1311 | a->file_name, node->line, node->col, type, ret_type); |
1171 | ret_type); | ||
1172 | a->err = true; | 1312 | a->err = true; |
1173 | } | 1313 | } |
1174 | 1314 | ||
@@ -1179,10 +1319,11 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1179 | return node->type; | 1319 | return node->type; |
1180 | } break; | 1320 | } break; |
1181 | default: { | 1321 | default: { |
1182 | emit_semantic_error(a, node, | 1322 | eprintln( |
1183 | cstr("type inference not implemented for this " | 1323 | "%s:%d:%d: error: type inference not implemented for node " |
1184 | "kind of expression")); | 1324 | "type: %s", |
1185 | println("KIND: %s", node_str[node->kind]); | 1325 | a->file_name, node->line, node->col, node_str[node->kind]); |
1326 | a->err = true; | ||
1186 | } break; | 1327 | } break; |
1187 | } | 1328 | } |
1188 | return cstr(""); | 1329 | return cstr(""); |
@@ -7,10 +7,13 @@ | |||
7 | #define N_CONST 256 | 7 | #define N_CONST 256 |
8 | #define STACK_SIZE KB(64) | 8 | #define STACK_SIZE KB(64) |
9 | typedef struct VM { | 9 | typedef struct VM { |
10 | Chunk *main; | ||
10 | Chunk *chunk; | 11 | Chunk *chunk; |
11 | Constant regs[N_CONST]; | ||
12 | u8 stack[STACK_SIZE]; | 12 | u8 stack[STACK_SIZE]; |
13 | Instruction *ip; | 13 | Instruction *ip; |
14 | u8 *sp; | ||
15 | u64 *fp; | ||
16 | Constant *regs; | ||
14 | } VM; | 17 | } VM; |
15 | 18 | ||
16 | void | 19 | void |
@@ -18,8 +21,13 @@ vm_init(VM *vm, Chunk *chunk) { | |||
18 | assert(vm); | 21 | assert(vm); |
19 | assert(chunk); | 22 | assert(chunk); |
20 | assert(chunk->code); | 23 | assert(chunk->code); |
24 | vm->main = chunk; | ||
21 | vm->chunk = chunk; | 25 | vm->chunk = chunk; |
22 | vm->ip = vm->chunk->code; | 26 | vm->ip = vm->chunk->code; |
27 | vm->fp = (u64 *)vm->stack; | ||
28 | vm->sp = vm->stack + chunk->var_off; | ||
29 | vm->regs = (Constant *)vm->sp; | ||
30 | vm->sp += sizeof(Constant) * chunk->reg_idx; | ||
23 | } | 31 | } |
24 | 32 | ||
25 | #define OP_UNARY(OP, TYPE) \ | 33 | #define OP_UNARY(OP, TYPE) \ |
@@ -134,61 +142,115 @@ vm_run(VM *vm) { | |||
134 | vm->regs[dst].f = | 142 | vm->regs[dst].f = |
135 | fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f); | 143 | fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f); |
136 | } break; | 144 | } break; |
145 | case OP_STGVAR: { | ||
146 | u8 dst = instruction.dst; | ||
147 | u8 src = instruction.a; | ||
148 | Variable var = vm->chunk->vars[dst]; | ||
149 | s64 *stack = (s64 *)&vm->stack[var.offset]; | ||
150 | *stack = vm->regs[src].i; | ||
151 | } break; | ||
152 | case OP_STGVARI: { | ||
153 | u8 dst = instruction.dst; | ||
154 | u8 src = instruction.a; | ||
155 | Variable var = vm->main->vars[dst]; | ||
156 | s64 *stack = (s64 *)&vm->stack[var.offset]; | ||
157 | *stack = vm->chunk->constants[src].i; | ||
158 | } break; | ||
137 | case OP_LDGVAR: { | 159 | case OP_LDGVAR: { |
138 | u8 dst = instruction.dst; | 160 | u8 dst = instruction.dst; |
139 | u8 src = instruction.a; | 161 | u8 src = instruction.a; |
140 | Variable var = vm->chunk->vars[src]; | 162 | Variable var = vm->main->vars[src]; |
141 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 163 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
142 | vm->regs[dst].i = *stack; | 164 | vm->regs[dst].i = *stack; |
143 | } break; | 165 | } break; |
144 | case OP_STGVAR: { | 166 | case OP_LDGADDR: { |
145 | u8 dst = instruction.dst; | 167 | u8 dst = instruction.dst; |
146 | u8 src = instruction.a; | 168 | u8 src = instruction.a; |
147 | Variable var = vm->chunk->vars[dst]; | 169 | Variable var = vm->main->vars[src]; |
148 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 170 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
149 | *stack = vm->regs[src].i; | 171 | vm->regs[dst].ptr = (ptrsize)stack; |
150 | } break; | 172 | } break; |
151 | case OP_STGVARI: { | 173 | case OP_STLVAR: { |
152 | u8 dst = instruction.dst; | 174 | u8 dst = instruction.dst; |
153 | u8 src = instruction.a; | 175 | u8 src = instruction.a; |
154 | Variable var = vm->chunk->vars[dst]; | 176 | Variable var = vm->chunk->vars[dst]; |
155 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 177 | vm->fp[var.offset / 8] = vm->regs[src].i; |
156 | *stack = vm->chunk->constants[src].i; | ||
157 | } break; | 178 | } break; |
158 | case OP_JMPI: { | 179 | case OP_STLVARI: { |
159 | sz offset = vm->chunk->constants[instruction.dst].i; | 180 | u8 dst = instruction.dst; |
160 | vm->ip += offset - 1; | 181 | u8 src = instruction.a; |
182 | Variable var = vm->chunk->vars[dst]; | ||
183 | vm->fp[var.offset / 8] = vm->chunk->constants[src].i; | ||
184 | } break; | ||
185 | case OP_LDLVAR: { | ||
186 | u8 dst = instruction.dst; | ||
187 | u8 src = instruction.a; | ||
188 | Variable var = vm->chunk->vars[src]; | ||
189 | vm->regs[dst].i = vm->fp[var.offset / 8]; | ||
190 | } break; | ||
191 | case OP_LDLADDR: { | ||
192 | u8 dst = instruction.dst; | ||
193 | u8 src = instruction.a; | ||
194 | Variable var = vm->chunk->vars[src]; | ||
195 | vm->regs[dst].i = (ptrsize)&vm->fp[var.offset / 8]; | ||
196 | } break; | ||
197 | case OP_ST64I: { | ||
198 | sz value = vm->regs[instruction.dst].i; | ||
199 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
200 | sz offset = vm->chunk->constants[instruction.b].i; | ||
201 | addr[offset] = value; | ||
202 | } break; | ||
203 | case OP_ST64: { | ||
204 | sz value = vm->regs[instruction.dst].i; | ||
205 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
206 | sz offset = vm->regs[instruction.b].i; | ||
207 | addr[offset] = value; | ||
208 | } break; | ||
209 | case OP_LD64I: { | ||
210 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
211 | sz offset = vm->chunk->constants[instruction.b].i; | ||
212 | vm->regs[instruction.dst].i = addr[offset]; | ||
213 | } break; | ||
214 | case OP_LD64: { | ||
215 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
216 | sz offset = vm->regs[instruction.b].i; | ||
217 | vm->regs[instruction.dst].i = addr[offset]; | ||
218 | } break; | ||
219 | case OP_JMP: { | ||
220 | u8 dst = instruction.dst; | ||
221 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
222 | vm->ip = vm->chunk->code + pos; | ||
161 | } break; | 223 | } break; |
162 | case OP_JMPFI: { | 224 | case OP_JMPFI: { |
163 | bool cond = vm->chunk->constants[instruction.dst].i; | 225 | u8 dst = instruction.dst; |
164 | sz offset = vm->chunk->constants[instruction.a].i; | 226 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; |
227 | bool cond = vm->chunk->constants[instruction.a].i; | ||
165 | if (!cond) { | 228 | if (!cond) { |
166 | vm->ip += offset - 1; | 229 | vm->ip = vm->chunk->code + pos; |
167 | } | 230 | } |
168 | } break; | 231 | } break; |
169 | case OP_JMPTI: { | 232 | case OP_JMPTI: { |
170 | bool cond = vm->chunk->constants[instruction.dst].i; | 233 | u8 dst = instruction.dst; |
171 | sz offset = vm->chunk->constants[instruction.a].i; | 234 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; |
235 | bool cond = vm->chunk->constants[instruction.a].i; | ||
172 | if (cond) { | 236 | if (cond) { |
173 | vm->ip += offset - 1; | 237 | vm->ip = vm->chunk->code + pos; |
174 | } | 238 | } |
175 | } break; | 239 | } break; |
176 | case OP_JMP: { | ||
177 | sz offset = vm->chunk->constants[instruction.dst].i; | ||
178 | vm->ip += offset - 1; | ||
179 | } break; | ||
180 | case OP_JMPF: { | 240 | case OP_JMPF: { |
181 | bool cond = vm->regs[instruction.dst].i; | 241 | u8 dst = instruction.dst; |
182 | sz offset = vm->chunk->constants[instruction.a].i; | 242 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; |
243 | bool cond = vm->regs[instruction.a].i; | ||
183 | if (!cond) { | 244 | if (!cond) { |
184 | vm->ip += offset - 1; | 245 | vm->ip = vm->chunk->code + pos; |
185 | } | 246 | } |
186 | } break; | 247 | } break; |
187 | case OP_JMPT: { | 248 | case OP_JMPT: { |
188 | bool cond = vm->regs[instruction.dst].i; | 249 | u8 dst = instruction.dst; |
189 | sz offset = vm->chunk->constants[instruction.a].i; | 250 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; |
251 | bool cond = vm->regs[instruction.a].i; | ||
190 | if (cond) { | 252 | if (cond) { |
191 | vm->ip += offset - 1; | 253 | vm->ip = vm->chunk->code + pos; |
192 | } | 254 | } |
193 | } break; | 255 | } break; |
194 | case OP_MOV64: { | 256 | case OP_MOV64: { |
@@ -211,10 +273,113 @@ vm_run(VM *vm) { | |||
211 | u8 src = instruction.a; | 273 | u8 src = instruction.a; |
212 | vm->regs[dst].i = vm->regs[src].i & 0xFF; | 274 | vm->regs[dst].i = vm->regs[src].i & 0xFF; |
213 | } break; | 275 | } break; |
276 | case OP_PRINTS64: { | ||
277 | u8 idx = instruction.dst; | ||
278 | print("%d", vm->regs[idx].i); | ||
279 | } break; | ||
280 | case OP_PRINTS64I: { | ||
281 | u8 idx = instruction.dst; | ||
282 | print("%d", vm->chunk->constants[idx].i); | ||
283 | } break; | ||
284 | case OP_PRINTF64: { | ||
285 | u8 idx = instruction.dst; | ||
286 | printf("%f", vm->regs[idx].f); | ||
287 | } break; | ||
288 | case OP_PRINTF64I: { | ||
289 | u8 idx = instruction.dst; | ||
290 | printf("%f", vm->chunk->constants[idx].f); | ||
291 | } break; | ||
292 | case OP_PRINTSTR: { | ||
293 | u8 idx = instruction.dst; | ||
294 | Str string = vm->chunk->strings[idx]; | ||
295 | print("%s", string); | ||
296 | } break; | ||
297 | case OP_RESERVE: { | ||
298 | sz offset = vm->chunk->constants[instruction.dst].i; | ||
299 | vm->sp += offset; | ||
300 | } break; | ||
301 | case OP_PUSH: { | ||
302 | sz val = vm->regs[instruction.dst].i; | ||
303 | u64 *p = (u64 *)vm->sp; | ||
304 | *p = val; | ||
305 | vm->sp += sizeof(ptrsize); | ||
306 | } break; | ||
307 | case OP_PUSHI: { | ||
308 | sz val = vm->chunk->constants[instruction.dst].i; | ||
309 | u64 *p = (u64 *)vm->sp; | ||
310 | *p = val; | ||
311 | vm->sp += sizeof(ptrsize); | ||
312 | } break; | ||
313 | case OP_POP: { | ||
314 | vm->sp -= sizeof(ptrsize); | ||
315 | u64 *p = (u64 *)vm->sp; | ||
316 | vm->regs[instruction.dst].i = *p; | ||
317 | } break; | ||
318 | case OP_PUTRET: { | ||
319 | sz val = vm->regs[instruction.dst].i; | ||
320 | vm->fp[-1] = val; | ||
321 | } break; | ||
322 | case OP_PUTRETI: { | ||
323 | sz val = vm->chunk->constants[instruction.dst].i; | ||
324 | vm->fp[-1] = val; | ||
325 | } break; | ||
326 | case OP_CALL: { | ||
327 | u8 dst = instruction.dst; | ||
328 | Chunk *func = vm->chunk->functions[dst]; | ||
329 | |||
330 | ptrsize chunk_addr = (ptrsize)vm->chunk; | ||
331 | ptrsize ip_addr = (ptrsize)vm->ip; | ||
332 | ptrsize reg_addr = (ptrsize)vm->regs; | ||
333 | ptrsize old_fp = (ptrsize)vm->fp; | ||
334 | |||
335 | // Allocate space for the locals. | ||
336 | vm->fp = (u64 *)(vm->sp - func->param_off); | ||
337 | vm->sp += func->var_off - func->param_off; | ||
338 | vm->chunk = func; | ||
339 | vm->ip = func->code; | ||
340 | vm->regs = (Constant *)vm->sp; | ||
341 | |||
342 | // Allocate registers. | ||
343 | vm->sp += sizeof(Constant) * func->reg_idx; | ||
344 | |||
345 | // Store memory addresses we need to return to this function. | ||
346 | u64 *p = (u64 *)vm->sp; | ||
347 | p[0] = chunk_addr; | ||
348 | p[1] = ip_addr; | ||
349 | p[2] = reg_addr; | ||
350 | p[3] = old_fp; | ||
351 | vm->sp += sizeof(ptrsize) * 4; | ||
352 | } break; | ||
353 | case OP_RET: { | ||
354 | u64 *p = (u64 *)vm->sp; | ||
355 | ptrsize chunk_addr = p[-4]; | ||
356 | ptrsize ip_addr = p[-3]; | ||
357 | ptrsize reg_addr = p[-2]; | ||
358 | ptrsize old_fp = p[-1]; | ||
359 | vm->sp -= sizeof(ptrsize) * 4; | ||
360 | |||
361 | // Deallocate registers. | ||
362 | vm->sp -= sizeof(Constant) * vm->chunk->reg_idx; | ||
363 | |||
364 | // Deallocate locals. | ||
365 | vm->sp -= vm->chunk->var_off; | ||
366 | |||
367 | // Restore previous activation record. | ||
368 | vm->regs = (Constant *)reg_addr; | ||
369 | vm->ip = (Instruction *)ip_addr; | ||
370 | vm->chunk = (Chunk *)chunk_addr; | ||
371 | vm->fp = (u64 *)old_fp; | ||
372 | } break; | ||
214 | case OP_HALT: { | 373 | case OP_HALT: { |
215 | println("VM HALT (int) -> %d", vm->regs[instruction.dst]); | 374 | println("VM halt..."); |
216 | println("VM HALT (float) -> %f", vm->regs[instruction.dst]); | 375 | if (instruction.a != 0) { |
217 | println("VM HALT (hex) -> %x", vm->regs[instruction.dst]); | 376 | println("Result:"); |
377 | Constant result = vm->regs[instruction.dst]; | ||
378 | printf("\tint -> %lld\n", result.i); | ||
379 | printf("\tfloat -> %.10e\n", result.f); | ||
380 | printf("\thex -> %llx\n", (u64)result.i); | ||
381 | println("\tbinary -> %b", result.i); | ||
382 | } | ||
218 | return; | 383 | return; |
219 | } | 384 | } |
220 | default: { | 385 | default: { |
diff --git a/tests/compilation.bad b/tests/compilation.bad index e0682a7..334c6ac 100644 --- a/tests/compilation.bad +++ b/tests/compilation.bad | |||
@@ -1,25 +1,23 @@ | |||
1 | ; let a = 8 | 1 | ; while true { |
2 | ; let b = 16 | 2 | ; if true { |
3 | ; let c = { | 3 | ; continue |
4 | ; let a = 32 | 4 | ; } |
5 | ; a + 8 | 5 | ; println("ding") |
6 | ; break | ||
7 | ; ; if true continue | ||
8 | ; ; break | ||
9 | ; ; println("dong") | ||
10 | ; } | ||
11 | ; println("hello") | ||
12 | ; fun max(a: int b: int): int { | ||
13 | ; if a > b return(a) else return(b) | ||
6 | ; } | 14 | ; } |
7 | 15 | ||
8 | ; a + b + c | 16 | ; println(max(1 2)) |
17 | ; println(max(4 3)) | ||
9 | 18 | ||
10 | ; if true { | 19 | ; fun adder(a: int b: int): int a + b |
11 | ; 1 + 2 | ||
12 | ; } | ||
13 | 20 | ||
14 | if false { | 21 | ; let fpnt = adder |
15 | 1 + 2 | ||
16 | } else { | ||
17 | 3 + 4 | ||
18 | } | ||
19 | 22 | ||
20 | ; let a = 0 | 23 | ; println("adder: " adder(1, 2)) |
21 | ; if 1 != 1 { | ||
22 | ; set a = 1 | ||
23 | ; } else { | ||
24 | ; set a = 2 | ||
25 | ; } | ||
diff --git a/tests/conditionals.bad b/tests/conditionals.bad index aca5c36..85de62a 100644 --- a/tests/conditionals.bad +++ b/tests/conditionals.bad | |||
@@ -1,8 +1,8 @@ | |||
1 | ; Basic if expressions. | 1 | ; Basic if expressions. |
2 | if true "hello" | 2 | if true "hello" else "world" |
3 | 3 | ||
4 | ; These can produce values and the result bound to a name. | 4 | ; These can produce values and the result bound to a name. |
5 | let a = if (2 + 2 >= 4) 42 | 5 | let a = if (2 + 2 >= 4) 42 else 0 |
6 | 6 | ||
7 | ; We support a single if expression. | 7 | ; We support a single if expression. |
8 | let b = if (0xff == 0x32) "hello" else "world" | 8 | let b = if (0xff == 0x32) "hello" else "world" |
diff --git a/tests/semantics.bad b/tests/semantics.bad index acf4316..befbfab 100644 --- a/tests/semantics.bad +++ b/tests/semantics.bad | |||
@@ -42,6 +42,7 @@ match a { | |||
42 | cond { | 42 | cond { |
43 | 1 == 1 = "ha" | 43 | 1 == 1 = "ha" |
44 | 2 != 2 = "ho" | 44 | 2 != 2 = "ho" |
45 | else = "let's go" | ||
45 | } | 46 | } |
46 | 47 | ||
47 | struct vec { | 48 | struct vec { |
@@ -160,9 +161,7 @@ let maybe = if (1 == 2) { | |||
160 | 44 | 161 | 44 |
161 | } | 162 | } |
162 | 163 | ||
163 | let single = if (true) { | 164 | let single = if (true) 123 else 0 |
164 | 123 | ||
165 | } | ||
166 | 165 | ||
167 | while (!true) { | 166 | while (!true) { |
168 | println("asjdflasdjf") | 167 | println("asjdflasdjf") |
@@ -185,5 +184,5 @@ fun nested(): int { | |||
185 | let c = 32 | 184 | let c = 32 |
186 | 5 + c | 185 | 5 + c |
187 | } | 186 | } |
188 | } | 187 | } else 0 |
189 | } | 188 | } |