diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-24 19:30:40 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-24 19:30:54 +0200 |
commit | 1c7d30ab77960be3a0c7ae259d601fa6aed640b3 (patch) | |
tree | 9a9aea79bd27d1c70354cee13f528bd499dc6ca2 | |
parent | 8658d71e8a0761f8407b35cdf08b4b80229f560e (diff) | |
download | bdl-1c7d30ab77960be3a0c7ae259d601fa6aed640b3.tar.gz bdl-1c7d30ab77960be3a0c7ae259d601fa6aed640b3.zip |
Add typechecking for structs and struct fields
-rw-r--r-- | src/main.c | 95 | ||||
-rw-r--r-- | tests/semantics.bad | 18 |
2 files changed, 105 insertions, 8 deletions
@@ -7,8 +7,10 @@ | |||
7 | #include "parser.c" | 7 | #include "parser.c" |
8 | #include "vm.c" | 8 | #include "vm.c" |
9 | 9 | ||
10 | // TODO: typecheck structs | 10 | // TODO: in a let/set expression if the type is a struct check that we |
11 | // TODO: add field accessor for struct symbols | 11 | // are assigning a struct literal, not just a symbol. |
12 | // TODO: nested structs | ||
13 | // TODO: unions | ||
12 | 14 | ||
13 | typedef enum ExecMode { | 15 | typedef enum ExecMode { |
14 | RUN_NORMAL, | 16 | RUN_NORMAL, |
@@ -65,6 +67,7 @@ typedef struct Fun { | |||
65 | 67 | ||
66 | typedef struct Field { | 68 | typedef struct Field { |
67 | Str name; | 69 | Str name; |
70 | Str type; | ||
68 | Node *val; | 71 | Node *val; |
69 | } Field; | 72 | } Field; |
70 | 73 | ||
@@ -73,10 +76,16 @@ typedef struct Enum { | |||
73 | struct FieldMap *fields; | 76 | struct FieldMap *fields; |
74 | } Enum; | 77 | } Enum; |
75 | 78 | ||
79 | typedef struct Struct { | ||
80 | Str name; | ||
81 | struct FieldMap *fields; | ||
82 | } Struct; | ||
83 | |||
76 | MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) | 84 | MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) |
77 | MAPDEF(TypeMap, typemap, Str, Type, str_hash, str_eq) | 85 | MAPDEF(TypeMap, typemap, Str, Type, str_hash, str_eq) |
78 | MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq) | 86 | MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq) |
79 | MAPDEF(EnumMap, enummap, Str, Enum, str_hash, str_eq) | 87 | MAPDEF(EnumMap, enummap, Str, Enum, str_hash, str_eq) |
88 | MAPDEF(StructMap, structmap, Str, Struct, str_hash, str_eq) | ||
80 | MAPDEF(FieldMap, fieldmap, Str, Field, str_hash, str_eq) | 89 | MAPDEF(FieldMap, fieldmap, Str, Field, str_hash, str_eq) |
81 | 90 | ||
82 | typedef struct Scope { | 91 | typedef struct Scope { |
@@ -92,6 +101,7 @@ typedef struct TypeScope { | |||
92 | TypeMap *types; | 101 | TypeMap *types; |
93 | FunMap *funcs; | 102 | FunMap *funcs; |
94 | EnumMap *enums; | 103 | EnumMap *enums; |
104 | StructMap *structs; | ||
95 | struct TypeScope *parent; | 105 | struct TypeScope *parent; |
96 | } TypeScope; | 106 | } TypeScope; |
97 | 107 | ||
@@ -174,6 +184,18 @@ find_enum(TypeScope *scope, Str type) { | |||
174 | return NULL; | 184 | return NULL; |
175 | } | 185 | } |
176 | 186 | ||
187 | StructMap * | ||
188 | find_struct(TypeScope *scope, Str type) { | ||
189 | while (scope != NULL) { | ||
190 | StructMap *val = structmap_lookup(&scope->structs, type); | ||
191 | if (val != NULL) { | ||
192 | return val; | ||
193 | } | ||
194 | scope = scope->parent; | ||
195 | } | ||
196 | return NULL; | ||
197 | } | ||
198 | |||
177 | void | 199 | void |
178 | graph_scope(Scope *scope, Arena a) { | 200 | graph_scope(Scope *scope, Arena a) { |
179 | if (!scope->symbols) { | 201 | if (!scope->symbols) { |
@@ -490,9 +512,46 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
490 | node->type = cstr("nil"); | 512 | node->type = cstr("nil"); |
491 | return node->type; | 513 | return node->type; |
492 | } break; | 514 | } break; |
515 | case NODE_STRUCT: { | ||
516 | node->type = cstr("nil"); | ||
517 | Str symbol = node->value.str; | ||
518 | // TODO: make sure it didn't exist before. | ||
519 | StructMap *e = structmap_insert( | ||
520 | &scope->structs, symbol, (Struct){.name = symbol}, a->storage); | ||
521 | for (sz i = 0; i < array_size(node->struct_field); i++) { | ||
522 | Node *field = node->struct_field[i]; | ||
523 | Str field_name = field->field_name->value.str; | ||
524 | Str field_type = field->field_type->value.str; | ||
525 | if (fieldmap_lookup(&e->val.fields, field_name)) { | ||
526 | eprintln( | ||
527 | "%s:%d:%d: error: struct field '%s.%s' already exists", | ||
528 | a->file_name, field->line, field->col, symbol, | ||
529 | field_name); | ||
530 | } | ||
531 | if (field->field_val) { | ||
532 | Type type = type_inference(a, field->field_val, scope); | ||
533 | if (!str_eq(type, field_type)) { | ||
534 | eprintln( | ||
535 | "%s:%d:%d: error: mismatched types in struct value " | ||
536 | "for '%s.%s' ", | ||
537 | a->file_name, field->line, field->col, symbol, | ||
538 | field_name); | ||
539 | } | ||
540 | } | ||
541 | fieldmap_insert(&e->val.fields, field_name, | ||
542 | (Field){.name = field_name, | ||
543 | .type = field_type, | ||
544 | .val = field->field_val}, | ||
545 | a->storage); | ||
546 | field->type = field_type; | ||
547 | } | ||
548 | typemap_insert(&scope->types, symbol, symbol, a->storage); | ||
549 | return node->type; | ||
550 | } break; | ||
493 | case NODE_ENUM: { | 551 | case NODE_ENUM: { |
494 | node->type = cstr("nil"); | 552 | node->type = cstr("nil"); |
495 | Str symbol = node->value.str; | 553 | Str symbol = node->value.str; |
554 | // TODO: make sure it didn't exist before. | ||
496 | EnumMap *e = enummap_insert(&scope->enums, symbol, | 555 | EnumMap *e = enummap_insert(&scope->enums, symbol, |
497 | (Enum){.name = symbol}, a->storage); | 556 | (Enum){.name = symbol}, a->storage); |
498 | for (sz i = 0; i < array_size(node->struct_field); i++) { | 557 | for (sz i = 0; i < array_size(node->struct_field); i++) { |
@@ -513,10 +572,11 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
513 | field_name); | 572 | field_name); |
514 | } | 573 | } |
515 | } | 574 | } |
516 | fieldmap_insert( | 575 | fieldmap_insert(&e->val.fields, field_name, |
517 | &e->val.fields, field_name, | 576 | (Field){.name = field_name, |
518 | (Field){.name = field_name, .val = field->field_val}, | 577 | .type = cstr("int"), |
519 | a->storage); | 578 | .val = field->field_val}, |
579 | a->storage); | ||
520 | field->type = cstr("int"); | 580 | field->type = cstr("int"); |
521 | } | 581 | } |
522 | typemap_insert(&scope->types, symbol, symbol, a->storage); | 582 | typemap_insert(&scope->types, symbol, symbol, a->storage); |
@@ -736,6 +796,7 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
736 | a->file_name, node->line, node->col, symbol); | 796 | a->file_name, node->line, node->col, symbol); |
737 | return cstr(""); | 797 | return cstr(""); |
738 | } | 798 | } |
799 | |||
739 | EnumMap *e = find_enum(scope, type->val); | 800 | EnumMap *e = find_enum(scope, type->val); |
740 | if (e && str_eq(symbol, type->val)) { | 801 | if (e && str_eq(symbol, type->val)) { |
741 | if (!node->next) { | 802 | if (!node->next) { |
@@ -753,7 +814,27 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
753 | a->file_name, node->line, node->col, symbol); | 814 | a->file_name, node->line, node->col, symbol); |
754 | return cstr(""); | 815 | return cstr(""); |
755 | } | 816 | } |
756 | node->next->type = symbol; | 817 | node->next->type = cstr("int"); |
818 | node->type = type->val; | ||
819 | return node->type; | ||
820 | } | ||
821 | |||
822 | StructMap *s = find_struct(scope, type->val); | ||
823 | if (s && !str_eq(symbol, type->val)) { | ||
824 | if (node->next) { | ||
825 | FieldMap *field = | ||
826 | fieldmap_lookup(&s->val.fields, node->next->value.str); | ||
827 | if (!field) { | ||
828 | eprintln( | ||
829 | "%s:%d:%d: error: unknown struct field for symbol " | ||
830 | "'%s'", | ||
831 | a->file_name, node->line, node->col, symbol); | ||
832 | return cstr(""); | ||
833 | } | ||
834 | node->next->type = field->val.type; | ||
835 | node->type = type->val; | ||
836 | return node->next->type; | ||
837 | } | ||
757 | } | 838 | } |
758 | node->type = type->val; | 839 | node->type = type->val; |
759 | return node->type; | 840 | return node->type; |
diff --git a/tests/semantics.bad b/tests/semantics.bad index 486f6c0..a6810a9 100644 --- a/tests/semantics.bad +++ b/tests/semantics.bad | |||
@@ -8,7 +8,23 @@ enum weekdays { | |||
8 | sun | 8 | sun |
9 | } | 9 | } |
10 | let a = weekdays.tue | 10 | let a = weekdays.tue |
11 | let b = a | 11 | ; let b = a |
12 | |||
13 | struct item { | ||
14 | id: int | ||
15 | name: str | ||
16 | } | ||
17 | |||
18 | ; let a: item = item { | ||
19 | ; id = 1 | ||
20 | ; } | ||
21 | ; let a: item = item ; this shouldn't work, or should it return the default val? | ||
22 | let c: item | ||
23 | set c.id = "hi" | ||
24 | let d = c.id | ||
25 | ; set a.name = "hello" | ||
26 | ; let b = a.id | ||
27 | ; let c = a.name | ||
12 | 28 | ||
13 | ; fun add10(a: int, b: str): int { | 29 | ; fun add10(a: int, b: str): int { |
14 | ; a + 10 | 30 | ; a + 10 |