diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | src/badlib.h | 1 | ||||
-rw-r--r-- | src/main.c | 1301 | ||||
-rw-r--r-- | src/parser.c | 5 | ||||
-rw-r--r-- | src/semantic.c | 1592 | ||||
-rw-r--r-- | src/vm.c | 90 | ||||
-rw-r--r-- | tests/compilation.bad | 10 |
7 files changed, 1257 insertions, 1744 deletions
@@ -7,7 +7,7 @@ BUILD_DIR := build | |||
7 | TESTS_DIR := tests | 7 | TESTS_DIR := tests |
8 | TEST_FILES := $(wildcard $(TESTS_DIR)/*.bad) | 8 | TEST_FILES := $(wildcard $(TESTS_DIR)/*.bad) |
9 | SRC_MAIN := $(SRC_DIR)/main.c | 9 | SRC_MAIN := $(SRC_DIR)/main.c |
10 | SRC_RUN := tests/semantics.bad | 10 | SRC_RUN := tests/compilation.bad |
11 | WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") | 11 | WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") |
12 | INC_DIRS := $(shell find $(SRC_DIR) -type d) | 12 | INC_DIRS := $(shell find $(SRC_DIR) -type d) |
13 | INC_FLAGS := $(addprefix -I,$(INC_DIRS)) | 13 | INC_FLAGS := $(addprefix -I,$(INC_DIRS)) |
diff --git a/src/badlib.h b/src/badlib.h index e6f0df6..25e4914 100644 --- a/src/badlib.h +++ b/src/badlib.h | |||
@@ -955,6 +955,7 @@ SETDEF(StrSet, strset, Str, str_hash, str_eq) | |||
955 | MAPDEF(StrIntMap, strintmap, Str, sz, str_hash, str_eq) | 955 | MAPDEF(StrIntMap, strintmap, Str, sz, str_hash, str_eq) |
956 | SETDEF(IntSet, intset, sz, _int_hash, _int_eq) | 956 | SETDEF(IntSet, intset, sz, _int_hash, _int_eq) |
957 | MAPDEF(IntStrMap, intstrmap, sz, Str, _int_hash, _int_eq) | 957 | MAPDEF(IntStrMap, intstrmap, sz, Str, _int_hash, _int_eq) |
958 | MAPDEF(IntIntMap, intintmap, sz, sz, _int_hash, _int_eq) | ||
958 | 959 | ||
959 | // | 960 | // |
960 | // Dynamic arrays. | 961 | // Dynamic arrays. |
@@ -5,6 +5,7 @@ | |||
5 | #include "badlib.h" | 5 | #include "badlib.h" |
6 | #include "lexer.c" | 6 | #include "lexer.c" |
7 | #include "parser.c" | 7 | #include "parser.c" |
8 | #include "semantic.c" | ||
8 | #include "vm.c" | 9 | #include "vm.c" |
9 | 10 | ||
10 | // TODO: unions | 11 | // TODO: unions |
@@ -27,1238 +28,6 @@ init(void) { | |||
27 | log_init_default(); | 28 | log_init_default(); |
28 | } | 29 | } |
29 | 30 | ||
30 | typedef enum { | ||
31 | SYM_UNKNOWN, | ||
32 | SYM_BUILTIN_FUN, | ||
33 | SYM_BUILTIN_TYPE, | ||
34 | SYM_FUN, | ||
35 | SYM_VAR, | ||
36 | SYM_PARAM, | ||
37 | SYM_ENUM, | ||
38 | SYM_ENUM_FIELD, | ||
39 | SYM_STRUCT, | ||
40 | SYM_STRUCT_FIELD, | ||
41 | } SymbolKind; | ||
42 | |||
43 | Str sym_kind_str[] = { | ||
44 | [SYM_UNKNOWN] = cstr("UNKNOWN "), | ||
45 | [SYM_BUILTIN_FUN] = cstr("BUILTIN FUN "), | ||
46 | [SYM_BUILTIN_TYPE] = cstr("BUILTIN TYPE "), | ||
47 | [SYM_FUN] = cstr("FUNCTION "), | ||
48 | [SYM_VAR] = cstr("VARIABLE "), | ||
49 | [SYM_PARAM] = cstr("PARAMETER "), | ||
50 | [SYM_ENUM] = cstr("ENUM "), | ||
51 | [SYM_ENUM_FIELD] = cstr("ENUM FIELD "), | ||
52 | [SYM_STRUCT] = cstr("STRUCT "), | ||
53 | [SYM_STRUCT_FIELD] = cstr("STRUCT FIELD "), | ||
54 | }; | ||
55 | |||
56 | typedef struct Symbol { | ||
57 | Str name; | ||
58 | SymbolKind kind; | ||
59 | } Symbol; | ||
60 | |||
61 | typedef struct Fun { | ||
62 | Str name; | ||
63 | Str param_type; | ||
64 | Str return_type; | ||
65 | } Fun; | ||
66 | |||
67 | typedef struct Enum { | ||
68 | Str name; | ||
69 | Node *val; | ||
70 | } Enum; | ||
71 | |||
72 | typedef struct Struct { | ||
73 | Str name; | ||
74 | Str type; | ||
75 | Node *val; | ||
76 | } Struct; | ||
77 | |||
78 | MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) | ||
79 | MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq) | ||
80 | MAPDEF(EnumMap, enummap, Str, Enum, str_hash, str_eq) | ||
81 | MAPDEF(StructMap, structmap, Str, Struct, str_hash, str_eq) | ||
82 | |||
83 | typedef struct Scope { | ||
84 | sz id; | ||
85 | sz depth; | ||
86 | Str name; | ||
87 | SymbolMap *symbols; | ||
88 | FunMap *funcs; | ||
89 | EnumMap *enums; | ||
90 | StructMap *structs; | ||
91 | struct Scope *parent; | ||
92 | } Scope; | ||
93 | |||
94 | typedef struct Analyzer { | ||
95 | Arena *storage; | ||
96 | Str file_name; | ||
97 | sz typescope_gen; | ||
98 | Scope **scopes; | ||
99 | StrSet *numeric_types; | ||
100 | StrSet *integer_types; | ||
101 | bool err; | ||
102 | } Analyzer; | ||
103 | |||
104 | Scope * | ||
105 | typescope_alloc(Analyzer *a, Scope *parent) { | ||
106 | Scope *scope = arena_calloc(sizeof(Scope), a->storage); | ||
107 | scope->parent = parent; | ||
108 | scope->id = a->typescope_gen++; | ||
109 | scope->depth = parent == NULL ? 0 : parent->depth + 1; | ||
110 | array_push(a->scopes, scope, a->storage); | ||
111 | return scope; | ||
112 | } | ||
113 | |||
114 | SymbolMap * | ||
115 | find_type(Scope *scope, Str type) { | ||
116 | while (scope != NULL) { | ||
117 | SymbolMap *val = symmap_lookup(&scope->symbols, type); | ||
118 | if (val != NULL) { | ||
119 | return val; | ||
120 | } | ||
121 | scope = scope->parent; | ||
122 | } | ||
123 | return NULL; | ||
124 | } | ||
125 | |||
126 | FunMap * | ||
127 | find_fun(Scope *scope, Str type) { | ||
128 | while (scope != NULL) { | ||
129 | FunMap *val = funmap_lookup(&scope->funcs, type); | ||
130 | if (val != NULL) { | ||
131 | return val; | ||
132 | } | ||
133 | scope = scope->parent; | ||
134 | } | ||
135 | return NULL; | ||
136 | } | ||
137 | |||
138 | typedef struct FindEnumResult { | ||
139 | EnumMap *map; | ||
140 | Scope *scope; | ||
141 | } FindEnumResult; | ||
142 | |||
143 | FindEnumResult | ||
144 | find_enum(Scope *scope, Str type) { | ||
145 | while (scope != NULL) { | ||
146 | EnumMap *val = enummap_lookup(&scope->enums, type); | ||
147 | if (val != NULL) { | ||
148 | return (FindEnumResult){.map = val, .scope = scope}; | ||
149 | } | ||
150 | scope = scope->parent; | ||
151 | } | ||
152 | return (FindEnumResult){0}; | ||
153 | } | ||
154 | |||
155 | typedef struct FindStructResult { | ||
156 | StructMap *map; | ||
157 | Scope *scope; | ||
158 | } FindStructResult; | ||
159 | |||
160 | FindStructResult | ||
161 | find_struct(Scope *scope, Str type) { | ||
162 | while (scope != NULL) { | ||
163 | StructMap *val = structmap_lookup(&scope->structs, type); | ||
164 | if (val != NULL) { | ||
165 | return (FindStructResult){.map = val, .scope = scope}; | ||
166 | } | ||
167 | scope = scope->parent; | ||
168 | } | ||
169 | return (FindStructResult){0}; | ||
170 | } | ||
171 | |||
172 | void | ||
173 | graph_typescope(Scope *scope, Arena a) { | ||
174 | if (!scope->symbols) { | ||
175 | return; | ||
176 | } | ||
177 | SymbolMapIter iter = symmap_iterator(scope->symbols, &a); | ||
178 | SymbolMap *type = symmap_next(&iter, &a); | ||
179 | print( | ||
180 | "%d[shape=\"none\" label=<<TABLE ALIGN=\"left\" STYLE=\"rounded\" " | ||
181 | "BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"6\" " | ||
182 | "COLUMNS=\"*\">", | ||
183 | scope->id); | ||
184 | print( | ||
185 | "<TR >" | ||
186 | "<TD ALIGN=\"left\" > NAME </TD>" | ||
187 | "<TD ALIGN=\"left\" > TYPE </TD>" | ||
188 | "</TR>"); | ||
189 | while (type) { | ||
190 | print( | ||
191 | "<TR>" | ||
192 | "<TD ALIGN=\"left\"> %s </TD>" | ||
193 | "<TD ALIGN=\"left\"> %s</TD>" | ||
194 | "</TR>", | ||
195 | type->key, type->val.name); | ||
196 | type = symmap_next(&iter, &a); | ||
197 | } | ||
198 | println("</TABLE>>];"); | ||
199 | |||
200 | sz this_id = scope->id; | ||
201 | while (scope->parent) { | ||
202 | if (scope->parent->symbols) { | ||
203 | println("%d:e->%d:w;", this_id, scope->parent->id); | ||
204 | break; | ||
205 | } else { | ||
206 | scope = scope->parent; | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | |||
211 | void | ||
212 | graph_functions(Scope *scope, Arena a) { | ||
213 | if (!scope->funcs) { | ||
214 | return; | ||
215 | } | ||
216 | FunMapIter iter = funmap_iterator(scope->funcs, &a); | ||
217 | FunMap *func = funmap_next(&iter, &a); | ||
218 | print( | ||
219 | "fun_%d[shape=\"none\" label=<<TABLE ALIGN=\"left\" STYLE=\"rounded\" " | ||
220 | "BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"6\" " | ||
221 | "COLUMNS=\"*\">", | ||
222 | scope->id); | ||
223 | print( | ||
224 | "<TR >" | ||
225 | "<TD ALIGN=\"left\" > NAME </TD>" | ||
226 | "<TD ALIGN=\"left\" > PARAMS </TD>" | ||
227 | "<TD ALIGN=\"left\" > RETURN </TD>" | ||
228 | "</TR>"); | ||
229 | while (func) { | ||
230 | print( | ||
231 | "<TR>" | ||
232 | "<TD PORT=\"%s\" ALIGN=\"left\" > %s </TD>" | ||
233 | "<TD ALIGN=\"left\" > %s </TD>" | ||
234 | "<TD ALIGN=\"left\" > %s </TD>" | ||
235 | "</TR>", | ||
236 | func->val.name, func->val.name, func->val.param_type, | ||
237 | func->val.return_type); | ||
238 | func = funmap_next(&iter, &a); | ||
239 | } | ||
240 | println("</TABLE>>];"); | ||
241 | sz this_id = scope->id; | ||
242 | while (scope->parent) { | ||
243 | if (scope->parent->symbols) { | ||
244 | println("fun_%d:e->fun_%d:%s:w;", this_id, scope->parent->id, | ||
245 | scope->name); | ||
246 | break; | ||
247 | } else { | ||
248 | scope = scope->parent; | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | |||
253 | void | ||
254 | graph_types(Scope **scopes, Arena a) { | ||
255 | if (scopes == NULL) return; | ||
256 | println("digraph types {"); | ||
257 | println("rankdir=LR;"); | ||
258 | println("ranksep=\"0.95 equally\";"); | ||
259 | println("nodesep=\"0.5 equally\";"); | ||
260 | println("overlap=scale;"); | ||
261 | println("bgcolor=\"transparent\";"); | ||
262 | for (sz i = 0; i < array_size(scopes); i++) { | ||
263 | Scope *scope = scopes[i]; | ||
264 | if (!scope) { | ||
265 | continue; | ||
266 | } | ||
267 | println("subgraph %d {", i); | ||
268 | graph_typescope(scope, a); | ||
269 | graph_functions(scope, a); | ||
270 | println("}"); | ||
271 | } | ||
272 | println("}"); | ||
273 | } | ||
274 | |||
275 | void | ||
276 | emit_semantic_error(Analyzer *a, Node *n, Str msg) { | ||
277 | eprintln("%s:%d:%d: error: %s", a->file_name, n->line, n->col, msg); | ||
278 | a->err = true; | ||
279 | } | ||
280 | |||
281 | Str type_inference(Analyzer *a, Node *node, Scope *scope); | ||
282 | |||
283 | void | ||
284 | typecheck_field(Analyzer *a, Node *node, Scope *scope, Str symbol) { | ||
285 | if (node->field_type->kind == NODE_COMPOUND_TYPE) { | ||
286 | Str field_name = str_concat(symbol, cstr("."), a->storage); | ||
287 | field_name = str_concat(field_name, node->value.str, a->storage); | ||
288 | if (structmap_lookup(&scope->structs, field_name)) { | ||
289 | eprintln("%s:%d:%d: error: struct field '%s' already exists", | ||
290 | a->file_name, node->line, node->col, field_name); | ||
291 | a->err = true; | ||
292 | } | ||
293 | Str type = cstr("\\{ "); | ||
294 | for (sz i = 0; i < array_size(node->field_type->elements); i++) { | ||
295 | Node *field = node->field_type->elements[i]; | ||
296 | typecheck_field(a, field, scope, field_name); | ||
297 | type = str_concat(type, field->type, a->storage); | ||
298 | type = str_concat(type, cstr(" "), a->storage); | ||
299 | } | ||
300 | type = str_concat(type, cstr("\\}"), a->storage); | ||
301 | node->type = type; | ||
302 | } else { | ||
303 | Str field_name = str_concat(symbol, cstr("."), a->storage); | ||
304 | field_name = str_concat(field_name, node->value.str, a->storage); | ||
305 | Str field_type = node->field_type->value.str; | ||
306 | if (!find_type(scope, field_type)) { | ||
307 | eprintln("%s:%d:%d: error: unknown type '%s'", a->file_name, | ||
308 | node->field_type->line, node->field_type->col, field_type); | ||
309 | a->err = true; | ||
310 | } | ||
311 | if (node->field_type->is_ptr) { | ||
312 | field_type = str_concat(cstr("@"), field_type, a->storage); | ||
313 | } | ||
314 | if (node->field_type->kind == NODE_ARR_TYPE) { | ||
315 | field_type = str_concat(cstr("@"), field_type, a->storage); | ||
316 | } | ||
317 | if (structmap_lookup(&scope->structs, field_name)) { | ||
318 | eprintln("%s:%d:%d: error: struct field '%s' already exists", | ||
319 | a->file_name, node->line, node->col, field_name); | ||
320 | a->err = true; | ||
321 | } | ||
322 | if (node->field_val) { | ||
323 | Str type = type_inference(a, node->field_val, scope); | ||
324 | if (!str_eq(type, field_type)) { | ||
325 | eprintln( | ||
326 | "%s:%d:%d: error: mismatched types in struct " | ||
327 | "value " | ||
328 | "for '%s': %s expected %s", | ||
329 | a->file_name, node->line, node->col, field_name, type, | ||
330 | field_type); | ||
331 | a->err = true; | ||
332 | } | ||
333 | } | ||
334 | structmap_insert(&scope->structs, field_name, | ||
335 | (Struct){ | ||
336 | .name = field_name, | ||
337 | .type = field_type, | ||
338 | .val = node->field_val, | ||
339 | }, | ||
340 | a->storage); | ||
341 | symmap_insert(&scope->symbols, field_name, | ||
342 | (Symbol){.name = field_type, .kind = SYM_STRUCT_FIELD}, | ||
343 | a->storage); | ||
344 | node->type = field_type; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | void | ||
349 | typecheck_lit_field(Analyzer *a, Node *node, Scope *scope, Str symbol) { | ||
350 | if (node->field_val->kind == NODE_COMPOUND_TYPE) { | ||
351 | Str type = cstr("\\{ "); | ||
352 | for (sz i = 0; i < array_size(node->field_val->elements); i++) { | ||
353 | Node *field = node->field_val->elements[i]; | ||
354 | Str field_name = str_concat(symbol, cstr("."), a->storage); | ||
355 | field_name = str_concat(field_name, field->value.str, a->storage); | ||
356 | typecheck_lit_field(a, field, scope, field_name); | ||
357 | type = str_concat(type, field->type, a->storage); | ||
358 | type = str_concat(type, cstr(" "), a->storage); | ||
359 | } | ||
360 | type = str_concat(type, cstr("\\}"), a->storage); | ||
361 | node->type = type; | ||
362 | } else { | ||
363 | StructMap *s = structmap_lookup(&scope->structs, symbol); | ||
364 | if (!s) { | ||
365 | eprintln("%s:%d:%d: error: unknown struct field '%s'", a->file_name, | ||
366 | node->line, node->col, symbol); | ||
367 | a->err = true; | ||
368 | return; | ||
369 | } | ||
370 | Str field_type = s->val.type; | ||
371 | Str type = type_inference(a, node->field_val, scope); | ||
372 | if (!str_eq(type, field_type)) { | ||
373 | eprintln( | ||
374 | "%s:%d:%d: error: mismatched types in struct " | ||
375 | "value " | ||
376 | "for '%s': %s expected %s", | ||
377 | a->file_name, node->line, node->col, symbol, type, field_type); | ||
378 | a->err = true; | ||
379 | } | ||
380 | node->type = field_type; | ||
381 | } | ||
382 | } | ||
383 | |||
384 | void | ||
385 | typecheck_returns(Analyzer *a, Node *node, Str expected) { | ||
386 | if (!node) { | ||
387 | return; | ||
388 | } | ||
389 | // Traverse the tree again. | ||
390 | switch (node->kind) { | ||
391 | case NODE_COND: | ||
392 | case NODE_MATCH: { | ||
393 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
394 | Node *next = node->match_cases[i]; | ||
395 | typecheck_returns(a, next, expected); | ||
396 | } | ||
397 | } break; | ||
398 | case NODE_RETURN: { | ||
399 | bool err = !str_eq(node->type, expected); | ||
400 | if (err) { | ||
401 | eprintln( | ||
402 | "%s:%d:%d: error: mismatched return type %s, expected %s", | ||
403 | a->file_name, node->line, node->col, node->type, expected); | ||
404 | a->err = true; | ||
405 | } | ||
406 | } break; | ||
407 | case NODE_BLOCK: { | ||
408 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
409 | Node *next = node->elements[i]; | ||
410 | typecheck_returns(a, next, expected); | ||
411 | } | ||
412 | } break; | ||
413 | case NODE_IF: { | ||
414 | if (node->cond_expr) { | ||
415 | typecheck_returns(a, node->cond_expr, expected); | ||
416 | } | ||
417 | if (node->cond_else) { | ||
418 | typecheck_returns(a, node->cond_else, expected); | ||
419 | } | ||
420 | } break; | ||
421 | case NODE_SET: | ||
422 | case NODE_LET: { | ||
423 | if (node->var_val) { | ||
424 | typecheck_returns(a, node->var_val, expected); | ||
425 | } | ||
426 | } break; | ||
427 | case NODE_ADD: | ||
428 | case NODE_SUB: | ||
429 | case NODE_DIV: | ||
430 | case NODE_MUL: | ||
431 | case NODE_MOD: | ||
432 | case NODE_NOT: | ||
433 | case NODE_AND: | ||
434 | case NODE_OR: | ||
435 | case NODE_EQ: | ||
436 | case NODE_NEQ: | ||
437 | case NODE_LT: | ||
438 | case NODE_GT: | ||
439 | case NODE_LE: | ||
440 | case NODE_GE: | ||
441 | case NODE_BITNOT: | ||
442 | case NODE_BITAND: | ||
443 | case NODE_BITOR: | ||
444 | case NODE_BITLSHIFT: | ||
445 | case NODE_BITRSHIFT: { | ||
446 | if (node->left) { | ||
447 | typecheck_returns(a, node->left, expected); | ||
448 | } | ||
449 | if (node->right) { | ||
450 | typecheck_returns(a, node->right, expected); | ||
451 | } | ||
452 | } break; | ||
453 | default: break; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | Str | ||
458 | type_inference(Analyzer *a, Node *node, Scope *scope) { | ||
459 | assert(a); | ||
460 | assert(scope); | ||
461 | if (!node) { | ||
462 | return cstr(""); | ||
463 | } | ||
464 | // NOTE: For now we are not going to do implicit numeric conversions. | ||
465 | switch (node->kind) { | ||
466 | case NODE_LET: { | ||
467 | node->type = cstr("nil"); | ||
468 | Str symbol = node->var_name->value.str; | ||
469 | if (symmap_lookup(&scope->symbols, symbol)) { | ||
470 | eprintln( | ||
471 | "%s:%d:%d: error: symbol '%s' already exists in current " | ||
472 | "scope ", | ||
473 | a->file_name, node->var_name->line, node->var_name->col, | ||
474 | symbol); | ||
475 | a->err = true; | ||
476 | return cstr(""); | ||
477 | } | ||
478 | if (node->var_type) { | ||
479 | Str type_name = node->var_type->value.str; | ||
480 | SymbolMap *type = find_type(scope, type_name); | ||
481 | if (type == NULL) { | ||
482 | eprintln("%s:%d:%d: error: unknown type '%s'", a->file_name, | ||
483 | node->var_type->line, node->var_type->col, | ||
484 | type_name); | ||
485 | a->err = true; | ||
486 | return cstr(""); | ||
487 | } | ||
488 | if (node->var_type->is_ptr) { | ||
489 | type_name = str_concat(cstr("@"), type_name, a->storage); | ||
490 | } | ||
491 | if (node->var_type->kind == NODE_ARR_TYPE) { | ||
492 | type_name = str_concat(cstr("@"), type_name, a->storage); | ||
493 | // TODO: typecheck size | ||
494 | // TODO: register array in scope | ||
495 | } | ||
496 | if (node->var_val) { | ||
497 | Str type = type_inference(a, node->var_val, scope); | ||
498 | if (!type.size) { | ||
499 | eprintln( | ||
500 | "%s:%d:%d: error: can't bind `nil` to variable " | ||
501 | "'%s'", | ||
502 | a->file_name, node->var_type->line, | ||
503 | node->var_type->col, symbol); | ||
504 | a->err = true; | ||
505 | return cstr(""); | ||
506 | } | ||
507 | // TODO: Consider compatible types. | ||
508 | if (!str_eq(type, type_name)) { | ||
509 | // Special case, enums can be treated as ints. | ||
510 | FindEnumResult res = find_enum(scope, type_name); | ||
511 | if (!(res.map && str_eq(type, cstr("int")))) { | ||
512 | eprintln( | ||
513 | "%s:%d:%d: error: type mismatch, trying to " | ||
514 | "assing " | ||
515 | "%s" | ||
516 | " to a variable of type %s", | ||
517 | a->file_name, node->var_type->line, | ||
518 | node->var_type->col, type, type_name); | ||
519 | a->err = true; | ||
520 | return cstr(""); | ||
521 | } | ||
522 | } | ||
523 | } | ||
524 | symmap_insert(&scope->symbols, symbol, | ||
525 | (Symbol){ | ||
526 | .name = type_name, | ||
527 | .kind = SYM_VAR, | ||
528 | }, | ||
529 | a->storage); | ||
530 | return node->type; | ||
531 | } | ||
532 | |||
533 | // We don't know the type for this symbol, perform inference. | ||
534 | Str type = type_inference(a, node->var_val, scope); | ||
535 | if (type.size) { | ||
536 | symmap_insert(&scope->symbols, symbol, | ||
537 | (Symbol){.name = type, .kind = SYM_VAR}, | ||
538 | a->storage); | ||
539 | node->var_name->type = type; | ||
540 | } | ||
541 | return node->type; | ||
542 | } break; | ||
543 | case NODE_SET: { | ||
544 | Str name = type_inference(a, node->var_name, scope); | ||
545 | Str val = type_inference(a, node->var_val, scope); | ||
546 | if (!str_eq(name, val)) { | ||
547 | eprintln( | ||
548 | "%s:%d:%d: error: type mismatch, trying to assing " | ||
549 | "%s" | ||
550 | " to a variable of type %s", | ||
551 | a->file_name, node->line, node->col, val, name); | ||
552 | a->err = true; | ||
553 | return cstr(""); | ||
554 | } | ||
555 | node->type = cstr("nil"); | ||
556 | return node->type; | ||
557 | } break; | ||
558 | case NODE_STRUCT: { | ||
559 | node->type = cstr("nil"); | ||
560 | Str symbol = node->value.str; | ||
561 | if (symmap_lookup(&scope->symbols, symbol) != NULL) { | ||
562 | eprintln( | ||
563 | "%s:%d:%d: error: struct '%s' already exists in current " | ||
564 | "scope", | ||
565 | a->file_name, node->line, node->col, symbol); | ||
566 | a->err = true; | ||
567 | return cstr(""); | ||
568 | } | ||
569 | structmap_insert(&scope->structs, symbol, (Struct){.name = symbol}, | ||
570 | a->storage); | ||
571 | for (sz i = 0; i < array_size(node->struct_field); i++) { | ||
572 | Node *field = node->struct_field[i]; | ||
573 | typecheck_field(a, field, scope, symbol); | ||
574 | } | ||
575 | symmap_insert(&scope->symbols, symbol, | ||
576 | (Symbol){.name = symbol, .kind = SYM_STRUCT}, | ||
577 | a->storage); | ||
578 | return node->type; | ||
579 | } break; | ||
580 | case NODE_ENUM: { | ||
581 | node->type = cstr("nil"); | ||
582 | Str symbol = node->value.str; | ||
583 | if (symmap_lookup(&scope->symbols, symbol) != NULL) { | ||
584 | eprintln( | ||
585 | "%s:%d:%d: error: enum '%s' already exists in current " | ||
586 | "scope", | ||
587 | a->file_name, node->line, node->col, symbol); | ||
588 | a->err = true; | ||
589 | return cstr(""); | ||
590 | } | ||
591 | enummap_insert(&scope->enums, symbol, | ||
592 | (Enum){ | ||
593 | .name = symbol, | ||
594 | .val = node->field_val, | ||
595 | }, | ||
596 | a->storage); | ||
597 | for (sz i = 0; i < array_size(node->struct_field); i++) { | ||
598 | Node *field = node->struct_field[i]; | ||
599 | Str field_name = str_concat(symbol, cstr("."), a->storage); | ||
600 | field_name = | ||
601 | str_concat(field_name, field->value.str, a->storage); | ||
602 | if (enummap_lookup(&scope->enums, field_name)) { | ||
603 | eprintln("%s:%d:%d: error: enum field '%s' already exists", | ||
604 | a->file_name, field->line, field->col, field_name); | ||
605 | a->err = true; | ||
606 | } | ||
607 | if (field->field_val) { | ||
608 | Str type = type_inference(a, field->field_val, scope); | ||
609 | if (!str_eq(type, cstr("int"))) { | ||
610 | eprintln( | ||
611 | "%s:%d:%d: error: non int enum value for '%s.%s'", | ||
612 | a->file_name, field->line, field->col, symbol, | ||
613 | field_name); | ||
614 | a->err = true; | ||
615 | } | ||
616 | } | ||
617 | enummap_insert(&scope->enums, field_name, | ||
618 | (Enum){.name = field_name}, a->storage); | ||
619 | symmap_insert( | ||
620 | &scope->symbols, field_name, | ||
621 | (Symbol){.name = field_name, .kind = SYM_ENUM_FIELD}, | ||
622 | a->storage); | ||
623 | field->type = symbol; | ||
624 | } | ||
625 | symmap_insert(&scope->symbols, symbol, | ||
626 | (Symbol){.name = symbol, .kind = SYM_ENUM}, | ||
627 | a->storage); | ||
628 | return node->type; | ||
629 | } break; | ||
630 | case NODE_IF: { | ||
631 | Str cond_type = type_inference(a, node->cond_if, scope); | ||
632 | if (!str_eq(cond_type, cstr("bool"))) { | ||
633 | emit_semantic_error( | ||
634 | a, node->cond_if, | ||
635 | cstr("non boolean expression on if condition")); | ||
636 | return cstr(""); | ||
637 | } | ||
638 | if (node->cond_expr->kind == NODE_BLOCK) { | ||
639 | node->type = type_inference(a, node->cond_expr, scope); | ||
640 | } else { | ||
641 | Scope *next = typescope_alloc(a, scope); | ||
642 | node->type = type_inference(a, node->cond_expr, next); | ||
643 | } | ||
644 | if (node->cond_else) { | ||
645 | Str else_type; | ||
646 | if (node->cond_else->kind == NODE_BLOCK) { | ||
647 | else_type = type_inference(a, node->cond_else, scope); | ||
648 | } else { | ||
649 | Scope *next = typescope_alloc(a, scope); | ||
650 | else_type = type_inference(a, node->cond_else, next); | ||
651 | } | ||
652 | if (!str_eq(node->type, else_type)) { | ||
653 | emit_semantic_error( | ||
654 | a, node, cstr("mismatch types for if/else branches")); | ||
655 | return cstr(""); | ||
656 | } | ||
657 | } | ||
658 | return node->type; | ||
659 | } break; | ||
660 | case NODE_WHILE: { | ||
661 | Str cond_type = type_inference(a, node->while_cond, scope); | ||
662 | if (!str_eq(cond_type, cstr("bool"))) { | ||
663 | emit_semantic_error( | ||
664 | a, node->cond_if, | ||
665 | cstr("non boolean expression on while condition")); | ||
666 | return cstr(""); | ||
667 | } | ||
668 | if (node->while_expr->kind != NODE_BLOCK) { | ||
669 | scope = typescope_alloc(a, scope); | ||
670 | } | ||
671 | type_inference(a, node->while_expr, scope); | ||
672 | node->type = cstr("nil"); | ||
673 | return node->type; | ||
674 | } break; | ||
675 | case NODE_COND: { | ||
676 | Str previous = cstr(""); | ||
677 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
678 | Node *expr = node->match_cases[i]; | ||
679 | Str next = type_inference(a, expr, scope); | ||
680 | if (i != 0 && !str_eq(next, previous)) { | ||
681 | emit_semantic_error( | ||
682 | a, node, | ||
683 | cstr("non-matching types for cond expressions")); | ||
684 | return cstr(""); | ||
685 | } | ||
686 | previous = next; | ||
687 | } | ||
688 | node->type = previous; | ||
689 | return node->type; | ||
690 | } break; | ||
691 | case NODE_MATCH: { | ||
692 | Str e = type_inference(a, node->match_expr, scope); | ||
693 | if (str_eq(e, cstr("int"))) { | ||
694 | // Integer matching. | ||
695 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
696 | Node *field = node->match_cases[i]; | ||
697 | if (field->case_value) { | ||
698 | if (field->case_value->kind != NODE_NUM_INT && | ||
699 | field->case_value->kind != NODE_NUM_UINT) { | ||
700 | emit_semantic_error( | ||
701 | a, field->case_value, | ||
702 | cstr( | ||
703 | "non-integer or enum types on match case")); | ||
704 | } | ||
705 | } | ||
706 | } | ||
707 | } else { | ||
708 | // Get enum type and de-structure the match. | ||
709 | FindEnumResult res = find_enum(scope, e); | ||
710 | Str enum_prefix = | ||
711 | str_concat(res.map->val.name, cstr("."), a->storage); | ||
712 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
713 | Node *field = node->match_cases[i]; | ||
714 | if (field->case_value) { | ||
715 | Str field_name = str_concat( | ||
716 | enum_prefix, field->case_value->value.str, | ||
717 | a->storage); | ||
718 | if (!enummap_lookup(&res.scope->enums, field_name)) { | ||
719 | eprintln("%s:%d:%d: error: unknown enum field '%s'", | ||
720 | a->file_name, field->case_value->line, | ||
721 | field->case_value->col, field_name); | ||
722 | a->err = true; | ||
723 | } | ||
724 | } | ||
725 | } | ||
726 | } | ||
727 | Str previous = cstr(""); | ||
728 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
729 | Node *expr = node->match_cases[i]; | ||
730 | Str next = type_inference(a, expr, scope); | ||
731 | if (i != 0 && !str_eq(next, previous)) { | ||
732 | emit_semantic_error( | ||
733 | a, node, | ||
734 | cstr("non-matching types for match expressions")); | ||
735 | return cstr(""); | ||
736 | } | ||
737 | previous = next; | ||
738 | } | ||
739 | node->type = previous; | ||
740 | return node->type; | ||
741 | } break; | ||
742 | case NODE_CASE_MATCH: { | ||
743 | if (node->case_expr->kind != NODE_BLOCK) { | ||
744 | scope = typescope_alloc(a, scope); | ||
745 | } | ||
746 | node->type = type_inference(a, node->case_expr, scope); | ||
747 | return node->type; | ||
748 | } break; | ||
749 | case NODE_CASE_COND: { | ||
750 | if (node->case_value) { | ||
751 | Str cond = type_inference(a, node->case_value, scope); | ||
752 | if (!str_eq(cond, cstr("bool"))) { | ||
753 | emit_semantic_error(a, node, | ||
754 | cstr("non-boolean case condition")); | ||
755 | } | ||
756 | } | ||
757 | if (node->case_expr->kind != NODE_BLOCK) { | ||
758 | scope = typescope_alloc(a, scope); | ||
759 | } | ||
760 | node->type = type_inference(a, node->case_expr, scope); | ||
761 | return node->type; | ||
762 | } break; | ||
763 | case NODE_TRUE: | ||
764 | case NODE_FALSE: { | ||
765 | node->type = cstr("bool"); | ||
766 | return node->type; | ||
767 | } break; | ||
768 | case NODE_NIL: { | ||
769 | node->type = cstr("nil"); | ||
770 | return node->type; | ||
771 | } break; | ||
772 | case NODE_NOT: | ||
773 | case NODE_AND: | ||
774 | case NODE_OR: { | ||
775 | Str left = type_inference(a, node->left, scope); | ||
776 | if (!str_eq(left, cstr("bool"))) { | ||
777 | emit_semantic_error(a, node, | ||
778 | cstr("expected bool on logic expression")); | ||
779 | return cstr(""); | ||
780 | } | ||
781 | if (node->right) { | ||
782 | Str right = type_inference(a, node->right, scope); | ||
783 | if (!str_eq(right, cstr("bool"))) { | ||
784 | emit_semantic_error( | ||
785 | a, node, cstr("expected bool on logic expression")); | ||
786 | return cstr(""); | ||
787 | } | ||
788 | } | ||
789 | node->type = cstr("bool"); | ||
790 | return node->type; | ||
791 | } break; | ||
792 | case NODE_EQ: | ||
793 | case NODE_NEQ: | ||
794 | case NODE_LT: | ||
795 | case NODE_GT: | ||
796 | case NODE_LE: | ||
797 | case NODE_GE: { | ||
798 | Str left = type_inference(a, node->left, scope); | ||
799 | Str right = type_inference(a, node->right, scope); | ||
800 | if (!str_eq(left, right)) { | ||
801 | emit_semantic_error( | ||
802 | a, node, cstr("mismatched types on binary expression")); | ||
803 | return cstr(""); | ||
804 | } | ||
805 | node->type = cstr("bool"); | ||
806 | return node->type; | ||
807 | } break; | ||
808 | case NODE_BITNOT: { | ||
809 | Str left = type_inference(a, node->left, scope); | ||
810 | if (!strset_lookup(&a->integer_types, left)) { | ||
811 | emit_semantic_error( | ||
812 | a, node, cstr("non integer type on bit twiddling expr")); | ||
813 | return cstr(""); | ||
814 | } | ||
815 | node->type = left; | ||
816 | return node->type; | ||
817 | } break; | ||
818 | case NODE_BITAND: | ||
819 | case NODE_BITOR: | ||
820 | case NODE_BITLSHIFT: | ||
821 | case NODE_BITRSHIFT: { | ||
822 | Str left = type_inference(a, node->left, scope); | ||
823 | Str right = type_inference(a, node->right, scope); | ||
824 | if (!strset_lookup(&a->integer_types, left) || | ||
825 | !strset_lookup(&a->integer_types, right)) { | ||
826 | emit_semantic_error( | ||
827 | a, node, cstr("non integer type on bit twiddling expr")); | ||
828 | return cstr(""); | ||
829 | } | ||
830 | node->type = left; | ||
831 | return node->type; | ||
832 | } break; | ||
833 | case NODE_ADD: | ||
834 | case NODE_SUB: | ||
835 | case NODE_DIV: | ||
836 | case NODE_MUL: | ||
837 | case NODE_MOD: { | ||
838 | Str left = type_inference(a, node->left, scope); | ||
839 | Str right = type_inference(a, node->right, scope); | ||
840 | if (!strset_lookup(&a->numeric_types, left) || | ||
841 | !strset_lookup(&a->numeric_types, right)) { | ||
842 | emit_semantic_error( | ||
843 | a, node, cstr("non numeric type on arithmetic expr")); | ||
844 | return cstr(""); | ||
845 | } | ||
846 | if (!str_eq(left, right)) { | ||
847 | emit_semantic_error( | ||
848 | a, node, cstr("mismatched types on binary expression")); | ||
849 | return cstr(""); | ||
850 | } | ||
851 | node->type = left; | ||
852 | return node->type; | ||
853 | } break; | ||
854 | case NODE_NUM_UINT: { | ||
855 | node->type = cstr("uint"); | ||
856 | return node->type; | ||
857 | } break; | ||
858 | case NODE_NUM_INT: { | ||
859 | node->type = cstr("int"); | ||
860 | return node->type; | ||
861 | } break; | ||
862 | case NODE_NUM_FLOAT: { | ||
863 | node->type = cstr("f64"); | ||
864 | return node->type; | ||
865 | } break; | ||
866 | case NODE_STRING: { | ||
867 | node->type = cstr("str"); | ||
868 | return node->type; | ||
869 | } break; | ||
870 | case NODE_ARR_TYPE: | ||
871 | case NODE_TYPE: { | ||
872 | SymbolMap *type = find_type(scope, node->value.str); | ||
873 | if (!type) { | ||
874 | emit_semantic_error(a, node, cstr("unknown type")); | ||
875 | return cstr(""); | ||
876 | } | ||
877 | node->type = type->val.name; | ||
878 | return node->type; | ||
879 | } break; | ||
880 | case NODE_SYMBOL_IDX: | ||
881 | case NODE_SYMBOL: { | ||
882 | Str symbol = node->value.str; | ||
883 | SymbolMap *type = find_type(scope, symbol); | ||
884 | if (!type) { | ||
885 | eprintln("%s:%d:%d: error: couldn't resolve symbol '%s'", | ||
886 | a->file_name, node->line, node->col, symbol); | ||
887 | a->err = true; | ||
888 | return cstr(""); | ||
889 | } | ||
890 | Str type_name = type->val.name; | ||
891 | if (node->kind == NODE_SYMBOL_IDX) { | ||
892 | Str idx_type = type_inference(a, node->arr_size, scope); | ||
893 | if (!strset_lookup(&a->integer_types, idx_type)) { | ||
894 | emit_semantic_error( | ||
895 | a, node, cstr("can't resolve non integer index")); | ||
896 | return cstr(""); | ||
897 | } | ||
898 | type_name = str_remove_prefix(type_name, cstr("@")); | ||
899 | } | ||
900 | if (node->is_ptr) { | ||
901 | type_name = str_concat(cstr("@"), type_name, a->storage); | ||
902 | } | ||
903 | |||
904 | FindEnumResult e = find_enum(scope, type_name); | ||
905 | if (e.map && str_eq(symbol, type_name)) { | ||
906 | if (!node->next) { | ||
907 | eprintln( | ||
908 | "%s:%d:%d: error: unspecified enum field for symbol " | ||
909 | "'%s'", | ||
910 | a->file_name, node->line, node->col, symbol); | ||
911 | a->err = true; | ||
912 | return cstr(""); | ||
913 | } | ||
914 | // Check if there is a next and it matches the enum field. | ||
915 | Str field = str_concat(type_name, cstr("."), a->storage); | ||
916 | field = str_concat(field, node->next->value.str, a->storage); | ||
917 | if (!enummap_lookup(&e.scope->enums, field)) { | ||
918 | eprintln( | ||
919 | "%s:%d:%d: error: unknown enum field for " | ||
920 | "'%s': %s", | ||
921 | a->file_name, node->line, node->col, symbol, | ||
922 | node->next->value.str); | ||
923 | a->err = true; | ||
924 | return cstr(""); | ||
925 | } | ||
926 | node->next->type = type_name; | ||
927 | node->type = type_name; | ||
928 | return node->next->type; | ||
929 | } | ||
930 | |||
931 | FindStructResult s = find_struct(scope, type_name); | ||
932 | if (s.map) { | ||
933 | if (str_eq(symbol, type_name)) { | ||
934 | eprintln( | ||
935 | "%s:%d:%d: error: struct incomplete struct literal " | ||
936 | "'%s', did you mean to use %s:{}?", | ||
937 | a->file_name, node->line, node->col, symbol, symbol); | ||
938 | a->err = true; | ||
939 | return cstr(""); | ||
940 | } else { | ||
941 | if (node->next) { | ||
942 | Str chain = type_name; | ||
943 | Node *next = node; | ||
944 | while (next->next) { | ||
945 | next = next->next; | ||
946 | chain = str_concat(chain, cstr("."), a->storage); | ||
947 | chain = | ||
948 | str_concat(chain, next->value.str, a->storage); | ||
949 | } | ||
950 | StructMap *field = | ||
951 | structmap_lookup(&s.scope->structs, chain); | ||
952 | if (!field) { | ||
953 | eprintln( | ||
954 | "%s:%d:%d: error: unknown struct field '%s'", | ||
955 | a->file_name, node->line, node->col, chain); | ||
956 | a->err = true; | ||
957 | return cstr(""); | ||
958 | } | ||
959 | Str field_type = field->val.type; | ||
960 | if (next->kind == NODE_SYMBOL_IDX) { | ||
961 | Str idx_type = | ||
962 | type_inference(a, next->arr_size, scope); | ||
963 | if (!strset_lookup(&a->integer_types, idx_type)) { | ||
964 | emit_semantic_error( | ||
965 | a, next, | ||
966 | cstr("can't resolve non integer index")); | ||
967 | return cstr(""); | ||
968 | } | ||
969 | field_type = | ||
970 | str_remove_prefix(field_type, cstr("@")); | ||
971 | } | ||
972 | node->type = field_type; | ||
973 | return node->type; | ||
974 | } | ||
975 | } | ||
976 | } | ||
977 | node->type = type_name; | ||
978 | return node->type; | ||
979 | } break; | ||
980 | case NODE_STRUCT_LIT: { | ||
981 | Str name = node->value.str; | ||
982 | FindStructResult s = find_struct(scope, name); | ||
983 | if (!s.map) { | ||
984 | eprintln("%s:%d:%d: error: unknown struct type '%s'", | ||
985 | a->file_name, node->line, node->col, name); | ||
986 | a->err = true; | ||
987 | return cstr(""); | ||
988 | } | ||
989 | |||
990 | StrSet *set = NULL; | ||
991 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
992 | Node *next = node->elements[i]; | ||
993 | Str field_name = str_concat(name, cstr("."), a->storage); | ||
994 | field_name = | ||
995 | str_concat(field_name, next->value.str, a->storage); | ||
996 | |||
997 | if (strset_lookup(&set, field_name)) { | ||
998 | eprintln( | ||
999 | "%s:%d:%d: error: field '%s' already present in struct " | ||
1000 | "literal", | ||
1001 | a->file_name, next->line, next->col, field_name); | ||
1002 | a->err = true; | ||
1003 | } else { | ||
1004 | strset_insert(&set, field_name, a->storage); | ||
1005 | } | ||
1006 | typecheck_lit_field(a, next, s.scope, field_name); | ||
1007 | } | ||
1008 | node->type = name; | ||
1009 | return node->type; | ||
1010 | } break; | ||
1011 | case NODE_FUNCALL: { | ||
1012 | Str symbol = node->value.str; | ||
1013 | FunMap *fun = find_fun(scope, symbol); | ||
1014 | if (!fun) { | ||
1015 | eprintln( | ||
1016 | "%s:%d:%d: error: function '%s' doesn't exist in current " | ||
1017 | "scope ", | ||
1018 | a->file_name, node->line, node->col, symbol); | ||
1019 | a->err = true; | ||
1020 | return cstr(""); | ||
1021 | } | ||
1022 | // Check that actual parameters typecheck | ||
1023 | Str args = cstr(""); | ||
1024 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1025 | Node *expr = node->elements[i]; | ||
1026 | Str type = type_inference(a, expr, scope); | ||
1027 | args = str_concat(args, type, a->storage); | ||
1028 | if (i != array_size(node->elements) - 1) { | ||
1029 | args = str_concat(args, cstr(","), a->storage); | ||
1030 | } | ||
1031 | } | ||
1032 | if (!args.size) { | ||
1033 | args = cstr("nil"); | ||
1034 | } | ||
1035 | Str expected = fun->val.param_type; | ||
1036 | if (!str_eq(args, expected) && !str_eq(expected, cstr("..."))) { | ||
1037 | eprintln( | ||
1038 | "%s:%d:%d: error: mismatched parameter types: %s expected " | ||
1039 | "%s", | ||
1040 | a->file_name, node->line, node->col, args, expected); | ||
1041 | a->err = true; | ||
1042 | } | ||
1043 | node->type = fun->val.return_type; | ||
1044 | return node->type; | ||
1045 | } break; | ||
1046 | case NODE_BLOCK: { | ||
1047 | scope = typescope_alloc(a, scope); | ||
1048 | Str type; | ||
1049 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1050 | Node *expr = node->elements[i]; | ||
1051 | type = type_inference(a, expr, scope); | ||
1052 | } | ||
1053 | node->type = type; | ||
1054 | return node->type; | ||
1055 | } break; | ||
1056 | case NODE_RETURN: { | ||
1057 | Str ret_type = cstr(""); | ||
1058 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1059 | Node *expr = node->elements[i]; | ||
1060 | Str type = type_inference(a, expr, scope); | ||
1061 | ret_type = str_concat(ret_type, type, a->storage); | ||
1062 | if (i != array_size(node->elements) - 1) { | ||
1063 | ret_type = str_concat(ret_type, cstr(","), a->storage); | ||
1064 | } | ||
1065 | } | ||
1066 | if (!ret_type.size) { | ||
1067 | ret_type = cstr("nil"); | ||
1068 | } | ||
1069 | node->type = ret_type; | ||
1070 | return node->type; | ||
1071 | } break; | ||
1072 | case NODE_FUN: { | ||
1073 | node->type = cstr("nil"); | ||
1074 | Scope *prev_scope = scope; | ||
1075 | scope = typescope_alloc(a, scope); | ||
1076 | Str param_type = cstr(""); | ||
1077 | for (sz i = 0; i < array_size(node->func_params); i++) { | ||
1078 | Node *param = node->func_params[i]; | ||
1079 | Str symbol = param->param_name->value.str; | ||
1080 | Str type = param->param_type->value.str; | ||
1081 | if (param->param_type->is_ptr) { | ||
1082 | type = str_concat(cstr("@"), type, a->storage); | ||
1083 | } | ||
1084 | if (param->param_type->kind == NODE_ARR_TYPE) { | ||
1085 | type = str_concat(cstr("@"), type, a->storage); | ||
1086 | } | ||
1087 | param->param_name->type = | ||
1088 | type_inference(a, param->param_type, scope); | ||
1089 | param->type = type; | ||
1090 | symmap_insert(&scope->symbols, symbol, | ||
1091 | (Symbol){.name = type, .kind = SYM_PARAM}, | ||
1092 | a->storage); | ||
1093 | param_type = str_concat(param_type, type, a->storage); | ||
1094 | if (i != array_size(node->func_params) - 1) { | ||
1095 | param_type = str_concat(param_type, cstr(","), a->storage); | ||
1096 | } | ||
1097 | } | ||
1098 | if (!param_type.size) { | ||
1099 | param_type = cstr("nil"); | ||
1100 | } | ||
1101 | node->fun_params = param_type; | ||
1102 | |||
1103 | Str ret_type = cstr(""); | ||
1104 | for (sz i = 0; i < array_size(node->func_ret); i++) { | ||
1105 | Node *expr = node->func_ret[i]; | ||
1106 | Str type = type_inference(a, expr, scope); | ||
1107 | if (expr->is_ptr) { | ||
1108 | type = str_concat(cstr("@"), type, a->storage); | ||
1109 | } | ||
1110 | if (expr->kind == NODE_ARR_TYPE) { | ||
1111 | type = str_concat(cstr("@"), type, a->storage); | ||
1112 | } | ||
1113 | ret_type = str_concat(ret_type, type, a->storage); | ||
1114 | if (i != array_size(node->func_ret) - 1) { | ||
1115 | ret_type = str_concat(ret_type, cstr(","), a->storage); | ||
1116 | } | ||
1117 | } | ||
1118 | if (!ret_type.size) { | ||
1119 | ret_type = cstr("nil"); | ||
1120 | } | ||
1121 | node->fun_return = ret_type; | ||
1122 | |||
1123 | Str symbol = node->func_name->value.str; | ||
1124 | if (prev_scope->parent != NULL) { | ||
1125 | if (symmap_lookup(&prev_scope->symbols, symbol)) { | ||
1126 | eprintln( | ||
1127 | "%s:%d:%d: error: function '%s' already defined in " | ||
1128 | "current " | ||
1129 | "scope ", | ||
1130 | a->file_name, node->var_name->line, node->var_name->col, | ||
1131 | symbol); | ||
1132 | a->err = true; | ||
1133 | return cstr(""); | ||
1134 | } | ||
1135 | symmap_insert(&scope->symbols, symbol, | ||
1136 | (Symbol){.name = symbol, .kind = SYM_FUN}, | ||
1137 | a->storage); | ||
1138 | } | ||
1139 | scope->name = symbol; | ||
1140 | funmap_insert(&prev_scope->funcs, symbol, | ||
1141 | (Fun){.name = symbol, | ||
1142 | .param_type = param_type, | ||
1143 | .return_type = ret_type}, | ||
1144 | a->storage); | ||
1145 | |||
1146 | if (node->func_body->kind == NODE_BLOCK) { | ||
1147 | Str type; | ||
1148 | for (sz i = 0; i < array_size(node->func_body->elements); i++) { | ||
1149 | Node *expr = node->func_body->elements[i]; | ||
1150 | type = type_inference(a, expr, scope); | ||
1151 | } | ||
1152 | if (!type.size) { | ||
1153 | type = cstr("nil"); | ||
1154 | } | ||
1155 | node->func_body->type = type; | ||
1156 | } else { | ||
1157 | type_inference(a, node->func_body, scope); | ||
1158 | } | ||
1159 | |||
1160 | // Ensure main body return matches the prototype. | ||
1161 | if (!str_eq(node->func_body->type, ret_type)) { | ||
1162 | eprintln( | ||
1163 | "%s:%d:%d: error: mismatched return type %s, expected %s", | ||
1164 | a->file_name, node->line, node->col, node->func_body->type, | ||
1165 | ret_type); | ||
1166 | a->err = true; | ||
1167 | } | ||
1168 | |||
1169 | // Ensure ALL return statements match the function prototype. | ||
1170 | typecheck_returns(a, node->func_body, ret_type); | ||
1171 | |||
1172 | // TODO: should return statements be allowed on let blocks? | ||
1173 | return node->type; | ||
1174 | } break; | ||
1175 | default: { | ||
1176 | emit_semantic_error(a, node, | ||
1177 | cstr("type inference not implemented for this " | ||
1178 | "kind of expression")); | ||
1179 | println("KIND: %s", node_str[node->kind]); | ||
1180 | } break; | ||
1181 | } | ||
1182 | return cstr(""); | ||
1183 | } | ||
1184 | |||
1185 | void | ||
1186 | symbolic_analysis(Analyzer *a, Parser *parser) { | ||
1187 | Scope *scope = typescope_alloc(a, NULL); | ||
1188 | assert(a); | ||
1189 | assert(parser); | ||
1190 | |||
1191 | // Fill builtin tables. | ||
1192 | Str builtin_functions[] = { | ||
1193 | cstr("print"), | ||
1194 | cstr("println"), | ||
1195 | }; | ||
1196 | for (sz i = 0; i < LEN(builtin_functions); i++) { | ||
1197 | Str symbol = builtin_functions[i]; | ||
1198 | symmap_insert(&scope->symbols, symbol, | ||
1199 | (Symbol){.name = symbol, .kind = SYM_BUILTIN_FUN}, | ||
1200 | a->storage); | ||
1201 | funmap_insert(&scope->funcs, symbol, | ||
1202 | (Fun){.name = symbol, | ||
1203 | .param_type = cstr("..."), | ||
1204 | .return_type = cstr("nil")}, | ||
1205 | a->storage); | ||
1206 | } | ||
1207 | Str builtin_types[] = { | ||
1208 | cstr("u8"), cstr("s8"), cstr("u16"), cstr("s16"), | ||
1209 | cstr("u32"), cstr("s32"), cstr("u64"), cstr("s64"), | ||
1210 | cstr("f32"), cstr("f64"), cstr("ptr"), cstr("int"), | ||
1211 | cstr("uint"), cstr("str"), cstr("bool"), cstr("nil")}; | ||
1212 | for (sz i = 0; i < LEN(builtin_types); i++) { | ||
1213 | Str type = builtin_types[i]; | ||
1214 | symmap_insert(&scope->symbols, type, | ||
1215 | (Symbol){.name = type, .kind = SYM_BUILTIN_TYPE}, | ||
1216 | a->storage); | ||
1217 | } | ||
1218 | Str numeric_types[] = { | ||
1219 | cstr("u8"), cstr("s8"), cstr("u16"), cstr("s16"), cstr("u32"), | ||
1220 | cstr("s32"), cstr("u64"), cstr("s64"), cstr("f32"), cstr("f64"), | ||
1221 | cstr("ptr"), cstr("int"), cstr("uint"), | ||
1222 | }; | ||
1223 | for (sz i = 0; i < LEN(numeric_types); i++) { | ||
1224 | Str type = numeric_types[i]; | ||
1225 | strset_insert(&a->numeric_types, type, a->storage); | ||
1226 | } | ||
1227 | Str integer_types[] = { | ||
1228 | cstr("u8"), cstr("s8"), cstr("u16"), cstr("s16"), | ||
1229 | cstr("u32"), cstr("s32"), cstr("u64"), cstr("s64"), | ||
1230 | cstr("ptr"), cstr("int"), cstr("uint"), | ||
1231 | }; | ||
1232 | for (sz i = 0; i < LEN(integer_types); i++) { | ||
1233 | Str type = integer_types[i]; | ||
1234 | strset_insert(&a->integer_types, type, a->storage); | ||
1235 | } | ||
1236 | // Find top level function declarations. | ||
1237 | for (sz i = 0; i < array_size(parser->nodes); i++) { | ||
1238 | Node *root = parser->nodes[i]; | ||
1239 | if (root->kind == NODE_FUN) { | ||
1240 | Str symbol = root->func_name->value.str; | ||
1241 | if (symmap_lookup(&scope->symbols, symbol)) { | ||
1242 | eprintln( | ||
1243 | "%s:%d:%d: error: function '%s' already defined in " | ||
1244 | "current " | ||
1245 | "scope ", | ||
1246 | a->file_name, root->var_name->line, root->var_name->col, | ||
1247 | symbol); | ||
1248 | a->err = true; | ||
1249 | } | ||
1250 | symmap_insert(&scope->symbols, symbol, | ||
1251 | (Symbol){.name = symbol, .kind = SYM_FUN}, | ||
1252 | a->storage); | ||
1253 | } | ||
1254 | } | ||
1255 | // Recursively fill symbol tables. | ||
1256 | for (sz i = 0; i < array_size(parser->nodes); i++) { | ||
1257 | Node *root = parser->nodes[i]; | ||
1258 | type_inference(a, root, scope); | ||
1259 | } | ||
1260 | } | ||
1261 | |||
1262 | void | 31 | void |
1263 | process_file(Str path) { | 32 | process_file(Str path) { |
1264 | #if DEBUG == 1 | 33 | #if DEBUG == 1 |
@@ -1396,43 +165,43 @@ process_file(Str path) { | |||
1396 | // TODO: Type checking. | 165 | // TODO: Type checking. |
1397 | 166 | ||
1398 | // Compile roots. | 167 | // Compile roots. |
1399 | // Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); | 168 | Arena bytecode_arena = arena_create(LEXER_MEM, os_allocator); |
1400 | // Chunk chunk = {.file_name = path, .storage = &bytecode_arena}; | 169 | Chunk chunk = {.file_name = path, .storage = &bytecode_arena}; |
1401 | // array_zero(chunk.constants, 256, &bytecode_arena); | 170 | array_zero(chunk.constants, 256, &bytecode_arena); |
1402 | // array_zero(chunk.code, 0xffff, &bytecode_arena); | 171 | array_zero(chunk.code, 0xffff, &bytecode_arena); |
1403 | // sz n_roots = array_size(parser.nodes); | 172 | sz n_roots = array_size(parser.nodes); |
1404 | // CompResult res; | 173 | CompResult res; |
1405 | // for (sz i = 0; i < n_roots; i++) { | 174 | for (sz i = 0; i < n_roots; i++) { |
1406 | // // The parser stores the root nodes as a stack. | 175 | // The parser stores the root nodes as a stack. |
1407 | // Node *root = parser.nodes[i]; | 176 | Node *root = parser.nodes[i]; |
1408 | // res = compile_expr(&chunk, root); | 177 | res = compile_expr(&chunk, root); |
1409 | // } | 178 | } |
1410 | // sz res_reg = 0; | 179 | sz res_reg = 0; |
1411 | // switch (res.type) { | 180 | switch (res.type) { |
1412 | // case COMP_CONST: { | 181 | case COMP_CONST: { |
1413 | // res_reg = chunk.reg_idx++; | 182 | res_reg = chunk.reg_idx++; |
1414 | // Instruction inst = | 183 | Instruction inst = |
1415 | // (Instruction){.op = OP_LD64K, .dst = res_reg, .a = res.idx}; | 184 | (Instruction){.op = OP_LD64K, .dst = res_reg, .a = res.idx}; |
1416 | // array_push(chunk.code, inst, chunk.storage); | 185 | array_push(chunk.code, inst, chunk.storage); |
1417 | // } break; | 186 | } break; |
1418 | // case COMP_REG: { | 187 | case COMP_REG: { |
1419 | // res_reg = res.idx; | 188 | res_reg = res.idx; |
1420 | // } break; | 189 | } break; |
1421 | // default: break; | 190 | default: break; |
1422 | // } | 191 | } |
1423 | // // After we are done move the last result to r0 for printing. | 192 | // After we are done move the last result to r0 for printing. |
1424 | // Instruction halt = (Instruction){.op = OP_HALT, .dst = res_reg}; | 193 | Instruction halt = (Instruction){.op = OP_HALT, .dst = res_reg}; |
1425 | // array_push(chunk.code, halt, &bytecode_arena); | 194 | array_push(chunk.code, halt, &bytecode_arena); |
1426 | 195 | ||
1427 | // // Run bytecode on VM. | 196 | // Run bytecode on VM. |
1428 | // VM vm = {0}; | 197 | VM vm = {0}; |
1429 | // vm_init(&vm, &chunk); | 198 | vm_init(&vm, &chunk); |
1430 | // // println("VM REGISTERS BEFORE:\n%{Mem}", | 199 | // println("VM REGISTERS BEFORE:\n%{Mem}", |
1431 | // // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); | 200 | // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); |
1432 | // vm_run(&vm); | 201 | vm_run(&vm); |
1433 | // println("VM REGISTERS AFTER:\n%{Mem}", | 202 | // println("VM REGISTERS AFTER:\n%{Mem}", |
1434 | // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); | 203 | // &(Array){.mem = (u8 *)&vm.regs, sizeof(vm.regs)}); |
1435 | // disassemble_chunk(chunk); | 204 | disassemble_chunk(chunk); |
1436 | 205 | ||
1437 | #if DEBUG == 1 | 206 | #if DEBUG == 1 |
1438 | println("Space used: %{Arena}", &lexer_arena); | 207 | println("Space used: %{Arena}", &lexer_arena); |
diff --git a/src/parser.c b/src/parser.c index cdd3c47..f7d0d41 100644 --- a/src/parser.c +++ b/src/parser.c | |||
@@ -450,7 +450,10 @@ parse_literal(Parser *parser) { | |||
450 | #endif | 450 | #endif |
451 | Node *node = NULL; | 451 | Node *node = NULL; |
452 | switch (prev.kind) { | 452 | switch (prev.kind) { |
453 | case TOK_TRUE: node = node_alloc(parser, NODE_TRUE, prev); break; | 453 | case TOK_TRUE: { |
454 | node = node_alloc(parser, NODE_TRUE, prev); | ||
455 | node->value.i = 1; | ||
456 | } break; | ||
454 | case TOK_FALSE: node = node_alloc(parser, NODE_FALSE, prev); break; | 457 | case TOK_FALSE: node = node_alloc(parser, NODE_FALSE, prev); break; |
455 | case TOK_NIL: node = node_alloc(parser, NODE_NIL, prev); break; | 458 | case TOK_NIL: node = node_alloc(parser, NODE_NIL, prev); break; |
456 | default: return; // Unreachable. | 459 | default: return; // Unreachable. |
diff --git a/src/semantic.c b/src/semantic.c index fe88249..428cc53 100644 --- a/src/semantic.c +++ b/src/semantic.c | |||
@@ -1,544 +1,1234 @@ | |||
1 | #include "hashtable.h" | 1 | #include "badlib.h" |
2 | 2 | ||
3 | typedef struct Scope { | 3 | typedef enum { |
4 | size_t id; | 4 | SYM_UNKNOWN, |
5 | struct Scope *parent; | 5 | SYM_BUILTIN_FUN, |
6 | HashTable *symbols; | 6 | SYM_BUILTIN_TYPE, |
7 | HashTable *types; | 7 | SYM_FUN, |
8 | } Scope; | 8 | SYM_VAR, |
9 | SYM_PARAM, | ||
10 | SYM_ENUM, | ||
11 | SYM_ENUM_FIELD, | ||
12 | SYM_STRUCT, | ||
13 | SYM_STRUCT_FIELD, | ||
14 | } SymbolKind; | ||
9 | 15 | ||
10 | typedef struct ParseTree { | 16 | Str sym_kind_str[] = { |
11 | Root *roots; | 17 | [SYM_UNKNOWN] = cstr("UNKNOWN "), |
12 | Scope **scopes; | 18 | [SYM_BUILTIN_FUN] = cstr("BUILTIN FUN "), |
13 | } ParseTree; | 19 | [SYM_BUILTIN_TYPE] = cstr("BUILTIN TYPE "), |
14 | 20 | [SYM_FUN] = cstr("FUNCTION "), | |
15 | typedef struct Type { | 21 | [SYM_VAR] = cstr("VARIABLE "), |
16 | StringView name; | 22 | [SYM_PARAM] = cstr("PARAMETER "), |
17 | size_t size; // (bytes) | 23 | [SYM_ENUM] = cstr("ENUM "), |
18 | } Type; | 24 | [SYM_ENUM_FIELD] = cstr("ENUM FIELD "), |
19 | 25 | [SYM_STRUCT] = cstr("STRUCT "), | |
20 | typedef enum DefaultType { | 26 | [SYM_STRUCT_FIELD] = cstr("STRUCT FIELD "), |
21 | TYPE_VOID, | ||
22 | TYPE_BOOL, | ||
23 | TYPE_STR, | ||
24 | TYPE_U8, | ||
25 | TYPE_U16, | ||
26 | TYPE_U32, | ||
27 | TYPE_U64, | ||
28 | TYPE_S8, | ||
29 | TYPE_S16, | ||
30 | TYPE_S32, | ||
31 | TYPE_S64, | ||
32 | TYPE_F32, | ||
33 | TYPE_F64, | ||
34 | } DefaultType; | ||
35 | |||
36 | static Type default_types[] = { | ||
37 | [TYPE_VOID] = {STRING("void"), 0}, | ||
38 | [TYPE_BOOL] = {STRING("bool"), 1}, | ||
39 | [TYPE_STR] = {STRING("str"), 16}, // size (8) + pointer to data (8). | ||
40 | [TYPE_U8] = {STRING("u8"), 1}, | ||
41 | [TYPE_U16] = {STRING("u16"), 2}, | ||
42 | [TYPE_U32] = {STRING("u32"), 4}, | ||
43 | [TYPE_U64] = {STRING("u64"), 8}, | ||
44 | [TYPE_S8] = {STRING("s8"), 1}, | ||
45 | [TYPE_S16] = {STRING("s16"), 2}, | ||
46 | [TYPE_S32] = {STRING("s32"), 4}, | ||
47 | [TYPE_S64] = {STRING("s64"), 8}, | ||
48 | [TYPE_F32] = {STRING("f32"), 4}, | ||
49 | [TYPE_F64] = {STRING("f64"), 8}, | ||
50 | }; | 27 | }; |
51 | 28 | ||
52 | typedef enum SymbolType { | ||
53 | SYMBOL_VAR, | ||
54 | SYMBOL_PAR, | ||
55 | SYMBOL_FUN, | ||
56 | } SymbolType; | ||
57 | |||
58 | typedef struct Symbol { | 29 | typedef struct Symbol { |
59 | Node *name; | 30 | Str name; |
60 | SymbolType type; | 31 | SymbolKind kind; |
61 | |||
62 | union { | ||
63 | struct { | ||
64 | Node *type; | ||
65 | } var; | ||
66 | |||
67 | struct { | ||
68 | Node **param_types; | ||
69 | Node *return_type; | ||
70 | } fun; | ||
71 | }; | ||
72 | } Symbol; | 32 | } Symbol; |
73 | 33 | ||
74 | static size_t scope_gen_id = 0; | 34 | typedef struct Fun { |
35 | Str name; | ||
36 | Str param_type; | ||
37 | Str return_type; | ||
38 | } Fun; | ||
75 | 39 | ||
76 | Symbol * | 40 | typedef struct Enum { |
77 | alloc_symval(Node *name, SymbolType type) { | 41 | Str name; |
78 | Symbol *val = malloc(sizeof(Symbol)); | 42 | Node *val; |
79 | val->name = name; | 43 | } Enum; |
80 | val->type = type; | ||
81 | return val; | ||
82 | } | ||
83 | 44 | ||
84 | u64 sym_hash(const struct HashTable *table, void *bytes) { | 45 | typedef struct Struct { |
85 | Node *symbol = bytes; | 46 | Str name; |
86 | u64 hash = _xor_shift_hash(symbol->string.start, symbol->string.n); | 47 | Str type; |
87 | hash = _fibonacci_hash(hash, table->shift_amount); | 48 | Node *val; |
88 | return hash; | 49 | } Struct; |
89 | } | ||
90 | 50 | ||
91 | bool sym_eq(void *a, void *b) { | 51 | MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) |
92 | Node *a_node = a; | 52 | MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq) |
93 | Node *b_node = b; | 53 | MAPDEF(EnumMap, enummap, Str, Enum, str_hash, str_eq) |
94 | assert(a_node->type == NODE_SYMBOL); | 54 | MAPDEF(StructMap, structmap, Str, Struct, str_hash, str_eq) |
95 | assert(b_node->type == NODE_SYMBOL); | ||
96 | return sv_equal(&a_node->string, &b_node->string); | ||
97 | } | ||
98 | 55 | ||
99 | u64 type_hash(const struct HashTable *table, void *bytes) { | 56 | typedef struct Scope { |
100 | StringView *type = bytes; | 57 | sz id; |
101 | u64 hash = _xor_shift_hash(type->start, type->n); | 58 | sz depth; |
102 | hash = _fibonacci_hash(hash, table->shift_amount); | 59 | Str name; |
103 | return hash; | 60 | SymbolMap *symbols; |
104 | } | 61 | FunMap *funcs; |
62 | EnumMap *enums; | ||
63 | StructMap *structs; | ||
64 | struct Scope *parent; | ||
65 | } Scope; | ||
105 | 66 | ||
106 | bool type_eq(void *a, void *b) { | 67 | typedef struct Analyzer { |
107 | StringView *a_type = a; | 68 | Arena *storage; |
108 | StringView *b_type = b; | 69 | Str file_name; |
109 | return sv_equal(a_type, b_type); | 70 | sz typescope_gen; |
110 | } | 71 | Scope **scopes; |
72 | StrSet *numeric_types; | ||
73 | StrSet *integer_types; | ||
74 | bool err; | ||
75 | } Analyzer; | ||
111 | 76 | ||
112 | Scope * | 77 | Scope * |
113 | alloc_scope(Scope *parent) { | 78 | typescope_alloc(Analyzer *a, Scope *parent) { |
114 | Scope *scope = malloc(sizeof(Scope)); | 79 | Scope *scope = arena_calloc(sizeof(Scope), a->storage); |
115 | scope->id = scope_gen_id++; | ||
116 | scope->parent = parent; | 80 | scope->parent = parent; |
117 | scope->symbols = ht_init(sym_hash, sym_eq); | 81 | scope->id = a->typescope_gen++; |
118 | scope->types = ht_init(type_hash, type_eq); | 82 | scope->depth = parent == NULL ? 0 : parent->depth + 1; |
83 | array_push(a->scopes, scope, a->storage); | ||
119 | return scope; | 84 | return scope; |
120 | } | 85 | } |
121 | 86 | ||
122 | Type * | 87 | SymbolMap * |
123 | find_type(Scope *scope, Node *type) { | 88 | find_type(Scope *scope, Str type) { |
124 | // TODO: Normally default types will be used more often. Since we don't | ||
125 | // allow type shadowing, we should search first on the global scope. | ||
126 | while (scope != NULL) { | 89 | while (scope != NULL) { |
127 | Type *ret = ht_lookup(scope->types, &type->string); | 90 | SymbolMap *val = symmap_lookup(&scope->symbols, type); |
128 | if (ret != NULL) { | 91 | if (val != NULL) { |
129 | return ret; | 92 | return val; |
130 | } | 93 | } |
131 | scope = scope->parent; | 94 | scope = scope->parent; |
132 | } | 95 | } |
133 | push_error(ERR_TYPE_SEMANTIC, ERR_UNKNOWN_TYPE, type->line, type->col); | ||
134 | return NULL; | 96 | return NULL; |
135 | } | 97 | } |
136 | 98 | ||
137 | bool | 99 | FunMap * |
138 | insert_symbol(Scope *scope, Node *symbol, Symbol *val) { | 100 | find_fun(Scope *scope, Str type) { |
139 | // Check if symbol already exists. | 101 | while (scope != NULL) { |
140 | HashTable *symbols = scope->symbols; | 102 | FunMap *val = funmap_lookup(&scope->funcs, type); |
141 | if (ht_lookup(symbols, symbol) != NULL) { | 103 | if (val != NULL) { |
142 | push_error(ERR_TYPE_SEMANTIC, ERR_SYMBOL_REDEF, symbol->line, symbol->col); | 104 | return val; |
143 | return false; | 105 | } |
106 | scope = scope->parent; | ||
144 | } | 107 | } |
145 | ht_insert(symbols, symbol, val); | 108 | return NULL; |
146 | return true; | ||
147 | } | 109 | } |
148 | 110 | ||
149 | Type * | 111 | typedef struct FindEnumResult { |
150 | coerce_numeric_types(Type *a, Type *b) { | 112 | EnumMap *map; |
151 | // TODO: Decide what to do with mixed numeric types. What are the promotion | 113 | Scope *scope; |
152 | // rules, etc. | 114 | } FindEnumResult; |
153 | if (a == &default_types[TYPE_U8]) { | 115 | |
154 | if (b == &default_types[TYPE_U16] || | 116 | FindEnumResult |
155 | b == &default_types[TYPE_U32] || | 117 | find_enum(Scope *scope, Str type) { |
156 | b == &default_types[TYPE_U64]) { | 118 | while (scope != NULL) { |
157 | return b; | 119 | EnumMap *val = enummap_lookup(&scope->enums, type); |
158 | } | 120 | if (val != NULL) { |
159 | } else if (a == &default_types[TYPE_U16]) { | 121 | return (FindEnumResult){.map = val, .scope = scope}; |
160 | if (b == &default_types[TYPE_U32] || | ||
161 | b == &default_types[TYPE_U64]) { | ||
162 | return b; | ||
163 | } | ||
164 | } else if (a == &default_types[TYPE_U32]) { | ||
165 | if (b == &default_types[TYPE_U64]) { | ||
166 | return b; | ||
167 | } | 122 | } |
168 | } else if (a == &default_types[TYPE_S8]) { | 123 | scope = scope->parent; |
169 | if (b == &default_types[TYPE_S16] || | 124 | } |
170 | b == &default_types[TYPE_S32] || | 125 | return (FindEnumResult){0}; |
171 | b == &default_types[TYPE_S64]) { | 126 | } |
172 | return b; | 127 | |
128 | typedef struct FindStructResult { | ||
129 | StructMap *map; | ||
130 | Scope *scope; | ||
131 | } FindStructResult; | ||
132 | |||
133 | FindStructResult | ||
134 | find_struct(Scope *scope, Str type) { | ||
135 | while (scope != NULL) { | ||
136 | StructMap *val = structmap_lookup(&scope->structs, type); | ||
137 | if (val != NULL) { | ||
138 | return (FindStructResult){.map = val, .scope = scope}; | ||
173 | } | 139 | } |
174 | } else if (a == &default_types[TYPE_S16]) { | 140 | scope = scope->parent; |
175 | if (b == &default_types[TYPE_S32] || | 141 | } |
176 | b == &default_types[TYPE_S64]) { | 142 | return (FindStructResult){0}; |
177 | return b; | 143 | } |
144 | |||
145 | void | ||
146 | graph_typescope(Scope *scope, Arena a) { | ||
147 | if (!scope->symbols) { | ||
148 | return; | ||
149 | } | ||
150 | SymbolMapIter iter = symmap_iterator(scope->symbols, &a); | ||
151 | SymbolMap *type = symmap_next(&iter, &a); | ||
152 | print( | ||
153 | "%d[shape=\"none\" label=<<TABLE ALIGN=\"left\" STYLE=\"rounded\" " | ||
154 | "BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"6\" " | ||
155 | "COLUMNS=\"*\">", | ||
156 | scope->id); | ||
157 | print( | ||
158 | "<TR >" | ||
159 | "<TD ALIGN=\"left\" > NAME </TD>" | ||
160 | "<TD ALIGN=\"left\" > TYPE </TD>" | ||
161 | "</TR>"); | ||
162 | while (type) { | ||
163 | print( | ||
164 | "<TR>" | ||
165 | "<TD ALIGN=\"left\"> %s </TD>" | ||
166 | "<TD ALIGN=\"left\"> %s</TD>" | ||
167 | "</TR>", | ||
168 | type->key, type->val.name); | ||
169 | type = symmap_next(&iter, &a); | ||
170 | } | ||
171 | println("</TABLE>>];"); | ||
172 | |||
173 | sz this_id = scope->id; | ||
174 | while (scope->parent) { | ||
175 | if (scope->parent->symbols) { | ||
176 | println("%d:e->%d:w;", this_id, scope->parent->id); | ||
177 | break; | ||
178 | } else { | ||
179 | scope = scope->parent; | ||
178 | } | 180 | } |
179 | } else if (a == &default_types[TYPE_S32]) { | 181 | } |
180 | if (b == &default_types[TYPE_S64]) { | 182 | } |
181 | return b; | 183 | |
184 | void | ||
185 | graph_functions(Scope *scope, Arena a) { | ||
186 | if (!scope->funcs) { | ||
187 | return; | ||
188 | } | ||
189 | FunMapIter iter = funmap_iterator(scope->funcs, &a); | ||
190 | FunMap *func = funmap_next(&iter, &a); | ||
191 | print( | ||
192 | "fun_%d[shape=\"none\" label=<<TABLE ALIGN=\"left\" STYLE=\"rounded\" " | ||
193 | "BORDER=\"1\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"6\" " | ||
194 | "COLUMNS=\"*\">", | ||
195 | scope->id); | ||
196 | print( | ||
197 | "<TR >" | ||
198 | "<TD ALIGN=\"left\" > NAME </TD>" | ||
199 | "<TD ALIGN=\"left\" > PARAMS </TD>" | ||
200 | "<TD ALIGN=\"left\" > RETURN </TD>" | ||
201 | "</TR>"); | ||
202 | while (func) { | ||
203 | print( | ||
204 | "<TR>" | ||
205 | "<TD PORT=\"%s\" ALIGN=\"left\" > %s </TD>" | ||
206 | "<TD ALIGN=\"left\" > %s </TD>" | ||
207 | "<TD ALIGN=\"left\" > %s </TD>" | ||
208 | "</TR>", | ||
209 | func->val.name, func->val.name, func->val.param_type, | ||
210 | func->val.return_type); | ||
211 | func = funmap_next(&iter, &a); | ||
212 | } | ||
213 | println("</TABLE>>];"); | ||
214 | sz this_id = scope->id; | ||
215 | while (scope->parent) { | ||
216 | if (scope->parent->symbols) { | ||
217 | println("fun_%d:e->fun_%d:%s:w;", this_id, scope->parent->id, | ||
218 | scope->name); | ||
219 | break; | ||
220 | } else { | ||
221 | scope = scope->parent; | ||
182 | } | 222 | } |
183 | } else if (a == &default_types[TYPE_F32]) { | 223 | } |
184 | if (b == &default_types[TYPE_F64]) { | 224 | } |
185 | return b; | 225 | |
226 | void | ||
227 | graph_types(Scope **scopes, Arena a) { | ||
228 | if (scopes == NULL) return; | ||
229 | println("digraph types {"); | ||
230 | println("rankdir=LR;"); | ||
231 | println("ranksep=\"0.95 equally\";"); | ||
232 | println("nodesep=\"0.5 equally\";"); | ||
233 | println("overlap=scale;"); | ||
234 | println("bgcolor=\"transparent\";"); | ||
235 | for (sz i = 0; i < array_size(scopes); i++) { | ||
236 | Scope *scope = scopes[i]; | ||
237 | if (!scope) { | ||
238 | continue; | ||
186 | } | 239 | } |
240 | println("subgraph %d {", i); | ||
241 | graph_typescope(scope, a); | ||
242 | graph_functions(scope, a); | ||
243 | println("}"); | ||
187 | } | 244 | } |
188 | return a; | 245 | println("}"); |
246 | } | ||
247 | |||
248 | void | ||
249 | emit_semantic_error(Analyzer *a, Node *n, Str msg) { | ||
250 | eprintln("%s:%d:%d: error: %s", a->file_name, n->line, n->col, msg); | ||
251 | a->err = true; | ||
189 | } | 252 | } |
190 | 253 | ||
191 | bool | 254 | Str type_inference(Analyzer *a, Node *node, Scope *scope); |
192 | type_is_numeric(Type *t) { | 255 | |
193 | if (t == &default_types[TYPE_U8] || | 256 | void |
194 | t == &default_types[TYPE_U16] || | 257 | typecheck_field(Analyzer *a, Node *node, Scope *scope, Str symbol) { |
195 | t == &default_types[TYPE_U32] || | 258 | if (node->field_type->kind == NODE_COMPOUND_TYPE) { |
196 | t == &default_types[TYPE_U64] || | 259 | Str field_name = str_concat(symbol, cstr("."), a->storage); |
197 | t == &default_types[TYPE_S8] || | 260 | field_name = str_concat(field_name, node->value.str, a->storage); |
198 | t == &default_types[TYPE_S16] || | 261 | if (structmap_lookup(&scope->structs, field_name)) { |
199 | t == &default_types[TYPE_S32] || | 262 | eprintln("%s:%d:%d: error: struct field '%s' already exists", |
200 | t == &default_types[TYPE_S64] || | 263 | a->file_name, node->line, node->col, field_name); |
201 | t == &default_types[TYPE_F32] || | 264 | a->err = true; |
202 | t == &default_types[TYPE_F64]) { | 265 | } |
203 | return true; | 266 | Str type = cstr("\\{ "); |
267 | for (sz i = 0; i < array_size(node->field_type->elements); i++) { | ||
268 | Node *field = node->field_type->elements[i]; | ||
269 | typecheck_field(a, field, scope, field_name); | ||
270 | type = str_concat(type, field->type, a->storage); | ||
271 | type = str_concat(type, cstr(" "), a->storage); | ||
272 | } | ||
273 | type = str_concat(type, cstr("\\}"), a->storage); | ||
274 | node->type = type; | ||
275 | } else { | ||
276 | Str field_name = str_concat(symbol, cstr("."), a->storage); | ||
277 | field_name = str_concat(field_name, node->value.str, a->storage); | ||
278 | Str field_type = node->field_type->value.str; | ||
279 | if (!find_type(scope, field_type)) { | ||
280 | eprintln("%s:%d:%d: error: unknown type '%s'", a->file_name, | ||
281 | node->field_type->line, node->field_type->col, field_type); | ||
282 | a->err = true; | ||
283 | } | ||
284 | if (node->field_type->is_ptr) { | ||
285 | field_type = str_concat(cstr("@"), field_type, a->storage); | ||
286 | } | ||
287 | if (node->field_type->kind == NODE_ARR_TYPE) { | ||
288 | field_type = str_concat(cstr("@"), field_type, a->storage); | ||
289 | } | ||
290 | if (structmap_lookup(&scope->structs, field_name)) { | ||
291 | eprintln("%s:%d:%d: error: struct field '%s' already exists", | ||
292 | a->file_name, node->line, node->col, field_name); | ||
293 | a->err = true; | ||
294 | } | ||
295 | if (node->field_val) { | ||
296 | Str type = type_inference(a, node->field_val, scope); | ||
297 | if (!str_eq(type, field_type)) { | ||
298 | eprintln( | ||
299 | "%s:%d:%d: error: mismatched types in struct " | ||
300 | "value " | ||
301 | "for '%s': %s expected %s", | ||
302 | a->file_name, node->line, node->col, field_name, type, | ||
303 | field_type); | ||
304 | a->err = true; | ||
305 | } | ||
306 | } | ||
307 | structmap_insert(&scope->structs, field_name, | ||
308 | (Struct){ | ||
309 | .name = field_name, | ||
310 | .type = field_type, | ||
311 | .val = node->field_val, | ||
312 | }, | ||
313 | a->storage); | ||
314 | symmap_insert(&scope->symbols, field_name, | ||
315 | (Symbol){.name = field_type, .kind = SYM_STRUCT_FIELD}, | ||
316 | a->storage); | ||
317 | node->type = field_type; | ||
204 | } | 318 | } |
205 | return false; | ||
206 | } | 319 | } |
207 | 320 | ||
208 | Symbol * | 321 | void |
209 | find_symbol(Scope *scope, Node *node) { | 322 | typecheck_lit_field(Analyzer *a, Node *node, Scope *scope, Str symbol) { |
210 | while (scope != NULL) { | 323 | if (node->field_val->kind == NODE_COMPOUND_TYPE) { |
211 | Symbol *val = ht_lookup(scope->symbols, node); | 324 | Str type = cstr("\\{ "); |
212 | if (val != NULL) { | 325 | for (sz i = 0; i < array_size(node->field_val->elements); i++) { |
213 | return val; | 326 | Node *field = node->field_val->elements[i]; |
327 | Str field_name = str_concat(symbol, cstr("."), a->storage); | ||
328 | field_name = str_concat(field_name, field->value.str, a->storage); | ||
329 | typecheck_lit_field(a, field, scope, field_name); | ||
330 | type = str_concat(type, field->type, a->storage); | ||
331 | type = str_concat(type, cstr(" "), a->storage); | ||
214 | } | 332 | } |
215 | scope = scope->parent; | 333 | type = str_concat(type, cstr("\\}"), a->storage); |
334 | node->type = type; | ||
335 | } else { | ||
336 | StructMap *s = structmap_lookup(&scope->structs, symbol); | ||
337 | if (!s) { | ||
338 | eprintln("%s:%d:%d: error: unknown struct field '%s'", a->file_name, | ||
339 | node->line, node->col, symbol); | ||
340 | a->err = true; | ||
341 | return; | ||
342 | } | ||
343 | Str field_type = s->val.type; | ||
344 | Str type = type_inference(a, node->field_val, scope); | ||
345 | if (!str_eq(type, field_type)) { | ||
346 | eprintln( | ||
347 | "%s:%d:%d: error: mismatched types in struct " | ||
348 | "value " | ||
349 | "for '%s': %s expected %s", | ||
350 | a->file_name, node->line, node->col, symbol, type, field_type); | ||
351 | a->err = true; | ||
352 | } | ||
353 | node->type = field_type; | ||
216 | } | 354 | } |
217 | push_error(ERR_TYPE_SEMANTIC, ERR_UNKNOWN_SYMBOL, node->line, node->col); | ||
218 | return NULL; | ||
219 | } | 355 | } |
220 | 356 | ||
221 | bool | 357 | void |
222 | resolve_type(ParseTree *ast, Scope *scope, Node *node) { | 358 | typecheck_returns(Analyzer *a, Node *node, Str expected) { |
223 | if (node->expr_type != NULL) { | 359 | if (!node) { |
224 | return true; | 360 | return; |
225 | } | 361 | } |
226 | switch (node->type) { | 362 | // Traverse the tree again. |
227 | case NODE_BUILTIN: { | 363 | switch (node->kind) { |
228 | for (size_t i = 0; i < array_size(node->builtin.args); ++i) { | 364 | case NODE_COND: |
229 | Node *arg = node->builtin.args[i]; | 365 | case NODE_MATCH: { |
230 | if (!resolve_type(ast, scope, arg)) { | 366 | for (sz i = 0; i < array_size(node->match_cases); i++) { |
231 | return false; | 367 | Node *next = node->match_cases[i]; |
232 | } | 368 | typecheck_returns(a, next, expected); |
233 | } | 369 | } |
234 | switch (node->builtin.type) { | 370 | } break; |
235 | // Numbers. | 371 | case NODE_RETURN: { |
236 | case TOKEN_ADD: | 372 | bool err = !str_eq(node->type, expected); |
237 | case TOKEN_SUB: | 373 | if (err) { |
238 | case TOKEN_MUL: | 374 | eprintln( |
239 | case TOKEN_DIV: | 375 | "%s:%d:%d: error: mismatched return type %s, expected %s", |
240 | case TOKEN_MOD: { | 376 | a->file_name, node->line, node->col, node->type, expected); |
241 | Type *type = NULL; | 377 | a->err = true; |
242 | for (size_t i = 0; i < array_size(node->builtin.args); ++i) { | 378 | } |
243 | Node *arg = node->builtin.args[i]; | 379 | } break; |
244 | 380 | case NODE_BLOCK: { | |
245 | // Check that all arguments are numbers. | 381 | for (sz i = 0; i < array_size(node->elements); i++) { |
246 | if (!type_is_numeric(arg->expr_type)) { | 382 | Node *next = node->elements[i]; |
247 | push_error(ERR_TYPE_SEMANTIC, ERR_WRONG_TYPE_NUM, | 383 | typecheck_returns(a, next, expected); |
248 | arg->line, arg->col); | 384 | } |
249 | return false; | 385 | } break; |
250 | } | 386 | case NODE_IF: { |
387 | if (node->cond_expr) { | ||
388 | typecheck_returns(a, node->cond_expr, expected); | ||
389 | } | ||
390 | if (node->cond_else) { | ||
391 | typecheck_returns(a, node->cond_else, expected); | ||
392 | } | ||
393 | } break; | ||
394 | case NODE_SET: | ||
395 | case NODE_LET: { | ||
396 | if (node->var_val) { | ||
397 | typecheck_returns(a, node->var_val, expected); | ||
398 | } | ||
399 | } break; | ||
400 | case NODE_ADD: | ||
401 | case NODE_SUB: | ||
402 | case NODE_DIV: | ||
403 | case NODE_MUL: | ||
404 | case NODE_MOD: | ||
405 | case NODE_NOT: | ||
406 | case NODE_AND: | ||
407 | case NODE_OR: | ||
408 | case NODE_EQ: | ||
409 | case NODE_NEQ: | ||
410 | case NODE_LT: | ||
411 | case NODE_GT: | ||
412 | case NODE_LE: | ||
413 | case NODE_GE: | ||
414 | case NODE_BITNOT: | ||
415 | case NODE_BITAND: | ||
416 | case NODE_BITOR: | ||
417 | case NODE_BITLSHIFT: | ||
418 | case NODE_BITRSHIFT: { | ||
419 | if (node->left) { | ||
420 | typecheck_returns(a, node->left, expected); | ||
421 | } | ||
422 | if (node->right) { | ||
423 | typecheck_returns(a, node->right, expected); | ||
424 | } | ||
425 | } break; | ||
426 | default: break; | ||
427 | } | ||
428 | } | ||
251 | 429 | ||
252 | if (type == NULL) { | 430 | Str |
253 | type = arg->expr_type; | 431 | type_inference(Analyzer *a, Node *node, Scope *scope) { |
254 | } else if (type != arg->expr_type) { | 432 | assert(a); |
255 | type = coerce_numeric_types(type, arg->expr_type); | 433 | assert(scope); |
256 | } | 434 | if (!node) { |
435 | return cstr(""); | ||
436 | } | ||
437 | // NOTE: For now we are not going to do implicit numeric conversions. | ||
438 | switch (node->kind) { | ||
439 | case NODE_LET: { | ||
440 | node->type = cstr("nil"); | ||
441 | Str symbol = node->var_name->value.str; | ||
442 | if (symmap_lookup(&scope->symbols, symbol)) { | ||
443 | eprintln( | ||
444 | "%s:%d:%d: error: symbol '%s' already exists in current " | ||
445 | "scope ", | ||
446 | a->file_name, node->var_name->line, node->var_name->col, | ||
447 | symbol); | ||
448 | a->err = true; | ||
449 | return cstr(""); | ||
450 | } | ||
451 | if (node->var_type) { | ||
452 | Str type_name = node->var_type->value.str; | ||
453 | SymbolMap *type = find_type(scope, type_name); | ||
454 | if (type == NULL) { | ||
455 | eprintln("%s:%d:%d: error: unknown type '%s'", a->file_name, | ||
456 | node->var_type->line, node->var_type->col, | ||
457 | type_name); | ||
458 | a->err = true; | ||
459 | return cstr(""); | ||
460 | } | ||
461 | if (node->var_type->is_ptr) { | ||
462 | type_name = str_concat(cstr("@"), type_name, a->storage); | ||
463 | } | ||
464 | if (node->var_type->kind == NODE_ARR_TYPE) { | ||
465 | type_name = str_concat(cstr("@"), type_name, a->storage); | ||
466 | // TODO: typecheck size | ||
467 | // TODO: register array in scope | ||
468 | } | ||
469 | if (node->var_val) { | ||
470 | Str type = type_inference(a, node->var_val, scope); | ||
471 | if (!type.size) { | ||
472 | eprintln( | ||
473 | "%s:%d:%d: error: can't bind `nil` to variable " | ||
474 | "'%s'", | ||
475 | a->file_name, node->var_type->line, | ||
476 | node->var_type->col, symbol); | ||
477 | a->err = true; | ||
478 | return cstr(""); | ||
257 | } | 479 | } |
258 | node->expr_type = type; | 480 | // TODO: Consider compatible types. |
259 | } break; | 481 | if (!str_eq(type, type_name)) { |
260 | // Bools. | 482 | // Special case, enums can be treated as ints. |
261 | case TOKEN_NOT: | 483 | FindEnumResult res = find_enum(scope, type_name); |
262 | // TODO: not should only take one argument and | 484 | if (!(res.map && str_eq(type, cstr("int")))) { |
263 | // return the inverse. | 485 | eprintln( |
264 | case TOKEN_AND: | 486 | "%s:%d:%d: error: type mismatch, trying to " |
265 | case TOKEN_OR: { | 487 | "assing " |
266 | // Check that all arguments are boolean. | 488 | "%s" |
267 | for (size_t i = 0; i < array_size(node->builtin.args); ++i) { | 489 | " to a variable of type %s", |
268 | Node *arg = node->builtin.args[i]; | 490 | a->file_name, node->var_type->line, |
269 | if (arg->expr_type != &default_types[TYPE_BOOL]) { | 491 | node->var_type->col, type, type_name); |
270 | push_error(ERR_TYPE_SEMANTIC, ERR_WRONG_TYPE_BOOL, | 492 | a->err = true; |
271 | arg->line, arg->col); | 493 | return cstr(""); |
272 | return false; | ||
273 | } | 494 | } |
274 | } | 495 | } |
275 | node->expr_type = &default_types[TYPE_BOOL]; | 496 | } |
276 | } break; | 497 | symmap_insert(&scope->symbols, symbol, |
277 | case TOKEN_EQ: | 498 | (Symbol){ |
278 | case TOKEN_LT: | 499 | .name = type_name, |
279 | case TOKEN_GT: | 500 | .kind = SYM_VAR, |
280 | case TOKEN_LE: | 501 | }, |
281 | case TOKEN_GE: { | 502 | a->storage); |
282 | // Check that all arguments are nums. | 503 | return node->type; |
283 | for (size_t i = 0; i < array_size(node->builtin.args); ++i) { | 504 | } |
284 | Node *arg = node->builtin.args[i]; | 505 | |
285 | // TODO: Make sure all arguments have the same type, | 506 | // We don't know the type for this symbol, perform inference. |
286 | // like with numeric expressions. | 507 | Str type = type_inference(a, node->var_val, scope); |
287 | if (!type_is_numeric(arg->expr_type)) { | 508 | if (type.size) { |
288 | push_error(ERR_TYPE_SEMANTIC, ERR_WRONG_TYPE_NUM, | 509 | symmap_insert(&scope->symbols, symbol, |
289 | arg->line, arg->col); | 510 | (Symbol){.name = type, .kind = SYM_VAR}, |
290 | return false; | 511 | a->storage); |
291 | } | 512 | node->var_name->type = type; |
513 | } | ||
514 | return node->type; | ||
515 | } break; | ||
516 | case NODE_SET: { | ||
517 | Str name = type_inference(a, node->var_name, scope); | ||
518 | Str val = type_inference(a, node->var_val, scope); | ||
519 | if (!str_eq(name, val)) { | ||
520 | eprintln( | ||
521 | "%s:%d:%d: error: type mismatch, trying to assing " | ||
522 | "%s" | ||
523 | " to a variable of type %s", | ||
524 | a->file_name, node->line, node->col, val, name); | ||
525 | a->err = true; | ||
526 | return cstr(""); | ||
527 | } | ||
528 | node->type = cstr("nil"); | ||
529 | return node->type; | ||
530 | } break; | ||
531 | case NODE_STRUCT: { | ||
532 | node->type = cstr("nil"); | ||
533 | Str symbol = node->value.str; | ||
534 | if (symmap_lookup(&scope->symbols, symbol) != NULL) { | ||
535 | eprintln( | ||
536 | "%s:%d:%d: error: struct '%s' already exists in current " | ||
537 | "scope", | ||
538 | a->file_name, node->line, node->col, symbol); | ||
539 | a->err = true; | ||
540 | return cstr(""); | ||
541 | } | ||
542 | structmap_insert(&scope->structs, symbol, (Struct){.name = symbol}, | ||
543 | a->storage); | ||
544 | for (sz i = 0; i < array_size(node->struct_field); i++) { | ||
545 | Node *field = node->struct_field[i]; | ||
546 | typecheck_field(a, field, scope, symbol); | ||
547 | } | ||
548 | symmap_insert(&scope->symbols, symbol, | ||
549 | (Symbol){.name = symbol, .kind = SYM_STRUCT}, | ||
550 | a->storage); | ||
551 | return node->type; | ||
552 | } break; | ||
553 | case NODE_ENUM: { | ||
554 | node->type = cstr("nil"); | ||
555 | Str symbol = node->value.str; | ||
556 | if (symmap_lookup(&scope->symbols, symbol) != NULL) { | ||
557 | eprintln( | ||
558 | "%s:%d:%d: error: enum '%s' already exists in current " | ||
559 | "scope", | ||
560 | a->file_name, node->line, node->col, symbol); | ||
561 | a->err = true; | ||
562 | return cstr(""); | ||
563 | } | ||
564 | enummap_insert(&scope->enums, symbol, | ||
565 | (Enum){ | ||
566 | .name = symbol, | ||
567 | .val = node->field_val, | ||
568 | }, | ||
569 | a->storage); | ||
570 | for (sz i = 0; i < array_size(node->struct_field); i++) { | ||
571 | Node *field = node->struct_field[i]; | ||
572 | Str field_name = str_concat(symbol, cstr("."), a->storage); | ||
573 | field_name = | ||
574 | str_concat(field_name, field->value.str, a->storage); | ||
575 | if (enummap_lookup(&scope->enums, field_name)) { | ||
576 | eprintln("%s:%d:%d: error: enum field '%s' already exists", | ||
577 | a->file_name, field->line, field->col, field_name); | ||
578 | a->err = true; | ||
579 | } | ||
580 | if (field->field_val) { | ||
581 | Str type = type_inference(a, field->field_val, scope); | ||
582 | if (!str_eq(type, cstr("int"))) { | ||
583 | eprintln( | ||
584 | "%s:%d:%d: error: non int enum value for '%s.%s'", | ||
585 | a->file_name, field->line, field->col, symbol, | ||
586 | field_name); | ||
587 | a->err = true; | ||
292 | } | 588 | } |
293 | node->expr_type = &default_types[TYPE_BOOL]; | 589 | } |
294 | } break; | 590 | enummap_insert(&scope->enums, field_name, |
295 | default: break; | 591 | (Enum){.name = field_name}, a->storage); |
592 | symmap_insert( | ||
593 | &scope->symbols, field_name, | ||
594 | (Symbol){.name = field_name, .kind = SYM_ENUM_FIELD}, | ||
595 | a->storage); | ||
596 | field->type = symbol; | ||
296 | } | 597 | } |
598 | symmap_insert(&scope->symbols, symbol, | ||
599 | (Symbol){.name = symbol, .kind = SYM_ENUM}, | ||
600 | a->storage); | ||
601 | return node->type; | ||
297 | } break; | 602 | } break; |
298 | case NODE_SYMBOL: { | 603 | case NODE_IF: { |
299 | Symbol *val = find_symbol(scope, node); | 604 | Str cond_type = type_inference(a, node->cond_if, scope); |
300 | if (val == NULL) { | 605 | if (!str_eq(cond_type, cstr("bool"))) { |
301 | return false; | 606 | emit_semantic_error( |
607 | a, node->cond_if, | ||
608 | cstr("non boolean expression on if condition")); | ||
609 | return cstr(""); | ||
302 | } | 610 | } |
303 | 611 | if (node->cond_expr->kind == NODE_BLOCK) { | |
304 | Type *type = NULL; | 612 | node->type = type_inference(a, node->cond_expr, scope); |
305 | switch (val->type) { | 613 | } else { |
306 | case SYMBOL_PAR: | 614 | Scope *next = typescope_alloc(a, scope); |
307 | case SYMBOL_VAR: { | 615 | node->type = type_inference(a, node->cond_expr, next); |
308 | type = find_type(scope, val->var.type); | ||
309 | } break; | ||
310 | case SYMBOL_FUN: { | ||
311 | type = find_type(scope, val->fun.return_type); | ||
312 | } break; | ||
313 | } | 616 | } |
314 | if (type == NULL) { | 617 | if (node->cond_else) { |
315 | return false; | 618 | Str else_type; |
619 | if (node->cond_else->kind == NODE_BLOCK) { | ||
620 | else_type = type_inference(a, node->cond_else, scope); | ||
621 | } else { | ||
622 | Scope *next = typescope_alloc(a, scope); | ||
623 | else_type = type_inference(a, node->cond_else, next); | ||
624 | } | ||
625 | if (!str_eq(node->type, else_type)) { | ||
626 | emit_semantic_error( | ||
627 | a, node, cstr("mismatch types for if/else branches")); | ||
628 | return cstr(""); | ||
629 | } | ||
316 | } | 630 | } |
317 | node->expr_type = type; | 631 | return node->type; |
318 | } break; | 632 | } break; |
319 | case NODE_FUN: { | 633 | case NODE_WHILE: { |
320 | // TODO: don't allow parameters of type void | 634 | Str cond_type = type_inference(a, node->while_cond, scope); |
321 | node->expr_type = &default_types[TYPE_VOID]; | 635 | if (!str_eq(cond_type, cstr("bool"))) { |
322 | 636 | emit_semantic_error( | |
323 | // Fill up new scope with parameters | 637 | a, node->cond_if, |
324 | scope = alloc_scope(scope); | 638 | cstr("non boolean expression on while condition")); |
325 | array_push(ast->scopes, scope); | 639 | return cstr(""); |
326 | 640 | } | |
327 | // Parameters. | 641 | if (node->while_expr->kind != NODE_BLOCK) { |
328 | for (size_t i = 0; i < array_size(node->fun.param_names); ++i) { | 642 | scope = typescope_alloc(a, scope); |
329 | Node *param = node->fun.param_names[i]; | 643 | } |
330 | Node *type = node->fun.param_types[i]; | 644 | type_inference(a, node->while_expr, scope); |
331 | Symbol *var = alloc_symval(param, SYMBOL_PAR); | 645 | node->type = cstr("nil"); |
332 | var->var.type = type; | 646 | return node->type; |
333 | if (!insert_symbol(scope, param, var)) { | 647 | } break; |
334 | return false; | 648 | case NODE_COND: { |
649 | Str previous = cstr(""); | ||
650 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
651 | Node *expr = node->match_cases[i]; | ||
652 | Str next = type_inference(a, expr, scope); | ||
653 | if (i != 0 && !str_eq(next, previous)) { | ||
654 | emit_semantic_error( | ||
655 | a, node, | ||
656 | cstr("non-matching types for cond expressions")); | ||
657 | return cstr(""); | ||
335 | } | 658 | } |
659 | previous = next; | ||
336 | } | 660 | } |
337 | 661 | node->type = previous; | |
338 | // Body. | 662 | return node->type; |
339 | Node *body = node->fun.body; | 663 | } break; |
340 | if (body->type == NODE_BLOCK) { | 664 | case NODE_MATCH: { |
341 | for (size_t i = 0; i < array_size(body->block.expr); ++i) { | 665 | Str e = type_inference(a, node->match_expr, scope); |
342 | Node *expr = body->block.expr[i]; | 666 | if (str_eq(e, cstr("int"))) { |
343 | if (!resolve_type(ast, scope, expr)) { | 667 | // Integer matching. |
344 | return false; | 668 | for (sz i = 0; i < array_size(node->match_cases); i++) { |
669 | Node *field = node->match_cases[i]; | ||
670 | if (field->case_value) { | ||
671 | if (field->case_value->kind != NODE_NUM_INT && | ||
672 | field->case_value->kind != NODE_NUM_UINT) { | ||
673 | emit_semantic_error( | ||
674 | a, field->case_value, | ||
675 | cstr( | ||
676 | "non-integer or enum types on match case")); | ||
677 | } | ||
345 | } | 678 | } |
346 | } | 679 | } |
347 | Node *last_expr = body->block.expr[array_size(body->block.expr) - 1]; | ||
348 | node->fun.body->expr_type = last_expr->expr_type; | ||
349 | } else { | 680 | } else { |
350 | if (!resolve_type(ast, scope, body)) { | 681 | // Get enum type and de-structure the match. |
351 | return false; | 682 | FindEnumResult res = find_enum(scope, e); |
683 | Str enum_prefix = | ||
684 | str_concat(res.map->val.name, cstr("."), a->storage); | ||
685 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
686 | Node *field = node->match_cases[i]; | ||
687 | if (field->case_value) { | ||
688 | Str field_name = str_concat( | ||
689 | enum_prefix, field->case_value->value.str, | ||
690 | a->storage); | ||
691 | if (!enummap_lookup(&res.scope->enums, field_name)) { | ||
692 | eprintln("%s:%d:%d: error: unknown enum field '%s'", | ||
693 | a->file_name, field->case_value->line, | ||
694 | field->case_value->col, field_name); | ||
695 | a->err = true; | ||
696 | } | ||
697 | } | ||
352 | } | 698 | } |
353 | } | 699 | } |
354 | 700 | Str previous = cstr(""); | |
355 | // Check that the type of body matches the return type. | 701 | for (sz i = 0; i < array_size(node->match_cases); i++) { |
356 | StringView *type_body = &node->fun.body->expr_type->name; | 702 | Node *expr = node->match_cases[i]; |
357 | StringView *return_type = &node->fun.return_type->string; | 703 | Str next = type_inference(a, expr, scope); |
358 | if (!sv_equal(type_body, return_type)) { | 704 | if (i != 0 && !str_eq(next, previous)) { |
359 | push_error(ERR_TYPE_SEMANTIC, ERR_WRONG_RET_TYPE, node->line, node->col); | 705 | emit_semantic_error( |
360 | return false; | 706 | a, node, |
707 | cstr("non-matching types for match expressions")); | ||
708 | return cstr(""); | ||
709 | } | ||
710 | previous = next; | ||
361 | } | 711 | } |
712 | node->type = previous; | ||
713 | return node->type; | ||
362 | } break; | 714 | } break; |
363 | case NODE_BLOCK: { | 715 | case NODE_CASE_MATCH: { |
364 | scope = alloc_scope(scope); | 716 | if (node->case_expr->kind != NODE_BLOCK) { |
365 | array_push(ast->scopes, scope); | 717 | scope = typescope_alloc(a, scope); |
366 | for (size_t i = 0; i < array_size(node->block.expr); ++i) { | 718 | } |
367 | Node *expr = node->block.expr[i]; | 719 | node->type = type_inference(a, node->case_expr, scope); |
368 | if (!resolve_type(ast, scope, expr)) { | 720 | return node->type; |
369 | return false; | 721 | } break; |
722 | case NODE_CASE_COND: { | ||
723 | if (node->case_value) { | ||
724 | Str cond = type_inference(a, node->case_value, scope); | ||
725 | if (!str_eq(cond, cstr("bool"))) { | ||
726 | emit_semantic_error(a, node, | ||
727 | cstr("non-boolean case condition")); | ||
370 | } | 728 | } |
371 | } | 729 | } |
372 | Node *last_expr = node->block.expr[array_size(node->block.expr) - 1]; | 730 | if (node->case_expr->kind != NODE_BLOCK) { |
373 | node->expr_type = last_expr->expr_type; | 731 | scope = typescope_alloc(a, scope); |
732 | } | ||
733 | node->type = type_inference(a, node->case_expr, scope); | ||
734 | return node->type; | ||
374 | } break; | 735 | } break; |
375 | case NODE_IF: { | 736 | case NODE_TRUE: |
376 | // TODO: If we don't have an else, ifexpr.expr_true | 737 | case NODE_FALSE: { |
377 | // must be void for consistency. | 738 | node->type = cstr("bool"); |
378 | if (!resolve_type(ast, scope, node->ifexpr.cond)) { | 739 | return node->type; |
379 | return false; | 740 | } break; |
380 | } | 741 | case NODE_NIL: { |
381 | if (!resolve_type(ast, scope, node->ifexpr.expr_true)) { | 742 | node->type = cstr("nil"); |
382 | return false; | 743 | return node->type; |
383 | } | 744 | } break; |
384 | Type *type_true = node->ifexpr.expr_true->expr_type; | 745 | case NODE_NOT: |
385 | node->expr_type = type_true; | 746 | case NODE_AND: |
386 | if (node->ifexpr.expr_false != NULL) { | 747 | case NODE_OR: { |
387 | if (!resolve_type(ast, scope, node->ifexpr.expr_false)) { | 748 | Str left = type_inference(a, node->left, scope); |
388 | return false; | 749 | if (!str_eq(left, cstr("bool"))) { |
750 | emit_semantic_error(a, node, | ||
751 | cstr("expected bool on logic expression")); | ||
752 | return cstr(""); | ||
753 | } | ||
754 | if (node->right) { | ||
755 | Str right = type_inference(a, node->right, scope); | ||
756 | if (!str_eq(right, cstr("bool"))) { | ||
757 | emit_semantic_error( | ||
758 | a, node, cstr("expected bool on logic expression")); | ||
759 | return cstr(""); | ||
760 | } | ||
761 | } | ||
762 | node->type = cstr("bool"); | ||
763 | return node->type; | ||
764 | } break; | ||
765 | case NODE_EQ: | ||
766 | case NODE_NEQ: | ||
767 | case NODE_LT: | ||
768 | case NODE_GT: | ||
769 | case NODE_LE: | ||
770 | case NODE_GE: { | ||
771 | Str left = type_inference(a, node->left, scope); | ||
772 | Str right = type_inference(a, node->right, scope); | ||
773 | if (!str_eq(left, right)) { | ||
774 | emit_semantic_error( | ||
775 | a, node, cstr("mismatched types on binary expression")); | ||
776 | return cstr(""); | ||
777 | } | ||
778 | node->type = cstr("bool"); | ||
779 | return node->type; | ||
780 | } break; | ||
781 | case NODE_BITNOT: { | ||
782 | Str left = type_inference(a, node->left, scope); | ||
783 | if (!strset_lookup(&a->integer_types, left)) { | ||
784 | emit_semantic_error( | ||
785 | a, node, cstr("non integer type on bit twiddling expr")); | ||
786 | return cstr(""); | ||
787 | } | ||
788 | node->type = left; | ||
789 | return node->type; | ||
790 | } break; | ||
791 | case NODE_BITAND: | ||
792 | case NODE_BITOR: | ||
793 | case NODE_BITLSHIFT: | ||
794 | case NODE_BITRSHIFT: { | ||
795 | Str left = type_inference(a, node->left, scope); | ||
796 | Str right = type_inference(a, node->right, scope); | ||
797 | if (!strset_lookup(&a->integer_types, left) || | ||
798 | !strset_lookup(&a->integer_types, right)) { | ||
799 | emit_semantic_error( | ||
800 | a, node, cstr("non integer type on bit twiddling expr")); | ||
801 | return cstr(""); | ||
802 | } | ||
803 | node->type = left; | ||
804 | return node->type; | ||
805 | } break; | ||
806 | case NODE_ADD: | ||
807 | case NODE_SUB: | ||
808 | case NODE_DIV: | ||
809 | case NODE_MUL: | ||
810 | case NODE_MOD: { | ||
811 | Str left = type_inference(a, node->left, scope); | ||
812 | Str right = type_inference(a, node->right, scope); | ||
813 | if (!strset_lookup(&a->numeric_types, left) || | ||
814 | !strset_lookup(&a->numeric_types, right)) { | ||
815 | emit_semantic_error( | ||
816 | a, node, cstr("non numeric type on arithmetic expr")); | ||
817 | return cstr(""); | ||
818 | } | ||
819 | if (!str_eq(left, right)) { | ||
820 | emit_semantic_error( | ||
821 | a, node, cstr("mismatched types on binary expression")); | ||
822 | return cstr(""); | ||
823 | } | ||
824 | node->type = left; | ||
825 | return node->type; | ||
826 | } break; | ||
827 | case NODE_NUM_UINT: { | ||
828 | node->type = cstr("uint"); | ||
829 | return node->type; | ||
830 | } break; | ||
831 | case NODE_NUM_INT: { | ||
832 | node->type = cstr("int"); | ||
833 | return node->type; | ||
834 | } break; | ||
835 | case NODE_NUM_FLOAT: { | ||
836 | node->type = cstr("f64"); | ||
837 | return node->type; | ||
838 | } break; | ||
839 | case NODE_STRING: { | ||
840 | node->type = cstr("str"); | ||
841 | return node->type; | ||
842 | } break; | ||
843 | case NODE_ARR_TYPE: | ||
844 | case NODE_TYPE: { | ||
845 | SymbolMap *type = find_type(scope, node->value.str); | ||
846 | if (!type) { | ||
847 | emit_semantic_error(a, node, cstr("unknown type")); | ||
848 | return cstr(""); | ||
849 | } | ||
850 | node->type = type->val.name; | ||
851 | return node->type; | ||
852 | } break; | ||
853 | case NODE_SYMBOL_IDX: | ||
854 | case NODE_SYMBOL: { | ||
855 | Str symbol = node->value.str; | ||
856 | SymbolMap *type = find_type(scope, symbol); | ||
857 | if (!type) { | ||
858 | eprintln("%s:%d:%d: error: couldn't resolve symbol '%s'", | ||
859 | a->file_name, node->line, node->col, symbol); | ||
860 | a->err = true; | ||
861 | return cstr(""); | ||
862 | } | ||
863 | Str type_name = type->val.name; | ||
864 | if (node->kind == NODE_SYMBOL_IDX) { | ||
865 | Str idx_type = type_inference(a, node->arr_size, scope); | ||
866 | if (!strset_lookup(&a->integer_types, idx_type)) { | ||
867 | emit_semantic_error( | ||
868 | a, node, cstr("can't resolve non integer index")); | ||
869 | return cstr(""); | ||
389 | } | 870 | } |
871 | type_name = str_remove_prefix(type_name, cstr("@")); | ||
872 | } | ||
873 | if (node->is_ptr) { | ||
874 | type_name = str_concat(cstr("@"), type_name, a->storage); | ||
390 | } | 875 | } |
391 | 876 | ||
392 | // Check ifexpr.cond is a bool. | 877 | FindEnumResult e = find_enum(scope, type_name); |
393 | Type *type_cond = node->ifexpr.cond->expr_type; | 878 | if (e.map && str_eq(symbol, type_name)) { |
394 | if (!sv_equal(&type_cond->name, &default_types[TYPE_BOOL].name)) { | 879 | if (!node->next) { |
395 | push_error(ERR_TYPE_SEMANTIC, ERR_WRONG_COND_TYPE, | 880 | eprintln( |
396 | node->line, node->col); | 881 | "%s:%d:%d: error: unspecified enum field for symbol " |
397 | return false; | 882 | "'%s'", |
883 | a->file_name, node->line, node->col, symbol); | ||
884 | a->err = true; | ||
885 | return cstr(""); | ||
886 | } | ||
887 | // Check if there is a next and it matches the enum field. | ||
888 | Str field = str_concat(type_name, cstr("."), a->storage); | ||
889 | field = str_concat(field, node->next->value.str, a->storage); | ||
890 | if (!enummap_lookup(&e.scope->enums, field)) { | ||
891 | eprintln( | ||
892 | "%s:%d:%d: error: unknown enum field for " | ||
893 | "'%s': %s", | ||
894 | a->file_name, node->line, node->col, symbol, | ||
895 | node->next->value.str); | ||
896 | a->err = true; | ||
897 | return cstr(""); | ||
898 | } | ||
899 | node->next->type = type_name; | ||
900 | node->type = type_name; | ||
901 | return node->next->type; | ||
398 | } | 902 | } |
399 | 903 | ||
400 | // Check if types of expr_true and expr_false match | 904 | FindStructResult s = find_struct(scope, type_name); |
401 | if (node->ifexpr.expr_false != NULL) { | 905 | if (s.map) { |
402 | Type *type_false = node->ifexpr.expr_false->expr_type; | 906 | if (str_eq(symbol, type_name)) { |
403 | if (type_is_numeric(type_true) && type_is_numeric(type_false)) { | 907 | eprintln( |
404 | node->expr_type = coerce_numeric_types(type_true, type_false); | 908 | "%s:%d:%d: error: struct incomplete struct literal " |
405 | } else if (!sv_equal(&type_true->name, &type_false->name)) { | 909 | "'%s', did you mean to use %s:{}?", |
406 | push_error(ERR_TYPE_SEMANTIC, ERR_WRONG_TYPE_T_F, | 910 | a->file_name, node->line, node->col, symbol, symbol); |
407 | node->line, node->col); | 911 | a->err = true; |
408 | return false; | 912 | return cstr(""); |
913 | } else { | ||
914 | if (node->next) { | ||
915 | Str chain = type_name; | ||
916 | Node *next = node; | ||
917 | while (next->next) { | ||
918 | next = next->next; | ||
919 | chain = str_concat(chain, cstr("."), a->storage); | ||
920 | chain = | ||
921 | str_concat(chain, next->value.str, a->storage); | ||
922 | } | ||
923 | StructMap *field = | ||
924 | structmap_lookup(&s.scope->structs, chain); | ||
925 | if (!field) { | ||
926 | eprintln( | ||
927 | "%s:%d:%d: error: unknown struct field '%s'", | ||
928 | a->file_name, node->line, node->col, chain); | ||
929 | a->err = true; | ||
930 | return cstr(""); | ||
931 | } | ||
932 | Str field_type = field->val.type; | ||
933 | if (next->kind == NODE_SYMBOL_IDX) { | ||
934 | Str idx_type = | ||
935 | type_inference(a, next->arr_size, scope); | ||
936 | if (!strset_lookup(&a->integer_types, idx_type)) { | ||
937 | emit_semantic_error( | ||
938 | a, next, | ||
939 | cstr("can't resolve non integer index")); | ||
940 | return cstr(""); | ||
941 | } | ||
942 | field_type = | ||
943 | str_remove_prefix(field_type, cstr("@")); | ||
944 | } | ||
945 | node->type = field_type; | ||
946 | return node->type; | ||
947 | } | ||
409 | } | 948 | } |
410 | } | 949 | } |
950 | node->type = type_name; | ||
951 | return node->type; | ||
411 | } break; | 952 | } break; |
412 | case NODE_SET: { | 953 | case NODE_STRUCT_LIT: { |
413 | node->expr_type = &default_types[TYPE_VOID]; | 954 | Str name = node->value.str; |
414 | if (!resolve_type(ast, scope, node->set.symbol)) { | 955 | FindStructResult s = find_struct(scope, name); |
415 | return false; | 956 | if (!s.map) { |
416 | } | 957 | eprintln("%s:%d:%d: error: unknown struct type '%s'", |
417 | if (!resolve_type(ast, scope, node->set.value)) { | 958 | a->file_name, node->line, node->col, name); |
418 | return false; | 959 | a->err = true; |
419 | } | 960 | return cstr(""); |
420 | Node *symbol = node->set.symbol; | ||
421 | Node *value = node->set.value; | ||
422 | if (!sv_equal(&symbol->expr_type->name, &value->expr_type->name)) { | ||
423 | push_error(ERR_TYPE_SEMANTIC, ERR_TYPE_MISMATCH, | ||
424 | node->line, node->col); | ||
425 | return false; | ||
426 | } | ||
427 | } break; | ||
428 | case NODE_DEF: { | ||
429 | // TODO: don't allow assignment of expressions that return void | ||
430 | // Prepare value for symbol table. | ||
431 | Symbol *var = alloc_symval(node->def.symbol, SYMBOL_VAR); | ||
432 | var->var.type = node->def.type; | ||
433 | if (!insert_symbol(scope, node->def.symbol, var)) { | ||
434 | return false; | ||
435 | } | ||
436 | |||
437 | Type *type = find_type(scope, node->def.type); | ||
438 | if (type == NULL) { | ||
439 | return false; | ||
440 | } | ||
441 | node->def.symbol->expr_type = type; | ||
442 | |||
443 | node->expr_type = &default_types[TYPE_VOID]; | ||
444 | // TODO: type inference from right side when not annotated? | ||
445 | if (!resolve_type(ast, scope, node->def.value)) { | ||
446 | return false; | ||
447 | } | ||
448 | Node *symbol = node->def.symbol; | ||
449 | Node *value = node->def.value; | ||
450 | if (!sv_equal(&symbol->expr_type->name, &value->expr_type->name)) { | ||
451 | push_error(ERR_TYPE_SEMANTIC, ERR_TYPE_MISMATCH, | ||
452 | node->line, node->col); | ||
453 | return false; | ||
454 | } | ||
455 | } break; | ||
456 | case NODE_NUMBER: { | ||
457 | // TODO: Numbers are f64/s64 unless explicitely annotated. Annotated | ||
458 | // numbers must fit in the given range (e.g. no negative constants | ||
459 | // inside a u64, no numbers bigger than 255 in a u8, etc.). | ||
460 | if (node->number.fractional != 0) { | ||
461 | // TODO: 1.0 should also be checked as float. | ||
462 | node->expr_type = &default_types[TYPE_F64]; | ||
463 | } else { | ||
464 | node->expr_type = &default_types[TYPE_S64]; | ||
465 | } | 961 | } |
962 | |||
963 | StrSet *set = NULL; | ||
964 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
965 | Node *next = node->elements[i]; | ||
966 | Str field_name = str_concat(name, cstr("."), a->storage); | ||
967 | field_name = | ||
968 | str_concat(field_name, next->value.str, a->storage); | ||
969 | |||
970 | if (strset_lookup(&set, field_name)) { | ||
971 | eprintln( | ||
972 | "%s:%d:%d: error: field '%s' already present in struct " | ||
973 | "literal", | ||
974 | a->file_name, next->line, next->col, field_name); | ||
975 | a->err = true; | ||
976 | } else { | ||
977 | strset_insert(&set, field_name, a->storage); | ||
978 | } | ||
979 | typecheck_lit_field(a, next, s.scope, field_name); | ||
980 | } | ||
981 | node->type = name; | ||
982 | return node->type; | ||
466 | } break; | 983 | } break; |
467 | case NODE_BOOL: { | 984 | case NODE_FUNCALL: { |
468 | node->expr_type = &default_types[TYPE_BOOL]; | 985 | Str symbol = node->value.str; |
986 | FunMap *fun = find_fun(scope, symbol); | ||
987 | if (!fun) { | ||
988 | eprintln( | ||
989 | "%s:%d:%d: error: function '%s' doesn't exist in current " | ||
990 | "scope ", | ||
991 | a->file_name, node->line, node->col, symbol); | ||
992 | a->err = true; | ||
993 | return cstr(""); | ||
994 | } | ||
995 | // Check that actual parameters typecheck | ||
996 | Str args = cstr(""); | ||
997 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
998 | Node *expr = node->elements[i]; | ||
999 | Str type = type_inference(a, expr, scope); | ||
1000 | args = str_concat(args, type, a->storage); | ||
1001 | if (i != array_size(node->elements) - 1) { | ||
1002 | args = str_concat(args, cstr(","), a->storage); | ||
1003 | } | ||
1004 | } | ||
1005 | if (!args.size) { | ||
1006 | args = cstr("nil"); | ||
1007 | } | ||
1008 | Str expected = fun->val.param_type; | ||
1009 | if (!str_eq(args, expected) && !str_eq(expected, cstr("..."))) { | ||
1010 | eprintln( | ||
1011 | "%s:%d:%d: error: mismatched parameter types: %s expected " | ||
1012 | "%s", | ||
1013 | a->file_name, node->line, node->col, args, expected); | ||
1014 | a->err = true; | ||
1015 | } | ||
1016 | node->type = fun->val.return_type; | ||
1017 | return node->type; | ||
469 | } break; | 1018 | } break; |
470 | case NODE_STRING: { | 1019 | case NODE_BLOCK: { |
471 | node->expr_type = &default_types[TYPE_STR]; | 1020 | scope = typescope_alloc(a, scope); |
1021 | Str type; | ||
1022 | for (sz i = 0; i < array_size(node->elements); i++) { | ||
1023 | Node *expr = node->elements[i]; | ||
1024 | type = type_inference(a, expr, scope); | ||
1025 | } | ||
1026 | node->type = type; | ||
1027 | return node->type; | ||
472 | } break; | 1028 | } break; |
473 | case NODE_FUNCALL: { | 1029 | case NODE_RETURN: { |
474 | Symbol *val = find_symbol(scope, node->funcall.name); | 1030 | Str ret_type = cstr(""); |
475 | if (!resolve_type(ast, scope, node->funcall.name)) { | 1031 | for (sz i = 0; i < array_size(node->elements); i++) { |
476 | return false; | 1032 | Node *expr = node->elements[i]; |
477 | } | 1033 | Str type = type_inference(a, expr, scope); |
478 | if (val->type != SYMBOL_FUN) { | 1034 | ret_type = str_concat(ret_type, type, a->storage); |
479 | push_error(ERR_TYPE_SEMANTIC, ERR_WRONG_TYPE_FUN, | 1035 | if (i != array_size(node->elements) - 1) { |
480 | node->funcall.name->line, node->funcall.name->col); | 1036 | ret_type = str_concat(ret_type, cstr(","), a->storage); |
481 | return false; | 1037 | } |
482 | } | 1038 | } |
483 | if (array_size(node->funcall.args) != array_size(val->fun.param_types)) { | 1039 | if (!ret_type.size) { |
484 | push_error(ERR_TYPE_SEMANTIC, ERR_BAD_ARGS, node->line, node->col); | 1040 | ret_type = cstr("nil"); |
485 | return false; | 1041 | } |
486 | } | 1042 | node->type = ret_type; |
487 | node->expr_type = node->funcall.name->expr_type; | 1043 | return node->type; |
488 | for (size_t i = 0; i < array_size(node->funcall.args); ++i) { | 1044 | } break; |
489 | Node *arg = node->funcall.args[i]; | 1045 | case NODE_FUN: { |
490 | if (!resolve_type(ast, scope, arg)) { | 1046 | node->type = cstr("nil"); |
491 | return false; | 1047 | Scope *prev_scope = scope; |
1048 | scope = typescope_alloc(a, scope); | ||
1049 | Str param_type = cstr(""); | ||
1050 | for (sz i = 0; i < array_size(node->func_params); i++) { | ||
1051 | Node *param = node->func_params[i]; | ||
1052 | Str symbol = param->param_name->value.str; | ||
1053 | Str type = param->param_type->value.str; | ||
1054 | if (param->param_type->is_ptr) { | ||
1055 | type = str_concat(cstr("@"), type, a->storage); | ||
1056 | } | ||
1057 | if (param->param_type->kind == NODE_ARR_TYPE) { | ||
1058 | type = str_concat(cstr("@"), type, a->storage); | ||
1059 | } | ||
1060 | param->param_name->type = | ||
1061 | type_inference(a, param->param_type, scope); | ||
1062 | param->type = type; | ||
1063 | symmap_insert(&scope->symbols, symbol, | ||
1064 | (Symbol){.name = type, .kind = SYM_PARAM}, | ||
1065 | a->storage); | ||
1066 | param_type = str_concat(param_type, type, a->storage); | ||
1067 | if (i != array_size(node->func_params) - 1) { | ||
1068 | param_type = str_concat(param_type, cstr(","), a->storage); | ||
1069 | } | ||
1070 | } | ||
1071 | if (!param_type.size) { | ||
1072 | param_type = cstr("nil"); | ||
1073 | } | ||
1074 | node->fun_params = param_type; | ||
1075 | |||
1076 | Str ret_type = cstr(""); | ||
1077 | for (sz i = 0; i < array_size(node->func_ret); i++) { | ||
1078 | Node *expr = node->func_ret[i]; | ||
1079 | Str type = type_inference(a, expr, scope); | ||
1080 | if (expr->is_ptr) { | ||
1081 | type = str_concat(cstr("@"), type, a->storage); | ||
1082 | } | ||
1083 | if (expr->kind == NODE_ARR_TYPE) { | ||
1084 | type = str_concat(cstr("@"), type, a->storage); | ||
492 | } | 1085 | } |
493 | Node *expected = val->fun.param_types[i]; | 1086 | ret_type = str_concat(ret_type, type, a->storage); |
494 | if (!sv_equal(&arg->expr_type->name, &expected->string)) { | 1087 | if (i != array_size(node->func_ret) - 1) { |
495 | push_error(ERR_TYPE_SEMANTIC, ERR_TYPE_MISMATCH, | 1088 | ret_type = str_concat(ret_type, cstr(","), a->storage); |
496 | arg->line, arg->col); | ||
497 | return false; | ||
498 | } | 1089 | } |
499 | } | 1090 | } |
1091 | if (!ret_type.size) { | ||
1092 | ret_type = cstr("nil"); | ||
1093 | } | ||
1094 | node->fun_return = ret_type; | ||
1095 | |||
1096 | Str symbol = node->func_name->value.str; | ||
1097 | if (prev_scope->parent != NULL) { | ||
1098 | if (symmap_lookup(&prev_scope->symbols, symbol)) { | ||
1099 | eprintln( | ||
1100 | "%s:%d:%d: error: function '%s' already defined in " | ||
1101 | "current " | ||
1102 | "scope ", | ||
1103 | a->file_name, node->var_name->line, node->var_name->col, | ||
1104 | symbol); | ||
1105 | a->err = true; | ||
1106 | return cstr(""); | ||
1107 | } | ||
1108 | symmap_insert(&scope->symbols, symbol, | ||
1109 | (Symbol){.name = symbol, .kind = SYM_FUN}, | ||
1110 | a->storage); | ||
1111 | } | ||
1112 | scope->name = symbol; | ||
1113 | funmap_insert(&prev_scope->funcs, symbol, | ||
1114 | (Fun){.name = symbol, | ||
1115 | .param_type = param_type, | ||
1116 | .return_type = ret_type}, | ||
1117 | a->storage); | ||
1118 | |||
1119 | if (node->func_body->kind == NODE_BLOCK) { | ||
1120 | Str type; | ||
1121 | for (sz i = 0; i < array_size(node->func_body->elements); i++) { | ||
1122 | Node *expr = node->func_body->elements[i]; | ||
1123 | type = type_inference(a, expr, scope); | ||
1124 | } | ||
1125 | if (!type.size) { | ||
1126 | type = cstr("nil"); | ||
1127 | } | ||
1128 | node->func_body->type = type; | ||
1129 | } else { | ||
1130 | type_inference(a, node->func_body, scope); | ||
1131 | } | ||
1132 | |||
1133 | // Ensure main body return matches the prototype. | ||
1134 | if (!str_eq(node->func_body->type, ret_type)) { | ||
1135 | eprintln( | ||
1136 | "%s:%d:%d: error: mismatched return type %s, expected %s", | ||
1137 | a->file_name, node->line, node->col, node->func_body->type, | ||
1138 | ret_type); | ||
1139 | a->err = true; | ||
1140 | } | ||
1141 | |||
1142 | // Ensure ALL return statements match the function prototype. | ||
1143 | typecheck_returns(a, node->func_body, ret_type); | ||
1144 | |||
1145 | // TODO: should return statements be allowed on let blocks? | ||
1146 | return node->type; | ||
1147 | } break; | ||
1148 | default: { | ||
1149 | emit_semantic_error(a, node, | ||
1150 | cstr("type inference not implemented for this " | ||
1151 | "kind of expression")); | ||
1152 | println("KIND: %s", node_str[node->kind]); | ||
500 | } break; | 1153 | } break; |
501 | default: break; | ||
502 | } | 1154 | } |
503 | return true; | 1155 | return cstr(""); |
504 | } | 1156 | } |
505 | 1157 | ||
506 | ParseTree * | 1158 | void |
507 | semantic_analysis(Root *roots) { | 1159 | symbolic_analysis(Analyzer *a, Parser *parser) { |
508 | ParseTree *parse_tree = malloc(sizeof(ParseTree)); | 1160 | Scope *scope = typescope_alloc(a, NULL); |
509 | parse_tree->roots = roots; | 1161 | assert(a); |
510 | array_init(parse_tree->scopes, 0); | 1162 | assert(parser); |
511 | Scope *scope = alloc_scope(NULL); | ||
512 | array_push(parse_tree->scopes, scope); | ||
513 | |||
514 | // Fill global scope with default types. | ||
515 | HashTable *types = scope->types; | ||
516 | for (size_t i = 0; i < sizeof(default_types)/sizeof(Type); ++i) { | ||
517 | Type *type = &default_types[i]; | ||
518 | ht_insert(types, &type->name, type); | ||
519 | } | ||
520 | 1163 | ||
521 | // Fill up global function symbols. | 1164 | // Fill builtin tables. |
522 | for (size_t i = 0; i < array_size(parse_tree->roots); ++i) { | 1165 | Str builtin_functions[] = { |
523 | Node *root = parse_tree->roots[i]; | 1166 | cstr("print"), |
524 | if (root->type == NODE_FUN) { | 1167 | cstr("println"), |
525 | Node *name = root->fun.name; | 1168 | }; |
526 | Symbol *fun = alloc_symval(root->fun.name, SYMBOL_FUN); | 1169 | for (sz i = 0; i < LEN(builtin_functions); i++) { |
527 | fun->fun.param_types = root->fun.param_types; | 1170 | Str symbol = builtin_functions[i]; |
528 | fun->fun.return_type = root->fun.return_type; | 1171 | symmap_insert(&scope->symbols, symbol, |
529 | if (!insert_symbol(scope, name, fun)) { | 1172 | (Symbol){.name = symbol, .kind = SYM_BUILTIN_FUN}, |
530 | return NULL; | 1173 | a->storage); |
1174 | funmap_insert(&scope->funcs, symbol, | ||
1175 | (Fun){.name = symbol, | ||
1176 | .param_type = cstr("..."), | ||
1177 | .return_type = cstr("nil")}, | ||
1178 | a->storage); | ||
1179 | } | ||
1180 | Str builtin_types[] = { | ||
1181 | cstr("u8"), cstr("s8"), cstr("u16"), cstr("s16"), | ||
1182 | cstr("u32"), cstr("s32"), cstr("u64"), cstr("s64"), | ||
1183 | cstr("f32"), cstr("f64"), cstr("ptr"), cstr("int"), | ||
1184 | cstr("uint"), cstr("str"), cstr("bool"), cstr("nil")}; | ||
1185 | for (sz i = 0; i < LEN(builtin_types); i++) { | ||
1186 | Str type = builtin_types[i]; | ||
1187 | symmap_insert(&scope->symbols, type, | ||
1188 | (Symbol){.name = type, .kind = SYM_BUILTIN_TYPE}, | ||
1189 | a->storage); | ||
1190 | } | ||
1191 | Str numeric_types[] = { | ||
1192 | cstr("u8"), cstr("s8"), cstr("u16"), cstr("s16"), cstr("u32"), | ||
1193 | cstr("s32"), cstr("u64"), cstr("s64"), cstr("f32"), cstr("f64"), | ||
1194 | cstr("ptr"), cstr("int"), cstr("uint"), | ||
1195 | }; | ||
1196 | for (sz i = 0; i < LEN(numeric_types); i++) { | ||
1197 | Str type = numeric_types[i]; | ||
1198 | strset_insert(&a->numeric_types, type, a->storage); | ||
1199 | } | ||
1200 | Str integer_types[] = { | ||
1201 | cstr("u8"), cstr("s8"), cstr("u16"), cstr("s16"), | ||
1202 | cstr("u32"), cstr("s32"), cstr("u64"), cstr("s64"), | ||
1203 | cstr("ptr"), cstr("int"), cstr("uint"), | ||
1204 | }; | ||
1205 | for (sz i = 0; i < LEN(integer_types); i++) { | ||
1206 | Str type = integer_types[i]; | ||
1207 | strset_insert(&a->integer_types, type, a->storage); | ||
1208 | } | ||
1209 | // Find top level function declarations. | ||
1210 | for (sz i = 0; i < array_size(parser->nodes); i++) { | ||
1211 | Node *root = parser->nodes[i]; | ||
1212 | if (root->kind == NODE_FUN) { | ||
1213 | Str symbol = root->func_name->value.str; | ||
1214 | if (symmap_lookup(&scope->symbols, symbol)) { | ||
1215 | eprintln( | ||
1216 | "%s:%d:%d: error: function '%s' already defined in " | ||
1217 | "current " | ||
1218 | "scope ", | ||
1219 | a->file_name, root->var_name->line, root->var_name->col, | ||
1220 | symbol); | ||
1221 | a->err = true; | ||
531 | } | 1222 | } |
1223 | symmap_insert(&scope->symbols, symbol, | ||
1224 | (Symbol){.name = symbol, .kind = SYM_FUN}, | ||
1225 | a->storage); | ||
532 | } | 1226 | } |
533 | } | 1227 | } |
534 | 1228 | // Recursively fill symbol tables. | |
535 | for (size_t i = 0; i < array_size(parse_tree->roots); ++i) { | 1229 | for (sz i = 0; i < array_size(parser->nodes); i++) { |
536 | // Fill up symbol tables in proper scope and resolve type of expression | 1230 | Node *root = parser->nodes[i]; |
537 | // for all elements. | 1231 | type_inference(a, root, scope); |
538 | if (!resolve_type(parse_tree, scope, parse_tree->roots[i])) { | ||
539 | return NULL; | ||
540 | } | ||
541 | } | 1232 | } |
542 | |||
543 | return parse_tree; | ||
544 | } | 1233 | } |
1234 | |||
@@ -17,10 +17,22 @@ typedef union Constant { | |||
17 | 17 | ||
18 | typedef struct Chunk { | 18 | typedef struct Chunk { |
19 | Instruction *code; | 19 | Instruction *code; |
20 | |||
21 | // Constant values that fit in 64 bits. | ||
20 | Constant *constants; | 22 | Constant *constants; |
21 | Str file_name; | 23 | IntIntMap *intmap; |
22 | sz reg_idx; | ||
23 | sz const_idx; | 24 | sz const_idx; |
25 | |||
26 | // Constant strings. | ||
27 | Str *strings; | ||
28 | StrIntMap *strmap; | ||
29 | sz str_idx; | ||
30 | |||
31 | // Number of registers currently used in this chunk. | ||
32 | sz reg_idx; | ||
33 | |||
34 | // Debugging. | ||
35 | Str file_name; | ||
24 | Arena *storage; | 36 | Arena *storage; |
25 | // TODO: line/col info for debugging. | 37 | // TODO: line/col info for debugging. |
26 | } Chunk; | 38 | } Chunk; |
@@ -205,6 +217,12 @@ disassemble_chunk(Chunk chunk) { | |||
205 | chunk.constants[i]); | 217 | chunk.constants[i]); |
206 | } | 218 | } |
207 | } | 219 | } |
220 | if (array_size(chunk.strings) > 0) { | ||
221 | println("%s: ========== strings =========", chunk.file_name); | ||
222 | for (sz i = 0; i < array_size(chunk.strings); i++) { | ||
223 | println("%s: %x{2}: %s", chunk.file_name, i, chunk.strings[i]); | ||
224 | } | ||
225 | } | ||
208 | } | 226 | } |
209 | 227 | ||
210 | #define N_CONST 256 | 228 | #define N_CONST 256 |
@@ -292,6 +310,7 @@ vm_run(VM *vm) { | |||
292 | 310 | ||
293 | typedef enum { | 311 | typedef enum { |
294 | COMP_CONST, | 312 | COMP_CONST, |
313 | COMP_STRING, | ||
295 | COMP_REG, | 314 | COMP_REG, |
296 | COMP_ERR, | 315 | COMP_ERR, |
297 | } CompResultType; | 316 | } CompResultType; |
@@ -303,11 +322,19 @@ typedef struct CompResult { | |||
303 | 322 | ||
304 | CompResult compile_expr(Chunk *chunk, Node *node); | 323 | CompResult compile_expr(Chunk *chunk, Node *node); |
305 | 324 | ||
306 | // #define EMIT_OP(OP, CHUNK, ARENA) | 325 | #define EMIT_OP(OP, DST, A, B, NODE, CHUNK) \ |
326 | do { \ | ||
327 | Instruction inst = (Instruction){ \ | ||
328 | .op = (OP), \ | ||
329 | .dst = (DST), \ | ||
330 | .a = (A), \ | ||
331 | .b = (B), \ | ||
332 | }; \ | ||
333 | array_push((CHUNK)->code, inst, (CHUNK)->storage); \ | ||
334 | } while (0) | ||
307 | 335 | ||
308 | CompResult | 336 | CompResult |
309 | compile_binary(OpCode op, Chunk *chunk, Node *node) { | 337 | compile_binary(OpCode op, Chunk *chunk, Node *node) { |
310 | sz reg_dst = chunk->reg_idx++; | ||
311 | CompResult comp_a = compile_expr(chunk, node->left); | 338 | CompResult comp_a = compile_expr(chunk, node->left); |
312 | CompResult comp_b = compile_expr(chunk, node->right); | 339 | CompResult comp_b = compile_expr(chunk, node->right); |
313 | sz reg_a; | 340 | sz reg_a; |
@@ -315,9 +342,7 @@ compile_binary(OpCode op, Chunk *chunk, Node *node) { | |||
315 | switch (comp_a.type) { | 342 | switch (comp_a.type) { |
316 | case COMP_CONST: { | 343 | case COMP_CONST: { |
317 | reg_a = chunk->reg_idx++; | 344 | reg_a = chunk->reg_idx++; |
318 | Instruction inst = | 345 | EMIT_OP(OP_LD64K, reg_a, comp_a.idx, 0, node, chunk); |
319 | (Instruction){.op = OP_LD64K, .dst = reg_a, .a = comp_a.idx}; | ||
320 | array_push(chunk->code, inst, chunk->storage); | ||
321 | } break; | 346 | } break; |
322 | case COMP_REG: { | 347 | case COMP_REG: { |
323 | reg_a = comp_a.idx; | 348 | reg_a = comp_a.idx; |
@@ -329,9 +354,7 @@ compile_binary(OpCode op, Chunk *chunk, Node *node) { | |||
329 | switch (comp_b.type) { | 354 | switch (comp_b.type) { |
330 | case COMP_CONST: { | 355 | case COMP_CONST: { |
331 | reg_b = chunk->reg_idx++; | 356 | reg_b = chunk->reg_idx++; |
332 | Instruction inst = | 357 | EMIT_OP(OP_LD64K, reg_b, comp_b.idx, 0, node, chunk); |
333 | (Instruction){.op = OP_LD64K, .dst = reg_b, .a = comp_b.idx}; | ||
334 | array_push(chunk->code, inst, chunk->storage); | ||
335 | } break; | 358 | } break; |
336 | case COMP_REG: { | 359 | case COMP_REG: { |
337 | reg_b = comp_b.idx; | 360 | reg_b = comp_b.idx; |
@@ -340,9 +363,9 @@ compile_binary(OpCode op, Chunk *chunk, Node *node) { | |||
340 | return (CompResult){.type = COMP_ERR}; | 363 | return (CompResult){.type = COMP_ERR}; |
341 | } break; | 364 | } break; |
342 | } | 365 | } |
343 | Instruction inst = | 366 | sz reg_dst = comp_a.idx; // Less registers |
344 | (Instruction){.op = op, .dst = reg_dst, .a = reg_a, .b = reg_b}; | 367 | // sz reg_dst = chunk->reg_idx++; // Better for optimization |
345 | array_push(chunk->code, inst, chunk->storage); | 368 | EMIT_OP(op, reg_dst, reg_a, reg_b, node, chunk); |
346 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; | 369 | return (CompResult){.type = COMP_REG, .idx = reg_dst}; |
347 | } | 370 | } |
348 | 371 | ||
@@ -354,26 +377,43 @@ compile_expr(Chunk *chunk, Node *node) { | |||
354 | case NODE_MUL: return compile_binary(OP_MUL, chunk, node); break; | 377 | case NODE_MUL: return compile_binary(OP_MUL, chunk, node); break; |
355 | case NODE_DIV: return compile_binary(OP_DIV, chunk, node); break; | 378 | case NODE_DIV: return compile_binary(OP_DIV, chunk, node); break; |
356 | case NODE_MOD: return compile_binary(OP_MOD, chunk, node); break; | 379 | case NODE_MOD: return compile_binary(OP_MOD, chunk, node); break; |
380 | case NODE_TRUE: | ||
381 | case NODE_FALSE: | ||
357 | case NODE_NUM_FLOAT: | 382 | case NODE_NUM_FLOAT: |
358 | case NODE_NUM_INT: { | 383 | case NODE_NUM_INT: { |
384 | sz value = node->value.i; | ||
359 | // Make sure we don't have duplicated constants. | 385 | // Make sure we don't have duplicated constants. |
360 | for (sz i = 0; i < chunk->const_idx; i++) { | 386 | IntIntMap *map = intintmap_lookup(&chunk->intmap, value); |
361 | if (node->value.i == chunk->constants[i].i) { | 387 | if (!map) { |
362 | return (CompResult){ | 388 | map = intintmap_insert(&chunk->intmap, value, |
363 | .type = COMP_CONST, | 389 | chunk->const_idx++, chunk->storage); |
364 | .idx = i, | 390 | Constant c = (Constant){.i = node->value.i}; |
365 | }; | 391 | array_push(chunk->constants, c, chunk->storage); |
366 | } | ||
367 | } | 392 | } |
368 | Constant c = (Constant){.i = node->value.i}; | ||
369 | array_push(chunk->constants, c, chunk->storage); | ||
370 | return (CompResult){ | 393 | return (CompResult){ |
371 | .type = COMP_CONST, | 394 | .type = COMP_CONST, |
372 | .idx = chunk->const_idx++, | 395 | .idx = map->val, |
396 | }; | ||
397 | } break; | ||
398 | case NODE_STRING: { | ||
399 | Str string = node->value.str; | ||
400 | // Make sure we don't have duplicated strings. | ||
401 | StrIntMap *map = strintmap_lookup(&chunk->strmap, string); | ||
402 | if (!map) { | ||
403 | map = strintmap_insert(&chunk->strmap, string, chunk->str_idx++, | ||
404 | chunk->storage); | ||
405 | array_push(chunk->strings, string, chunk->storage); | ||
406 | } | ||
407 | return (CompResult){ | ||
408 | .type = COMP_STRING, | ||
409 | .idx = map->val, | ||
373 | }; | 410 | }; |
374 | } break; | 411 | } break; |
375 | default: break; | 412 | default: { |
413 | eprintln("error: compilation not implemented for node %s", | ||
414 | node_str[node->kind]); | ||
415 | exit(EXIT_FAILURE); | ||
416 | } break; | ||
376 | } | 417 | } |
377 | return (CompResult){.type = COMP_ERR}; | 418 | return (CompResult){.type = COMP_ERR}; |
378 | } | 419 | } |
379 | |||
diff --git a/tests/compilation.bad b/tests/compilation.bad new file mode 100644 index 0000000..782e75f --- /dev/null +++ b/tests/compilation.bad | |||
@@ -0,0 +1,10 @@ | |||
1 | 1 + 2 * 3 | ||
2 | true | ||
3 | false | ||
4 | 0 1 | ||
5 | "hello" | ||
6 | "world" | ||
7 | ; fun foo(): int { | ||
8 | ; 32 | ||
9 | ; } | ||
10 | ; 1 + 2 + foo() | ||