aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2024-06-24 19:30:40 +0200
committerBad Diode <bd@badd10de.dev>2024-06-24 19:30:54 +0200
commit1c7d30ab77960be3a0c7ae259d601fa6aed640b3 (patch)
tree9a9aea79bd27d1c70354cee13f528bd499dc6ca2
parent8658d71e8a0761f8407b35cdf08b4b80229f560e (diff)
downloadbdl-1c7d30ab77960be3a0c7ae259d601fa6aed640b3.tar.gz
bdl-1c7d30ab77960be3a0c7ae259d601fa6aed640b3.zip
Add typechecking for structs and struct fields
-rw-r--r--src/main.c95
-rw-r--r--tests/semantics.bad18
2 files changed, 105 insertions, 8 deletions
diff --git a/src/main.c b/src/main.c
index d493d8e..9d8ace8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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
13typedef enum ExecMode { 15typedef enum ExecMode {
14 RUN_NORMAL, 16 RUN_NORMAL,
@@ -65,6 +67,7 @@ typedef struct Fun {
65 67
66typedef struct Field { 68typedef 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
79typedef struct Struct {
80 Str name;
81 struct FieldMap *fields;
82} Struct;
83
76MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) 84MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq)
77MAPDEF(TypeMap, typemap, Str, Type, str_hash, str_eq) 85MAPDEF(TypeMap, typemap, Str, Type, str_hash, str_eq)
78MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq) 86MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq)
79MAPDEF(EnumMap, enummap, Str, Enum, str_hash, str_eq) 87MAPDEF(EnumMap, enummap, Str, Enum, str_hash, str_eq)
88MAPDEF(StructMap, structmap, Str, Struct, str_hash, str_eq)
80MAPDEF(FieldMap, fieldmap, Str, Field, str_hash, str_eq) 89MAPDEF(FieldMap, fieldmap, Str, Field, str_hash, str_eq)
81 90
82typedef struct Scope { 91typedef 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
187StructMap *
188find_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
177void 199void
178graph_scope(Scope *scope, Arena a) { 200graph_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}
10let a = weekdays.tue 10let a = weekdays.tue
11let b = a 11; let b = a
12
13struct 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?
22let c: item
23set c.id = "hi"
24let 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