aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBad Diode <bd@badd10de.dev>2024-06-25 10:36:02 +0200
committerBad Diode <bd@badd10de.dev>2024-06-25 10:36:02 +0200
commitbcc40682a0d699e8c36df62975c12cdea491d167 (patch)
tree6511cdef1730d39bb81fceaf9839658654280f32
parent9eb5c9bfa3fcf8d7b03efd7d4def71e39ae3f799 (diff)
downloadbdl-bcc40682a0d699e8c36df62975c12cdea491d167.tar.gz
bdl-bcc40682a0d699e8c36df62975c12cdea491d167.zip
Add struct literals typechecking
-rw-r--r--src/main.c58
-rw-r--r--src/parser.c2
-rw-r--r--tests/semantics.bad52
-rw-r--r--tests/variables.bad53
4 files changed, 106 insertions, 59 deletions
diff --git a/src/main.c b/src/main.c
index 33d193b..6c3f3a5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -12,6 +12,8 @@
12// TODO: nested structs 12// TODO: nested structs
13// TODO: unions 13// TODO: unions
14// TODO: match deconstruct structs 14// TODO: match deconstruct structs
15// TODO: arrays and pointers
16// TODO: struct literals
15 17
16typedef enum ExecMode { 18typedef enum ExecMode {
17 RUN_NORMAL, 19 RUN_NORMAL,
@@ -486,13 +488,18 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) {
486 } 488 }
487 // TODO: Consider compatible types. 489 // TODO: Consider compatible types.
488 if (!str_eq(type, type_name)) { 490 if (!str_eq(type, type_name)) {
489 eprintln( 491 // Special case, enums can be treated as ints.
490 "%s:%d:%d: error: type mismatch, trying to assing " 492 EnumMap *e = find_enum(scope, type_name);
491 "%s" 493 if (!(e && str_eq(type, cstr("int")))) {
492 " to a variable of type %s", 494 eprintln(
493 a->file_name, node->var_type->line, 495 "%s:%d:%d: error: type mismatch, trying to "
494 node->var_type->col, type, type_name); 496 "assing "
495 return cstr(""); 497 "%s"
498 " to a variable of type %s",
499 a->file_name, node->var_type->line,
500 node->var_type->col, type, type_name);
501 return cstr("");
502 }
496 } 503 }
497 } 504 }
498 typemap_insert(&scope->types, symbol, type->val, a->storage); 505 typemap_insert(&scope->types, symbol, type->val, a->storage);
@@ -515,8 +522,7 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) {
515 "%s:%d:%d: error: type mismatch, trying to assing " 522 "%s:%d:%d: error: type mismatch, trying to assing "
516 "%s" 523 "%s"
517 " to a variable of type %s", 524 " to a variable of type %s",
518 a->file_name, node->line, node->col, 525 a->file_name, node->line, node->col, val, name);
519 val, name);
520 return cstr(""); 526 return cstr("");
521 } 527 }
522 node->type = cstr("nil"); 528 node->type = cstr("nil");
@@ -849,6 +855,40 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) {
849 node->type = type->val; 855 node->type = type->val;
850 return node->type; 856 return node->type;
851 } break; 857 } break;
858 case NODE_STRUCT_LIT: {
859 Str name = node->value.str;
860 StructMap *s = find_struct(scope, name);
861 if (!s) {
862 eprintln("%s:%d:%d: error: unknown struct type '%s'",
863 a->file_name, node->line, node->col, name);
864 return cstr("");
865 }
866 for (sz i = 0; i < array_size(node->elements); i++) {
867 Node *next = node->elements[i];
868 Str field_name = next->field_name->value.str;
869 FieldMap *field = fieldmap_lookup(&s->val.fields, field_name);
870 if (!field) {
871 eprintln(
872 "%s:%d:%d: error: unknown struct field for symbol '%s'",
873 a->file_name, next->line, next->col, field_name);
874 } else {
875 if (next->field_val) {
876 Type type = type_inference(a, next->field_val, scope);
877 if (!str_eq(type, field->val.type)) {
878 eprintln(
879 "%s:%d:%d: error: mismatched types in struct "
880 "value "
881 "for '%s.%s': %s expected %s",
882 a->file_name, next->line, next->col, name,
883 field_name, type, field->val.type);
884 }
885 }
886 next->type = field->val.type;
887 }
888 }
889 node->type = name;
890 return node->type;
891 } break;
852 case NODE_FUNCALL: { 892 case NODE_FUNCALL: {
853 Str symbol = node->value.str; 893 Str symbol = node->value.str;
854 FunMap *fun = find_fun(scope, symbol); 894 FunMap *fun = find_fun(scope, symbol);
diff --git a/src/parser.c b/src/parser.c
index 7b49b55..b08c82a 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -959,7 +959,7 @@ parse_symbol(Parser *parser) {
959 field->field_name = array_pop(parser->nodes); 959 field->field_name = array_pop(parser->nodes);
960 parse_consume(parser, TOK_ASSIGN, cstr("expected assignment")); 960 parse_consume(parser, TOK_ASSIGN, cstr("expected assignment"));
961 parse_expr(parser, PREC_LOW); 961 parse_expr(parser, PREC_LOW);
962 field->field_type = array_pop(parser->nodes); 962 field->field_val = array_pop(parser->nodes);
963 array_push(node->elements, field, parser->storage); 963 array_push(node->elements, field, parser->storage);
964 } 964 }
965 } else if (parse_match(parser, TOK_LSQUARE)) { 965 } else if (parse_match(parser, TOK_LSQUARE)) {
diff --git a/tests/semantics.bad b/tests/semantics.bad
index 95e827b..60e47d1 100644
--- a/tests/semantics.bad
+++ b/tests/semantics.bad
@@ -1,27 +1,33 @@
1enum weekdays { 1struct vec {
2 mon = 1 2 x: f64
3 tue 3 y: f64
4 wed 4 z: f64
5 thu
6 fri
7 sat
8 sun
9} 5}
10let a = weekdays.tue 6let v = vec : { x = 1.0 }
11; let b = a 7; enum weekdays {
12 8; mon = 1
13struct item { 9; tue
14 id: int 10; wed
15 name: str 11; thu
16} 12; fri
17 13; sat
18; let a: item = item { 14; sun
19; id = 1 15; }
20; } 16; let a = weekdays.tue
21; let a: item = item ; this shouldn't work, or should it return the default val? 17; ; let b = a
22let c: item 18
23set c.id = 1 19; struct item {
24let d = c.id 20; id: int
21; name: str
22; }
23
24; ; let a: item = item {
25; ; id = 1
26; ; }
27; ; let a: item = item ; this shouldn't work, or should it return the default val?
28; let c: item
29; set c.id = 1
30; let d = c.id
25; set a.name = "hello" 31; set a.name = "hello"
26; let b = a.id 32; let b = a.id
27; let c = a.name 33; let c = a.name
diff --git a/tests/variables.bad b/tests/variables.bad
index 88e04d1..34293dc 100644
--- a/tests/variables.bad
+++ b/tests/variables.bad
@@ -11,10 +11,11 @@ set c = (1 + 2 - 3)
11 11
12; Struct definitions. 12; Struct definitions.
13struct vec { 13struct vec {
14 x: f32 14 x: f64
15 y: f32 15 y: f64
16 z: f32 16 z: f64
17} 17}
18let v = vec : { x = 2.0 y = 3.0 }
18 19
19; Default values are allowed, including const expressions. 20; Default values are allowed, including const expressions.
20struct person { 21struct person {
@@ -28,41 +29,41 @@ set player_a.name = "alex"
28set player_a.age = 32 29set player_a.age = 32
29let player_b = player_a 30let player_b = player_a
30 31
31; Anonymous structs can also be declared inline.
32let user: { id: u64 name: str }
33set user.id = 10
34set user.name = "haxor"
35
36; We can have anonymous struct fields. 32; We can have anonymous struct fields.
37struct entity { 33struct entity {
38 pos: vec 34 pos: vec
39 vel: vec 35 vel: vec
40 attr: { 36 ; TODO: ...
41 id: u64 37 ; attr: {
42 name: str 38 ; id: u64
43 } 39 ; name: str
40 ; }
44} 41}
45 42
46; Symbols followed by curly braces output struct literals. 43; Symbols followed by curly braces output struct literals.
47let particle = entity : { 44let particle = entity : {
48 ; Two ways of initializing inner fields. 45 ; Two ways of initializing inner fields.
49 pos = vec : { x = 1 y = 2 } 46 pos = vec : { x = 1.0 y = 2.0 }
50 attr.id = 1 47 ; TODO: Get rid of this, unnecessary complexity on the implementation, let's
51 attr.name = "particle" 48 ; just do the top option.
49 ; attr.id = 1
50 ; attr.name = "particle"
52 51
53 ; Missing initialization fields default to zero. 52 ; Missing initialization fields default to zero.
54 vel = vec : { y = -3 } 53 vel = vec : { y = -3.0 }
55} 54}
55; let particle = entity : {}
56; TODO: Now we can get rid of parenthesis on if/while statements
56 57
57; We can have static arrays and have indexed access. 58; ; We can have static arrays and have indexed access.
58let numbers: u32[0xff] 59; let numbers: u32[0xff]
59set numbers[0] = 32 60; set numbers[0] = 32
60set numbers[1] = 42 61; set numbers[1] = 42
61 62
62; Arrays are syntactic sugar for pointers (@). 63; ; Arrays are syntactic sugar for pointers (@).
63let ptr:@u32 = numbers 64; let ptr:@u32 = numbers
64set ptr[10] = 33 65; set ptr[10] = 33
65 66
66; Strings hold a .mem and .size fields with the number of bytes it holds. 67; ; Strings hold a .mem and .size fields with the number of bytes it holds.
67let hello: str = "hello world" 68; let hello: str = "hello world"
68set c[1] = 'a' ; "hallo world" 69; set c[1] = 'a' ; "hallo world"