added basics on switch statements; will need much testing
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 25 Aug 2020 03:48:53 +0000 (22:48 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 25 Aug 2020 03:48:53 +0000 (22:48 -0500)
19 files changed:
Makefile
core/builtin.onyx
core/string.onyx
docs/plan
include/onyxastnodes.h
include/onyxlex.h
include/onyxmsgs.h
include/onyxwasm.h
misc/onyx.sublime-syntax
misc/onyx.vim
onyx
progs/wasi_test.onyx
src/onyxchecker.c
src/onyxlex.c
src/onyxmsgs.c
src/onyxparser.c
src/onyxsymres.c
src/onyxutils.c
src/onyxwasm.c

index 05c150b3b7215d3313901151d154c916b6bb66d5..fa739ddaa69ea581d9d4cc81271c171da1d2230b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-RELEASE=1
+RELEASE=0
 
 OBJ_FILES=\
        build/onyxlex.o \
index ebac598566db61ed461aebfa5d60ba75c27f555b..58fdd589dddb69f114593dd9c6a8361c437aa5fb 100644 (file)
@@ -1,4 +1,4 @@
 package builtin
 
 string  :: #type []u8;
-cstring :: #type ^u8;
\ No newline at end of file
+cstring :: #type ^u8;
index 1743e19f3e8cb7e439602158470b924153608653..0668398f610e63e7bd9e97562c752032c9ae9bc4 100644 (file)
@@ -4,10 +4,7 @@ use package builtin { string, cstring }
 use package memory
 use package wasi
 
-Buffer :: struct {
-    data   : rawptr = null;
-    len    : u32    = 0;
-}
+Buffer :: #type []void;
 
 string_make :: proc #overloaded { string_make_from_cstring }
 
@@ -139,9 +136,9 @@ u64_to_string :: proc (n_: u64, base: u64, buf: Buffer) -> string {
     n := n_;
 
     str :: cast(^u8) buf.data;
-    for i: 0, buf.len do str[i] = #char "\0";
+    for i: 0, buf.count do str[i] = #char "\0";
 
-    c := cast(^u8) ^str[buf.len - 1];
+    c := cast(^u8) ^str[buf.count - 1];
     len := 0;
 
     s :: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
@@ -188,7 +185,7 @@ string_builder_add_u64 :: proc (use sb: ^StringBuilder, n: u64) -> ^StringBuilde
 
 string_builder_add_u64_with_base :: proc (use sb: ^StringBuilder, n: u64, base: u64) -> ^StringBuilder {
     buf : [256] u8;
-    s := u64_to_string(n, base, Buffer.{ cast(^u8) buf, 256 });
+    s := u64_to_string(n, base, Buffer.{ cast(rawptr) buf, 256 });
     return string_builder_add_string(sb, s);
 }
 
index b34d368c3eb7a6cda6e7fc97eee198cefc5d9133..9fcbc957b2d771e1bfafa4263e2085f89c380897 100644 (file)
--- a/docs/plan
+++ b/docs/plan
@@ -207,6 +207,15 @@ HOW:
                     count : u32;
                 }
 
+        [X] Switch statements
+
+        [ ] fallthrough on cases in switch statements
+
+        [ ] #file and #line directives
+            - string and u32 respectively that represent the current file and line number where the directive is
+
+        [ ] transmute
+
         [ ] 'use' enums and packages at an arbitrary scope
 
         [ ] convert to using an 'atom' like table
@@ -259,8 +268,6 @@ HOW:
             - Checking which things are allowed to cast to/from should be checked in the checker,
                 not in the wasm generatation
 
-        [ ] Switch statements
-
 
 
 
index c67803f9aa620a97695e20ecfb836a6f98df19d7..796d8795daef1df4555279d990a2ece060b4e257 100644 (file)
@@ -34,6 +34,8 @@ typedef struct AstBlock AstBlock;
 typedef struct AstIfWhile AstIfWhile;
 typedef struct AstFor AstFor;
 typedef struct AstDefer AstDefer;
+typedef struct AstSwitchCase AstSwitchCase;
+typedef struct AstSwitch AstSwitch;
 
 typedef struct AstType AstType;
 typedef struct AstBasicType AstBasicType;
@@ -128,6 +130,8 @@ typedef enum AstKind {
     Ast_Kind_Break,
     Ast_Kind_Continue,
     Ast_Kind_Defer,
+    Ast_Kind_Switch,
+    Ast_Kind_Switch_Case,
 
     Ast_Kind_Count
 } AstKind;
@@ -255,7 +259,7 @@ typedef enum CallingConvention {
     AstKind kind;             \
     u32 flags;                \
     OnyxToken *token;         \
-    AstNode *next;            
+    AstNode *next;
 struct AstNode { AstNode_base };
 
 // NOTE: 'type_node' is filled out by the parser.
@@ -272,7 +276,7 @@ struct AstNode { AstNode_base };
 #define AstTyped_base       \
     AstNode_base;           \
     AstType *type_node;     \
-    Type *type;                
+    Type *type;
 struct AstTyped { AstTyped_base };
 
 // Expression Nodes
@@ -334,6 +338,25 @@ struct AstIfWhile {
     AstBlock *true_stmt;
     AstBlock *false_stmt;
 };
+struct AstSwitchCase { AstTyped *value; AstBlock *block; };
+struct AstSwitch {
+    AstNode_base;
+
+    // NOTE: These are not currently used;
+    Scope *scope;
+    AstLocal *local;
+    AstBinaryOp *assignment;
+
+    AstTyped *expr;
+
+    bh_arr(AstSwitchCase) cases;
+    AstBlock *default_case;
+
+    // NOTE: This is a mapping from the compile time known case value
+    // to a pointer to the block that it is associated with.
+    bh_imap case_map;
+    u64 min_case, max_case;
+};
 
 // Type Nodes
 // NOTE: This node is very similar to an AstNode, just
index 614eee80430ff162e0c230393435d95d78f0a349..8d9ad1f9e44e12ae6c06e32c1a580ef79dae366b 100644 (file)
@@ -30,6 +30,8 @@ typedef enum TokenType {
     Token_Type_Keyword_Alignof,
     Token_Type_Keyword_Defer,
     Token_Type_Keyword_Do,
+    Token_Type_Keyword_Case,
+    Token_Type_Keyword_Switch,
 
     Token_Type_Right_Arrow,
     Token_Type_Left_Arrow,
index 0e9ada8ff756a298a0b5ffce4a6fb5eedec4999a..81c41865dc23d32f81ce55edd553db0d2081ef15 100644 (file)
@@ -33,6 +33,8 @@ typedef enum MsgType {
     Msg_Type_Duplicate_Value,
     Msg_Type_Field_No_Value,
 
+    Msg_Type_Multiple_Cases,
+
     Msg_Type_Unresolved_Type,
     Msg_Type_Unresolved_Symbol,
 
index 2b6d97610e0af2e64063e94c06e1ed1246e93824..11a6c4a6516dc9fe21f364300f19a2f9d1695989 100644 (file)
@@ -243,6 +243,12 @@ typedef struct WasmInstruction {
     WasmInstructionData data;
 } WasmInstruction;
 
+typedef struct BranchTable {
+    u32 count;
+    u32 default_case;
+    u32 cases[];    
+} BranchTable;
+
 #define LOCAL_IS_WASM 0x8000000000000
 typedef struct LocalAllocator {
     u32 param_count;
index e60ade0408fbc120e136834f93e1af0f0d0c5d05..4640a21890932f2076a3ca8ba340144627a872f0 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)\b'
+    - 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'
       scope: keyword.control.onyx
 
     - match: '\b(bool|void|i8|u8|i16|u16|i32|u32|i64|u64|f32|f64|rawptr)\b'
index 1333756672d3c63484c4f9180c30826f2b1df878..071e6e19f6d089a5fe6de7a2773f396b4e9b3c44 100644 (file)
@@ -13,6 +13,7 @@ set cpo&vim
 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 as cast sizeof alignof
 
diff --git a/onyx b/onyx
index 8d63f147f2f29d97dd444b68c41d3e4bca0119c0..7083980b5180acd48d13159bfdabb883e884bff9 100755 (executable)
Binary files a/onyx and b/onyx differ
index ec764c0018c51c848b1d2fa0429bcca5daef5f6a..7227834d072982811e3dc36fef538d45a5d2bb39 100644 (file)
@@ -273,13 +273,20 @@ main :: proc (args: []cstring) {
 
     acc := 0;
     for i: 0, tokens.count {
-        tok :: tokens.data[i].data[0];
-
-        if     tok == #char "+" do acc += 1;
-        elseif tok == #char "-" do acc -= 1;
-        elseif tok == #char "*" do acc *= 2;
-        elseif tok == #char "/" do acc /= 2;
-        elseif tok == #char "s" do acc *= acc;
+        switch tokens.data[i].data[0] {
+            case #char "+" do acc += 1;
+            case #char "-" do acc -= 1;
+            case #char "*" do acc *= 2;
+            case #char "/" do acc /= 2;
+
+            case #char "s" do acc *= acc;
+
+            case #default {
+                print("Unexpected token: ");
+                print_u64_with_base(cast(u64) tokens.data[i].data[0], 16l);
+                print("\n");
+            }
+        }
     }
 
     string_builder_clear(^sb);
index c93ed3c4dcc51c110565b19beb59dd50b59ed276..f24d4b0f1c9e724a7d6bb3f22b343dafe95afdb5 100644 (file)
@@ -12,6 +12,7 @@ CHECK(return, AstReturn* retnode);
 CHECK(if, AstIfWhile* ifnode);
 CHECK(while, AstIfWhile* whilenode);
 CHECK(for, AstFor* fornode);
+CHECK(switch, AstSwitch* switchnode);
 CHECK(call, AstCall* call);
 CHECK(binaryop, AstBinaryOp** pbinop, b32 assignment_is_ok);
 CHECK(unaryop, AstUnaryOp** punop);
@@ -156,6 +157,52 @@ CHECK(for, AstFor* fornode) {
     return 0;
 }
 
+CHECK(switch, AstSwitch* switchnode) {
+    if (check_expression(&switchnode->expr)) return 1;
+    if (!type_is_integer(switchnode->expr->type)) {
+        onyx_message_add(Msg_Type_Literal,
+                switchnode->expr->token->pos,
+                "expected integer type for switch expression");
+        return 1;
+    }
+
+    bh_imap_init(&switchnode->case_map, global_heap_allocator, bh_arr_length(switchnode->cases) * 2);
+
+    switchnode->min_case = 0xffffffffffffffff;
+
+    bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
+        if (check_block(sc->block)) return 1;
+        if (check_expression(&sc->value)) return 1;
+
+        if (sc->value->kind != Ast_Kind_NumLit) {
+            onyx_message_add(Msg_Type_Literal,
+                    sc->value->token->pos,
+                    "case statement expected compile time known integer");
+            return 1;
+        }
+
+        promote_numlit_to_larger((AstNumLit *) sc->value);
+
+        u64 value = ((AstNumLit *) sc->value)->value.l;
+        switchnode->min_case = bh_min(switchnode->min_case, value);
+        switchnode->max_case = bh_max(switchnode->max_case, value);
+
+        if (bh_imap_has(&switchnode->case_map, value)) {
+            onyx_message_add(Msg_Type_Multiple_Cases,
+                    sc->value->token->pos,
+                    value);
+            return 1;
+        }
+
+        bh_imap_put(&switchnode->case_map, value, (u64) sc->block);
+    }
+
+    if (switchnode->default_case)
+        check_block(switchnode->default_case);
+
+    return 0;
+}
+
 static AstTyped* match_overloaded_function(AstCall* call, AstOverloadedFunction* ofunc) {
     bh_arr_each(AstTyped *, node, ofunc->overloads) {
         AstFunction* overload = (AstFunction *) *node;
@@ -958,6 +1005,7 @@ CHECK(statement, AstNode* stmt) {
         case Ast_Kind_If:         return check_if((AstIfWhile *) stmt);
         case Ast_Kind_While:      return check_while((AstIfWhile *) stmt);
         case Ast_Kind_For:        return check_for((AstFor *) stmt);
+        case Ast_Kind_Switch:     return check_switch((AstSwitch *) stmt);
         case Ast_Kind_Block:      return check_block((AstBlock *) stmt);
         case Ast_Kind_Defer: {
             if (!semstate.defer_allowed) {
index e826ce6ba3d54368a25907d0951efd65e982b6fa..88c849be4c52887f2b6d0bb18fc2abc58162a2d4 100644 (file)
@@ -28,6 +28,8 @@ static const char* token_type_names[] = {
     "alignof",
     "defer",
     "do",
+    "switch",
+    "case",
 
     "->",
     "<-",
@@ -258,6 +260,7 @@ whitespace_skipped:
         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);
         break;
@@ -290,6 +293,7 @@ whitespace_skipped:
     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);
         break;
     case 't':
         LITERAL_TOKEN("true",       1, Token_Type_Literal_True);
index c65b0d72f76b74275272acd30c96040e0f337087..539a8730339a6d61ae9835e0500211c565ae5b9c 100644 (file)
@@ -31,6 +31,8 @@ static const char* msg_formats[] = {
     "duplicate value for struct member '%b'",
     "no value provided for field '%b'",
 
+    "multiple cases for '%l'",
+
     "unable to resolve type for symbol '%b'",
     "unable to resolve symbol '%b'",
 
index 5c1b18c8e8c14b1356925d502b29acb861ddd476..580ed3cbbe861ecc9681a5c7cf49edb8b4c14d52 100644 (file)
@@ -30,6 +30,7 @@ static AstTyped*      parse_expression(OnyxParser* parser);
 static AstIfWhile*    parse_if_stmt(OnyxParser* parser);
 static AstIfWhile*    parse_while_stmt(OnyxParser* parser);
 static AstFor*        parse_for_stmt(OnyxParser* parser);
+static AstSwitch*     parse_switch_stmt(OnyxParser* parser);
 static b32            parse_possible_symbol_declaration(OnyxParser* parser, AstNode** ret);
 static AstReturn*     parse_return_statement(OnyxParser* parser);
 static AstBlock*      parse_block(OnyxParser* parser);
@@ -842,6 +843,54 @@ static AstFor* parse_for_stmt(OnyxParser* parser) {
     return for_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);
+
+    switch_node->expr = parse_expression(parser);
+    expect_token(parser, '{');
+
+    AstTyped** batch_cases = NULL;
+    bh_arr_new(global_scratch_allocator, batch_cases, 16);
+
+    while (parser->curr->type == Token_Type_Keyword_Case) {
+        expect_token(parser, Token_Type_Keyword_Case);
+        if (parser->hit_unexpected_token) return switch_node;
+
+        if (parse_possible_directive(parser, "default")) {
+            switch_node->default_case = parse_block(parser);
+            continue;
+        }
+
+        AstTyped* value = parse_expression(parser);
+        bh_arr_push(batch_cases, value);
+        while (parser->curr->type == ',') {
+            if (parser->hit_unexpected_token) return switch_node;
+
+            consume_token(parser);
+            value = parse_expression(parser);
+            bh_arr_push(batch_cases, value);
+        }
+
+        AstBlock* block = parse_block(parser);
+
+        AstSwitchCase sc_node;
+        sc_node.block = block;
+
+        bh_arr_each(AstTyped *, value, batch_cases) {
+            sc_node.value = *value;
+            bh_arr_push(switch_node->cases, sc_node);
+        }
+
+        bh_arr_clear(batch_cases);
+    }
+
+    expect_token(parser, '}');
+    return switch_node;
+}
+
 // Returns 1 if the symbol was consumed. Returns 0 otherwise
 // ret is set to the statement to insert
 // <symbol> : <type> = <expr>
@@ -972,6 +1021,11 @@ static AstNode* parse_statement(OnyxParser* parser) {
             retval = (AstNode *) parse_for_stmt(parser);
             break;
 
+        case Token_Type_Keyword_Switch:
+            needs_semicolon = 0;
+            retval = (AstNode *) parse_switch_stmt(parser);
+            break;
+
         case Token_Type_Keyword_Break: {
             AstBreak* bnode = make_node(AstBreak, Ast_Kind_Break);
             bnode->token = expect_token(parser, Token_Type_Keyword_Break);
index 4c88b126511117a2eb5418f8ca111014f77ca016..169cf9ea048d975a9ca414b33fb93abd1316600f 100644 (file)
@@ -19,6 +19,7 @@ static void symres_return(AstReturn* ret);
 static void symres_if(AstIfWhile* ifnode);
 static void symres_while(AstIfWhile* whilenode);
 static void symres_for(AstFor* fornode);
+static void symres_switch(AstSwitch* switchnode);
 static void symres_statement_chain(AstNode** walker);
 static b32  symres_statement(AstNode** stmt);
 static void symres_block(AstBlock* block);
@@ -389,6 +390,18 @@ static void symres_for(AstFor* fornode) {
     scope_leave();
 }
 
+static void symres_switch(AstSwitch* switchnode) {
+    symres_expression(&switchnode->expr);
+
+    bh_arr_each(AstSwitchCase, sc, switchnode->cases) {
+        symres_expression(&sc->value);
+        symres_block(sc->block);
+    }
+
+    if (switchnode->default_case)
+        symres_block(switchnode->default_case);
+}
+
 // NOTE: Returns 1 if the statment should be removed
 static b32 symres_statement(AstNode** stmt) {
     switch ((*stmt)->kind) {
@@ -397,6 +410,7 @@ static b32 symres_statement(AstNode** stmt) {
         case Ast_Kind_If:         symres_if((AstIfWhile *) *stmt);                   return 0;
         case Ast_Kind_While:      symres_while((AstIfWhile *) *stmt);                return 0;
         case Ast_Kind_For:        symres_for((AstFor *) *stmt);                      return 0;
+        case Ast_Kind_Switch:     symres_switch((AstSwitch *) *stmt);                return 0;
         case Ast_Kind_Call:       symres_call((AstCall *) *stmt);                    return 0;
         case Ast_Kind_Argument:   symres_expression((AstTyped **) &((AstArgument *) *stmt)->value); return 0;
         case Ast_Kind_Block:      symres_block((AstBlock *) *stmt);                  return 0;
index 3ed98c55f24bed31629637b9af0acc7bddcf1c6a..e8152d23a85805019f983f3321d58b15a725e87f 100644 (file)
@@ -73,6 +73,8 @@ static const char* ast_node_names[] = {
     "BREAK",
     "CONTINUE",
     "DEFER",
+    "SWITCH",
+    "SWITCH CASE"
 
     "AST_NODE_KIND_COUNT",
 };
index 8902529828560930249d20bf533a59de278310e1..80936ab7631ebf4acbd98f9032efaf76b6dd39f1 100644 (file)
@@ -360,6 +360,7 @@ COMPILE_FUNC(load_instruction,              Type* type, u32 offset);
 COMPILE_FUNC(if,                            AstIfWhile* if_node);
 COMPILE_FUNC(while,                         AstIfWhile* while_node);
 COMPILE_FUNC(for,                           AstFor* for_node);
+COMPILE_FUNC(switch,                        AstSwitch* switch_node);
 COMPILE_FUNC(defer,                         AstDefer* defer);
 COMPILE_FUNC(deferred_stmts,                AstNode* node);
 COMPILE_FUNC(binop,                         AstBinaryOp* binop);
@@ -452,6 +453,7 @@ COMPILE_FUNC(statement, AstNode* stmt) {
         case Ast_Kind_If:         compile_if(mod, &code, (AstIfWhile *) stmt); break;
         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_Block:      compile_block(mod, &code, (AstBlock *) stmt, 1); break;
@@ -804,6 +806,70 @@ COMPILE_FUNC(for, AstFor* for_node) {
     *pcode = code;
 }
 
+COMPILE_FUNC(switch, AstSwitch* switch_node) {
+    bh_arr(WasmInstruction) code = *pcode;
+
+    bh_imap block_map;
+    bh_imap_init(&block_map, global_heap_allocator, bh_arr_length(switch_node->cases));
+
+    if (switch_node->default_case != NULL) {
+        WID(WI_BLOCK_START, 0x40);
+        bh_arr_push(mod->structured_jump_target, 0);
+    }
+
+    u64 block_num = 0;
+    bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
+        if (bh_imap_has(&block_map, (u64) sc->block)) continue;
+
+        WID(WI_BLOCK_START, 0x40);
+        bh_arr_push(mod->structured_jump_target, 0);
+
+        bh_imap_put(&block_map, (u64) sc->block, block_num);
+        block_num++;
+    }
+
+    u64 count = switch_node->max_case + 1 - switch_node->min_case;
+    BranchTable* bt = bh_alloc(global_heap_allocator, sizeof(BranchTable) + sizeof(u32) * count);
+    bt->count = count;
+    bt->default_case = block_num;
+    fori (i, 0, bt->count) bt->cases[i] = bt->default_case;
+
+    bh_arr_each(bh__imap_entry, sc, switch_node->case_map.entries) {
+        bt->cases[sc->key - switch_node->min_case] = bh_imap_get(&block_map, (u64) sc->value);
+    }
+
+    WID(WI_BLOCK_START, 0x40);
+    compile_expression(mod, &code, switch_node->expr);
+    if (switch_node->min_case != 0) {
+        WID(WI_I32_CONST, switch_node->min_case);
+        WI(WI_I32_SUB);
+    }
+    WIL(WI_JUMP_TABLE, (u64) bt);
+    WI(WI_BLOCK_END);
+
+    bh_arr_each(AstSwitchCase, sc, switch_node->cases) {
+        if (bh_imap_get(&block_map, (u64) sc->block) == 0xdeadbeef) continue;
+
+        u64 bn = bh_imap_get(&block_map, (u64) sc->block);
+
+        compile_block(mod, &code, sc->block, 0);
+        WID(WI_JUMP, block_num - bn);
+        WI(WI_BLOCK_END);
+        bh_arr_pop(mod->structured_jump_target);
+
+        bh_imap_put(&block_map, (u64) sc->block, 0xdeadbeef);
+    }
+
+    if (switch_node->default_case != NULL) {
+        compile_block(mod, &code, switch_node->default_case, 0);
+        WI(WI_BLOCK_END);
+        bh_arr_pop(mod->structured_jump_target);
+    }
+
+    bh_imap_free(&block_map);
+    *pcode = code;
+}
+
 COMPILE_FUNC(defer, AstDefer* defer) {
     bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
         .depth = bh_arr_length(mod->structured_jump_target),