aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2024-06-24 17:28:46 +0200
committerBad Diode <bd@badd10de.dev>2024-06-24 17:28:46 +0200
commit2560da172f15982da7464a6534702c08f9b7575d (patch)
tree29dd60aeb65e40c17e52b5bad677502dbb18e163
parent6d8b7b0bf66d22d6de13474b18617204adfe32e9 (diff)
downloadbdl-2560da172f15982da7464a6534702c08f9b7575d.tar.gz
bdl-2560da172f15982da7464a6534702c08f9b7575d.zip
Add typechecking for enum fields
-rw-r--r--src/main.c60
-rw-r--r--tests/comparisons.bad4
-rw-r--r--tests/conditionals.bad8
-rw-r--r--tests/semantics.bad43
4 files changed, 81 insertions, 34 deletions
diff --git a/src/main.c b/src/main.c
index 4cbce58..f2c4d4f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -6,15 +6,9 @@
6#include "lexer.c" 6#include "lexer.c"
7#include "parser.c" 7#include "parser.c"
8#include "vm.c" 8#include "vm.c"
9// TODO: To typecheck IF we need to make sure it always returns a value if we 9
10// are using it as an expression. Not as straightforward as I initially 10// TODO: typecheck structs
11// imagined! An alternative is implicit zero value in the previous case: 11// TODO: add field accessor for struct/enum symbols
12//
13// let a = if (expr) 123
14//
15// If the expression evalueates to `true`, a will be 123, otherwise, 0.
16//
17// This pattern seems quite common in practice no?
18 12
19typedef enum ExecMode { 13typedef enum ExecMode {
20 RUN_NORMAL, 14 RUN_NORMAL,
@@ -69,9 +63,21 @@ typedef struct Fun {
69 Str return_type; 63 Str return_type;
70} Fun; 64} Fun;
71 65
66typedef struct Field {
67 Str name;
68 Node *val;
69} Field;
70
71typedef struct Enum {
72 Str name;
73 struct FieldMap *fields;
74} Enum;
75
72MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) 76MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq)
73MAPDEF(TypeMap, typemap, Str, Type, str_hash, str_eq) 77MAPDEF(TypeMap, typemap, Str, Type, str_hash, str_eq)
74MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq) 78MAPDEF(FunMap, funmap, Str, Fun, str_hash, str_eq)
79MAPDEF(EnumMap, enummap, Str, Enum, str_hash, str_eq)
80MAPDEF(FieldMap, fieldmap, Str, Field, str_hash, str_eq)
75 81
76typedef struct Scope { 82typedef struct Scope {
77 sz id; 83 sz id;
@@ -85,6 +91,7 @@ typedef struct TypeScope {
85 Str name; 91 Str name;
86 TypeMap *types; 92 TypeMap *types;
87 FunMap *funcs; 93 FunMap *funcs;
94 EnumMap *enums;
88 struct TypeScope *parent; 95 struct TypeScope *parent;
89} TypeScope; 96} TypeScope;
90 97
@@ -471,6 +478,37 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) {
471 node->type = cstr("nil"); 478 node->type = cstr("nil");
472 return node->type; 479 return node->type;
473 } break; 480 } break;
481 case NODE_ENUM: {
482 node->type = cstr("nil");
483 Str symbol = node->value.str;
484 EnumMap *e = enummap_insert(&scope->enums, symbol,
485 (Enum){.name = symbol}, a->storage);
486 for (sz i = 0; i < array_size(node->struct_field); i++) {
487 Node *field = node->struct_field[i];
488 Str field_name = field->field_name->value.str;
489 if (fieldmap_lookup(&e->val.fields, field_name)) {
490 eprintln(
491 "%s:%d:%d: error: enum field '%s.%s' already exists",
492 a->file_name, field->line, field->col, symbol,
493 field_name);
494 }
495 if (field->field_val) {
496 Type type = type_inference(a, field->field_val, scope);
497 if (!str_eq(type, cstr("int"))) {
498 eprintln(
499 "%s:%d:%d: error: non int enum value for '%s.%s'",
500 a->file_name, field->line, field->col, symbol,
501 field_name);
502 }
503 }
504 fieldmap_insert(
505 &e->val.fields, field_name,
506 (Field){.name = field_name, .val = field->field_val},
507 a->storage);
508 field->type = cstr("int");
509 }
510 return node->type;
511 } break;
474 case NODE_IF: { 512 case NODE_IF: {
475 Type cond_type = type_inference(a, node->cond_if, scope); 513 Type cond_type = type_inference(a, node->cond_if, scope);
476 if (!str_eq(cond_type, cstr("bool"))) { 514 if (!str_eq(cond_type, cstr("bool"))) {
@@ -566,6 +604,10 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) {
566 node->type = cstr("bool"); 604 node->type = cstr("bool");
567 return node->type; 605 return node->type;
568 } break; 606 } break;
607 case NODE_NIL: {
608 node->type = cstr("nil");
609 return node->type;
610 } break;
569 case NODE_NOT: 611 case NODE_NOT:
570 case NODE_AND: 612 case NODE_AND:
571 case NODE_OR: { 613 case NODE_OR: {
diff --git a/tests/comparisons.bad b/tests/comparisons.bad
index 7bc5d33..512feac 100644
--- a/tests/comparisons.bad
+++ b/tests/comparisons.bad
@@ -7,7 +7,3 @@ true != false
72 >= 1 && 2 > 1 72 >= 1 && 2 > 1
83 >= 3 || 4 <= 5 83 >= 3 || 4 <= 5
94 < (5 + 6) == true 94 < (5 + 6) == true
10
11nil != false
12nil != true
13nil != 1
diff --git a/tests/conditionals.bad b/tests/conditionals.bad
index b5ef61f..50d0cfc 100644
--- a/tests/conditionals.bad
+++ b/tests/conditionals.bad
@@ -5,7 +5,7 @@ if (true) "hello"
5let a = if (2 + 2 >= 4) 42 5let a = if (2 + 2 >= 4) 42
6 6
7; We support a single if expression. 7; We support a single if expression.
8let b = if (0xff == 255) "hello" else "world" 8let b = if (0xff == 0x32) "hello" else "world"
9 9
10; ... but these can compound on each other 10; ... but these can compound on each other
11if (1 < 2) 6 11if (1 < 2) 6
@@ -51,7 +51,7 @@ match (a) {
51; Conditional `cond` statements are syntactic sugar for a chain of if-else 51; Conditional `cond` statements are syntactic sugar for a chain of if-else
52; statements. 52; statements.
53let msg:str = cond { 53let msg:str = cond {
54 case a == b = "hello" 54 a == 1 = "hello"
55 case a != f = "world" 55 a != 2 = "world"
56 else = "what" 56 else = "what"
57} 57}
diff --git a/tests/semantics.bad b/tests/semantics.bad
index 3479915..ee56c14 100644
--- a/tests/semantics.bad
+++ b/tests/semantics.bad
@@ -1,10 +1,19 @@
1fun add10(a: int, b: str): int { 1enum weekdays {
2 a + 10 2 mon = 1
3 tue
4 wed
5 thu
6 fri
7 sat
8 sun
3} 9}
10; fun add10(a: int, b: str): int {
11; a + 10
12; }
4 13
5fun foo(): int { 14; fun foo(): int {
6 add10(1, "hello") 15; add10(1, "hello")
7} 16; }
8 17
9; let a:f32 = (1.0 + 2.0 * 2.0) / 2.0 18; let a:f32 = (1.0 + 2.0 * 2.0) / 2.0
10 19
@@ -54,18 +63,18 @@ fun foo(): int {
54; let a:my_struct 63; let a:my_struct
55; set a.field_a = 1 64; set a.field_a = 1
56 65
57fun nested(): int { 66; fun nested(): int {
58 fun adder(a: u32, b: u32): u32 { 67; fun adder(a: u32, b: u32): u32 {
59 a + b 68; a + b
60 } 69; }
61 if (1 + 1 == 2) { 70; if (1 + 1 == 2) {
62 let b = 15 71; let b = 15
63 5 + b 72; {
64 ; { 73; let c = 32
65 ; let b = 32 74; 5 + c
66 ; } 75; }
67 } 76; }
68} 77; }
69 78
70; enum field { 79; enum field {
71; a 80; a