diff options
author | Bad Diode <bd@badd10de.dev> | 2024-06-18 19:47:39 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2024-06-18 19:47:39 +0200 |
commit | f9d0fe5ad641e453724dabc2b17634a44e198ce0 (patch) | |
tree | 9d86cd3f89b36ae9053a383fd44f93f9c044b0fe | |
parent | c9db27abbbfa80a79515e26efaae9012d3275404 (diff) | |
download | bdl-f9d0fe5ad641e453724dabc2b17634a44e198ce0.tar.gz bdl-f9d0fe5ad641e453724dabc2b17634a44e198ce0.zip |
Add enum parsing
-rw-r--r-- | src/lexer.c | 12 | ||||
-rw-r--r-- | src/main.c | 55 | ||||
-rw-r--r-- | tests/conditionals.bad | 62 |
3 files changed, 99 insertions, 30 deletions
diff --git a/src/lexer.c b/src/lexer.c index 36a5636..2d2b6fc 100644 --- a/src/lexer.c +++ b/src/lexer.c | |||
@@ -23,11 +23,13 @@ typedef enum TokenKind { | |||
23 | // Keywords. | 23 | // Keywords. |
24 | TOK_BREAK, // break | 24 | TOK_BREAK, // break |
25 | TOK_CASE, // case | 25 | TOK_CASE, // case |
26 | TOK_COND, // match | ||
26 | TOK_CONTINUE, // continue | 27 | TOK_CONTINUE, // continue |
28 | TOK_ELSE, // else | ||
29 | TOK_ENUM, // enum | ||
27 | TOK_FALSE, // false | 30 | TOK_FALSE, // false |
28 | TOK_FUN, // fun | 31 | TOK_FUN, // fun |
29 | TOK_IF, // if | 32 | TOK_IF, // if |
30 | TOK_ELSE, // else | ||
31 | TOK_LET, // let | 33 | TOK_LET, // let |
32 | TOK_MATCH, // match | 34 | TOK_MATCH, // match |
33 | TOK_NIL, // nil | 35 | TOK_NIL, // nil |
@@ -93,8 +95,10 @@ Str token_str[] = { | |||
93 | // Keywords. | 95 | // Keywords. |
94 | [TOK_BREAK] = cstr("BREAK"), | 96 | [TOK_BREAK] = cstr("BREAK"), |
95 | [TOK_CASE] = cstr("CASE"), | 97 | [TOK_CASE] = cstr("CASE"), |
98 | [TOK_COND] = cstr("COND"), | ||
96 | [TOK_CONTINUE] = cstr("CONTINUE"), | 99 | [TOK_CONTINUE] = cstr("CONTINUE"), |
97 | [TOK_ELSE] = cstr("ELSE"), | 100 | [TOK_ELSE] = cstr("ELSE"), |
101 | [TOK_ENUM] = cstr("ENUM"), | ||
98 | [TOK_FALSE] = cstr("FALSE"), | 102 | [TOK_FALSE] = cstr("FALSE"), |
99 | [TOK_FUN] = cstr("FUN"), | 103 | [TOK_FUN] = cstr("FUN"), |
100 | [TOK_IF] = cstr("IF"), | 104 | [TOK_IF] = cstr("IF"), |
@@ -540,11 +544,17 @@ scan_token(Scanner *scanner) { | |||
540 | if (str_has_prefix(val, cstr("continue"))) { | 544 | if (str_has_prefix(val, cstr("continue"))) { |
541 | return emit_token(current, scanner, TOK_CONTINUE); | 545 | return emit_token(current, scanner, TOK_CONTINUE); |
542 | } | 546 | } |
547 | if (str_has_prefix(val, cstr("cond"))) { | ||
548 | return emit_token(current, scanner, TOK_COND); | ||
549 | } | ||
543 | } break; | 550 | } break; |
544 | case 'e': { | 551 | case 'e': { |
545 | if (str_has_prefix(val, cstr("else"))) { | 552 | if (str_has_prefix(val, cstr("else"))) { |
546 | return emit_token(current, scanner, TOK_ELSE); | 553 | return emit_token(current, scanner, TOK_ELSE); |
547 | } | 554 | } |
555 | if (str_has_prefix(val, cstr("enum"))) { | ||
556 | return emit_token(current, scanner, TOK_ENUM); | ||
557 | } | ||
548 | } break; | 558 | } break; |
549 | case 'f': { | 559 | case 'f': { |
550 | if (str_has_prefix(val, cstr("false"))) { | 560 | if (str_has_prefix(val, cstr("false"))) { |
@@ -78,12 +78,14 @@ typedef enum NodeKind { | |||
78 | NODE_IF, | 78 | NODE_IF, |
79 | NODE_MATCH, | 79 | NODE_MATCH, |
80 | NODE_CASE, | 80 | NODE_CASE, |
81 | NODE_COND, | ||
82 | NODE_ENUM, | ||
81 | // Helpers. | 83 | // Helpers. |
82 | NODE_SYMBOL_IDX, | 84 | NODE_SYMBOL_IDX, |
83 | NODE_TYPE, | 85 | NODE_TYPE, |
84 | NODE_ARR_TYPE, | 86 | NODE_ARR_TYPE, |
85 | NODE_COMPOUND_TYPE, | 87 | NODE_COMPOUND_TYPE, |
86 | NODE_STRUCT_FIELD, | 88 | NODE_VAL_FIELD, |
87 | NODE_BLOCK, | 89 | NODE_BLOCK, |
88 | } NodeKind; | 90 | } NodeKind; |
89 | 91 | ||
@@ -127,12 +129,14 @@ Str node_str[] = { | |||
127 | [NODE_IF] = cstr("IF"), | 129 | [NODE_IF] = cstr("IF"), |
128 | [NODE_MATCH] = cstr("MATCH"), | 130 | [NODE_MATCH] = cstr("MATCH"), |
129 | [NODE_CASE] = cstr("CASE"), | 131 | [NODE_CASE] = cstr("CASE"), |
132 | [NODE_COND] = cstr("COND"), | ||
133 | [NODE_ENUM] = cstr("ENUM"), | ||
130 | // Helpers. | 134 | // Helpers. |
131 | [NODE_TYPE] = cstr("TYPE"), | 135 | [NODE_TYPE] = cstr("TYPE"), |
132 | [NODE_ARR_TYPE] = cstr("TYPE (ARR)"), | 136 | [NODE_ARR_TYPE] = cstr("TYPE (ARR)"), |
133 | [NODE_SYMBOL_IDX] = cstr("SYMBOL[IDX]"), | 137 | [NODE_SYMBOL_IDX] = cstr("SYMBOL[IDX]"), |
134 | [NODE_COMPOUND_TYPE] = cstr("TYPE (COMPOUND)"), | 138 | [NODE_COMPOUND_TYPE] = cstr("TYPE (COMPOUND)"), |
135 | [NODE_STRUCT_FIELD] = cstr("FIELD"), | 139 | [NODE_VAL_FIELD] = cstr("FIELD"), |
136 | [NODE_BLOCK] = cstr("BLOCK"), | 140 | [NODE_BLOCK] = cstr("BLOCK"), |
137 | }; | 141 | }; |
138 | 142 | ||
@@ -290,8 +294,10 @@ ParseRule parse_rules[] = { | |||
290 | [TOK_LET] = {parse_keyword, NULL, PREC_NONE}, | 294 | [TOK_LET] = {parse_keyword, NULL, PREC_NONE}, |
291 | [TOK_SET] = {parse_keyword, NULL, PREC_NONE}, | 295 | [TOK_SET] = {parse_keyword, NULL, PREC_NONE}, |
292 | [TOK_STRUCT] = {parse_keyword, NULL, PREC_NONE}, | 296 | [TOK_STRUCT] = {parse_keyword, NULL, PREC_NONE}, |
297 | [TOK_ENUM] = {parse_keyword, NULL, PREC_NONE}, | ||
293 | [TOK_IF] = {parse_keyword, NULL, PREC_NONE}, | 298 | [TOK_IF] = {parse_keyword, NULL, PREC_NONE}, |
294 | [TOK_MATCH] = {parse_keyword, NULL, PREC_NONE}, | 299 | [TOK_MATCH] = {parse_keyword, NULL, PREC_NONE}, |
300 | [TOK_COND] = {parse_keyword, NULL, PREC_NONE}, | ||
295 | [TOK_WHILE] = {parse_keyword, NULL, PREC_NONE}, | 301 | [TOK_WHILE] = {parse_keyword, NULL, PREC_NONE}, |
296 | [TOK_CONTINUE] = {parse_keyword, NULL, PREC_NONE}, | 302 | [TOK_CONTINUE] = {parse_keyword, NULL, PREC_NONE}, |
297 | [TOK_BREAK] = {parse_keyword, NULL, PREC_NONE}, | 303 | [TOK_BREAK] = {parse_keyword, NULL, PREC_NONE}, |
@@ -535,14 +541,15 @@ parse_keyword(Parser *parser) { | |||
535 | 541 | ||
536 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { | 542 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { |
537 | Node *field = | 543 | Node *field = |
538 | node_alloc(parser, NODE_STRUCT_FIELD, parser->current); | 544 | node_alloc(parser, NODE_VAL_FIELD, parser->current); |
539 | if (!field) return; | 545 | if (!field) return; |
540 | parse_consume( | 546 | parse_consume( |
541 | parser, TOK_SYMBOL, | 547 | parser, TOK_SYMBOL, |
542 | cstr("expected symbol name on struct definition")); | 548 | cstr("expected symbol name on struct definition")); |
543 | parse_symbol(parser); | 549 | parse_symbol(parser); |
544 | field->field_name = array_pop(parser->nodes); | 550 | field->field_name = array_pop(parser->nodes); |
545 | if (field->field_name->next) { | 551 | if (field->field_name->next || |
552 | field->field_name->kind == NODE_SYMBOL_IDX) { | ||
546 | parse_emit_err(parser, prev, | 553 | parse_emit_err(parser, prev, |
547 | cstr("invalid symbol name in struct field")); | 554 | cstr("invalid symbol name in struct field")); |
548 | return; | 555 | return; |
@@ -589,12 +596,12 @@ parse_keyword(Parser *parser) { | |||
589 | // Are we on the default case. | 596 | // Are we on the default case. |
590 | if (!parse_match(parser, TOK_ELSE)) { | 597 | if (!parse_match(parser, TOK_ELSE)) { |
591 | parse_consume(parser, TOK_CASE, | 598 | parse_consume(parser, TOK_CASE, |
592 | cstr("expected case statement")); | 599 | cstr("expected case statement")); |
593 | parse_expr(parser, PREC_LOW); | 600 | parse_expr(parser, PREC_LOW); |
594 | tmp->case_value = array_pop(parser->nodes); | 601 | tmp->case_value = array_pop(parser->nodes); |
595 | } | 602 | } |
596 | parse_consume(parser, TOK_ASSIGN, | 603 | parse_consume(parser, TOK_ASSIGN, |
597 | cstr("malformed case statement")); | 604 | cstr("malformed case statement")); |
598 | parse_expr(parser, PREC_LOW); | 605 | parse_expr(parser, PREC_LOW); |
599 | tmp->case_expr = array_pop(parser->nodes); | 606 | tmp->case_expr = array_pop(parser->nodes); |
600 | array_push(node->match_cases, tmp, parser->storage); | 607 | array_push(node->match_cases, tmp, parser->storage); |
@@ -604,6 +611,36 @@ parse_keyword(Parser *parser) { | |||
604 | // TODO: Check that there are no multiple default or duplicated | 611 | // TODO: Check that there are no multiple default or duplicated |
605 | // cases. | 612 | // cases. |
606 | } break; | 613 | } break; |
614 | case TOK_ENUM: { | ||
615 | node = node_alloc(parser, NODE_ENUM, prev); | ||
616 | if (!node) return; | ||
617 | parse_consume(parser, TOK_SYMBOL, | ||
618 | cstr("expected symbol name on enum definition")); | ||
619 | // Just consume this to avoid conflicts with struct literals. | ||
620 | node->value.sym = parser->previous.val; | ||
621 | parse_consume(parser, TOK_LCURLY, | ||
622 | cstr("expected '{' on enum definition")); | ||
623 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { | ||
624 | Node *field = | ||
625 | node_alloc(parser, NODE_VAL_FIELD, parser->current); | ||
626 | if (!field) return; | ||
627 | parse_consume(parser, TOK_SYMBOL, | ||
628 | cstr("expected symbol name on enum definition")); | ||
629 | parse_symbol(parser); | ||
630 | field->field_name = array_pop(parser->nodes); | ||
631 | if (field->field_name->next || | ||
632 | field->field_name->kind == NODE_SYMBOL_IDX) { | ||
633 | parse_emit_err(parser, prev, | ||
634 | cstr("invalid symbol name in enum field")); | ||
635 | return; | ||
636 | } | ||
637 | if (parse_match(parser, TOK_ASSIGN)) { | ||
638 | parse_expr(parser, PREC_LOW); | ||
639 | field->field_val = array_pop(parser->nodes); | ||
640 | } | ||
641 | array_push(node->struct_field, field, parser->storage); | ||
642 | } | ||
643 | } break; | ||
607 | default: return; // Unreachable. | 644 | default: return; // Unreachable. |
608 | } | 645 | } |
609 | array_push(parser->nodes, node, parser->storage); | 646 | array_push(parser->nodes, node, parser->storage); |
@@ -741,8 +778,7 @@ parse_symbol(Parser *parser) { | |||
741 | // Struct literal. | 778 | // Struct literal. |
742 | node->kind = NODE_STRUCT_LIT; | 779 | node->kind = NODE_STRUCT_LIT; |
743 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { | 780 | while (!parse_match(parser, TOK_RCURLY) && !parser->panic) { |
744 | Node *field = | 781 | Node *field = node_alloc(parser, NODE_VAL_FIELD, parser->current); |
745 | node_alloc(parser, NODE_STRUCT_FIELD, parser->current); | ||
746 | parse_consume(parser, TOK_SYMBOL, cstr("expected symbol name")); | 782 | parse_consume(parser, TOK_SYMBOL, cstr("expected symbol name")); |
747 | parse_symbol(parser); | 783 | parse_symbol(parser); |
748 | field->field_name = array_pop(parser->nodes); | 784 | field->field_name = array_pop(parser->nodes); |
@@ -831,6 +867,7 @@ graph_node(Node *node) { | |||
831 | graph_node(next); | 867 | graph_node(next); |
832 | } | 868 | } |
833 | } break; | 869 | } break; |
870 | case NODE_ENUM: | ||
834 | case NODE_STRUCT: { | 871 | case NODE_STRUCT: { |
835 | for (sz i = 0; i < array_size(node->struct_field); i++) { | 872 | for (sz i = 0; i < array_size(node->struct_field); i++) { |
836 | Node *next = node->struct_field[i]; | 873 | Node *next = node->struct_field[i]; |
@@ -852,7 +889,7 @@ graph_node(Node *node) { | |||
852 | graph_node(node->cond_else); | 889 | graph_node(node->cond_else); |
853 | } | 890 | } |
854 | } break; | 891 | } break; |
855 | case NODE_STRUCT_FIELD: | 892 | case NODE_VAL_FIELD: |
856 | case NODE_SET: | 893 | case NODE_SET: |
857 | case NODE_LET: { | 894 | case NODE_LET: { |
858 | if (node->var_name) { | 895 | if (node->var_name) { |
diff --git a/tests/conditionals.bad b/tests/conditionals.bad index b1c6a89..44467a8 100644 --- a/tests/conditionals.bad +++ b/tests/conditionals.bad | |||
@@ -1,28 +1,50 @@ | |||
1 | ; Basic if expressions. | 1 | ; ; Basic if expressions. |
2 | if true "hello" | 2 | ; if true "hello" |
3 | 3 | ||
4 | ; These can produce values and the result bound to a name. | 4 | ; ; These can produce values and the result bound to a name. |
5 | let a = if (2 + 2 >= 4) 42 | 5 | ; let a = if (2 + 2 >= 4) 42 |
6 | 6 | ||
7 | ; We support a single if expression. | 7 | ; ; We support a single if expression. |
8 | let b = if 0xff == 255 "hello" else "world" | 8 | ; let b = if 0xff == 255 "hello" else "world" |
9 | 9 | ||
10 | ; ... but these can compound on each other | 10 | ; ; ... but these can compound on each other |
11 | if 1 < 2 6 | 11 | ; if 1 < 2 6 |
12 | else if 1 > 2 7 | 12 | ; else if 1 > 2 7 |
13 | else 8 | 13 | ; else 8 |
14 | 14 | ||
15 | ; A block is an expression, and if raise the scope level regardless if a block | 15 | ; ; A block is an expression, and if raise the scope level regardless if a block |
16 | ; is used or not. | 16 | ; ; is used or not. |
17 | if true != false { | 17 | ; if true != false { |
18 | let a = "yo" | 18 | ; let a = "yo" |
19 | ; } | ||
20 | |||
21 | ; ; Match cases should only apply to literal values, for example `case 1 + 2` | ||
22 | ; ; isn't allowed. They should generally convert to a lookup table. | ||
23 | ; match a = { | ||
24 | ; case 8 = "hello" | ||
25 | ; case 9 = "world" | ||
26 | ; else = "what" | ||
27 | ; } | ||
28 | |||
29 | ; cond { | ||
30 | ; case a == b = "hello" | ||
31 | ; case a != c = "world" | ||
32 | ; else = "what" | ||
33 | ; } | ||
34 | |||
35 | ; Enums are integers with values that increase automatically or that can be set | ||
36 | ; with a constexpr. | ||
37 | enum flags { | ||
38 | node_add = 1 << 0 | ||
39 | node_sub = 1 << 1 | ||
40 | node_div = 1 << 2 | ||
41 | node_mul | ||
19 | } | 42 | } |
43 | let a:flags = flags.node_add | ||
20 | 44 | ||
21 | ; Match cases should only apply to literal values, for example `case 1 + 2` | 45 | ; No need to qualify enum fields inside match-case. |
22 | ; isn't allowed. They should generally convert to a lookup table. | ||
23 | match a = { | 46 | match a = { |
24 | case 8 = "hello" | 47 | case node_add = "adding something..." |
25 | case 9 = "world" | 48 | case node_add = "subbing something..." |
26 | else = "what" | 49 | else = "default case" |
27 | } | 50 | } |
28 | |||