diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-25 10:36:02 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-25 10:36:02 +0200 |
commit | bcc40682a0d699e8c36df62975c12cdea491d167 (patch) | |
tree | 6511cdef1730d39bb81fceaf9839658654280f32 | |
parent | 9eb5c9bfa3fcf8d7b03efd7d4def71e39ae3f799 (diff) | |
download | bdl-bcc40682a0d699e8c36df62975c12cdea491d167.tar.gz bdl-bcc40682a0d699e8c36df62975c12cdea491d167.zip |
Add struct literals typechecking
-rw-r--r-- | src/main.c | 58 | ||||
-rw-r--r-- | src/parser.c | 2 | ||||
-rw-r--r-- | tests/semantics.bad | 52 | ||||
-rw-r--r-- | tests/variables.bad | 53 |
4 files changed, 106 insertions, 59 deletions
@@ -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 | ||
16 | typedef enum ExecMode { | 18 | typedef 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 @@ | |||
1 | enum weekdays { | 1 | struct 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 | } |
10 | let a = weekdays.tue | 6 | let v = vec : { x = 1.0 } |
11 | ; let b = a | 7 | ; enum weekdays { |
12 | 8 | ; mon = 1 | |
13 | struct 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 |
22 | let c: item | 18 | |
23 | set c.id = 1 | 19 | ; struct item { |
24 | let 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. |
13 | struct vec { | 13 | struct vec { |
14 | x: f32 | 14 | x: f64 |
15 | y: f32 | 15 | y: f64 |
16 | z: f32 | 16 | z: f64 |
17 | } | 17 | } |
18 | let 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. |
20 | struct person { | 21 | struct person { |
@@ -28,41 +29,41 @@ set player_a.name = "alex" | |||
28 | set player_a.age = 32 | 29 | set player_a.age = 32 |
29 | let player_b = player_a | 30 | let player_b = player_a |
30 | 31 | ||
31 | ; Anonymous structs can also be declared inline. | ||
32 | let user: { id: u64 name: str } | ||
33 | set user.id = 10 | ||
34 | set user.name = "haxor" | ||
35 | |||
36 | ; We can have anonymous struct fields. | 32 | ; We can have anonymous struct fields. |
37 | struct entity { | 33 | struct 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. |
47 | let particle = entity : { | 44 | let 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. |
58 | let numbers: u32[0xff] | 59 | ; let numbers: u32[0xff] |
59 | set numbers[0] = 32 | 60 | ; set numbers[0] = 32 |
60 | set numbers[1] = 42 | 61 | ; set numbers[1] = 42 |
61 | 62 | ||
62 | ; Arrays are syntactic sugar for pointers (@). | 63 | ; ; Arrays are syntactic sugar for pointers (@). |
63 | let ptr:@u32 = numbers | 64 | ; let ptr:@u32 = numbers |
64 | set 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. |
67 | let hello: str = "hello world" | 68 | ; let hello: str = "hello world" |
68 | set c[1] = 'a' ; "hallo world" | 69 | ; set c[1] = 'a' ; "hallo world" |