From: Brendan Hansen Date: Sat, 22 Jan 2022 03:45:51 +0000 (-0600) Subject: switch cases are looser in the syntax tree now X-Git-Url: https://git.brendanfh.com/?a=commitdiff_plain;h=8f1d6524ca6e4c3820de7eb31f08e75b5c7285e5;p=onyx.git switch cases are looser in the syntax tree now --- diff --git a/core/net/tcp.onyx b/core/net/tcp.onyx index 2db30b6d..3a874d8b 100644 --- a/core/net/tcp.onyx +++ b/core/net/tcp.onyx @@ -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; } } diff --git a/include/astnodes.h b/include/astnodes.h index 03b6abcf..521952f8 100644 --- a/include/astnodes.h +++ b/include/astnodes.h @@ -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; diff --git a/src/astnodes.c b/src/astnodes.c index 5ee3c544..e983062c 100644 --- a/src/astnodes.c +++ b/src/astnodes.c @@ -83,7 +83,7 @@ static const char* ast_node_names[] = { "USE", "DEFER", "SWITCH", - "SWITCH CASE", + "CASE", "SOLIDIFY", "STATIC IF", diff --git a/src/checker.c b/src/checker.c index 2abaa5f9..6d944690 100644 --- a/src/checker.c +++ b/src/checker.c @@ -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; diff --git a/src/clone.c b/src/clone.c index 958a932c..db7215a5 100644 --- a/src/clone.c +++ b/src/clone.c @@ -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; } diff --git a/src/lex.c b/src/lex.c index e3152c48..7a458b88 100644 --- 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", diff --git a/src/parser.c b/src/parser.c index 9f941849..9345a005 100644 --- a/src/parser.c +++ b/src/parser.c @@ -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; diff --git a/src/symres.c b/src/symres.c index 153f182f..41bee0a0 100644 --- a/src/symres.c +++ b/src/symres.c @@ -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: diff --git a/src/wasm_emit.c b/src/wasm_emit.c index ee9b840c..9bb333ea 100644 --- a/src/wasm_emit.c +++ b/src/wasm_emit.c @@ -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; diff --git a/tests/switch_using_equals.onyx b/tests/switch_using_equals.onyx index 803c6540..cd94d2c5 100644 --- a/tests/switch_using_equals.onyx +++ b/tests/switch_using_equals.onyx @@ -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