diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-23 21:18:44 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-23 21:18:44 +0200 |
commit | 25444d18759bb7dde28dc6cb7a1554a08a125e34 (patch) | |
tree | 8e934c5e72bf124e6a558acc1dfa2607740652a1 | |
parent | 42046a947e1ad636a1ee23be58ce0be2d8abbab2 (diff) | |
download | bdl-25444d18759bb7dde28dc6cb7a1554a08a125e34.tar.gz bdl-25444d18759bb7dde28dc6cb7a1554a08a125e34.zip |
Add non-exhaustive typechecking for cond/match expr
-rw-r--r-- | src/main.c | 48 | ||||
-rw-r--r-- | src/parser.c | 14 | ||||
-rw-r--r-- | tests/semantics.bad | 13 |
3 files changed, 64 insertions, 11 deletions
@@ -304,6 +304,51 @@ type_inference(Analyzer *a, Node *node, TypeScope *scope) { | |||
304 | node->type = cstr("nil"); | 304 | node->type = cstr("nil"); |
305 | return node->type; | 305 | return node->type; |
306 | } break; | 306 | } break; |
307 | case NODE_COND: | ||
308 | case NODE_MATCH: { | ||
309 | if (node->match_expr) { | ||
310 | type_inference(a, node->match_expr, scope); | ||
311 | } | ||
312 | Type previous = cstr(""); | ||
313 | for (sz i = 0; i < array_size(node->match_cases); i++) { | ||
314 | Node *expr = node->match_cases[i]; | ||
315 | Type next = type_inference(a, expr, scope); | ||
316 | if (i != 0 && !str_eq(next, previous)) { | ||
317 | emit_semantic_error( | ||
318 | a, node, | ||
319 | cstr("non-matching types for case expressions")); | ||
320 | return cstr(""); | ||
321 | } | ||
322 | previous = next; | ||
323 | } | ||
324 | // TODO: proper typecheck matchcase expressions here... | ||
325 | node->type = previous; | ||
326 | return node->type; | ||
327 | } break; | ||
328 | case NODE_CASE_MATCH: { | ||
329 | if (node->case_value) { | ||
330 | type_inference(a, node->case_value, scope); | ||
331 | } | ||
332 | if (node->case_expr->kind != NODE_BLOCK) { | ||
333 | scope = typescope_alloc(a, scope); | ||
334 | } | ||
335 | node->type = type_inference(a, node->case_expr, scope); | ||
336 | return node->type; | ||
337 | } break; | ||
338 | case NODE_CASE_COND: { | ||
339 | if (node->case_value) { | ||
340 | Type cond = type_inference(a, node->case_value, scope); | ||
341 | if (!str_eq(cond, cstr("bool"))) { | ||
342 | emit_semantic_error(a, node, | ||
343 | cstr("non-boolean case condition")); | ||
344 | } | ||
345 | } | ||
346 | if (node->case_expr->kind != NODE_BLOCK) { | ||
347 | scope = typescope_alloc(a, scope); | ||
348 | } | ||
349 | node->type = type_inference(a, node->case_expr, scope); | ||
350 | return node->type; | ||
351 | } break; | ||
307 | case NODE_TRUE: | 352 | case NODE_TRUE: |
308 | case NODE_FALSE: { | 353 | case NODE_FALSE: { |
309 | node->type = cstr("bool"); | 354 | node->type = cstr("bool"); |
@@ -564,7 +609,8 @@ analyzer_symbols(Analyzer *a, Node *node, Scope *scope) { | |||
564 | symmap_insert(&map->val.fields, field_name, s, a->storage); | 609 | symmap_insert(&map->val.fields, field_name, s, a->storage); |
565 | } | 610 | } |
566 | } break; | 611 | } break; |
567 | case NODE_CASE: { | 612 | case NODE_CASE_MATCH: |
613 | case NODE_CASE_COND: { | ||
568 | analyzer_symbols(a, node->case_value, scope); | 614 | analyzer_symbols(a, node->case_value, scope); |
569 | if (node->case_expr) { | 615 | if (node->case_expr) { |
570 | if (node->case_expr->kind != NODE_BLOCK) { | 616 | if (node->case_expr->kind != NODE_BLOCK) { |
diff --git a/src/parser.c b/src/parser.c index 9079df9..b82579b 100644 --- a/src/parser.c +++ b/src/parser.c | |||
@@ -47,8 +47,9 @@ typedef enum NodeKind { | |||
47 | NODE_STRUCT, | 47 | NODE_STRUCT, |
48 | NODE_IF, | 48 | NODE_IF, |
49 | NODE_MATCH, | 49 | NODE_MATCH, |
50 | NODE_CASE, | ||
51 | NODE_COND, | 50 | NODE_COND, |
51 | NODE_CASE_MATCH, | ||
52 | NODE_CASE_COND, | ||
52 | NODE_ENUM, | 53 | NODE_ENUM, |
53 | NODE_BREAK, | 54 | NODE_BREAK, |
54 | NODE_CONTINUE, | 55 | NODE_CONTINUE, |
@@ -111,8 +112,9 @@ Str node_str[] = { | |||
111 | [NODE_STRUCT] = cstr("STRUCT DEF"), | 112 | [NODE_STRUCT] = cstr("STRUCT DEF"), |
112 | [NODE_IF] = cstr("IF"), | 113 | [NODE_IF] = cstr("IF"), |
113 | [NODE_MATCH] = cstr("MATCH"), | 114 | [NODE_MATCH] = cstr("MATCH"), |
114 | [NODE_CASE] = cstr("CASE"), | ||
115 | [NODE_COND] = cstr("COND"), | 115 | [NODE_COND] = cstr("COND"), |
116 | [NODE_CASE_MATCH] = cstr("CASE"), | ||
117 | [NODE_CASE_COND] = cstr("CASE"), | ||
116 | [NODE_ENUM] = cstr("ENUM"), | 118 | [NODE_ENUM] = cstr("ENUM"), |
117 | [NODE_BREAK] = cstr("BREAK"), | 119 | [NODE_BREAK] = cstr("BREAK"), |
118 | [NODE_CONTINUE] = cstr("CONTINUE"), | 120 | [NODE_CONTINUE] = cstr("CONTINUE"), |
@@ -609,7 +611,8 @@ parse_keyword(Parser *parser) { | |||
609 | parse_consume(parser, TOK_LCURLY, | 611 | parse_consume(parser, TOK_LCURLY, |
610 | cstr("expected block of match cases")); | 612 | cstr("expected block of match cases")); |
611 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { | 613 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { |
612 | Node *tmp = node_alloc(parser, NODE_CASE, parser->previous); | 614 | Node *tmp = |
615 | node_alloc(parser, NODE_CASE_MATCH, parser->previous); | ||
613 | if (!tmp) return; | 616 | if (!tmp) return; |
614 | // Are we on the default case. | 617 | // Are we on the default case. |
615 | if (!parse_match(parser, TOK_ELSE)) { | 618 | if (!parse_match(parser, TOK_ELSE)) { |
@@ -668,12 +671,11 @@ parse_keyword(Parser *parser) { | |||
668 | parse_consume(parser, TOK_LCURLY, | 671 | parse_consume(parser, TOK_LCURLY, |
669 | cstr("expected '{' on cond expression")); | 672 | cstr("expected '{' on cond expression")); |
670 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { | 673 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { |
671 | Node *tmp = node_alloc(parser, NODE_CASE, parser->previous); | 674 | Node *tmp = |
675 | node_alloc(parser, NODE_CASE_COND, parser->previous); | ||
672 | if (!tmp) return; | 676 | if (!tmp) return; |
673 | // Are we on the default case. | 677 | // Are we on the default case. |
674 | if (!parse_match(parser, TOK_ELSE)) { | 678 | if (!parse_match(parser, TOK_ELSE)) { |
675 | parse_consume(parser, TOK_CASE, | ||
676 | cstr("expected case statement")); | ||
677 | parse_expr(parser, PREC_LOW); | 679 | parse_expr(parser, PREC_LOW); |
678 | tmp->case_value = array_pop(parser->nodes); | 680 | tmp->case_value = array_pop(parser->nodes); |
679 | } | 681 | } |
diff --git a/tests/semantics.bad b/tests/semantics.bad index a938979..5b5c971 100644 --- a/tests/semantics.bad +++ b/tests/semantics.bad | |||
@@ -18,15 +18,20 @@ | |||
18 | ; 44 | 18 | ; 44 |
19 | ; } | 19 | ; } |
20 | 20 | ||
21 | let single = if (true) { | 21 | ; let single = if (true) { |
22 | 123 | 22 | ; 123 |
23 | } | 23 | ; } |
24 | 24 | ||
25 | ; while (!true) { | 25 | ; while (!true) { |
26 | ; "asjdflasdjf" | 26 | ; "asjdflasdjf" |
27 | ; } | 27 | ; } |
28 | 28 | ||
29 | set single = 32 | 29 | ; set single = 32 |
30 | |||
31 | cond { | ||
32 | 1 == 2 = "hello" | ||
33 | else = "dong" | ||
34 | } | ||
30 | 35 | ||
31 | ; enum test { | 36 | ; enum test { |
32 | ; a = 1 | 37 | ; a = 1 |