diff options
-rw-r--r-- | Makefile | 4 | ||||
-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 | 39 | ||||
-rw-r--r-- | bench/rule110.py | 29 | ||||
-rw-r--r-- | src/badlib.h | 28 | ||||
-rw-r--r-- | src/compiler.c | 828 | ||||
-rw-r--r-- | src/main.c | 35 | ||||
-rw-r--r-- | src/semantic.c | 45 | ||||
-rw-r--r-- | src/vm.c | 213 | ||||
-rw-r--r-- | tests/compilation.bad | 121 | ||||
-rw-r--r-- | tests/conditionals.bad | 4 | ||||
-rw-r--r-- | tests/semantics.bad | 7 |
14 files changed, 1306 insertions, 141 deletions
@@ -26,9 +26,9 @@ BIN := $(BUILD_DIR)/$(TARGET) | |||
26 | 26 | ||
27 | # Compiler and linker configuration. | 27 | # Compiler and linker configuration. |
28 | CC := cc | 28 | CC := cc |
29 | CFLAGS := -Wall -Wextra -pedantic -std=c11 -DBIN_NAME=\"$(TARGET)\" | 29 | CFLAGS := -Wall -Wextra -std=c11 -DBIN_NAME=\"$(TARGET)\" |
30 | CFLAGS += $(INC_FLAGS) | 30 | CFLAGS += $(INC_FLAGS) |
31 | LDFLAGS := | 31 | LDFLAGS := -lm |
32 | LDLIBS := | 32 | LDLIBS := |
33 | RELEASE_CFLAGS := -DNDEBUG -O2 | 33 | RELEASE_CFLAGS := -DNDEBUG -O2 |
34 | DEBUG_CFLAGS := -DDEBUG -g -fsanitize=address,undefined | 34 | DEBUG_CFLAGS := -DDEBUG -g -fsanitize=address,undefined |
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..3ca0697 --- /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: int[64] | ||
4 | ; let new_board: int[64] | ||
5 | ; let stride = 8 | ||
6 | |||
7 | ; This board is 32 * 32 = 1024 cells. | ||
8 | let n_cells = 1024 | ||
9 | let board: int[1024] | ||
10 | let new_board: int[1024] | ||
11 | let stride = 32 | ||
12 | |||
13 | ; Initialize glider | ||
14 | set board[stride * 2 + 5] = 1 | ||
15 | set board[stride * 3 + 6] = 1 | ||
16 | set board[stride * 4 + 4] = 1 | ||
17 | set board[stride * 4 + 5] = 1 | ||
18 | set board[stride * 4 + 6] = 1 | ||
19 | |||
20 | let n_iter = 1000 | ||
21 | while n_iter > 0 { | ||
22 | set n_iter = n_iter - 1 | ||
23 | ; Print the board. | ||
24 | { | ||
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 | ; Update the board. | ||
38 | { | ||
39 | let i = 0 | ||
40 | while i < n_cells { | ||
41 | let left = if i > 0 board[i - 1] else 0 | ||
42 | let right = if i < n_cells - 1 board[i + 1] else 0 | ||
43 | let top = if i >= stride board[i - stride] else 0 | ||
44 | let topleft = if i >= stride board[i - stride - 1] else 0 | ||
45 | let topright = if i >= stride board[i - stride + 1] else 0 | ||
46 | let bot = if i <= n_cells - stride board[i + stride] else 0 | ||
47 | let botleft = if i <= n_cells - stride board[i + stride - 1] else 0 | ||
48 | let botright = if i <= n_cells - stride board[i + stride + 1] else 0 | ||
49 | |||
50 | let neig = left | ||
51 | + right | ||
52 | + top | ||
53 | + bot | ||
54 | + topleft | ||
55 | + topright | ||
56 | + botleft | ||
57 | + botright | ||
58 | |||
59 | cond { | ||
60 | board[i] == 0 && neig == 3 = set new_board[i] = 1 | ||
61 | board[i] == 1 && (neig == 2 || neig == 3) = set new_board[i] = 1 | ||
62 | else = set new_board[i] = 0 | ||
63 | } | ||
64 | |||
65 | set i = i + 1 | ||
66 | } | ||
67 | |||
68 | ; Copy the new board. | ||
69 | { | ||
70 | let i = 0 | ||
71 | while i < n_cells { | ||
72 | set board[i] = new_board[i] | ||
73 | set i = i + 1 | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | } | ||
diff --git a/bench/rule110.bad b/bench/rule110.bad new file mode 100644 index 0000000..14aa00e --- /dev/null +++ b/bench/rule110.bad | |||
@@ -0,0 +1,39 @@ | |||
1 | ; Parameters. | ||
2 | let line = 0b00000000000000000000000000000001 | ||
3 | let max_iter = 30000 | ||
4 | |||
5 | let iter = 0 | ||
6 | while iter < max_iter { | ||
7 | ; Print current line. | ||
8 | let i = 0 | ||
9 | while i < 64 { | ||
10 | let val = line >> (63 - i) & 0b1 | ||
11 | if val == 0b1 { | ||
12 | print("▀ ") | ||
13 | } else { | ||
14 | print(" ") | ||
15 | } | ||
16 | set i = i + 1 | ||
17 | } | ||
18 | println("") | ||
19 | |||
20 | ; Update next line | ||
21 | let next = 0 | ||
22 | let j = 0 | ||
23 | while j < 61 { | ||
24 | let val = line >> 60 - j & 0b111 | ||
25 | set val = cond { | ||
26 | val == 1 = 1 | ||
27 | val == 2 = 1 | ||
28 | val == 3 = 1 | ||
29 | val == 5 = 1 | ||
30 | val == 6 = 1 | ||
31 | else = 0 | ||
32 | } | ||
33 | set next = next | val << 61 - j | ||
34 | set j = j + 1 | ||
35 | } | ||
36 | set line = next | 1 | ||
37 | |||
38 | set iter = iter + 1 | ||
39 | } | ||
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 25e4914..c73b57f 100644 --- a/src/badlib.h +++ b/src/badlib.h | |||
@@ -20,7 +20,9 @@ | |||
20 | #include <stdint.h> | 20 | #include <stdint.h> |
21 | #include <stdio.h> | 21 | #include <stdio.h> |
22 | #include <stdlib.h> | 22 | #include <stdlib.h> |
23 | #include <strings.h> | 23 | #include <string.h> |
24 | #include <time.h> | ||
25 | #include <unistd.h> | ||
24 | 26 | ||
25 | // | 27 | // |
26 | // Basic types. | 28 | // Basic types. |
@@ -43,7 +45,7 @@ typedef ptrdiff_t sz; | |||
43 | typedef uintptr_t ptrsize; | 45 | typedef uintptr_t ptrsize; |
44 | typedef size_t usize; | 46 | typedef size_t usize; |
45 | 47 | ||
46 | #define KB(N) ((sz)(N)*1024) | 48 | #define KB(N) ((sz)(N) * 1024) |
47 | #define MB(N) ((sz)KB(N) * 1024) | 49 | #define MB(N) ((sz)KB(N) * 1024) |
48 | #define GB(N) ((sz)MB(N) * 1024) | 50 | #define GB(N) ((sz)MB(N) * 1024) |
49 | #define TB(N) ((sz)GB(N) * 1024) | 51 | #define TB(N) ((sz)GB(N) * 1024) |
@@ -287,9 +289,9 @@ buf_pop(Buf *buf, void *dst, sz size) { | |||
287 | typedef Array Str; | 289 | typedef Array Str; |
288 | 290 | ||
289 | // Create a string object from a C literal. | 291 | // Create a string object from a C literal. |
290 | #define cstr(s) \ | 292 | #define cstr(s) \ |
291 | (Str) { \ | 293 | (Str) { \ |
292 | (u8 *)(s), LEN(s) - 1 \ | 294 | .mem = (u8 *)(s), .size = LEN(s) - 1, \ |
293 | } | 295 | } |
294 | 296 | ||
295 | // Create a string object from a char* array. | 297 | // Create a string object from a char* array. |
@@ -345,7 +347,9 @@ str_split_fn(Str *a, StrSplitFn split_fn) { | |||
345 | void | 347 | void |
346 | str_replace(Str *a, Str from, Str to) { | 348 | str_replace(Str *a, Str from, Str to) { |
347 | assert(a != NULL); | 349 | assert(a != NULL); |
348 | assert(from.size == to.size); | 350 | if (from.size != to.size) { |
351 | return; | ||
352 | } | ||
349 | SearchResult res = array_find_next(*a, from); | 353 | SearchResult res = array_find_next(*a, from); |
350 | if (res.found) { | 354 | if (res.found) { |
351 | memcpy(a->mem + res.pos, to.mem, res.matched); | 355 | memcpy(a->mem + res.pos, to.mem, res.matched); |
@@ -945,7 +949,7 @@ _int_eq(sz a, sz b) { | |||
945 | return a == b; | 949 | return a == b; |
946 | } | 950 | } |
947 | 951 | ||
948 | bool | 952 | u64 |
949 | _int_hash(sz a) { | 953 | _int_hash(sz a) { |
950 | return a * UINT64_C(11400714819323198485); | 954 | return a * UINT64_C(11400714819323198485); |
951 | } | 955 | } |
@@ -1235,14 +1239,6 @@ Allocator os_allocator = { | |||
1235 | .free = platform_free, | 1239 | .free = platform_free, |
1236 | }; | 1240 | }; |
1237 | 1241 | ||
1238 | #include <time.h> | ||
1239 | #include <unistd.h> | ||
1240 | |||
1241 | void | ||
1242 | platform_sleep(size_t microseconds) { | ||
1243 | usleep(microseconds); | ||
1244 | } | ||
1245 | |||
1246 | sz | 1242 | sz |
1247 | platform_time(void) { | 1243 | platform_time(void) { |
1248 | struct timespec ts; | 1244 | struct timespec ts; |
@@ -1504,7 +1500,7 @@ void | |||
1504 | log_func_memory(Logger *l, void *in) { | 1500 | log_func_memory(Logger *l, void *in) { |
1505 | assert(l); | 1501 | assert(l); |
1506 | Array *val = in; | 1502 | Array *val = in; |
1507 | for (sz i = 0; i < MIN(64, val->size); i++) { | 1503 | for (sz i = 0; i < MIN(256, val->size); i++) { |
1508 | Arena scratch = l->storage; | 1504 | Arena scratch = l->storage; |
1509 | log_str(l, str_from_hex(val->mem[i], 2, &scratch)); | 1505 | log_str(l, str_from_hex(val->mem[i], 2, &scratch)); |
1510 | if ((i + 1) % 16 == 0) { | 1506 | if ((i + 1) % 16 == 0) { |
diff --git a/src/compiler.c b/src/compiler.c index bd50955..98a5e7b 100644 --- a/src/compiler.c +++ b/src/compiler.c | |||
@@ -25,13 +25,33 @@ 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 | ||
32 | typedef struct LineCol { | ||
33 | sz line; | ||
34 | sz col; | ||
35 | } LineCol; | ||
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 | |||
32 | typedef struct Chunk { | 46 | typedef struct Chunk { |
33 | sz id; | 47 | sz id; |
48 | Str name; | ||
49 | struct Chunk *parent; | ||
50 | |||
34 | Instruction *code; | 51 | Instruction *code; |
52 | IntIntMap *labels; // label -> chunk_index | ||
53 | IntIntMap *labels_rev; // chunk_index -> label | ||
54 | sz labels_idx; | ||
35 | 55 | ||
36 | // Constant values that fit in 64 bits. | 56 | // Constant values that fit in 64 bits. |
37 | Constant *constants; | 57 | Constant *constants; |
@@ -51,41 +71,63 @@ typedef struct Chunk { | |||
51 | // Number of registers currently used in this chunk. | 71 | // Number of registers currently used in this chunk. |
52 | sz reg_idx; | 72 | sz reg_idx; |
53 | 73 | ||
74 | // Number of functions currently used in this chunk. | ||
75 | struct Chunk **functions; | ||
76 | FunctionMap *funmap; | ||
77 | sz fun_idx; | ||
78 | |||
54 | // Debugging. | 79 | // Debugging. |
55 | Str file_name; | 80 | Str file_name; |
56 | Arena *storage; | 81 | Arena *storage; |
57 | // TODO: line/col info for debugging. | 82 | LineCol *linecol; |
58 | } Chunk; | 83 | } Chunk; |
59 | 84 | ||
60 | typedef enum OpCode { | 85 | typedef enum OpCode { |
61 | // OP DST A B | 86 | // OP DST A B |
62 | // --------------------------------------------------------------- | 87 | // --------------------------------------------------------------- |
63 | // VM/high level instructions. | 88 | // VM/high level instructions. |
64 | OP_HALT, // halt | 89 | OP_HALT, // halt |
65 | OP_STVARI, // stvari vx, ca | 90 | // NOTE: LDGVAR/STGVAR* could be obtained in terms of LDGADDR. |
66 | OP_STVAR, // stvar vx, ra | 91 | OP_STGVARI, // stgvari vx, ca |
67 | OP_LDVAR, // ldvar rx, vx | 92 | OP_STGVAR, // stgvar vx, ra |
93 | OP_LDGVAR, // ldgvar rx, va | ||
94 | OP_LDGADDR, // ldgaddr rx, va | ||
95 | // Functions. | ||
96 | OP_CALL, // call fx ; Bumps the stack pointer by cx | ||
97 | OP_RET, // ret ; Returns from current function | ||
98 | OP_RESERVE, // reserve cx ; Increments the stack pointer by cx bytes | ||
99 | OP_POP, // pop rx ; Pops the last value of the stack into rx. | ||
100 | OP_PUSH, // push rx ; Push the rx value to the stack. | ||
101 | OP_PUSHI, // pushi cx ; Push the cx value to the stack. | ||
102 | OP_PUTRET, // putret rx ; Put rx into the return value memory. | ||
103 | OP_PUTRETI, // putreti cx ; Put cx into the return value memory. | ||
104 | // Printing values with builtin print/println functions. | ||
105 | OP_PRINTSTR, // p rx | ||
106 | OP_PRINTS64, // p rx | ||
107 | OP_PRINTF64, // p rx | ||
108 | OP_PRINTS64I, // p cx | ||
109 | OP_PRINTF64I, // p cx | ||
68 | // Load/Store instructions. | 110 | // Load/Store instructions. |
69 | OP_LD8K, // ld8k rx, ca -> u8 rx = ca | 111 | OP_LD8K, // ld8k rx, ca -> u8 rx = ca |
70 | OP_LD16K, // ld16k rx, ca -> u16 rx = ca | 112 | OP_LD16K, // ld16k rx, ca -> u16 rx = ca |
71 | OP_LD32K, // ld32k rx, ca -> u32 rx = ca | 113 | OP_LD32K, // ld32k rx, ca -> u32 rx = ca |
72 | OP_LD64K, // ld64k rx, ca -> u64 rx = ca | 114 | OP_LD64K, // ld64k rx, ca -> u64 rx = ca |
73 | OP_LD8I, // ld8i rx, ra, cb -> u8 *p; rx = p[ra + cb] | 115 | OP_LD8I, // ld8i rx, ra, cb -> u8 *p = ra; rx = p[cb] |
74 | OP_LD16I, // ld16i rx, ra, cb -> u16 *p; rx = p[ra + cb] | 116 | OP_LD16I, // ld16i rx, ra, cb -> u16 *p = ra; rx = p[cb] |
75 | OP_LD32I, // ld32i rx, ra, cb -> u32 *p; rx = p[ra + cb] | 117 | OP_LD32I, // ld32i rx, ra, cb -> u32 *p = ra; rx = p[cb] |
76 | OP_LD64I, // ld64i rx, ra, cb -> u64 *p; rx = p[ra + cb] | 118 | OP_LD64I, // ld64i rx, ra, cb -> u64 *p = ra; rx = p[cb] |
77 | OP_LD8, // ld8 rx, ra, rb -> u8 *p; rx = p[ra + rb] | 119 | OP_LD8, // ld8 rx, ra, rb -> u8 *p = ra; rx = p[rb] |
78 | OP_LD16, // ld16 rx, ra, rb -> u16 *p; rx = p[ra + rb] | 120 | OP_LD16, // ld16 rx, ra, rb -> u16 *p = ra; rx = p[rb] |
79 | OP_LD32, // ld32 rx, ra, rb -> u32 *p; rx = p[ra + rb] | 121 | OP_LD32, // ld32 rx, ra, rb -> u32 *p = ra; rx = p[rb] |
80 | OP_LD64, // ld64 rx, ra, rb -> u64 *p; rx = p[ra + rb] | 122 | OP_LD64, // ld64 rx, ra, rb -> u64 *p = ra; rx = p[rb] |
81 | OP_ST8I, // st8i rx, ra, cb -> u8 *p; p[ra + cb] = rx | 123 | OP_ST8I, // st8i rx, ra, cb -> u8 *p = ra; p[cb] = rx |
82 | OP_ST16I, // st16i rx, ra, cb -> u16 *p; p[ra + cb] = rx | 124 | OP_ST16I, // st16i rx, ra, cb -> u16 *p = ra; p[cb] = rx |
83 | OP_ST32I, // st32i rx, ra, cb -> u32 *p; p[ra + cb] = rx | 125 | OP_ST32I, // st32i rx, ra, cb -> u32 *p = ra; p[cb] = rx |
84 | OP_ST64I, // st64i rx, ra, cb -> u64 *p; p[ra + cb] = rx | 126 | OP_ST64I, // st64i rx, ra, cb -> u64 *p = ra; p[cb] = rx |
85 | OP_ST8, // st8 rx, ra, rb -> u8 *p; p[ra + rb] = rx | 127 | OP_ST8, // st8 rx, ra, rb -> u8 *p = ra; p[rb] = rx |
86 | OP_ST16, // st16 rx, ra, rb -> u16 *p; p[ra + rb] = rx | 128 | OP_ST16, // st16 rx, ra, rb -> u16 *p = ra; p[rb] = rx |
87 | OP_ST32, // st32 rx, ra, rb -> u32 *p; p[ra + rb] = rx | 129 | OP_ST32, // st32 rx, ra, rb -> u32 *p = ra; p[rb] = rx |
88 | OP_ST64, // st64 rx, ra, rb -> u64 *p; p[ra + rb] = rx | 130 | OP_ST64, // st64 rx, ra, rb -> u64 *p = ra; p[rb] = rx |
89 | // Integer arithmetic (only int/s64 for now). | 131 | // Integer arithmetic (only int/s64 for now). |
90 | OP_ADDI, // addk rx, ra, cb | 132 | OP_ADDI, // addk rx, ra, cb |
91 | OP_SUBI, // subk rx, ra, cb | 133 | OP_SUBI, // subk rx, ra, cb |
@@ -143,13 +185,35 @@ typedef enum OpCode { | |||
143 | OP_BITAND, // band rx, ra, rb | 185 | OP_BITAND, // band rx, ra, rb |
144 | OP_BITOR, // bor rx, ra, rb | 186 | OP_BITOR, // bor rx, ra, rb |
145 | OP_BITNOT, // bnot rx, ra | 187 | OP_BITNOT, // bnot rx, ra |
188 | // Jump instructions. | ||
189 | OP_JMP, // jmp lx ; jmp to label lx | ||
190 | OP_JMPF, // jmpf lx, rx ; jmp to label lx if rx is false | ||
191 | OP_JMPT, // jmpt lx, rx ; jmp to label lx if rx is true | ||
192 | OP_JMPFI, // jmpf lx, cx ; jmp to label lx if rx is false | ||
193 | OP_JMPTI, // jmpt lx, cx ; jmp to label lx if rx is true | ||
146 | } OpCode; | 194 | } OpCode; |
147 | 195 | ||
148 | Str op_str[] = { | 196 | Str op_str[] = { |
197 | // High level ops. | ||
149 | [OP_HALT] = cstr("HALT "), | 198 | [OP_HALT] = cstr("HALT "), |
150 | [OP_STVAR] = cstr("STVAR "), | 199 | [OP_STGVAR] = cstr("STGVAR "), |
151 | [OP_STVARI] = cstr("STVARI "), | 200 | [OP_STGVARI] = cstr("STGVARI "), |
152 | [OP_LDVAR] = cstr("LDVAR "), | 201 | [OP_LDGVAR] = cstr("LDGVAR "), |
202 | [OP_LDGADDR] = cstr("LDGADDR "), | ||
203 | [OP_PRINTSTR] = cstr("PRNTSTR "), | ||
204 | [OP_PRINTS64] = cstr("PRNTS64 "), | ||
205 | [OP_PRINTF64] = cstr("PRNTF64 "), | ||
206 | [OP_PRINTS64I] = cstr("PRNTS64I"), | ||
207 | [OP_PRINTF64I] = cstr("PRNTF64I"), | ||
208 | [OP_PUTRET] = cstr("PUTRET "), | ||
209 | [OP_PUTRETI] = cstr("PUTRETI "), | ||
210 | // Functions. | ||
211 | [OP_CALL] = cstr("CALL "), | ||
212 | [OP_RET] = cstr("RET "), | ||
213 | [OP_RESERVE] = cstr("RESERVE "), | ||
214 | [OP_POP] = cstr("POP "), | ||
215 | [OP_PUSH] = cstr("PUSH "), | ||
216 | [OP_PUSHI] = cstr("PUSHI "), | ||
153 | // Load ops. | 217 | // Load ops. |
154 | [OP_LD8K] = cstr("LD8K "), | 218 | [OP_LD8K] = cstr("LD8K "), |
155 | [OP_LD16K] = cstr("LD16K "), | 219 | [OP_LD16K] = cstr("LD16K "), |
@@ -228,6 +292,12 @@ Str op_str[] = { | |||
228 | [OP_BITAND] = cstr("BAND "), | 292 | [OP_BITAND] = cstr("BAND "), |
229 | [OP_BITOR] = cstr("BOR "), | 293 | [OP_BITOR] = cstr("BOR "), |
230 | [OP_BITNOT] = cstr("BNOT "), | 294 | [OP_BITNOT] = cstr("BNOT "), |
295 | // Jump instructions. | ||
296 | [OP_JMP] = cstr("JMP "), | ||
297 | [OP_JMPF] = cstr("JMPF "), | ||
298 | [OP_JMPT] = cstr("JMPT "), | ||
299 | [OP_JMPFI] = cstr("JMPFI "), | ||
300 | [OP_JMPTI] = cstr("JMPTI "), | ||
231 | }; | 301 | }; |
232 | 302 | ||
233 | typedef enum { | 303 | typedef enum { |
@@ -245,15 +315,21 @@ typedef struct CompResult { | |||
245 | 315 | ||
246 | CompResult compile_expr(Chunk *chunk, Node *node); | 316 | CompResult compile_expr(Chunk *chunk, Node *node); |
247 | 317 | ||
248 | #define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ | 318 | #define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ |
249 | do { \ | 319 | do { \ |
250 | Instruction inst = (Instruction){ \ | 320 | Instruction inst = (Instruction){ \ |
251 | .op = (OP), \ | 321 | .op = (OP), \ |
252 | .dst = (DST), \ | 322 | .dst = (DST), \ |
253 | .a = (A), \ | 323 | .a = (A), \ |
254 | .b = (B), \ | 324 | .b = (B), \ |
255 | }; \ | 325 | }; \ |
256 | array_push((CHUNK)->code, inst, (CHUNK)->storage); \ | 326 | array_push((CHUNK)->code, inst, (CHUNK)->storage); \ |
327 | LineCol linecol = (LineCol){0}; \ | ||
328 | if (NODE) { \ | ||
329 | Node *_node = (NODE); \ | ||
330 | linecol = (LineCol){.line = _node->line, .col = _node->col}; \ | ||
331 | } \ | ||
332 | array_push((CHUNK)->linecol, linecol, (CHUNK)->storage); \ | ||
257 | } while (0) | 333 | } while (0) |
258 | 334 | ||
259 | CompResult | 335 | CompResult |
@@ -427,9 +503,456 @@ compile_unary(Chunk *chunk, Node *node) { | |||
427 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 503 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
428 | } | 504 | } |
429 | 505 | ||
506 | sz | ||
507 | add_constant(Chunk *chunk, sz value) { | ||
508 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); | ||
509 | // Make sure we don't have duplicated constants. | ||
510 | if (!map) { | ||
511 | map = intintmap_insert(&chunk->intmap, value, chunk->const_idx++, | ||
512 | chunk->storage); | ||
513 | Constant c = (Constant){.i = value}; | ||
514 | array_push(chunk->constants, c, chunk->storage); | ||
515 | } | ||
516 | return map->val; | ||
517 | } | ||
518 | |||
519 | sz | ||
520 | add_string(Chunk *chunk, Str string) { | ||
521 | // Make sure we don't have duplicated string. | ||
522 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
523 | if (!map) { | ||
524 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
525 | chunk->storage); | ||
526 | array_push(chunk->strings, string, chunk->storage); | ||
527 | } | ||
528 | return map->val; | ||
529 | } | ||
530 | |||
531 | CompResult | ||
532 | compile_if(Chunk *chunk, Node *node) { | ||
533 | CompResult cond = compile_expr(chunk, node->cond_if); | ||
534 | OpCode jmpop; | ||
535 | switch (cond.type) { | ||
536 | case COMP_CONST: { | ||
537 | jmpop = OP_JMPFI; | ||
538 | } break; | ||
539 | case COMP_REG: { | ||
540 | jmpop = OP_JMPF; | ||
541 | } break; | ||
542 | default: { | ||
543 | return (CompResult){.type = COMP_ERR}; | ||
544 | } break; | ||
545 | } | ||
546 | |||
547 | if (!str_eq(node->type, cstr("nil"))) { | ||
548 | sz reg_dst = chunk->reg_idx++; | ||
549 | |||
550 | // Jump to the `false` branch. | ||
551 | sz lab0 = chunk->labels_idx++; | ||
552 | EMIT_OP(jmpop, lab0, cond.idx, 0, node->cond_if, chunk); | ||
553 | |||
554 | // Condition is true. | ||
555 | CompResult then_expr = compile_expr(chunk, node->cond_expr); | ||
556 | switch (then_expr.type) { | ||
557 | case COMP_CONST: { | ||
558 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, node->cond_if, | ||
559 | chunk); | ||
560 | } break; | ||
561 | case COMP_REG: { | ||
562 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, node->cond_if, | ||
563 | chunk); | ||
564 | } break; | ||
565 | default: { | ||
566 | return (CompResult){.type = COMP_ERR}; | ||
567 | } break; | ||
568 | } | ||
569 | |||
570 | // Jump to the end of the expression. | ||
571 | sz pos0 = array_size(chunk->code); | ||
572 | sz lab1 = chunk->labels_idx++; | ||
573 | EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk); | ||
574 | |||
575 | // Else expression. | ||
576 | CompResult else_expr = compile_expr(chunk, node->cond_else); | ||
577 | switch (else_expr.type) { | ||
578 | case COMP_CONST: { | ||
579 | EMIT_OP(OP_LD64K, reg_dst, else_expr.idx, 0, node->cond_else, | ||
580 | chunk); | ||
581 | } break; | ||
582 | case COMP_REG: { | ||
583 | EMIT_OP(OP_MOV64, reg_dst, else_expr.idx, 0, node->cond_else, | ||
584 | chunk); | ||
585 | } break; | ||
586 | default: { | ||
587 | return (CompResult){.type = COMP_ERR}; | ||
588 | } break; | ||
589 | } | ||
590 | sz pos1 = array_size(chunk->code); | ||
591 | |||
592 | // Update labels. | ||
593 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
594 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
595 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage); | ||
596 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
597 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
598 | } | ||
599 | |||
600 | // Jump to the `false` branch. | ||
601 | sz lab0 = chunk->labels_idx++; | ||
602 | EMIT_OP(jmpop, lab0, cond.idx, 0, node->cond_if, chunk); | ||
603 | |||
604 | // Condition is true. | ||
605 | compile_expr(chunk, node->cond_expr); | ||
606 | |||
607 | // Jump to the end of the expression. | ||
608 | sz pos0 = array_size(chunk->code); | ||
609 | sz lab1 = chunk->labels_idx++; | ||
610 | EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk); | ||
611 | |||
612 | // Else expression. | ||
613 | if (node->cond_else) { | ||
614 | compile_expr(chunk, node->cond_else); | ||
615 | } | ||
616 | sz pos1 = array_size(chunk->code); | ||
617 | |||
618 | // Update labels. | ||
619 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
620 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage); | ||
621 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
622 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
623 | |||
624 | return (CompResult){.type = COMP_NIL}; | ||
625 | } | ||
626 | |||
627 | CompResult | ||
628 | compile_cond(Chunk *chunk, Node *node) { | ||
629 | if (str_eq(node->type, cstr("nil"))) { | ||
630 | sz lab1 = chunk->labels_idx++; | ||
631 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
632 | // condition = expression | ||
633 | Node *expr = node->match_cases[i]; | ||
634 | if (expr->case_value) { | ||
635 | CompResult cond = compile_expr(chunk, expr->case_value); | ||
636 | OpCode jmpop; | ||
637 | switch (cond.type) { | ||
638 | case COMP_CONST: { | ||
639 | jmpop = OP_JMPFI; | ||
640 | } break; | ||
641 | case COMP_REG: { | ||
642 | jmpop = OP_JMPF; | ||
643 | } break; | ||
644 | default: { | ||
645 | return (CompResult){.type = COMP_ERR}; | ||
646 | } break; | ||
647 | } | ||
648 | // Jump to the `next` branch. | ||
649 | sz lab0 = chunk->labels_idx++; | ||
650 | EMIT_OP(jmpop, lab0, cond.idx, 0, expr->case_expr, chunk); | ||
651 | |||
652 | // Condition is true. | ||
653 | compile_expr(chunk, expr->case_expr); | ||
654 | if (i != array_size(node->match_cases) - 1) { | ||
655 | // Jump to the end of the expression. | ||
656 | sz pos0 = array_size(chunk->code); | ||
657 | EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk); | ||
658 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, | ||
659 | chunk->storage); | ||
660 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, | ||
661 | chunk->storage); | ||
662 | } | ||
663 | } else { | ||
664 | compile_expr(chunk, expr->case_expr); | ||
665 | break; | ||
666 | } | ||
667 | } | ||
668 | sz pos1 = array_size(chunk->code); | ||
669 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
670 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
671 | return (CompResult){.type = COMP_NIL}; | ||
672 | } | ||
673 | |||
674 | sz reg_dst = chunk->reg_idx++; | ||
675 | sz lab1 = chunk->labels_idx++; | ||
676 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
677 | // condition = expression | ||
678 | Node *expr = node->match_cases[i]; | ||
679 | if (expr->case_value) { | ||
680 | CompResult cond = compile_expr(chunk, expr->case_value); | ||
681 | OpCode jmpop; | ||
682 | switch (cond.type) { | ||
683 | case COMP_CONST: { | ||
684 | jmpop = OP_JMPFI; | ||
685 | } break; | ||
686 | case COMP_REG: { | ||
687 | jmpop = OP_JMPF; | ||
688 | } break; | ||
689 | default: { | ||
690 | return (CompResult){.type = COMP_ERR}; | ||
691 | } break; | ||
692 | } | ||
693 | // Jump to the `next` branch. | ||
694 | sz lab0 = chunk->labels_idx++; | ||
695 | EMIT_OP(jmpop, lab0, cond.idx, 0, expr->case_expr, chunk); | ||
696 | |||
697 | // Condition is true. | ||
698 | CompResult then_expr = compile_expr(chunk, expr->case_expr); | ||
699 | switch (then_expr.type) { | ||
700 | case COMP_CONST: { | ||
701 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, | ||
702 | expr->case_expr, chunk); | ||
703 | } break; | ||
704 | case COMP_REG: { | ||
705 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, | ||
706 | expr->case_expr, chunk); | ||
707 | } break; | ||
708 | default: { | ||
709 | return (CompResult){.type = COMP_ERR}; | ||
710 | } break; | ||
711 | } | ||
712 | if (i != array_size(node->match_cases) - 1) { | ||
713 | // Jump to the end of the expression. | ||
714 | sz pos0 = array_size(chunk->code); | ||
715 | EMIT_OP(OP_JMP, lab1, 0, 0, node->cond_else, chunk); | ||
716 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, | ||
717 | chunk->storage); | ||
718 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, | ||
719 | chunk->storage); | ||
720 | } | ||
721 | } else { | ||
722 | CompResult then_expr = compile_expr(chunk, expr->case_expr); | ||
723 | switch (then_expr.type) { | ||
724 | case COMP_CONST: { | ||
725 | EMIT_OP(OP_LD64K, reg_dst, then_expr.idx, 0, | ||
726 | expr->case_expr, chunk); | ||
727 | } break; | ||
728 | case COMP_REG: { | ||
729 | EMIT_OP(OP_MOV64, reg_dst, then_expr.idx, 0, | ||
730 | expr->case_expr, chunk); | ||
731 | } break; | ||
732 | default: { | ||
733 | return (CompResult){.type = COMP_ERR}; | ||
734 | } break; | ||
735 | } | ||
736 | break; | ||
737 | } | ||
738 | } | ||
739 | sz pos1 = array_size(chunk->code); | ||
740 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
741 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
742 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
743 | } | ||
744 | |||
745 | CompResult | ||
746 | compile_while(Chunk *chunk, Node *node) { | ||
747 | sz pos1 = array_size(chunk->code); | ||
748 | CompResult cond = compile_expr(chunk, node->while_cond); | ||
749 | OpCode jmpop; | ||
750 | switch (cond.type) { | ||
751 | case COMP_CONST: { | ||
752 | jmpop = OP_JMPFI; | ||
753 | } break; | ||
754 | case COMP_REG: { | ||
755 | jmpop = OP_JMPF; | ||
756 | } break; | ||
757 | default: { | ||
758 | return (CompResult){.type = COMP_ERR}; | ||
759 | } break; | ||
760 | } | ||
761 | |||
762 | // Jump to the `end of the loop` branch. | ||
763 | sz lab0 = chunk->labels_idx++; | ||
764 | EMIT_OP(jmpop, lab0, cond.idx, 0, node->while_cond, chunk); | ||
765 | |||
766 | // Condition is true. | ||
767 | compile_expr(chunk, node->while_expr); | ||
768 | sz pos0 = array_size(chunk->code); | ||
769 | sz lab1 = chunk->labels_idx++; | ||
770 | EMIT_OP(OP_JMP, lab1, 0, 0, node, chunk); | ||
771 | |||
772 | // Update labels. | ||
773 | intintmap_insert(&chunk->labels, lab0, pos0 + 1, chunk->storage); | ||
774 | intintmap_insert(&chunk->labels_rev, pos0 + 1, lab0, chunk->storage); | ||
775 | intintmap_insert(&chunk->labels, lab1, pos1, chunk->storage); | ||
776 | intintmap_insert(&chunk->labels_rev, pos1, lab1, chunk->storage); | ||
777 | |||
778 | // Return. | ||
779 | return (CompResult){.type = COMP_NIL}; | ||
780 | } | ||
781 | |||
782 | CompResult | ||
783 | compile_funcall(Chunk *chunk, Node *node) { | ||
784 | Str name = node->value.str; | ||
785 | |||
786 | // Builtins. | ||
787 | if (str_eq(name, cstr("print")) || str_eq(name, cstr("println"))) { | ||
788 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
789 | Node *expr = node->elements[i]; | ||
790 | CompResult result = compile_expr(chunk, expr); | ||
791 | if (str_eq(expr->type, cstr("int"))) { | ||
792 | switch (result.type) { | ||
793 | case COMP_CONST: { | ||
794 | EMIT_OP(OP_PRINTS64I, result.idx, 0, 0, expr, chunk); | ||
795 | } break; | ||
796 | case COMP_REG: { | ||
797 | EMIT_OP(OP_PRINTS64, result.idx, 0, 0, expr, chunk); | ||
798 | } break; | ||
799 | default: { | ||
800 | return (CompResult){.type = COMP_ERR}; | ||
801 | } break; | ||
802 | } | ||
803 | } else if (str_eq(expr->type, cstr("f64"))) { | ||
804 | switch (result.type) { | ||
805 | case COMP_CONST: { | ||
806 | EMIT_OP(OP_PRINTF64I, result.idx, 0, 0, expr, chunk); | ||
807 | } break; | ||
808 | case COMP_REG: { | ||
809 | EMIT_OP(OP_PRINTF64, result.idx, 0, 0, expr, chunk); | ||
810 | } break; | ||
811 | default: { | ||
812 | return (CompResult){.type = COMP_ERR}; | ||
813 | } break; | ||
814 | } | ||
815 | } else if (str_eq(expr->type, cstr("str"))) { | ||
816 | switch (result.type) { | ||
817 | case COMP_STRING: { | ||
818 | EMIT_OP(OP_PRINTSTR, result.idx, 0, 0, expr, chunk); | ||
819 | } break; | ||
820 | default: { | ||
821 | return (CompResult){.type = COMP_ERR}; | ||
822 | } break; | ||
823 | } | ||
824 | } | ||
825 | } | ||
826 | if (str_eq(name, cstr("println"))) { | ||
827 | sz idx = add_string(chunk, cstr("\n")); | ||
828 | EMIT_OP(OP_PRINTSTR, idx, 0, 0, node, chunk); | ||
829 | } | ||
830 | return (CompResult){.type = COMP_NIL}; | ||
831 | } | ||
832 | |||
833 | // TODO: need to find this on the parents, not just in the current chunk. | ||
834 | FunctionMap *map = funcmap_lookup(&chunk->funmap, node->unique_name); | ||
835 | if (!map) { | ||
836 | println("how come?"); | ||
837 | } | ||
838 | Function fun = map->val; | ||
839 | |||
840 | // Reserve space for the return value if needed. | ||
841 | if (fun.return_arity > 0) { | ||
842 | // Put the return data into a register | ||
843 | sz ret_size = add_constant(chunk, 8); | ||
844 | EMIT_OP(OP_RESERVE, ret_size, 0, 0, node, chunk); | ||
845 | } | ||
846 | |||
847 | // Send parameters to the stack. | ||
848 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
849 | Node *expr = node->elements[i]; | ||
850 | CompResult result = compile_expr(chunk, expr); | ||
851 | // TODO: Assuming all values are 8 bytes... again. | ||
852 | switch (result.type) { | ||
853 | case COMP_CONST: { | ||
854 | EMIT_OP(OP_PUSHI, result.idx, 0, 0, expr, chunk); | ||
855 | } break; | ||
856 | case COMP_REG: { | ||
857 | EMIT_OP(OP_PUSH, result.idx, 0, 0, expr, chunk); | ||
858 | } break; | ||
859 | default: { | ||
860 | return (CompResult){.type = COMP_ERR}; | ||
861 | } break; | ||
862 | } | ||
863 | } | ||
864 | |||
865 | // // TODO: prologue (how much space do we actually need)? we need the | ||
866 | // symbol | ||
867 | // // table, arity and return parameters boy. | ||
868 | // // FIXME: assuming all values are 8 bytes for now... | ||
869 | // sz alloc_size = (fun.param_arity + fun.return_arity) * 8; | ||
870 | // sz alloc_const = add_constant(chunk, alloc_size); | ||
871 | // if (alloc_size > 0) { | ||
872 | // // EMIT_OP(OP_RESERVE, alloc_const, 0, 0, node, chunk); | ||
873 | // } | ||
874 | |||
875 | // // println("FUNCALL: unique name: %s", node->unique_name); | ||
876 | // // println("FUN: param_arity: %d", fun.param_arity); | ||
877 | // // println("FUN: return_arity: %d", fun.return_arity); | ||
878 | EMIT_OP(OP_CALL, fun.index, 0, 0, node, chunk); | ||
879 | |||
880 | // Only one return parameter for now. | ||
881 | if (fun.return_arity > 0) { | ||
882 | // Put the return data into a register | ||
883 | sz reg_dst = chunk->reg_idx++; | ||
884 | EMIT_OP(OP_POP, reg_dst, 0, 0, node, chunk); | ||
885 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
886 | } | ||
887 | |||
888 | // if (alloc_size > 0) { | ||
889 | // // EMIT_OP(OP_POP, alloc_const, 0, 0, node, chunk); | ||
890 | // } | ||
891 | // TODO: epilogue (how much space do we actually need)? we need to remove | ||
892 | // the AR data here after using it. If we only have one return parameter we | ||
893 | // can put it in a register but what if that's not actually the case? | ||
894 | return (CompResult){.type = COMP_NIL}; | ||
895 | } | ||
896 | |||
897 | Chunk * | ||
898 | chunk_alloc(Chunk *parent) { | ||
899 | static sz chunk_idx = 1; | ||
900 | Chunk *chunk = arena_calloc((sz)sizeof(Chunk), parent->storage); | ||
901 | chunk->parent = parent; | ||
902 | chunk->id = chunk_idx++; | ||
903 | chunk->storage = parent->storage; | ||
904 | chunk->file_name = parent->file_name; | ||
905 | return chunk; | ||
906 | } | ||
907 | |||
908 | CompResult | ||
909 | compile_function(Chunk *chunk, Node *node) { | ||
910 | // The current activation record procedure for the VM is as follows: | ||
911 | // | ||
912 | // [caller][callee ] | ||
913 | // [ .... ][ RET VAL ][ PARAMS ][ LOCALS ][ REGISTERS ][ RET META ] | ||
914 | // ^ | ||
915 | // frame pointer | ||
916 | // | ||
917 | // The caller is responsible for allocating the return memory and the | ||
918 | // parameter memory and filling the param data before OP_CALL. | ||
919 | // | ||
920 | Chunk *func = chunk_alloc(chunk); | ||
921 | func->name = node->unique_name; | ||
922 | Function fun = (Function){ | ||
923 | .name = func->name, | ||
924 | .index = chunk->fun_idx++, | ||
925 | .param_arity = array_size(node->func_params), | ||
926 | .return_arity = array_size(node->func_ret), | ||
927 | }; | ||
928 | funcmap_insert(&chunk->funmap, func->name, fun, chunk->storage); | ||
929 | array_push(chunk->functions, func, chunk->storage); | ||
930 | |||
931 | // Put return values into memory. | ||
932 | CompResult res = compile_expr(func, node->func_body); | ||
933 | switch (res.type) { | ||
934 | case COMP_CONST: { | ||
935 | EMIT_OP(OP_PUTRETI, res.idx, 0, 0, node, func); | ||
936 | } break; | ||
937 | case COMP_REG: { | ||
938 | EMIT_OP(OP_PUTRET, res.idx, 0, 0, node, func); | ||
939 | } break; | ||
940 | default: break; | ||
941 | } | ||
942 | |||
943 | // TODO: handle captured locals/globals? | ||
944 | EMIT_OP(OP_RET, 0, 0, 0, node, func); | ||
945 | return (CompResult){.type = COMP_NIL}; | ||
946 | } | ||
947 | |||
430 | CompResult | 948 | CompResult |
431 | compile_expr(Chunk *chunk, Node *node) { | 949 | compile_expr(Chunk *chunk, Node *node) { |
432 | switch (node->kind) { | 950 | switch (node->kind) { |
951 | case NODE_FUN: return compile_function(chunk, node); | ||
952 | case NODE_FUNCALL: return compile_funcall(chunk, node); | ||
953 | case NODE_WHILE: return compile_while(chunk, node); | ||
954 | case NODE_IF: return compile_if(chunk, node); | ||
955 | case NODE_COND: return compile_cond(chunk, node); | ||
433 | // Logic. | 956 | // Logic. |
434 | // case NODE_XOR: | 957 | // case NODE_XOR: |
435 | case NODE_BITNOT: | 958 | case NODE_BITNOT: |
@@ -459,31 +982,18 @@ compile_expr(Chunk *chunk, Node *node) { | |||
459 | case NODE_NUM_UINT: | 982 | case NODE_NUM_UINT: |
460 | case NODE_NUM_INT: { | 983 | case NODE_NUM_INT: { |
461 | sz value = node->value.i; | 984 | sz value = node->value.i; |
462 | // Make sure we don't have duplicated constants. | 985 | sz const_idx = add_constant(chunk, value); |
463 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); | ||
464 | if (!map) { | ||
465 | map = intintmap_insert(&chunk->intmap, value, | ||
466 | chunk->const_idx++, chunk->storage); | ||
467 | Constant c = (Constant){.i = node->value.i}; | ||
468 | array_push(chunk->constants, c, chunk->storage); | ||
469 | } | ||
470 | return (CompResult){ | 986 | return (CompResult){ |
471 | .type = COMP_CONST, | 987 | .type = COMP_CONST, |
472 | .idx = map->val, | 988 | .idx = const_idx, |
473 | }; | 989 | }; |
474 | } break; | 990 | } break; |
475 | case NODE_STRING: { | 991 | case NODE_STRING: { |
476 | Str string = node->value.str; | 992 | Str string = node->value.str; |
477 | // Make sure we don't have duplicated strings. | 993 | sz str_idx = add_string(chunk, string); |
478 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
479 | if (!map) { | ||
480 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
481 | chunk->storage); | ||
482 | array_push(chunk->strings, string, chunk->storage); | ||
483 | } | ||
484 | return (CompResult){ | 994 | return (CompResult){ |
485 | .type = COMP_STRING, | 995 | .type = COMP_STRING, |
486 | .idx = map->val, | 996 | .idx = str_idx, |
487 | }; | 997 | }; |
488 | } break; | 998 | } break; |
489 | case NODE_LET: { | 999 | case NODE_LET: { |
@@ -496,6 +1006,18 @@ compile_expr(Chunk *chunk, Node *node) { | |||
496 | if (str_eq(type, cstr("str"))) { | 1006 | if (str_eq(type, cstr("str"))) { |
497 | size = 16; | 1007 | size = 16; |
498 | } | 1008 | } |
1009 | if (str_has_prefix(type, cstr("@"))) { | ||
1010 | if (node->var_type && node->var_type->kind == NODE_ARR_TYPE && | ||
1011 | node->var_type->arr_size->value.i > 0) { | ||
1012 | // TODO: get the proper storage size for the multiplication. | ||
1013 | size *= node->var_type->arr_size->value.i; | ||
1014 | // FIXME: this should be done on the static analysis, plus, | ||
1015 | // we shouldn't be checking all these types by hand, but | ||
1016 | // using the symbol tables. | ||
1017 | type = str_remove_prefix(type, cstr("@")); | ||
1018 | type = str_concat(cstr("[]"), type, chunk->storage); | ||
1019 | } | ||
1020 | } | ||
499 | Variable var = (Variable){ | 1021 | Variable var = (Variable){ |
500 | .name = name, | 1022 | .name = name, |
501 | .type = type, | 1023 | .type = type, |
@@ -512,11 +1034,11 @@ compile_expr(Chunk *chunk, Node *node) { | |||
512 | CompResult res = compile_expr(chunk, node->var_val); | 1034 | CompResult res = compile_expr(chunk, node->var_val); |
513 | switch (res.type) { | 1035 | switch (res.type) { |
514 | case COMP_CONST: { | 1036 | case COMP_CONST: { |
515 | EMIT_OP(OP_STVARI, idx, res.idx, 0, node->var_val, | 1037 | EMIT_OP(OP_STGVARI, idx, res.idx, 0, node->var_val, |
516 | chunk); | 1038 | chunk); |
517 | } break; | 1039 | } break; |
518 | case COMP_REG: { | 1040 | case COMP_REG: { |
519 | EMIT_OP(OP_STVAR, idx, res.idx, 0, node->var_val, | 1041 | EMIT_OP(OP_STGVAR, idx, res.idx, 0, node->var_val, |
520 | chunk); | 1042 | chunk); |
521 | } break; | 1043 | } break; |
522 | default: { | 1044 | default: { |
@@ -527,6 +1049,74 @@ compile_expr(Chunk *chunk, Node *node) { | |||
527 | 1049 | ||
528 | return (CompResult){.type = COMP_NIL}; | 1050 | return (CompResult){.type = COMP_NIL}; |
529 | } break; | 1051 | } break; |
1052 | case NODE_SET: { | ||
1053 | Str name = node->unique_name; | ||
1054 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); | ||
1055 | if (!map) { | ||
1056 | eprintln("error: node_set: symbol name not found: %s", name); | ||
1057 | } | ||
1058 | CompResult res = compile_expr(chunk, node->var_val); | ||
1059 | if (node->var_name->kind == NODE_SYMBOL_IDX) { | ||
1060 | // Value. | ||
1061 | sz reg_val; | ||
1062 | switch (res.type) { | ||
1063 | case COMP_CONST: { | ||
1064 | reg_val = chunk->reg_idx++; | ||
1065 | EMIT_OP(OP_LD64K, reg_val, res.idx, 0, node, chunk); | ||
1066 | } break; | ||
1067 | case COMP_REG: { | ||
1068 | reg_val = res.idx; | ||
1069 | } break; | ||
1070 | default: { | ||
1071 | return (CompResult){.type = COMP_ERR}; | ||
1072 | } break; | ||
1073 | } | ||
1074 | |||
1075 | // Address. | ||
1076 | sz reg_addr = chunk->reg_idx++; | ||
1077 | // Is this a pointer access or an array access? | ||
1078 | if (str_has_prefix(map->val.type, cstr("[]"))) { | ||
1079 | EMIT_OP(OP_LDGADDR, reg_addr, map->val.idx, 0, | ||
1080 | node->var_val, chunk); | ||
1081 | } else { | ||
1082 | EMIT_OP(OP_LDGVAR, reg_addr, map->val.idx, 0, node->var_val, | ||
1083 | chunk); | ||
1084 | } | ||
1085 | |||
1086 | // Index. | ||
1087 | CompResult idx = compile_expr(chunk, node->var_name->arr_size); | ||
1088 | switch (idx.type) { | ||
1089 | case COMP_CONST: { | ||
1090 | EMIT_OP(OP_ST64I, reg_val, reg_addr, idx.idx, node, | ||
1091 | chunk); | ||
1092 | } break; | ||
1093 | case COMP_REG: { | ||
1094 | EMIT_OP(OP_ST64, reg_val, reg_addr, idx.idx, node, | ||
1095 | chunk); | ||
1096 | } break; | ||
1097 | default: { | ||
1098 | return (CompResult){.type = COMP_ERR}; | ||
1099 | } break; | ||
1100 | } | ||
1101 | // TODO: offset should be in bytes, in this case we are assuming | ||
1102 | // 64bit types, hence ST64 | ||
1103 | return (CompResult){.type = COMP_NIL}; | ||
1104 | } | ||
1105 | switch (res.type) { | ||
1106 | case COMP_CONST: { | ||
1107 | EMIT_OP(OP_STGVARI, map->val.idx, res.idx, 0, node->var_val, | ||
1108 | chunk); | ||
1109 | } break; | ||
1110 | case COMP_REG: { | ||
1111 | EMIT_OP(OP_STGVAR, map->val.idx, res.idx, 0, node->var_val, | ||
1112 | chunk); | ||
1113 | } break; | ||
1114 | default: { | ||
1115 | return (CompResult){.type = COMP_ERR}; | ||
1116 | } break; | ||
1117 | } | ||
1118 | return (CompResult){.type = COMP_NIL}; | ||
1119 | } break; | ||
530 | case NODE_SYMBOL: { | 1120 | case NODE_SYMBOL: { |
531 | Str name = node->unique_name; | 1121 | Str name = node->unique_name; |
532 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); | 1122 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); |
@@ -536,7 +1126,48 @@ compile_expr(Chunk *chunk, Node *node) { | |||
536 | } | 1126 | } |
537 | Variable var = map->val; | 1127 | Variable var = map->val; |
538 | u8 reg_dst = chunk->reg_idx++; | 1128 | u8 reg_dst = chunk->reg_idx++; |
539 | EMIT_OP(OP_LDVAR, reg_dst, var.idx, 0, node->var_val, chunk); | 1129 | if (node->is_ptr) { |
1130 | EMIT_OP(OP_LDGADDR, reg_dst, var.idx, 0, node, chunk); | ||
1131 | } else { | ||
1132 | EMIT_OP(OP_LDGVAR, reg_dst, var.idx, 0, node, chunk); | ||
1133 | } | ||
1134 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | ||
1135 | } break; | ||
1136 | case NODE_SYMBOL_IDX: { | ||
1137 | Str name = node->unique_name; | ||
1138 | StrVarMap *map = varmap_lookup(&chunk->varmap, name); | ||
1139 | if (!map) { | ||
1140 | println("error: unreachable... name: %s", name); | ||
1141 | exit(EXIT_FAILURE); | ||
1142 | } | ||
1143 | |||
1144 | // Destination. | ||
1145 | u8 reg_dst = chunk->reg_idx++; | ||
1146 | |||
1147 | // Address. | ||
1148 | sz reg_addr = chunk->reg_idx++; | ||
1149 | if (str_has_prefix(map->val.type, cstr("[]"))) { | ||
1150 | EMIT_OP(OP_LDGADDR, reg_addr, map->val.idx, 0, node->var_val, | ||
1151 | chunk); | ||
1152 | } else { | ||
1153 | EMIT_OP(OP_LDGVAR, reg_addr, map->val.idx, 0, node->var_val, | ||
1154 | chunk); | ||
1155 | } | ||
1156 | |||
1157 | // Index. | ||
1158 | CompResult idx = compile_expr(chunk, node->arr_size); | ||
1159 | switch (idx.type) { | ||
1160 | case COMP_CONST: { | ||
1161 | EMIT_OP(OP_LD64I, reg_dst, reg_addr, idx.idx, node, chunk); | ||
1162 | } break; | ||
1163 | case COMP_REG: { | ||
1164 | EMIT_OP(OP_LD64, reg_dst, reg_addr, idx.idx, node, chunk); | ||
1165 | } break; | ||
1166 | default: { | ||
1167 | return (CompResult){.type = COMP_ERR}; | ||
1168 | } break; | ||
1169 | } | ||
1170 | // TODO: hardcoding the type size for now (LD64/LD64I). | ||
540 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 1171 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
541 | } break; | 1172 | } break; |
542 | case NODE_BLOCK: { | 1173 | case NODE_BLOCK: { |
@@ -559,6 +1190,10 @@ compile_expr(Chunk *chunk, Node *node) { | |||
559 | void | 1190 | void |
560 | disassemble_instruction(Instruction instruction) { | 1191 | disassemble_instruction(Instruction instruction) { |
561 | switch (instruction.op) { | 1192 | switch (instruction.op) { |
1193 | case OP_CALL: | ||
1194 | println("%s f%d", op_str[instruction.op], instruction.dst, | ||
1195 | instruction.a, instruction.b); | ||
1196 | break; | ||
562 | case OP_MOV8: | 1197 | case OP_MOV8: |
563 | case OP_MOV16: | 1198 | case OP_MOV16: |
564 | case OP_MOV32: | 1199 | case OP_MOV32: |
@@ -566,6 +1201,11 @@ disassemble_instruction(Instruction instruction) { | |||
566 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | 1201 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, |
567 | instruction.a, instruction.b); | 1202 | instruction.a, instruction.b); |
568 | break; | 1203 | break; |
1204 | case OP_JMPF: | ||
1205 | case OP_JMPT: | ||
1206 | println("%s l%d, r%d", op_str[instruction.op], instruction.dst, | ||
1207 | instruction.a, instruction.b); | ||
1208 | break; | ||
569 | case OP_LD8K: | 1209 | case OP_LD8K: |
570 | case OP_LD16K: | 1210 | case OP_LD16K: |
571 | case OP_LD32K: | 1211 | case OP_LD32K: |
@@ -639,15 +1279,16 @@ disassemble_instruction(Instruction instruction) { | |||
639 | println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, | 1279 | println("%s r%d, r%d, r%d", op_str[instruction.op], instruction.dst, |
640 | instruction.a, instruction.b); | 1280 | instruction.a, instruction.b); |
641 | break; | 1281 | break; |
642 | case OP_LDVAR: | 1282 | case OP_LDGVAR: |
1283 | case OP_LDGADDR: | ||
643 | println("%s r%d, v%d", op_str[instruction.op], instruction.dst, | 1284 | println("%s r%d, v%d", op_str[instruction.op], instruction.dst, |
644 | instruction.a, instruction.b); | 1285 | instruction.a, instruction.b); |
645 | break; | 1286 | break; |
646 | case OP_STVAR: | 1287 | case OP_STGVAR: |
647 | println("%s v%d, r%d", op_str[instruction.op], instruction.dst, | 1288 | println("%s v%d, r%d", op_str[instruction.op], instruction.dst, |
648 | instruction.a, instruction.b); | 1289 | instruction.a, instruction.b); |
649 | break; | 1290 | break; |
650 | case OP_STVARI: | 1291 | case OP_STGVARI: |
651 | println("%s v%d, c%d", op_str[instruction.op], instruction.dst, | 1292 | println("%s v%d, c%d", op_str[instruction.op], instruction.dst, |
652 | instruction.a, instruction.b); | 1293 | instruction.a, instruction.b); |
653 | break; | 1294 | break; |
@@ -661,6 +1302,33 @@ disassemble_instruction(Instruction instruction) { | |||
661 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, | 1302 | println("%s r%d, r%d", op_str[instruction.op], instruction.dst, |
662 | instruction.a, instruction.b); | 1303 | instruction.a, instruction.b); |
663 | break; | 1304 | break; |
1305 | case OP_JMP: | ||
1306 | println("%s l%d", op_str[instruction.op], instruction.dst, | ||
1307 | instruction.a, instruction.b); | ||
1308 | break; | ||
1309 | case OP_JMPFI: | ||
1310 | case OP_JMPTI: | ||
1311 | println("%s l%d, c%d", op_str[instruction.op], instruction.dst, | ||
1312 | instruction.a, instruction.b); | ||
1313 | break; | ||
1314 | case OP_PRINTS64: | ||
1315 | case OP_PRINTF64: | ||
1316 | case OP_PRINTSTR: | ||
1317 | case OP_PUSH: | ||
1318 | case OP_POP: | ||
1319 | case OP_PUTRET: | ||
1320 | println("%s r%d", op_str[instruction.op], instruction.dst, | ||
1321 | instruction.a, instruction.b); | ||
1322 | break; | ||
1323 | case OP_PRINTS64I: | ||
1324 | case OP_PRINTF64I: | ||
1325 | case OP_RESERVE: | ||
1326 | case OP_PUSHI: | ||
1327 | case OP_PUTRETI: | ||
1328 | println("%s c%d", op_str[instruction.op], instruction.dst, | ||
1329 | instruction.a, instruction.b); | ||
1330 | break; | ||
1331 | case OP_RET: | ||
664 | case OP_HALT: println("%s", op_str[instruction.op]); break; | 1332 | case OP_HALT: println("%s", op_str[instruction.op]); break; |
665 | default: println("Unknown opcode %d", instruction.op); break; | 1333 | default: println("Unknown opcode %d", instruction.op); break; |
666 | } | 1334 | } |
@@ -668,36 +1336,56 @@ disassemble_instruction(Instruction instruction) { | |||
668 | 1336 | ||
669 | void | 1337 | void |
670 | disassemble_chunk(Chunk chunk) { | 1338 | disassemble_chunk(Chunk chunk) { |
671 | println("%s: =========== code ===========", chunk.file_name); | 1339 | println("CHUNK %d: %s%s", chunk.id, chunk.file_name, chunk.name); |
1340 | println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", | ||
1341 | chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, | ||
1342 | chunk.const_idx); | ||
1343 | println("================== code =================="); | ||
1344 | println(" LINE:COL INUM LABELS OP OPERANDS "); | ||
1345 | println("------------------------------------------"); | ||
672 | for (sz i = 0; i < array_size(chunk.code); i++) { | 1346 | for (sz i = 0; i < array_size(chunk.code); i++) { |
673 | print("%s: %x{4}: ", chunk.file_name, i); | 1347 | printf(" %.4ld:%.4ld %.4lx ", chunk.linecol[i].line, |
1348 | chunk.linecol[i].col, i); | ||
1349 | IntIntMap *label = intintmap_lookup(&chunk.labels_rev, i); | ||
1350 | if (label) { | ||
1351 | printf(".L%.2ld ", label->val); | ||
1352 | } else { | ||
1353 | printf(" %2s ", ""); | ||
1354 | } | ||
674 | disassemble_instruction(chunk.code[i]); | 1355 | disassemble_instruction(chunk.code[i]); |
675 | } | 1356 | } |
676 | if (array_size(chunk.constants) > 0) { | 1357 | if (array_size(chunk.constants) > 0) { |
677 | println("%s: ========= constants ========", chunk.file_name); | 1358 | println("================ constants ===============", chunk.file_name); |
678 | for (sz i = 0; i < array_size(chunk.constants); i++) { | 1359 | for (sz i = 0; i < array_size(chunk.constants); i++) { |
679 | println("%s: %x{2}: %x{8}", chunk.file_name, i, | 1360 | println(" %x{2}: %x{8}", i, chunk.constants[i]); |
680 | chunk.constants[i]); | ||
681 | } | 1361 | } |
682 | } | 1362 | } |
683 | if (array_size(chunk.strings) > 0) { | 1363 | if (array_size(chunk.strings) > 0) { |
684 | println("%s: ========== strings =========", chunk.file_name); | 1364 | println("================= strings ================", chunk.file_name); |
685 | for (sz i = 0; i < array_size(chunk.strings); i++) { | 1365 | for (sz i = 0; i < array_size(chunk.strings); i++) { |
686 | println("%s: %x{2}: %s", chunk.file_name, i, chunk.strings[i]); | 1366 | println(" %x{2}: %s", i, chunk.strings[i]); |
687 | } | 1367 | } |
688 | } | 1368 | } |
689 | if (array_size(chunk.vars) > 0) { | 1369 | if (array_size(chunk.vars) > 0) { |
690 | println("%s: ========= variables ========", chunk.file_name); | 1370 | println("================ variables ===============", chunk.file_name); |
691 | for (sz i = 0; i < array_size(chunk.vars); i++) { | 1371 | for (sz i = 0; i < array_size(chunk.vars); i++) { |
692 | println("%s: %x{2}: [%x{4}:%x{4}] %s: %s", chunk.file_name, i, | 1372 | println(" %x{2}: [%x{4}:%x{4}] %s: %s", i, chunk.vars[i].offset, |
693 | chunk.vars[i].offset, | ||
694 | chunk.vars[i].offset + chunk.vars[i].size, | 1373 | chunk.vars[i].offset + chunk.vars[i].size, |
695 | chunk.vars[i].name, chunk.vars[i].type); | 1374 | chunk.vars[i].name, chunk.vars[i].type); |
696 | } | 1375 | } |
697 | } | 1376 | } |
698 | println("n_regs: %d, n_vars: %d, n_strings: %d, n_consts: %d", | 1377 | if (array_size(chunk.functions) > 0) { |
699 | chunk.reg_idx, array_size(chunk.vars), chunk.str_idx, | 1378 | println("================ functions ===============", chunk.file_name); |
700 | chunk.const_idx); | 1379 | for (sz i = 0; i < array_size(chunk.functions); i++) { |
1380 | Chunk *func = chunk.functions[i]; | ||
1381 | println(" %x{2}: func%d: %s", i, func->id, func->name); | ||
1382 | } | ||
1383 | } | ||
1384 | println("=========================================="); | ||
1385 | for (sz i = 0; i < array_size(chunk.functions); i++) { | ||
1386 | Chunk *func = chunk.functions[i]; | ||
1387 | disassemble_chunk(*func); | ||
1388 | } | ||
701 | } | 1389 | } |
702 | 1390 | ||
703 | #endif // COMPILER_C | 1391 | #endif // COMPILER_C |
@@ -13,6 +13,18 @@ | |||
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: fix semantics for arrays: static arrays can't have size 0. | ||
24 | // TODO: consider making all tye types PascalCase: Int F64 Str... | ||
25 | // TODO: add a `const` keyword that can only take literals or constexpr values. | ||
26 | // TODO: add assignment operators instead of `set` :=, +=, -= | ||
27 | // TODO: add mifix operators ++ and -- | ||
16 | 28 | ||
17 | typedef enum ExecMode { | 29 | typedef enum ExecMode { |
18 | RUN_NORMAL, | 30 | RUN_NORMAL, |
@@ -167,11 +179,15 @@ process_file(Str path) { | |||
167 | 179 | ||
168 | // Compile roots. | 180 | // Compile roots. |
169 | Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); | 181 | Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); |
170 | Chunk chunk = {.file_name = path, .storage = &bytecode_arena}; | 182 | Chunk chunk = { |
183 | .file_name = path, | ||
184 | .storage = &bytecode_arena, | ||
185 | .name = cstr(".main"), | ||
186 | }; | ||
171 | array_zero(chunk.constants, 256, &bytecode_arena); | 187 | array_zero(chunk.constants, 256, &bytecode_arena); |
172 | array_zero(chunk.code, 0xffff, &bytecode_arena); | 188 | array_zero(chunk.code, 0xffff, &bytecode_arena); |
173 | sz n_roots = array_size(parser.nodes); | 189 | sz n_roots = array_size(parser.nodes); |
174 | CompResult res; | 190 | CompResult res = {0}; |
175 | for (sz i = 0; i < n_roots; i++) { | 191 | for (sz i = 0; i < n_roots; i++) { |
176 | // The parser stores the root nodes as a stack. | 192 | // The parser stores the root nodes as a stack. |
177 | Node *root = parser.nodes[i]; | 193 | Node *root = parser.nodes[i]; |
@@ -181,7 +197,9 @@ process_file(Str path) { | |||
181 | exit(EXIT_FAILURE); | 197 | exit(EXIT_FAILURE); |
182 | } | 198 | } |
183 | } | 199 | } |
200 | // Make sure the last result is on r0. | ||
184 | sz res_reg = 0; | 201 | sz res_reg = 0; |
202 | bool is_nil = false; | ||
185 | switch (res.type) { | 203 | switch (res.type) { |
186 | case COMP_CONST: { | 204 | case COMP_CONST: { |
187 | res_reg = chunk.reg_idx++; | 205 | res_reg = chunk.reg_idx++; |
@@ -192,11 +210,12 @@ process_file(Str path) { | |||
192 | case COMP_REG: { | 210 | case COMP_REG: { |
193 | res_reg = res.idx; | 211 | res_reg = res.idx; |
194 | } break; | 212 | } break; |
213 | case COMP_NIL: { | ||
214 | is_nil = true; | ||
215 | } break; | ||
195 | default: break; | 216 | default: break; |
196 | } | 217 | } |
197 | // After we are done move the last result to r0 for printing. | 218 | 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 | 219 | ||
201 | if (chunk.const_idx >= 256) { | 220 | if (chunk.const_idx >= 256) { |
202 | eprintln("too many constants on chunk %s", chunk.id); | 221 | eprintln("too many constants on chunk %s", chunk.id); |
@@ -219,10 +238,10 @@ process_file(Str path) { | |||
219 | // println("VM REGISTERS BEFORE:\n%{Mem}", | 238 | // println("VM REGISTERS BEFORE:\n%{Mem}", |
220 | // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); | 239 | // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); |
221 | vm_run(&vm); | 240 | vm_run(&vm); |
222 | println("VM REGISTERS AFTER:\n%{Mem}", | 241 | #if DEBUG == 1 |
223 | &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); | 242 | println("MEMORY:\n%{Mem}", |
224 | println("VM MEMORY AFTER:\n%{Mem}", | ||
225 | &(Array){.mem = (u8 *)&vm.stack, sizeof(vm.stack)}); | 243 | &(Array){.mem = (u8 *)&vm.stack, sizeof(vm.stack)}); |
244 | #endif | ||
226 | 245 | ||
227 | #if DEBUG == 1 | 246 | #if DEBUG == 1 |
228 | println("Space used: %{Arena}", &lexer_arena); | 247 | println("Space used: %{Arena}", &lexer_arena); |
diff --git a/src/semantic.c b/src/semantic.c index d782cac..a0231d5 100644 --- a/src/semantic.c +++ b/src/semantic.c | |||
@@ -521,6 +521,11 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
521 | .kind = SYM_VAR, | 521 | .kind = SYM_VAR, |
522 | }, | 522 | }, |
523 | a->storage); | 523 | a->storage); |
524 | node->var_name->type = type_name; | ||
525 | symbol = str_concat(cstr("."), symbol, a->storage); | ||
526 | symbol = str_concat(symbol, str_from_int(scope->id, a->storage), | ||
527 | a->storage); | ||
528 | node->unique_name = symbol; | ||
524 | return node->type; | 529 | return node->type; |
525 | } | 530 | } |
526 | 531 | ||
@@ -550,6 +555,12 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
550 | a->err = true; | 555 | a->err = true; |
551 | return cstr(""); | 556 | return cstr(""); |
552 | } | 557 | } |
558 | Str symbol = node->var_name->value.str; | ||
559 | FindSymbolResult sym = find_symbol(scope, symbol); | ||
560 | node->unique_name = str_concat(cstr("."), symbol, a->storage); | ||
561 | node->unique_name = | ||
562 | str_concat(node->unique_name, | ||
563 | str_from_int(sym.scope->id, a->storage), a->storage); | ||
553 | node->type = cstr("nil"); | 564 | node->type = cstr("nil"); |
554 | return node->type; | 565 | return node->type; |
555 | } break; | 566 | } break; |
@@ -653,6 +664,15 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
653 | return cstr(""); | 664 | return cstr(""); |
654 | } | 665 | } |
655 | } | 666 | } |
667 | // If it returns a value, verify it contains an `else` statement. | ||
668 | if (!str_eq(node->type, cstr("nil"))) { | ||
669 | if (!node->cond_else) { | ||
670 | emit_semantic_error( | ||
671 | a, node, | ||
672 | cstr("missing else statement in if expression")); | ||
673 | return cstr(""); | ||
674 | } | ||
675 | } | ||
656 | return node->type; | 676 | return node->type; |
657 | } break; | 677 | } break; |
658 | case NODE_WHILE: { | 678 | case NODE_WHILE: { |
@@ -672,6 +692,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
672 | } break; | 692 | } break; |
673 | case NODE_COND: { | 693 | case NODE_COND: { |
674 | Str previous = cstr(""); | 694 | Str previous = cstr(""); |
695 | bool has_else = false; | ||
675 | for (sz i = 0; i < array_size(node->match_cases); i++) { | 696 | for (sz i = 0; i < array_size(node->match_cases); i++) { |
676 | Node *expr = node->match_cases[i]; | 697 | Node *expr = node->match_cases[i]; |
677 | Str next = type_inference(a, expr, scope); | 698 | Str next = type_inference(a, expr, scope); |
@@ -681,8 +702,20 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
681 | cstr("non-matching types for cond expressions")); | 702 | cstr("non-matching types for cond expressions")); |
682 | return cstr(""); | 703 | return cstr(""); |
683 | } | 704 | } |
705 | if (!expr->case_value) { | ||
706 | has_else = true; | ||
707 | } | ||
684 | previous = next; | 708 | previous = next; |
685 | } | 709 | } |
710 | // If it returns a value, verify it contains an `else` statement. | ||
711 | if (!str_eq(previous, cstr("nil"))) { | ||
712 | if (!has_else) { | ||
713 | emit_semantic_error( | ||
714 | a, node, | ||
715 | cstr("missing else statement in cond expression")); | ||
716 | return cstr(""); | ||
717 | } | ||
718 | } | ||
686 | node->type = previous; | 719 | node->type = previous; |
687 | return node->type; | 720 | return node->type; |
688 | } break; | 721 | } break; |
@@ -850,7 +883,7 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
850 | return node->type; | 883 | return node->type; |
851 | } break; | 884 | } break; |
852 | case NODE_NUM_UINT: { | 885 | case NODE_NUM_UINT: { |
853 | node->type = cstr("uint"); | 886 | node->type = cstr("int"); |
854 | return node->type; | 887 | return node->type; |
855 | } break; | 888 | } break; |
856 | case NODE_NUM_INT: { | 889 | case NODE_NUM_INT: { |
@@ -1025,6 +1058,12 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1025 | a->err = true; | 1058 | a->err = true; |
1026 | return cstr(""); | 1059 | return cstr(""); |
1027 | } | 1060 | } |
1061 | FindSymbolResult sym = find_symbol(scope, symbol); | ||
1062 | node->unique_name = str_concat(cstr("."), symbol, a->storage); | ||
1063 | node->unique_name = | ||
1064 | str_concat(node->unique_name, | ||
1065 | str_from_int(sym.scope->id, a->storage), a->storage); | ||
1066 | |||
1028 | // Check that actual parameters typecheck | 1067 | // Check that actual parameters typecheck |
1029 | Str args = cstr(""); | 1068 | Str args = cstr(""); |
1030 | for (sz i = 0; i < array_size(node->elements); i++) { | 1069 | for (sz i = 0; i < array_size(node->elements); i++) { |
@@ -1148,6 +1187,10 @@ type_inference(Analyzer *a, Node *node, Scope *scope) { | |||
1148 | .param_type = param_type, | 1187 | .param_type = param_type, |
1149 | .return_type = ret_type}, | 1188 | .return_type = ret_type}, |
1150 | a->storage); | 1189 | a->storage); |
1190 | symbol = str_concat(cstr("."), symbol, a->storage); | ||
1191 | symbol = str_concat(symbol, str_from_int(prev_scope->id, a->storage), | ||
1192 | a->storage); | ||
1193 | node->unique_name = symbol; | ||
1151 | 1194 | ||
1152 | if (node->func_body->kind == NODE_BLOCK) { | 1195 | if (node->func_body->kind == NODE_BLOCK) { |
1153 | Str type; | 1196 | Str type; |
@@ -8,9 +8,11 @@ | |||
8 | #define STACK_SIZE KB(64) | 8 | #define STACK_SIZE KB(64) |
9 | typedef struct VM { | 9 | typedef struct VM { |
10 | Chunk *chunk; | 10 | Chunk *chunk; |
11 | Constant regs[N_CONST]; | ||
12 | u8 stack[STACK_SIZE]; | 11 | u8 stack[STACK_SIZE]; |
13 | Instruction *ip; | 12 | Instruction *ip; |
13 | u8 *sp; | ||
14 | u64 *fp; | ||
15 | Constant *regs; | ||
14 | } VM; | 16 | } VM; |
15 | 17 | ||
16 | void | 18 | void |
@@ -20,6 +22,10 @@ vm_init(VM *vm, Chunk *chunk) { | |||
20 | assert(chunk->code); | 22 | assert(chunk->code); |
21 | vm->chunk = chunk; | 23 | vm->chunk = chunk; |
22 | vm->ip = vm->chunk->code; | 24 | vm->ip = vm->chunk->code; |
25 | vm->fp = (u64 *)vm->stack; | ||
26 | vm->sp = vm->stack + chunk->var_off; | ||
27 | vm->regs = (Constant *)vm->sp; | ||
28 | vm->sp += sizeof(Constant) * chunk->reg_idx; | ||
23 | } | 29 | } |
24 | 30 | ||
25 | #define OP_UNARY(OP, TYPE) \ | 31 | #define OP_UNARY(OP, TYPE) \ |
@@ -134,36 +140,225 @@ vm_run(VM *vm) { | |||
134 | vm->regs[dst].f = | 140 | vm->regs[dst].f = |
135 | fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f); | 141 | fmod(vm->regs[src_a].f, vm->chunk->constants[src_b].f); |
136 | } break; | 142 | } break; |
137 | case OP_LDVAR: { | 143 | case OP_LDGVAR: { |
138 | u8 dst = instruction.dst; | 144 | u8 dst = instruction.dst; |
139 | u8 src = instruction.a; | 145 | u8 src = instruction.a; |
140 | println("dst: %d src: %d", dst, src); | ||
141 | Variable var = vm->chunk->vars[src]; | 146 | Variable var = vm->chunk->vars[src]; |
142 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 147 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
143 | vm->regs[dst].i = *stack; | 148 | vm->regs[dst].i = *stack; |
144 | } break; | 149 | } break; |
145 | case OP_STVAR: { | 150 | case OP_LDGADDR: { |
151 | u8 dst = instruction.dst; | ||
152 | u8 src = instruction.a; | ||
153 | Variable var = vm->chunk->vars[src]; | ||
154 | s64 *stack = (s64 *)&vm->stack[var.offset]; | ||
155 | vm->regs[dst].ptr = (ptrsize)stack; | ||
156 | } break; | ||
157 | case OP_ST64I: { | ||
158 | sz value = vm->regs[instruction.dst].i; | ||
159 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
160 | sz offset = vm->chunk->constants[instruction.b].i; | ||
161 | addr[offset] = value; | ||
162 | } break; | ||
163 | case OP_ST64: { | ||
164 | sz value = vm->regs[instruction.dst].i; | ||
165 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
166 | sz offset = vm->regs[instruction.b].i; | ||
167 | addr[offset] = value; | ||
168 | } break; | ||
169 | case OP_LD64I: { | ||
170 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
171 | sz offset = vm->chunk->constants[instruction.b].i; | ||
172 | vm->regs[instruction.dst].i = addr[offset]; | ||
173 | } break; | ||
174 | case OP_LD64: { | ||
175 | s64 *addr = (s64 *)vm->regs[instruction.a].ptr; | ||
176 | sz offset = vm->regs[instruction.b].i; | ||
177 | vm->regs[instruction.dst].i = addr[offset]; | ||
178 | } break; | ||
179 | case OP_STGVAR: { | ||
146 | u8 dst = instruction.dst; | 180 | u8 dst = instruction.dst; |
147 | u8 src = instruction.a; | 181 | u8 src = instruction.a; |
148 | Variable var = vm->chunk->vars[dst]; | 182 | Variable var = vm->chunk->vars[dst]; |
149 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 183 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
150 | *stack = vm->regs[src].i; | 184 | *stack = vm->regs[src].i; |
151 | } break; | 185 | } break; |
152 | case OP_STVARI: { | 186 | case OP_STGVARI: { |
153 | u8 dst = instruction.dst; | 187 | u8 dst = instruction.dst; |
154 | u8 src = instruction.a; | 188 | u8 src = instruction.a; |
155 | Variable var = vm->chunk->vars[dst]; | 189 | Variable var = vm->chunk->vars[dst]; |
156 | s64 *stack = (s64 *)&vm->stack[var.offset]; | 190 | s64 *stack = (s64 *)&vm->stack[var.offset]; |
157 | *stack = vm->chunk->constants[src].i; | 191 | *stack = vm->chunk->constants[src].i; |
158 | } break; | 192 | } break; |
193 | case OP_JMP: { | ||
194 | u8 dst = instruction.dst; | ||
195 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
196 | vm->ip = vm->chunk->code + pos; | ||
197 | } break; | ||
198 | case OP_JMPFI: { | ||
199 | u8 dst = instruction.dst; | ||
200 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
201 | bool cond = vm->chunk->constants[instruction.a].i; | ||
202 | if (!cond) { | ||
203 | vm->ip = vm->chunk->code + pos; | ||
204 | } | ||
205 | } break; | ||
206 | case OP_JMPTI: { | ||
207 | u8 dst = instruction.dst; | ||
208 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
209 | bool cond = vm->chunk->constants[instruction.a].i; | ||
210 | if (cond) { | ||
211 | vm->ip = vm->chunk->code + pos; | ||
212 | } | ||
213 | } break; | ||
214 | case OP_JMPF: { | ||
215 | u8 dst = instruction.dst; | ||
216 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
217 | bool cond = vm->regs[instruction.a].i; | ||
218 | if (!cond) { | ||
219 | vm->ip = vm->chunk->code + pos; | ||
220 | } | ||
221 | } break; | ||
222 | case OP_JMPT: { | ||
223 | u8 dst = instruction.dst; | ||
224 | sz pos = intintmap_lookup(&vm->chunk->labels, dst)->val; | ||
225 | bool cond = vm->regs[instruction.a].i; | ||
226 | if (cond) { | ||
227 | vm->ip = vm->chunk->code + pos; | ||
228 | } | ||
229 | } break; | ||
230 | case OP_MOV64: { | ||
231 | u8 dst = instruction.dst; | ||
232 | u8 src = instruction.a; | ||
233 | vm->regs[dst] = vm->regs[src]; | ||
234 | } break; | ||
235 | case OP_MOV32: { | ||
236 | u8 dst = instruction.dst; | ||
237 | u8 src = instruction.a; | ||
238 | vm->regs[dst].i = vm->regs[src].i & 0xFFFFFFFF; | ||
239 | } break; | ||
240 | case OP_MOV16: { | ||
241 | u8 dst = instruction.dst; | ||
242 | u8 src = instruction.a; | ||
243 | vm->regs[dst].i = vm->regs[src].i & 0xFFFF; | ||
244 | } break; | ||
245 | case OP_MOV8: { | ||
246 | u8 dst = instruction.dst; | ||
247 | u8 src = instruction.a; | ||
248 | vm->regs[dst].i = vm->regs[src].i & 0xFF; | ||
249 | } break; | ||
250 | case OP_PRINTS64: { | ||
251 | u8 idx = instruction.dst; | ||
252 | print("%d", vm->regs[idx].i); | ||
253 | } break; | ||
254 | case OP_PRINTS64I: { | ||
255 | u8 idx = instruction.dst; | ||
256 | print("%d", vm->chunk->constants[idx].i); | ||
257 | } break; | ||
258 | case OP_PRINTF64: { | ||
259 | u8 idx = instruction.dst; | ||
260 | printf("%f", vm->regs[idx].f); | ||
261 | } break; | ||
262 | case OP_PRINTF64I: { | ||
263 | u8 idx = instruction.dst; | ||
264 | printf("%f", vm->chunk->constants[idx].f); | ||
265 | } break; | ||
266 | case OP_PRINTSTR: { | ||
267 | u8 idx = instruction.dst; | ||
268 | Str string = vm->chunk->strings[idx]; | ||
269 | print("%s", string); | ||
270 | } break; | ||
271 | case OP_RESERVE: { | ||
272 | sz offset = vm->chunk->constants[instruction.dst].i; | ||
273 | vm->sp += offset; | ||
274 | } break; | ||
275 | case OP_PUSH: { | ||
276 | sz val = vm->regs[instruction.dst].i; | ||
277 | u64 *p = (u64 *)vm->sp; | ||
278 | *p = val; | ||
279 | vm->sp += sizeof(ptrsize); | ||
280 | } break; | ||
281 | case OP_PUSHI: { | ||
282 | sz val = vm->chunk->constants[instruction.dst].i; | ||
283 | u64 *p = (u64 *)vm->sp; | ||
284 | *p = val; | ||
285 | vm->sp += sizeof(ptrsize); | ||
286 | } break; | ||
287 | case OP_POP: { | ||
288 | vm->sp -= sizeof(ptrsize); | ||
289 | u64 *p = (u64 *)vm->sp; | ||
290 | vm->regs[instruction.dst].i = *p; | ||
291 | } break; | ||
292 | case OP_PUTRET: { | ||
293 | sz val = vm->regs[instruction.dst].i; | ||
294 | vm->fp[-1] = val; | ||
295 | } break; | ||
296 | case OP_PUTRETI: { | ||
297 | sz val = vm->chunk->constants[instruction.dst].i; | ||
298 | vm->fp[-1] = val; | ||
299 | } break; | ||
300 | case OP_CALL: { | ||
301 | u8 dst = instruction.dst; | ||
302 | Chunk *func = vm->chunk->functions[dst]; | ||
303 | |||
304 | ptrsize chunk_addr = (ptrsize)vm->chunk; | ||
305 | ptrsize ip_addr = (ptrsize)vm->ip; | ||
306 | ptrsize reg_addr = (ptrsize)vm->regs; | ||
307 | ptrsize old_fp = (ptrsize)vm->fp; | ||
308 | |||
309 | // Allocate space for the locals. | ||
310 | vm->fp = (u64 *)vm->sp; | ||
311 | vm->sp += func->var_off; // FIXME: - func->n_params! | ||
312 | vm->chunk = func; | ||
313 | vm->ip = func->code; | ||
314 | vm->regs = (Constant *)vm->sp; | ||
315 | |||
316 | // Allocate registers. | ||
317 | vm->sp += sizeof(Constant) * func->reg_idx; | ||
318 | |||
319 | // Store memory addresses we need to return to this function. | ||
320 | u64 *p = (u64 *)vm->sp; | ||
321 | p[0] = chunk_addr; | ||
322 | p[1] = ip_addr; | ||
323 | p[2] = reg_addr; | ||
324 | p[3] = old_fp; | ||
325 | vm->sp += sizeof(ptrsize) * 4; | ||
326 | } break; | ||
327 | case OP_RET: { | ||
328 | u64 *p = (u64 *)vm->sp; | ||
329 | ptrsize chunk_addr = p[-4]; | ||
330 | ptrsize ip_addr = p[-3]; | ||
331 | ptrsize reg_addr = p[-2]; | ||
332 | ptrsize old_fp = p[-1]; | ||
333 | vm->sp -= sizeof(ptrsize) * 4; | ||
334 | |||
335 | // Deallocate registers. | ||
336 | vm->sp -= sizeof(Constant) * vm->chunk->reg_idx; | ||
337 | |||
338 | // Deallocate locals. | ||
339 | vm->sp -= vm->chunk->var_off; | ||
340 | |||
341 | // Restore previous activation record. | ||
342 | vm->regs = (Constant *)reg_addr; | ||
343 | vm->ip = (Instruction *)ip_addr; | ||
344 | vm->chunk = (Chunk *)chunk_addr; | ||
345 | vm->fp = (u64 *)old_fp; | ||
346 | } break; | ||
159 | case OP_HALT: { | 347 | case OP_HALT: { |
160 | println("VM HALT (int) -> %d", vm->regs[instruction.dst]); | 348 | println("VM halt..."); |
161 | println("VM HALT (float) -> %f", vm->regs[instruction.dst]); | 349 | if (instruction.a != 0) { |
162 | println("VM HALT (hex) -> %x", vm->regs[instruction.dst]); | 350 | println("Result:"); |
351 | Constant result = vm->regs[instruction.dst]; | ||
352 | printf("\tint -> %lld\n", result.i); | ||
353 | printf("\tfloat -> %.10e\n", result.f); | ||
354 | printf("\thex -> %llx\n", (u64)result.i); | ||
355 | println("\tbinary -> %b", result.i); | ||
356 | } | ||
163 | return; | 357 | return; |
164 | } | 358 | } |
165 | default: { | 359 | default: { |
166 | eprintln("unimplemented OP code: %d", instruction.op); | 360 | // eprintln("unimplemented OP code: %d", instruction.op); |
361 | eprintln("unimplemented OP code: %s", op_str[instruction.op]); | ||
167 | return; | 362 | return; |
168 | } | 363 | } |
169 | } | 364 | } |
diff --git a/tests/compilation.bad b/tests/compilation.bad index 0ffbb06..ea467d2 100644 --- a/tests/compilation.bad +++ b/tests/compilation.bad | |||
@@ -1,30 +1,93 @@ | |||
1 | let b = 8 | 1 | fun adder(a: int): int { |
2 | let a = 8 | 2 | ; a + 1 |
3 | ; { | 3 | 4 |
4 | ; let a = 1 + 2 * 4 | 4 | } |
5 | ; let b = 3.0 | 5 | adder(2) |
6 | ; } | 6 | ; let a = 1 |
7 | 7 | ; let b = 8 | |
8 | 1 + a | 8 | |
9 | ; 0xf | 0xf0 | 9 | ; a + b |
10 | ; 0xf & 0xff | ||
11 | ; 0x1 << 2 | ||
12 | ; 0x1 << 2 | ||
13 | ; (~0xf & 0xfff << 8) == 0xfff00 | ||
14 | ; 0xf << 4 | 0xf | ||
15 | ; 1 + 2 | 10 | ; 1 + 2 |
16 | ; 1 < 2 | 11 | ; fun greet(name: str): nil { |
17 | ; !(1 == 0 || 1 <= 1) | 12 | ; fun greet(name: str): nil { |
18 | ; 1 == 1 && 1 <= 1 | 13 | ; println("hello ") |
19 | ; !((1 * 2 * 3) * (1 * 2 * 3) >= 3 && 4 != 3 + 2) | 14 | ; } |
20 | ; 1 + 2 * 3 | 15 | |
21 | ; 1.0 + 2.0 * 3.0 | 16 | ; greet("alex") |
22 | ; true | 17 | ; greet("ding") |
23 | ; false | 18 | ; greet() |
24 | ; 0 1 | 19 | ; if true { |
25 | ; "hello" | 20 | ; 1 |
26 | ; "world" | 21 | ; nil |
27 | ; fun foo(): int { | 22 | ; } |
28 | ; 32 | 23 | ; let a = 1 |
29 | ; } | 24 | ; let p = @a |
30 | ; 1 + 2 + foo() | 25 | ; let arr: int[8] |
26 | ; set p[0] = 2 | ||
27 | |||
28 | ; let b = p[0] | ||
29 | ; println("a: " a) | ||
30 | ; println("b: " b) | ||
31 | ; let i = 0 | ||
32 | ; while i < 8 { | ||
33 | ; set arr[i] = i | ||
34 | ; set i = i + 1 | ||
35 | ; } | ||
36 | ; println("arr[0]: " arr[0]) | ||
37 | ; println("arr[1]: " arr[1]) | ||
38 | |||
39 | ; let arr: int[8] | ||
40 | ; set arr[1] = 2 | ||
41 | |||
42 | ; let idx = 3 | ||
43 | ; set arr[idx] = 3 | ||
44 | |||
45 | ; println("arr[0]: " arr[0]) | ||
46 | ; println("arr[1]: " arr[1]) | ||
47 | ; println("arr[2]: " arr[2]) | ||
48 | ; println("arr[3]: " arr[3]) | ||
49 | |||
50 | ; arr[0] + arr[1] + arr[2] + arr[3] | ||
51 | |||
52 | ; set a = 2 | ||
53 | |||
54 | ; let a = cond { | ||
55 | ; 1 == 1 = 42 | ||
56 | ; 1 == 3 = 55 | ||
57 | ; 1 == 1 = 66 | ||
58 | ; else = 10 | ||
59 | ; } | ||
60 | |||
61 | ; a | ||
62 | |||
63 | ; let a = 8 | ||
64 | ; let b = 16 | ||
65 | ; let c = { | ||
66 | ; let a = 32 | ||
67 | ; a + 8 | ||
68 | ; } | ||
69 | |||
70 | ; a + b + c | ||
71 | |||
72 | ; let a = 1 | ||
73 | ; if false { | ||
74 | ; set a = 2 | ||
75 | ; } | ||
76 | ; a | ||
77 | |||
78 | ; if true { | ||
79 | ; 1 + 2 | ||
80 | ; } else { | ||
81 | ; 3 + 4 | ||
82 | ; } | ||
83 | |||
84 | ; let a = 0 | ||
85 | ; while a < 10 { | ||
86 | ; set a = a + 1 | ||
87 | ; } | ||
88 | |||
89 | ; a | ||
90 | |||
91 | ; print(1) | ||
92 | ; println(1.0 + 2.0) | ||
93 | ; println("hello world " 1 " " 2.0 " wow!") | ||
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 | } |