switch cases are looser in the syntax tree now
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 22 Jan 2022 03:45:51 +0000 (21:45 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 22 Jan 2022 03:45:51 +0000 (21:45 -0600)
core/net/tcp.onyx
include/astnodes.h
src/astnodes.c
src/checker.c
src/clone.c
src/lex.c
src/parser.c
src/symres.c
src/wasm_emit.c
tests/switch_using_equals.onyx

index 2db30b6d2fef79914e60e2f82536ede19f2a5996..3a874d8b32c9af9c8b357135274c48ab1afd22b5 100644 (file)
@@ -245,9 +245,7 @@ tcp_server_broadcast :: (use server: ^TCP_Server, data: [] u8, except: ^TCP_Serv
 
 tcp_server_handle_events :: macro (server: ^TCP_Server, handler: Code) {
     while server->pulse() {
-        for server->get_events() {
-            #insert handler;
-        }
+        for server->get_events() do switch it.kind do #insert handler;
     }
 }
 
index 03b6abcf1f78dfbb92042a22cdb9120766f10c3d..521952f8ac53c39e27de225bc44e1e5fb3080788 100644 (file)
@@ -797,10 +797,14 @@ typedef struct CaseToBlock {
 } CaseToBlock;
 
 struct AstSwitchCase {
+    AstNode_base;
+
     // NOTE: All expressions that end up in this block
     bh_arr(AstTyped *) values;
     
     AstBlock *block;
+
+    b32 is_default: 1; // Could this be inferred by the values array being null?
 };
 
 struct AstSwitch {
@@ -811,7 +815,9 @@ struct AstSwitch {
 
     AstTyped *expr;
 
-    bh_arr(AstSwitchCase) cases;
+    AstBlock *case_block;
+
+    bh_arr(AstSwitchCase *) cases;
     AstBlock *default_case;
 
     i32 yield_return_index;
index 5ee3c544e0a5b68d59b07efa2d56d294ab2f2621..e983062cb66094e5120ae0f0c4ddee77449f4137 100644 (file)
@@ -83,7 +83,7 @@ static const char* ast_node_names[] = {
     "USE",
     "DEFER",
     "SWITCH",
-    "SWITCH CASE",
+    "CASE",
 
     "SOLIDIFY",
     "STATIC IF",
index 2abaa5f9bceb86ef99a5ad845dc0a2577312819a..6d9446906e38b912747648e7302b3216401fba69 100644 (file)
@@ -328,6 +328,40 @@ static b32 add_case_to_switch_statement(AstSwitch* switchnode, u64 case_value, A
     return 0;
 }
 
+static CheckStatus collect_switch_case_blocks(AstSwitch* switchnode, AstBlock* root) {
+    AstNode *walker = root->body;
+    while (walker != NULL) {
+        switch (walker->kind) {
+            case Ast_Kind_Block:
+                collect_switch_case_blocks(switchnode, (AstBlock *) walker);
+                break;
+
+            case Ast_Kind_Switch_Case: {
+                AstSwitchCase *case_node = (AstSwitchCase *) walker;
+                if (case_node->is_default) {
+                    if (switchnode->default_case != NULL && switchnode->default_case != case_node->block) {
+                        ERROR(case_node->token->pos, "Multiple #default cases given");
+                        ERROR(switchnode->default_case->token->pos, "Multiple #default cases given");
+                        return Check_Error;
+                    }
+
+                    switchnode->default_case = case_node->block;
+                } else {
+                    bh_arr_push(switchnode->cases, case_node);
+                }
+                break;
+            }
+
+            default:
+                ERROR(walker->token->pos, "This statement is not allowed here.");
+        }
+
+        walker = walker->next;
+    }
+
+    return Check_Success;
+}
+
 CheckStatus check_switch(AstSwitch* switchnode) {
     if (switchnode->initialization != NULL) CHECK(statement_chain, &switchnode->initialization);
 
@@ -345,12 +379,11 @@ CheckStatus check_switch(AstSwitch* switchnode) {
         switch (switchnode->switch_kind) {
             case Switch_Kind_Integer:
                 switchnode->min_case = 0xffffffffffffffff;
-                bh_imap_init(&switchnode->case_map, global_heap_allocator, bh_arr_length(switchnode->cases) * 2);
+                bh_imap_init(&switchnode->case_map, global_heap_allocator, 4);
                 break;
 
             case Switch_Kind_Use_Equals:
-                // Guessing the maximum number of case expressions there will be.
-                bh_arr_new(global_heap_allocator, switchnode->case_exprs, bh_arr_length(switchnode->cases) * 2);
+                bh_arr_new(global_heap_allocator, switchnode->case_exprs, 4);
                 break;
 
             default: assert(0);
@@ -358,8 +391,24 @@ CheckStatus check_switch(AstSwitch* switchnode) {
     }
     switchnode->flags |= Ast_Flag_Has_Been_Checked;
 
+    // Should the case block code be checked here?
+    // Or should this just exist to resolve macros and expand #inserts
+    // then the cases are consumed into the array or cases, THEN the blocks
+    // are actually checked?
+    if (switchnode->cases == NULL) {
+        CHECK(block, switchnode->case_block);
+
+        bh_arr_new(global_heap_allocator, switchnode->cases, 4);
+        if (collect_switch_case_blocks(switchnode, switchnode->case_block) != Check_Success) {
+            return Check_Error;
+        }
+
+        // This is important, otherwise if this block has to return to symbol resolution.
+        switchnode->case_block->statement_idx = 0;
+    }
+
     fori (i, switchnode->yield_return_index, bh_arr_length(switchnode->cases)) {
-        AstSwitchCase *sc = &switchnode->cases[i];
+        AstSwitchCase *sc = switchnode->cases[i];
         CHECK(block, sc->block);
 
         bh_arr_each(AstTyped *, value, sc->values) {
@@ -1827,6 +1876,7 @@ CheckStatus check_expression(AstTyped** pexpr) {
         case Ast_Kind_Error: break;
         case Ast_Kind_Unary_Field_Access: break;
         case Ast_Kind_Constraint_Sentinel: break;
+        case Ast_Kind_Switch_Case: break;
 
         default:
             retval = Check_Error;
index 958a932c4ca5458e4ca392ec41f52c07268941ad..db7215a5fcdf2785898d9613e767e6af9593beb9 100644 (file)
@@ -266,29 +266,30 @@ AstNode* ast_clone(bh_allocator a, void* n) {
             C(AstIfWhile, false_stmt);
             break;
 
+        case Ast_Kind_Switch_Case: {
+            C(AstSwitchCase, block);
+
+            AstSwitchCase *dw = (AstSwitchCase *) nn;
+            AstSwitchCase *sw = (AstSwitchCase *) node;
+
+            dw->values = NULL;
+            bh_arr_new(global_heap_allocator, dw->values, bh_arr_length(sw->values));
+            bh_arr_each(AstTyped *, value, sw->values)
+                bh_arr_push(dw->values, (AstTyped *) ast_clone(a, *value));
+
+            break;
+        }
+
         case Ast_Kind_Switch: {
             AstSwitch* dw = (AstSwitch *) nn;
             AstSwitch* sw = (AstSwitch *) node;
 
-            ((AstSwitch *) nn)->initialization = ast_clone_list(a, ((AstSwitch *) node)->initialization);
-            dw->expr = (AstTyped *) ast_clone(a, sw->expr);
+            dw->initialization = ast_clone_list(a, sw->initialization);
+            C(AstSwitch, expr);
 
-            dw->default_case = (AstBlock *) ast_clone(a, sw->default_case);
 
             dw->cases = NULL;
-            bh_arr_new(global_heap_allocator, dw->cases, bh_arr_length(sw->cases));
-
-            bh_arr_each(AstSwitchCase, c, sw->cases) {
-                bh_arr(AstTyped *) new_values = NULL;
-                bh_arr_new(global_heap_allocator, new_values, bh_arr_length(c->values));
-                bh_arr_each(AstTyped *, value, c->values)
-                    bh_arr_push(new_values, (AstTyped *) ast_clone(a, *value));
-
-                AstSwitchCase sc;
-                sc.values = new_values; 
-                sc.block = (AstBlock *) ast_clone(a, c->block);
-                bh_arr_push(dw->cases, sc);
-            }
+            C(AstSwitch, case_block);
             break;
         }
 
index e3152c48e3dc3938b05150b44255fbee00ef8818..7a458b881705fd3e40ec174c4f40dfc22c00db33 100644 (file)
--- a/src/lex.c
+++ b/src/lex.c
@@ -32,8 +32,8 @@ static const char* token_type_names[] = {
     "typeof",
     "defer",
     "do",
-    "switch",
     "case",
+    "switch",
     "fallthrough",
     "macro",
     "interface",
index 9f94184943ca41f7433c9b64cee991b9d9a3a0bf..9345a00541b4c11a1fad8c40602f28daca50a356 100644 (file)
@@ -47,6 +47,7 @@ static AstTyped*      parse_expression(OnyxParser* parser, b32 assignment_allowe
 static AstIfWhile*    parse_if_stmt(OnyxParser* parser);
 static AstIfWhile*    parse_while_stmt(OnyxParser* parser);
 static AstFor*        parse_for_stmt(OnyxParser* parser);
+static AstSwitchCase* parse_case_stmt(OnyxParser* parser);
 static AstSwitch*     parse_switch_stmt(OnyxParser* parser);
 static i32            parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret);
 static AstReturn*     parse_return_stmt(OnyxParser* parser);
@@ -1139,12 +1140,35 @@ static AstFor* parse_for_stmt(OnyxParser* parser) {
     return for_node;
 }
 
+static AstSwitchCase* parse_case_stmt(OnyxParser* parser) {
+    AstSwitchCase *sc_node = make_node(AstSwitchCase, Ast_Kind_Switch_Case);
+    sc_node->token = expect_token(parser, Token_Type_Keyword_Case);
+
+    if (parse_possible_directive(parser, "default")) {
+        sc_node->is_default = 1;
+
+    } else {
+        bh_arr_new(global_heap_allocator, sc_node->values, 1);
+
+        AstTyped* value = parse_expression(parser, 1);
+        bh_arr_push(sc_node->values, value);
+        while (consume_token_if_next(parser, ',')) {
+            if (parser->hit_unexpected_token) return sc_node;
+
+            value = parse_expression(parser, 1);
+            bh_arr_push(sc_node->values, value);
+        }
+    }
+
+    sc_node->block = parse_block(parser, 1, NULL);
+
+    return sc_node;
+}
+
 static AstSwitch* parse_switch_stmt(OnyxParser* parser) {
     AstSwitch* switch_node = make_node(AstSwitch, Ast_Kind_Switch);
     switch_node->token = expect_token(parser, Token_Type_Keyword_Switch);
 
-    bh_arr_new(global_heap_allocator, switch_node->cases, 4);
-
     AstTyped* expr;
     AstNode* initialization_or_expr=NULL;
     b32 had_initialization = 0;
@@ -1170,42 +1194,7 @@ static AstSwitch* parse_switch_stmt(OnyxParser* parser) {
     switch_node->initialization = initialization_or_expr;
     switch_node->expr = expr;
 
-    expect_token(parser, '{');
-
-    while (consume_token_if_next(parser, Token_Type_Keyword_Case)) {
-        if (parser->hit_unexpected_token) return switch_node;
-
-        bh_arr(AstTyped *) case_values = NULL;
-        bh_arr_new(global_heap_allocator, case_values, 1);
-
-        if (parse_possible_directive(parser, "default")) {
-            switch_node->default_case = parse_block(parser, 1, NULL);
-
-            if (parser->curr->type != '}') {
-                onyx_report_error(parser->curr->pos, Error_Critical, "The #default case must be the last case in a switch statement.\n");
-            }
-            break;
-        }
-
-        AstTyped* value = parse_expression(parser, 1);
-        bh_arr_push(case_values, value);
-        while (consume_token_if_next(parser, ',')) {
-            if (parser->hit_unexpected_token) return switch_node;
-
-            value = parse_expression(parser, 1);
-            bh_arr_push(case_values, value);
-        }
-
-        AstBlock* block = parse_block(parser, 1, NULL);
-
-        AstSwitchCase sc_node;
-        sc_node.block  = block;
-        sc_node.values = case_values;
-
-        bh_arr_push(switch_node->cases, sc_node);
-    }
-
-    expect_token(parser, '}');
+    switch_node->case_block = parse_block(parser, 1, NULL);
     return switch_node;
 }
 
@@ -1453,6 +1442,11 @@ static AstNode* parse_statement(OnyxParser* parser) {
             retval = (AstNode *) parse_switch_stmt(parser);
             break;
 
+        case Token_Type_Keyword_Case:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_case_stmt(parser);
+            break;
+
         case Token_Type_Keyword_Break:
             retval = parse_jump_stmt(parser, Token_Type_Keyword_Break, Jump_Type_Break);
             break;
index 153f182fe0aecfa59723aa1fd25b4cbb1d8d62e6..41bee0a0cfcae0b90dc937dbbebf7d936f042f2c 100644 (file)
@@ -49,6 +49,7 @@ static SymresStatus symres_return(AstReturn* ret);
 static SymresStatus symres_if(AstIfWhile* ifnode);
 static SymresStatus symres_while(AstIfWhile* whilenode);
 static SymresStatus symres_for(AstFor* fornode);
+static SymresStatus symres_case(AstSwitchCase *casenode);
 static SymresStatus symres_switch(AstSwitch* switchnode);
 static SymresStatus symres_use(AstUse* use);
 static SymresStatus symres_directive_solidify(AstDirectiveSolidify** psolid);
@@ -626,6 +627,17 @@ static SymresStatus symres_for(AstFor* fornode) {
     return Symres_Success;
 }
 
+static SymresStatus symres_case(AstSwitchCase *casenode) {
+    if (!casenode->is_default) {
+        bh_arr_each(AstTyped *, expr, casenode->values) {
+            SYMRES(expression, expr);
+        }
+    }
+
+    SYMRES(block, casenode->block);
+    return Symres_Success;
+}
+
 static SymresStatus symres_switch(AstSwitch* switchnode) {
     if (switchnode->initialization != NULL) {
         switchnode->scope = scope_create(context.ast_alloc, curr_scope, switchnode->token->pos);
@@ -636,15 +648,7 @@ static SymresStatus symres_switch(AstSwitch* switchnode) {
 
     SYMRES(expression, &switchnode->expr);
 
-    bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
-        bh_arr_each(AstTyped *, value, sc->values)
-            SYMRES(expression, value);
-
-        SYMRES(block, sc->block);
-    }
-
-    if (switchnode->default_case)
-        SYMRES(block, switchnode->default_case);
+    SYMRES(block, switchnode->case_block);
 
     if (switchnode->switch_kind == Switch_Kind_Use_Equals && switchnode->case_exprs) {
         bh_arr_each(CaseToBlock, ctb, switchnode->case_exprs) {
@@ -829,6 +833,7 @@ static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
         case Ast_Kind_Argument:    SYMRES(expression, (AstTyped **) &((AstArgument *) *stmt)->value); break;
         case Ast_Kind_Block:       SYMRES(block, (AstBlock *) *stmt);                    break;
         case Ast_Kind_Defer:       SYMRES(statement, &((AstDefer *) *stmt)->stmt, NULL); break;
+        case Ast_Kind_Switch_Case: SYMRES(case, (AstSwitchCase *) *stmt);                break;
         case Ast_Kind_Jump:        break;
 
         case Ast_Kind_Local:
index ee9b840ca3cde72fd08fe776f276e01e790ce407..9bb333ea22a075084c7ecc17dd1a104dcc3405db 100644 (file)
@@ -1154,12 +1154,12 @@ EMIT_FUNC(switch, AstSwitch* switch_node) {
     emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
 
     u64 block_num = 0;
-    bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
-        if (bh_imap_has(&block_map, (u64) sc->block)) continue;
+    bh_arr_each(AstSwitchCase *, sc, switch_node->cases) {
+        if (bh_imap_has(&block_map, (u64) (*sc)->block)) continue;
 
         emit_enter_structured_block(mod, &code, SBT_Fallthrough_Block);
 
-        bh_imap_put(&block_map, (u64) sc->block, block_num);
+        bh_imap_put(&block_map, (u64) (*sc)->block, block_num);
         block_num++;
     }
 
@@ -1216,7 +1216,8 @@ EMIT_FUNC(switch, AstSwitch* switch_node) {
         }
     }
 
-    bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
+    bh_arr_each(AstSwitchCase *, psc, switch_node->cases) {
+        AstSwitchCase *sc = *psc;
         if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
 
         u64 bn = bh_imap_get(&block_map, (u64) sc->block);
@@ -2891,6 +2892,13 @@ EMIT_FUNC(expression, AstTyped* expr) {
             break;
         }
 
+        case Ast_Kind_Switch_Case: {
+            // This error message should be moved to checking, but this is the
+            // best place to do it right now.
+            onyx_report_error(expr->token->pos, Error_Critical, "'case' statements are only allowed in a 'switch' statement.");
+            break;
+        }
+
         default:
             bh_printf("Unhandled case: %d\n", expr->kind);
             DEBUG_HERE;
index 803c65405c55d36ca2b4e5bc4161c537cbbfdca4..cd94d2c5d570390e399b7292a0c514fe0fb2d5bb 100644 (file)
@@ -5,12 +5,18 @@ use package core
 Vector2 :: struct { x, y: i32; }
 #operator == macro (v1: Vector2, v2: Vector2) => v1.x == v2.x && v1.y == v2.y;
 
+none_of_the_above :: #code {
+    case #default {
+        println("Got default!");
+    }
+}
+
 main :: (args: [] cstr) {
     for .[ "Some", "Thing", "Other" ] {
         switch it {
             case "Thing" do println("Got thing!");
             case "Some"  do println("Got some!");
-            case #default do println("Got default!");
+            #insert none_of_the_above;
         }
     }
 
@@ -20,6 +26,7 @@ main :: (args: [] cstr) {
         case .{ 0, 1 } do println("0, 1");
         case .{ 1, 0 } do println("1, 0");
         case .{ 1, 1 } do println("1, 1");
-        case #default do println("none of the above.");
+
+        #insert none_of_the_above;
     }
 }
\ No newline at end of file