added fallthrough on switch statements; code cleanup
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 25 Aug 2020 12:58:55 +0000 (07:58 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 25 Aug 2020 12:58:55 +0000 (07:58 -0500)
12 files changed:
docs/plan
include/onyxastnodes.h
include/onyxlex.h
misc/onyx.sublime-syntax
misc/onyx.vim
onyx
progs/wasi_test.onyx
src/onyxchecker.c
src/onyxlex.c
src/onyxparser.c
src/onyxsymres.c
src/onyxwasm.c

index 9fcbc957b2d771e1bfafa4263e2085f89c380897..23908c9162e6bb6aac89f99a4e6713fbe1bdf346 100644 (file)
--- a/docs/plan
+++ b/docs/plan
@@ -209,7 +209,9 @@ HOW:
 
         [X] Switch statements
 
-        [ ] fallthrough on cases in switch statements
+        [X] fallthrough on cases in switch statements
+
+        [ ] initializers on switch statements
 
         [ ] #file and #line directives
             - string and u32 respectively that represent the current file and line number where the directive is
index 796d8795daef1df4555279d990a2ece060b4e257..f7a103bfae7648065e3c8e52363b3a37a46a3446 100644 (file)
@@ -27,8 +27,7 @@ typedef struct AstFileContents AstFileContents;
 typedef struct AstStructLiteral AstStructLiteral;
 
 typedef struct AstReturn AstReturn;
-typedef struct AstBreak AstBreak;
-typedef struct AstContinue AstContinue;
+typedef struct AstJump AstJump;
 
 typedef struct AstBlock AstBlock;
 typedef struct AstIfWhile AstIfWhile;
@@ -127,8 +126,7 @@ typedef enum AstKind {
     Ast_Kind_If,
     Ast_Kind_For,
     Ast_Kind_While,
-    Ast_Kind_Break,
-    Ast_Kind_Continue,
+    Ast_Kind_Jump,
     Ast_Kind_Defer,
     Ast_Kind_Switch,
     Ast_Kind_Switch_Case,
@@ -253,6 +251,14 @@ typedef enum CallingConvention {
     CC_Return_Stack
 } CallingConvention;
 
+typedef enum JumpType {
+    Jump_Type_Break,
+    Jump_Type_Continue,
+    Jump_Type_Fallthrough,
+
+    Jump_Type_Count,
+} JumpType;
+
 
 // Base Nodes
 #define AstNode_base \
@@ -307,8 +313,7 @@ struct AstStructLiteral {
 
 // Intruction Node
 struct AstReturn        { AstNode_base; AstTyped* expr; };
-struct AstBreak         { AstNode_base; u64 count; };
-struct AstContinue      { AstNode_base; u64 count; };
+struct AstJump          { AstNode_base; JumpType jump; u32 count; };
 
 // Structure Nodes
 struct AstBlock         { AstNode_base; AstNode *body; Scope *scope; bh_arr(AstLocal *) locals; };
index 8d9ad1f9e44e12ae6c06e32c1a580ef79dae366b..21d4b3a0b680fb4d6adf7bed2f47684f58afc3a5 100644 (file)
@@ -32,6 +32,7 @@ typedef enum TokenType {
     Token_Type_Keyword_Do,
     Token_Type_Keyword_Case,
     Token_Type_Keyword_Switch,
+    Token_Type_Keyword_Fallthrough,
 
     Token_Type_Right_Arrow,
     Token_Type_Left_Arrow,
index 4640a21890932f2076a3ca8ba340144627a872f0..6a4c460636eeec64ed798fc692c7981fd123743e 100644 (file)
@@ -23,7 +23,7 @@ contexts:
     # strings in YAML. When using single quoted strings, only single quotes
     # need to be escaped: this is done by using two single quotes next to each
     # other.
-    - match: '\b(package|struct|proc|use|global|enum|if|elseif|else|for|while|do|break|continue|return|as|cast|sizeof|alignof|defer|switch|case)\b'
+    - match: '\b(package|struct|proc|use|global|enum|if|elseif|else|for|while|do|break|continue|fallthrough|return|as|cast|sizeof|alignof|defer|switch|case)\b'
       scope: keyword.control.onyx
 
     - match: '\b(bool|void|i8|u8|i16|u16|i32|u32|i64|u64|f32|f64|rawptr)\b'
index 071e6e19f6d089a5fe6de7a2773f396b4e9b3c44..eceb008a7c9da5041f76e37a8bf4fb36ad9f527a 100644 (file)
@@ -14,7 +14,7 @@ syn keyword onyxKeyword package struct proc use global
 syn keyword onyxKeyword if elseif else
 syn keyword onyxKeyword for while do
 syn keyword onyxKeyword switch case
-syn keyword onyxKeyword break continue return defer
+syn keyword onyxKeyword break continue return defer fallthrough
 syn keyword onyxKeyword as cast sizeof alignof
 
 syn keyword onyxType bool void
diff --git a/onyx b/onyx
index 7083980b5180acd48d13159bfdabb883e884bff9..869201d2b1f3675cb1aa7bf43b4951822296eac0 100755 (executable)
Binary files a/onyx and b/onyx differ
index 7227834d072982811e3dc36fef538d45a5d2bb39..7ac5eb5a00b5316466a6929bf14b4b3d6f0e55fe 100644 (file)
@@ -337,4 +337,19 @@ main :: proc (args: []cstring) {
     if ss.count > 0 {
         print(ss);
     }
+    print("\n");
+
+    num := 36;
+    switch num {
+        case 36 do print("6 squared\n");
+
+        case 42 {
+            print("The meaning of life\n");
+            fallthrough;
+        }
+
+        case #default {
+            print("Nothing was found");
+        }
+    }
 }
index f24d4b0f1c9e724a7d6bb3f22b343dafe95afdb5..cbd8710fa03538211c69da18124de66308f7703e 100644 (file)
@@ -998,8 +998,7 @@ CHECK(global, AstGlobal* global) {
 
 CHECK(statement, AstNode* stmt) {
     switch (stmt->kind) {
-        case Ast_Kind_Break:      return 0;
-        case Ast_Kind_Continue:   return 0;
+        case Ast_Kind_Jump:      return 0;
 
         case Ast_Kind_Return:     return check_return((AstReturn *) stmt);
         case Ast_Kind_If:         return check_if((AstIfWhile *) stmt);
index 88c849be4c52887f2b6d0bb18fc2abc58162a2d4..4d3e3fdbc6c74df32fea54c3302da3253ea14daf 100644 (file)
@@ -30,6 +30,7 @@ static const char* token_type_names[] = {
     "do",
     "switch",
     "case",
+    "fallthrough",
 
     "->",
     "<-",
@@ -253,98 +254,99 @@ whitespace_skipped:
     char curr = *tokenizer->curr;
     switch (curr) {
     case 'a':
-        LITERAL_TOKEN("alignof",    1, Token_Type_Keyword_Alignof);
-        LITERAL_TOKEN("as",         1, Token_Type_Keyword_As);
+        LITERAL_TOKEN("alignof",     1, Token_Type_Keyword_Alignof);
+        LITERAL_TOKEN("as",          1, Token_Type_Keyword_As);
         break;
     case 'b':
-        LITERAL_TOKEN("break",      1, Token_Type_Keyword_Break);
+        LITERAL_TOKEN("break",       1, Token_Type_Keyword_Break);
         break;
     case 'c':
-        LITERAL_TOKEN("case",       1, Token_Type_Keyword_Case);
-        LITERAL_TOKEN("cast",       1, Token_Type_Keyword_Cast);
-        LITERAL_TOKEN("continue",   1, Token_Type_Keyword_Continue);
+        LITERAL_TOKEN("case",        1, Token_Type_Keyword_Case);
+        LITERAL_TOKEN("cast",        1, Token_Type_Keyword_Cast);
+        LITERAL_TOKEN("continue",    1, Token_Type_Keyword_Continue);
         break;
     case 'd':
-        LITERAL_TOKEN("defer",      1, Token_Type_Keyword_Defer);
-        LITERAL_TOKEN("do",         1, Token_Type_Keyword_Do);
+        LITERAL_TOKEN("defer",       1, Token_Type_Keyword_Defer);
+        LITERAL_TOKEN("do",          1, Token_Type_Keyword_Do);
         break;
     case 'e':
-        LITERAL_TOKEN("enum"  ,     1, Token_Type_Keyword_Enum);
-        LITERAL_TOKEN("elseif",     1, Token_Type_Keyword_Elseif);
-        LITERAL_TOKEN("else",       1, Token_Type_Keyword_Else);
+        LITERAL_TOKEN("enum",        1, Token_Type_Keyword_Enum);
+        LITERAL_TOKEN("elseif",      1, Token_Type_Keyword_Elseif);
+        LITERAL_TOKEN("else",        1, Token_Type_Keyword_Else);
         break;
     case 'f':
-        LITERAL_TOKEN("for",        1, Token_Type_Keyword_For);
-        LITERAL_TOKEN("false",      1, Token_Type_Literal_False);
+        LITERAL_TOKEN("for",         1, Token_Type_Keyword_For);
+        LITERAL_TOKEN("false",       1, Token_Type_Literal_False);
+        LITERAL_TOKEN("fallthrough", 1, Token_Type_Keyword_Fallthrough);
         break;
     case 'g':
-        LITERAL_TOKEN("global",     1, Token_Type_Keyword_Global);
+        LITERAL_TOKEN("global",      1, Token_Type_Keyword_Global);
         break;
     case 'i':
-        LITERAL_TOKEN("if",         1, Token_Type_Keyword_If);
+        LITERAL_TOKEN("if",          1, Token_Type_Keyword_If);
         break;
     case 'p':
-        LITERAL_TOKEN("package",    1, Token_Type_Keyword_Package);
-        LITERAL_TOKEN("proc",       1, Token_Type_Keyword_Proc);
+        LITERAL_TOKEN("package",     1, Token_Type_Keyword_Package);
+        LITERAL_TOKEN("proc",        1, Token_Type_Keyword_Proc);
         break;
     case 'r':
-        LITERAL_TOKEN("return",     1, Token_Type_Keyword_Return);
+        LITERAL_TOKEN("return",      1, Token_Type_Keyword_Return);
         break;
     case 's':
-        LITERAL_TOKEN("sizeof",     1, Token_Type_Keyword_Sizeof);
-        LITERAL_TOKEN("struct",     1, Token_Type_Keyword_Struct);
-        LITERAL_TOKEN("switch",     1, Token_Type_Keyword_Switch);
+        LITERAL_TOKEN("sizeof",      1, Token_Type_Keyword_Sizeof);
+        LITERAL_TOKEN("struct",      1, Token_Type_Keyword_Struct);
+        LITERAL_TOKEN("switch",      1, Token_Type_Keyword_Switch);
         break;
     case 't':
-        LITERAL_TOKEN("true",       1, Token_Type_Literal_True);
+        LITERAL_TOKEN("true",        1, Token_Type_Literal_True);
         break;
     case 'u':
-        LITERAL_TOKEN("use",        1, Token_Type_Keyword_Use);
+        LITERAL_TOKEN("use",         1, Token_Type_Keyword_Use);
         break;
     case 'w':
-        LITERAL_TOKEN("while",      1, Token_Type_Keyword_While);
+        LITERAL_TOKEN("while",       1, Token_Type_Keyword_While);
         break;
 
     case '-':
-        LITERAL_TOKEN("->",         0, Token_Type_Right_Arrow);
-        LITERAL_TOKEN("---",        0, Token_Type_Empty_Block);
-        LITERAL_TOKEN("-=",         0, Token_Type_Minus_Equal);
+        LITERAL_TOKEN("->",          0, Token_Type_Right_Arrow);
+        LITERAL_TOKEN("---",         0, Token_Type_Empty_Block);
+        LITERAL_TOKEN("-=",          0, Token_Type_Minus_Equal);
         break;
 
     case '<':
-        LITERAL_TOKEN("<-",         0, Token_Type_Right_Arrow);
-        LITERAL_TOKEN("<<=",        0, Token_Type_Shl_Equal);
-        LITERAL_TOKEN("<<",         0, Token_Type_Shift_Left);
-        LITERAL_TOKEN("<=",         0, Token_Type_Less_Equal);
+        LITERAL_TOKEN("<-",          0, Token_Type_Right_Arrow);
+        LITERAL_TOKEN("<<=",         0, Token_Type_Shl_Equal);
+        LITERAL_TOKEN("<<",          0, Token_Type_Shift_Left);
+        LITERAL_TOKEN("<=",          0, Token_Type_Less_Equal);
         break;
 
     case '>':
-        LITERAL_TOKEN(">>>=",       0, Token_Type_Sar_Equal);
-        LITERAL_TOKEN(">>=",        0, Token_Type_Shr_Equal);
-        LITERAL_TOKEN(">>>",        0, Token_Type_Shift_Arith_Right);
-        LITERAL_TOKEN(">>",         0, Token_Type_Shift_Right);
-        LITERAL_TOKEN(">=",         0, Token_Type_Greater_Equal);
+        LITERAL_TOKEN(">>>=",        0, Token_Type_Sar_Equal);
+        LITERAL_TOKEN(">>=",         0, Token_Type_Shr_Equal);
+        LITERAL_TOKEN(">>>",         0, Token_Type_Shift_Arith_Right);
+        LITERAL_TOKEN(">>",          0, Token_Type_Shift_Right);
+        LITERAL_TOKEN(">=",          0, Token_Type_Greater_Equal);
         break;
 
     case '&':
-        LITERAL_TOKEN("&&",         0, Token_Type_And_And);
-        LITERAL_TOKEN("&=",         0, Token_Type_And_Equal);
+        LITERAL_TOKEN("&&",          0, Token_Type_And_And);
+        LITERAL_TOKEN("&=",          0, Token_Type_And_Equal);
         break;
 
     case '|':
-        LITERAL_TOKEN("|>",         0, Token_Type_Pipe);
-        LITERAL_TOKEN("||",         0, Token_Type_Or_Or);
-        LITERAL_TOKEN("|=",         0, Token_Type_Or_Equal);
+        LITERAL_TOKEN("|>",          0, Token_Type_Pipe);
+        LITERAL_TOKEN("||",          0, Token_Type_Or_Or);
+        LITERAL_TOKEN("|=",          0, Token_Type_Or_Equal);
         break;
 
     default:
-        LITERAL_TOKEN("^=",         0, Token_Type_Xor_Equal);
-        LITERAL_TOKEN("==",         0, Token_Type_Equal_Equal);
-        LITERAL_TOKEN("!=",         0, Token_Type_Not_Equal);
-        LITERAL_TOKEN("+=",         0, Token_Type_Plus_Equal);
-        LITERAL_TOKEN("*=",         0, Token_Type_Star_Equal);
-        LITERAL_TOKEN("/=",         0, Token_Type_Fslash_Equal);
-        LITERAL_TOKEN("%=",         0, Token_Type_Percent_Equal);
+        LITERAL_TOKEN("^=",          0, Token_Type_Xor_Equal);
+        LITERAL_TOKEN("==",          0, Token_Type_Equal_Equal);
+        LITERAL_TOKEN("!=",          0, Token_Type_Not_Equal);
+        LITERAL_TOKEN("+=",          0, Token_Type_Plus_Equal);
+        LITERAL_TOKEN("*=",          0, Token_Type_Star_Equal);
+        LITERAL_TOKEN("/=",          0, Token_Type_Fslash_Equal);
+        LITERAL_TOKEN("%=",          0, Token_Type_Percent_Equal);
         break;
     }
 
index 580ed3cbbe861ecc9681a5c7cf49edb8b4c14d52..e56d34832771cb81f380469075b11c17f77987d6 100644 (file)
@@ -1027,8 +1027,9 @@ static AstNode* parse_statement(OnyxParser* parser) {
             break;
 
         case Token_Type_Keyword_Break: {
-            AstBreak* bnode = make_node(AstBreak, Ast_Kind_Break);
+            AstJump* bnode = make_node(AstJump, Ast_Kind_Jump);
             bnode->token = expect_token(parser, Token_Type_Keyword_Break);
+            bnode->jump  = Jump_Type_Break;
 
             u64 count = 1;
             while (parser->curr->type == Token_Type_Keyword_Break) {
@@ -1042,8 +1043,9 @@ static AstNode* parse_statement(OnyxParser* parser) {
         }
 
         case Token_Type_Keyword_Continue: {
-            AstContinue* cnode = make_node(AstBreak, Ast_Kind_Continue);
+            AstJump* cnode = make_node(AstJump, Ast_Kind_Jump);
             cnode->token = expect_token(parser, Token_Type_Keyword_Continue);
+            cnode->jump  = Jump_Type_Continue;
 
             u64 count = 1;
             while (parser->curr->type == Token_Type_Keyword_Continue) {
@@ -1056,6 +1058,22 @@ static AstNode* parse_statement(OnyxParser* parser) {
             break;
         }
 
+        case Token_Type_Keyword_Fallthrough: {
+            AstJump* cnode = make_node(AstJump, Ast_Kind_Jump);
+            cnode->token = expect_token(parser, Token_Type_Keyword_Fallthrough);
+            cnode->jump  = Jump_Type_Fallthrough;
+
+            u64 count = 1;
+            while (parser->curr->type == Token_Type_Keyword_Fallthrough) {
+                consume_token(parser);
+                count++;
+            }
+            cnode->count = count;
+
+            retval = (AstNode *) cnode;
+            break;
+        }
+
         case Token_Type_Keyword_Defer: {
             needs_semicolon = 0;
 
index 169cf9ea048d975a9ca414b33fb93abd1316600f..698133690357109ec058715348a06f0449366b16 100644 (file)
@@ -416,8 +416,7 @@ static b32 symres_statement(AstNode** stmt) {
         case Ast_Kind_Block:      symres_block((AstBlock *) *stmt);                  return 0;
         case Ast_Kind_Defer:      symres_statement(&((AstDefer *) *stmt)->stmt);     return 0;
 
-        case Ast_Kind_Break:      return 0;
-        case Ast_Kind_Continue:   return 0;
+        case Ast_Kind_Jump:      return 0;
 
         default:                  symres_expression((AstTyped **) stmt);           return 0;
     }
index 80936ab7631ebf4acbd98f9032efaf76b6dd39f1..e370fa0761b59a23f03291d87f8596e0a82af9e4 100644 (file)
@@ -416,15 +416,15 @@ COMPILE_FUNC(block, AstBlock* block, b32 generate_block_headers) {
     *pcode = code;
 }
 
-COMPILE_FUNC(structured_jump, i32 jump_count) {
+COMPILE_FUNC(structured_jump, i32 jump_count, JumpType jump) {
     bh_arr(WasmInstruction) code = *pcode;
 
+    static const u8 wants[Jump_Type_Count] = { 1, 2, 3 };
+
     i32 labelidx = 0;
-    u8 wanted = (jump_count < 0) ? 2 : 1;
+    u8 wanted = wants[jump];
     b32 success = 0;
 
-    if (jump_count < 0) jump_count = -jump_count;
-
     i32 len = bh_arr_length(mod->structured_jump_target) - 1;
     for (u8* t = &bh_arr_last(mod->structured_jump_target); len >= 0; len--, t--) {
         if (*t == wanted) jump_count--;
@@ -454,8 +454,7 @@ COMPILE_FUNC(statement, AstNode* stmt) {
         case Ast_Kind_While:      compile_while(mod, &code, (AstIfWhile *) stmt); break;
         case Ast_Kind_For:        compile_for(mod, &code, (AstFor *) stmt); break;
         case Ast_Kind_Switch:     compile_switch(mod, &code, (AstSwitch *) stmt); break;
-        case Ast_Kind_Break:      compile_structured_jump(mod, &code, ((AstBreak *) stmt)->count); break;
-        case Ast_Kind_Continue:   compile_structured_jump(mod, &code, -((AstContinue *) stmt)->count); break;
+        case Ast_Kind_Jump:       compile_structured_jump(mod, &code, ((AstJump *) stmt)->count, ((AstJump *) stmt)->jump); break;
         case Ast_Kind_Block:      compile_block(mod, &code, (AstBlock *) stmt, 1); break;
         case Ast_Kind_Defer:      compile_defer(mod, &code, (AstDefer *) stmt); break;
         default:                  compile_expression(mod, &code, (AstTyped *) stmt); break;
@@ -812,9 +811,13 @@ COMPILE_FUNC(switch, AstSwitch* switch_node) {
     bh_imap block_map;
     bh_imap_init(&block_map, global_heap_allocator, bh_arr_length(switch_node->cases));
 
+    u32 jump_target = 1;
+
     if (switch_node->default_case != NULL) {
         WID(WI_BLOCK_START, 0x40);
-        bh_arr_push(mod->structured_jump_target, 0);
+
+        bh_arr_push(mod->structured_jump_target, jump_target);
+        jump_target = 3;
     }
 
     u64 block_num = 0;
@@ -822,7 +825,7 @@ COMPILE_FUNC(switch, AstSwitch* switch_node) {
         if (bh_imap_has(&block_map, (u64) sc->block)) continue;
 
         WID(WI_BLOCK_START, 0x40);
-        bh_arr_push(mod->structured_jump_target, 0);
+        bh_arr_push(mod->structured_jump_target, jump_target);
 
         bh_imap_put(&block_map, (u64) sc->block, block_num);
         block_num++;
@@ -2800,6 +2803,23 @@ static void output_instruction(WasmFunc* func, WasmInstruction* instr, bh_buffer
             bh_buffer_append(buff, leb, leb_len);
             break;
 
+        case WI_JUMP_TABLE: {
+            BranchTable* bt = (BranchTable *) instr->data.p; 
+
+            leb = uint_to_uleb128((u64) bt->count, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+
+            fori (i, 0, bt->count) {
+                leb = uint_to_uleb128((u64) bt->cases[i], &leb_len);
+                bh_buffer_append(buff, leb, leb_len);
+            }
+
+            leb = uint_to_uleb128((u64) bt->default_case, &leb_len);
+            bh_buffer_append(buff, leb, leb_len);
+            break;
+        }
+
+
         case WI_CALL_INDIRECT:
         case WI_I32_STORE:
         case WI_I32_STORE_8: