diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-21 15:59:32 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-21 15:59:32 +0200 |
commit | 59b9c661cbdf125b0a17f8f955158a1f77f163f8 (patch) | |
tree | 36c5b0abe4bb8202f6b23c30759706cd1c0f8a49 | |
parent | d336498c7cf397de496fa46fccc31883480ecb1d (diff) | |
download | bdl-59b9c661cbdf125b0a17f8f955158a1f77f163f8.tar.gz bdl-59b9c661cbdf125b0a17f8f955158a1f77f163f8.zip |
Add semantic checking for enum constructs
-rw-r--r-- | src/main.c | 56 | ||||
-rw-r--r-- | src/parser.c | 5 | ||||
-rw-r--r-- | tests/conditionals.bad | 17 | ||||
-rw-r--r-- | tests/semantics.bad | 84 |
4 files changed, 108 insertions, 54 deletions
@@ -28,16 +28,21 @@ typedef enum { | |||
28 | SYM_FUN, | 28 | SYM_FUN, |
29 | SYM_VAR, | 29 | SYM_VAR, |
30 | SYM_PARAM, | 30 | SYM_PARAM, |
31 | SYM_ENUM, | ||
32 | SYM_ENUM_FIELD, | ||
31 | } SymbolKind; | 33 | } SymbolKind; |
32 | 34 | ||
33 | Str sym_kind_str[] = { | 35 | Str sym_kind_str[] = { |
34 | [SYM_UNKNOWN] = cstr("UNKNOWN "), [SYM_BUILTIN] = cstr("BUILTIN "), | 36 | [SYM_UNKNOWN] = cstr("UNKNOWN "), [SYM_BUILTIN] = cstr("BUILTIN "), |
35 | [SYM_FUN] = cstr("FUNCTION "), [SYM_VAR] = cstr("VARIABLE "), | 37 | [SYM_FUN] = cstr("FUNCTION "), [SYM_VAR] = cstr("VARIABLE "), |
36 | [SYM_PARAM] = cstr("PARAMETER "), | 38 | [SYM_PARAM] = cstr("PARAMETER "), [SYM_ENUM] = cstr("ENUM "), |
39 | [SYM_ENUM_FIELD] = cstr("ENUM FIELD"), | ||
37 | }; | 40 | }; |
38 | 41 | ||
39 | typedef struct Symbol { | 42 | typedef struct Symbol { |
40 | SymbolKind kind; | 43 | SymbolKind kind; |
44 | Str name; | ||
45 | struct Symbol *fields; | ||
41 | } Symbol; | 46 | } Symbol; |
42 | 47 | ||
43 | MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) | 48 | MAPDEF(SymbolMap, symmap, Str, Symbol, str_hash, str_eq) |
@@ -100,15 +105,18 @@ analyzer_symbols(Analyzer *a, Node *node, Scope *scope) { | |||
100 | node->func_name->col, symbol); | 105 | node->func_name->col, symbol); |
101 | } | 106 | } |
102 | symmap_insert(&scope->symbols, symbol, | 107 | symmap_insert(&scope->symbols, symbol, |
103 | (Symbol){.kind = SYM_FUN}, a->storage); | 108 | (Symbol){.kind = SYM_FUN, .name = symbol}, |
109 | a->storage); | ||
104 | } | 110 | } |
105 | 111 | ||
106 | scope = scope_alloc(a, scope); | 112 | scope = scope_alloc(a, scope); |
107 | node->scope = scope; | 113 | node->scope = scope; |
108 | for (sz i = 0; i < array_size(node->func_params); i++) { | 114 | for (sz i = 0; i < array_size(node->func_params); i++) { |
109 | Node *param = node->func_params[i]; | 115 | Node *param = node->func_params[i]; |
110 | symmap_insert(&scope->symbols, param->param_name->value.str, | 116 | Str param_name = param->param_name->value.str; |
111 | (Symbol){.kind = SYM_PARAM}, a->storage); | 117 | symmap_insert(&scope->symbols, param_name, |
118 | (Symbol){.kind = SYM_PARAM, .name = param_name}, | ||
119 | a->storage); | ||
112 | } | 120 | } |
113 | if (node->func_body->kind == NODE_BLOCK) { | 121 | if (node->func_body->kind == NODE_BLOCK) { |
114 | node = node->func_body; | 122 | node = node->func_body; |
@@ -150,6 +158,27 @@ analyzer_symbols(Analyzer *a, Node *node, Scope *scope) { | |||
150 | analyzer_symbols(a, expr, next); | 158 | analyzer_symbols(a, expr, next); |
151 | } | 159 | } |
152 | } break; | 160 | } break; |
161 | case NODE_ENUM: { | ||
162 | Str symbol = node->value.str; | ||
163 | if (symmap_lookup(&scope->symbols, symbol) != NULL) { | ||
164 | eprintln( | ||
165 | "%s:%d:%d: error: symbol '%s' already exists in current " | ||
166 | " scope ", | ||
167 | a->file_name, node->line, node->col, symbol); | ||
168 | } | ||
169 | SymbolMap *map = symmap_insert( | ||
170 | &scope->symbols, symbol, | ||
171 | (Symbol){.kind = SYM_ENUM, .name = symbol}, a->storage); | ||
172 | // TODO: symcheck the value expression? | ||
173 | for (sz i = 0; i < array_size(node->struct_field); i++) { | ||
174 | Node *field = node->struct_field[i]; | ||
175 | Str field_name = field->field_name->value.str; | ||
176 | field_name = str_concat(cstr("."), field_name, a->storage); | ||
177 | field_name = str_concat(symbol, field_name, a->storage); | ||
178 | Symbol s = (Symbol){.kind = SYM_ENUM_FIELD, .name = field_name}; | ||
179 | array_push(map->val.fields, s, a->storage); | ||
180 | } | ||
181 | } break; | ||
153 | case NODE_CASE: { | 182 | case NODE_CASE: { |
154 | analyzer_symbols(a, node->case_value, scope); | 183 | analyzer_symbols(a, node->case_value, scope); |
155 | if (node->case_expr) { | 184 | if (node->case_expr) { |
@@ -225,7 +254,8 @@ analyzer_symbols(Analyzer *a, Node *node, Scope *scope) { | |||
225 | a->file_name, node->var_name->line, node->var_name->col, | 254 | a->file_name, node->var_name->line, node->var_name->col, |
226 | symbol); | 255 | symbol); |
227 | } | 256 | } |
228 | symmap_insert(&scope->symbols, symbol, (Symbol){.kind = SYM_VAR}, | 257 | symmap_insert(&scope->symbols, symbol, |
258 | (Symbol){.kind = SYM_VAR, .name = symbol}, | ||
229 | a->storage); | 259 | a->storage); |
230 | } break; | 260 | } break; |
231 | // Binary ops | 261 | // Binary ops |
@@ -269,7 +299,7 @@ symbolic_analysis(Analyzer *a, Parser *parser) { | |||
269 | }; | 299 | }; |
270 | for (sz i = 0; i < LEN(builtin_functions); i++) { | 300 | for (sz i = 0; i < LEN(builtin_functions); i++) { |
271 | Str symbol = builtin_functions[i]; | 301 | Str symbol = builtin_functions[i]; |
272 | Symbol sym = (Symbol){.kind = SYM_BUILTIN}; | 302 | Symbol sym = (Symbol){.kind = SYM_BUILTIN, .name = symbol}; |
273 | symmap_insert(&scope->symbols, symbol, sym, a->storage); | 303 | symmap_insert(&scope->symbols, symbol, sym, a->storage); |
274 | } | 304 | } |
275 | 305 | ||
@@ -285,7 +315,8 @@ symbolic_analysis(Analyzer *a, Parser *parser) { | |||
285 | a->file_name, root->func_name->line, root->func_name->col, | 315 | a->file_name, root->func_name->line, root->func_name->col, |
286 | symbol); | 316 | symbol); |
287 | } | 317 | } |
288 | symmap_insert(&scope->symbols, symbol, (Symbol){.kind = SYM_FUN}, | 318 | symmap_insert(&scope->symbols, symbol, |
319 | (Symbol){.kind = SYM_FUN, .name = symbol}, | ||
289 | a->storage); | 320 | a->storage); |
290 | } | 321 | } |
291 | } | 322 | } |
@@ -376,7 +407,12 @@ process_file(Str path) { | |||
376 | SymbolMap *sym = symmap_next(&iter, &scratch); | 407 | SymbolMap *sym = symmap_next(&iter, &scratch); |
377 | while (sym) { | 408 | while (sym) { |
378 | println("%s: SCOPE: %d DEPTH: %d\t%s %s", path, scope->id, | 409 | println("%s: SCOPE: %d DEPTH: %d\t%s %s", path, scope->id, |
379 | scope->depth, sym_kind_str[sym->val.kind], sym->key); | 410 | scope->depth, sym_kind_str[sym->val.kind], sym->val.name); |
411 | for (sz i = 0; i < array_size(sym->val.fields); i++) { | ||
412 | Symbol field = sym->val.fields[i]; | ||
413 | println("%s: SCOPE: %d DEPTH: %d\t%s %s", path, scope->id, | ||
414 | scope->depth, sym_kind_str[field.kind], field.name); | ||
415 | } | ||
380 | sym = symmap_next(&iter, &lexer_arena); | 416 | sym = symmap_next(&iter, &lexer_arena); |
381 | } | 417 | } |
382 | } | 418 | } |
diff --git a/src/parser.c b/src/parser.c index 04692b3..67fc739 100644 --- a/src/parser.c +++ b/src/parser.c | |||
@@ -192,7 +192,7 @@ typedef struct Node { | |||
192 | }; | 192 | }; |
193 | }; | 193 | }; |
194 | bool is_ptr; | 194 | bool is_ptr; |
195 | struct Scope* scope; | 195 | struct Scope *scope; |
196 | } Node; | 196 | } Node; |
197 | 197 | ||
198 | // | 198 | // |
@@ -651,6 +651,9 @@ parse_keyword(Parser *parser) { | |||
651 | } | 651 | } |
652 | array_push(node->struct_field, field, parser->storage); | 652 | array_push(node->struct_field, field, parser->storage); |
653 | } | 653 | } |
654 | if (array_size(node->struct_field) == 0) { | ||
655 | parse_emit_err(parser, prev, cstr("empty enum declaration")); | ||
656 | } | ||
654 | } break; | 657 | } break; |
655 | case TOK_COND: { | 658 | case TOK_COND: { |
656 | node = node_alloc(parser, NODE_COND, prev); | 659 | node = node_alloc(parser, NODE_COND, prev); |
diff --git a/tests/conditionals.bad b/tests/conditionals.bad index e1a456e..b5ef61f 100644 --- a/tests/conditionals.bad +++ b/tests/conditionals.bad | |||
@@ -34,12 +34,17 @@ enum flags { | |||
34 | node_div = 1 << 2 | 34 | node_div = 1 << 2 |
35 | node_mul | 35 | node_mul |
36 | } | 36 | } |
37 | let a:flags = flags.node_add | 37 | let f:flags = flags.node_add |
38 | 38 | ||
39 | ; No need to qualify enum fields inside match-case. | 39 | ; TODO: No need to qualify enum fields inside match-case. |
40 | ; match (a) { | ||
41 | ; case node_add = "adding something..." | ||
42 | ; case node_add = "subbing something..." | ||
43 | ; else = "default case" | ||
44 | ; } | ||
40 | match (a) { | 45 | match (a) { |
41 | case node_add = "adding something..." | 46 | case flags.node_add = "adding something..." |
42 | case node_add = "subbing something..." | 47 | case flags.node_add = "subbing something..." |
43 | else = "default case" | 48 | else = "default case" |
44 | } | 49 | } |
45 | 50 | ||
@@ -47,6 +52,6 @@ match (a) { | |||
47 | ; statements. | 52 | ; statements. |
48 | let msg:str = cond { | 53 | let msg:str = cond { |
49 | case a == b = "hello" | 54 | case a == b = "hello" |
50 | case a != c = "world" | 55 | case a != f = "world" |
51 | else = "what" | 56 | else = "what" |
52 | } | 57 | } |
diff --git a/tests/semantics.bad b/tests/semantics.bad index 4ce40b9..29c91c4 100644 --- a/tests/semantics.bad +++ b/tests/semantics.bad | |||
@@ -1,49 +1,59 @@ | |||
1 | fun foo(): nil { | 1 | enum field { |
2 | bar() | 2 | a |
3 | b | ||
4 | ; a | ||
5 | ; c = field.a | ||
6 | ; c = field.c | ||
7 | ; c = c | ||
8 | ; c = a | ||
3 | } | 9 | } |
4 | 10 | ||
5 | fun bar(): nil { | 11 | ; fun foo(): nil { |
6 | foo() | 12 | ; bar() |
7 | } | 13 | ; } |
8 | 14 | ||
9 | ; There are some builtint functions. | 15 | ; fun bar(): nil { |
10 | println("hello world") | 16 | ; foo() |
17 | ; } | ||
11 | 18 | ||
12 | ; Let/set. | 19 | ; ; There are some builtint functions. |
13 | let num = 1 | 20 | ; println("hello world") |
14 | set num = 2 | ||
15 | set num = 0 + num | ||
16 | 21 | ||
17 | ; Loops and conditionals. | 22 | ; ; Let/set. |
18 | if (num) 3 else 1 | 23 | ; let num = 1 |
19 | let val = if (2 + num == 4) num else num | 24 | ; set num = 2 |
20 | if (true) { | 25 | ; set num = 0 + num |
21 | let c = 1 | ||
22 | 1 + 1 + c | ||
23 | } | ||
24 | 26 | ||
25 | match (num) { | 27 | ; ; Loops and conditionals. |
26 | case 1 = 23 | 28 | ; if (num) 3 else 1 |
27 | case 2 = num | 29 | ; let val = if (2 + num == 4) num else num |
28 | else = num | 30 | ; if (true) { |
29 | } | 31 | ; let c = 1 |
32 | ; 1 + 1 + c | ||
33 | ; } | ||
30 | 34 | ||
31 | cond { | 35 | ; match (num) { |
32 | case 1 == 1 = 23 | 36 | ; case 1 = 23 |
33 | case 2 != 1 = num | 37 | ; case 2 = num |
34 | else = num | 38 | ; else = num |
35 | } | 39 | ; } |
36 | 40 | ||
37 | while (num == 1) num | 41 | ; cond { |
38 | while (true) { | 42 | ; case 1 == 1 = 23 |
39 | let c = 1 | 43 | ; case 2 != 1 = num |
40 | 1 + 1 + c | 44 | ; else = num |
41 | } | 45 | ; } |
42 | 46 | ||
43 | fun nested(): u32 { | 47 | ; while (num == 1) num |
44 | fun adder(a: u32, b: u32): u32 a + b | 48 | ; while (true) { |
45 | adder(1,2) | 49 | ; let c = 1 |
46 | } | 50 | ; 1 + 1 + c |
51 | ; } | ||
52 | |||
53 | ; fun nested(): u32 { | ||
54 | ; fun adder(a: u32, b: u32): u32 a + b | ||
55 | ; adder(1,2) | ||
56 | ; } | ||
47 | 57 | ||
48 | ; This should err. | 58 | ; This should err. |
49 | ; fun foo(): nil { | 59 | ; fun foo(): nil { |