added '#remove' directive in for-loops
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 31 Mar 2022 18:38:15 +0000 (13:38 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 31 Mar 2022 18:38:15 +0000 (13:38 -0500)
core/builtin.onyx
core/container/iter.onyx
include/astnodes.h
include/wasm_emit.h
src/astnodes.c
src/checker.c
src/clone.c
src/parser.c
src/symres.c
src/wasm_emit.c

index abd390c97c64ac7aa6f8be7b06c67480c67cad38..99dcf209e1ffbbf4d04b695c42eabab3421d770f 100644 (file)
@@ -183,9 +183,10 @@ cfree   :: (ptr: rawptr) do raw_free(context.allocator, ptr);
 
 
 Iterator :: struct (Iter_Type: type_expr) {
-    data:  rawptr;
-    next:  (data: rawptr) -> (Iter_Type, bool);
-    close: (data: rawptr) -> void = null_proc;
+    data:   rawptr;
+    next:   (data: rawptr) -> (Iter_Type, bool);
+    close:  (data: rawptr) -> void = null_proc;
+    remove: (data: rawptr) -> void = null_proc;
 }
 
 
index 43c52871b5ca7520494214ded84bfc597f737f95..b7b3efe5076b4ab3a5fe7839e55bcfbf06e16b3f 100644 (file)
@@ -437,6 +437,46 @@ from_array :: (arr: [] $T) -> Iterator(^T) {
     };
 }
 
+#match as_iterator (x: ^[..] $T) -> Iterator(^T) {
+    Context :: struct (T: type_expr) {
+        arr: ^[..] T;
+        current: u32;
+    }
+
+    c := make(Context(T));
+    c.arr = x;
+    c.current = 0;
+
+    next :: (use _: ^Context($T)) -> (^T, bool) {
+        if current < arr.count {
+            defer current += 1;
+            return ^arr.data[current], true;
+
+        } else {
+            return null, false;
+        }
+    }
+
+    close :: (data: rawptr) {
+        cfree(data);
+    }
+
+    remove :: (use _: ^Context($T)) {
+        //
+        // This is current - 1 because current will have already
+        // been incremented by the time this element calls #remove.
+        array :: package core.array
+        array.delete(arr, current - 1);
+    }
+
+    return .{
+        data  = c,
+        next  = #solidify next { T = T },
+        close = close,
+        remove = #solidify remove { T = T },
+    };
+}
+
 #match as_iterator (r: range) -> Iterator(i32) {
     Context :: struct {
         r: range;
index 4f052b136d1b4ccd86f6efdc8c224ac2983c1731..94ea66f581c25e73f2df2a6814568b5ec7010ac0 100644 (file)
@@ -42,6 +42,7 @@
     NODE(DirectiveTag)         \
     NODE(DirectiveInit)        \
     NODE(DirectiveLibrary)     \
+    NODE(DirectiveRemove)      \
                                \
     NODE(Return)               \
     NODE(Jump)                 \
@@ -213,6 +214,7 @@ typedef enum AstKind {
     Ast_Kind_Directive_Tag,
     Ast_Kind_Directive_Init,
     Ast_Kind_Directive_Library,
+    Ast_Kind_Directive_Remove,
     Ast_Kind_Call_Site,
 
     Ast_Kind_Code_Block,
@@ -1259,6 +1261,10 @@ struct AstDirectiveTag {
     AstTyped* tag;
 };
 
+struct AstDirectiveRemove {
+    AstNode_base;
+};
+
 struct AstNote {
     AstNode_base;
 };
index a369bb2f627a60c3f828e454b834300a4df9c357..71141188f0e58366a5482ff74d43c4be10b29404 100644 (file)
@@ -613,6 +613,14 @@ typedef struct PatchInfo {
     u32 instruction_index;
 } PatchInfo;
 
+typedef struct ForRemoveInfo {
+    // These are WASM locals
+    u64 iterator_remove_func;
+    u64 iterator_data_ptr;
+
+    i32 remove_func_type_idx;
+} ForRemoveInfo;
+
 typedef struct OnyxWasmModule {
     bh_allocator allocator;
 
@@ -635,6 +643,8 @@ typedef struct OnyxWasmModule {
 
     bh_arr(PatchInfo) stack_leave_patches;
 
+    bh_arr(ForRemoveInfo) for_remove_info;
+
     // NOTE: Used internally as a map from strings that represent function types,
     // 0x7f 0x7f : 0x7f ( (i32, i32) -> i32 )
     // to the function type index if it has been created.
index d354d520e9f43e71e52302aeaaf3c89b948053d4..9a3819a88d658c1854a006d092d84390dff3d5ea 100644 (file)
@@ -96,6 +96,7 @@ static const char* ast_node_names[] = {
     "TAG",
     "INIT",
     "LIBRARY",
+    "REMOVE",
     "CALL SITE",
 
     "CODE BLOCK",
index 0d510a411e0a495cf8b7d929fe000a887977dc30..fa0e330042e76d465a384dc4fe7affd7dc8e25df 100644 (file)
@@ -112,6 +112,7 @@ CheckStatus check_polyquery(AstPolyQuery *query);
 // HACK HACK HACK
 b32 expression_types_must_be_known = 0;
 b32 all_checks_are_final           = 1;
+b32 inside_for_iterator            = 0;
 
 #define STATEMENT_LEVEL 1
 #define EXPRESSION_LEVEL 2
@@ -307,9 +308,16 @@ CheckStatus check_for(AstFor* fornode) {
     fornode->flags |= Ast_Flag_Has_Been_Checked;
 
 fornode_expr_checked:
+    b32 old_inside_for_iterator = inside_for_iterator;
+    inside_for_iterator = 0;
+    iter_type = fornode->iter->type;
+    if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
+        inside_for_iterator = 1;
+    }
 
     CHECK(block, fornode->stmt);
 
+    inside_for_iterator = old_inside_for_iterator;
     return Check_Success;
 }
 
@@ -1965,6 +1973,14 @@ CheckStatus check_directive_solidify(AstDirectiveSolidify** psolid) {
     return Check_Success;
 }
 
+CheckStatus check_remove_directive(AstDirectiveRemove *remove) {
+    if (!inside_for_iterator) {
+        ERROR(remove->token->pos, "#remove is only allowed in the body of a for-loop over an iterator.");
+    }
+
+    return Check_Success;
+}
+
 CheckStatus check_statement(AstNode** pstmt) {
     AstNode* stmt = *pstmt;
 
@@ -1981,6 +1997,7 @@ CheckStatus check_statement(AstNode** pstmt) {
         case Ast_Kind_Switch:     return check_switch((AstSwitch *) stmt);
         case Ast_Kind_Block:      return check_block((AstBlock *) stmt);
         case Ast_Kind_Defer:      return check_statement(&((AstDefer *) stmt)->stmt);
+        case Ast_Kind_Directive_Remove: return check_remove_directive((AstDirectiveRemove *) stmt);
         case Ast_Kind_Call: {
             CHECK(call, (AstCall **) pstmt);
             (*pstmt)->flags |= Ast_Flag_Expr_Ignored;
index 324ddee390065048e44df1aa4ffb2995f035a51c..55ffc3fd194039144db431a6bc990e08ddd90e64 100644 (file)
@@ -113,6 +113,7 @@ static inline i32 ast_kind_to_size(AstNode* node) {
         case Ast_Kind_Directive_Defined: return sizeof(AstDirectiveDefined);
         case Ast_Kind_Do_Block: return sizeof(AstDoBlock);
         case Ast_Kind_Constraint: return sizeof(AstConstraint);
+        case Ast_Kind_Directive_Remove: return sizeof(AstDirectiveRemove);
         case Ast_Kind_Count: return 0;
     }
 
index 83b722cd76de0526d4f5619df4cdd14014c80815..4c8195eb0a2dd5954f91ce23cfce9a6f862f802c 100644 (file)
@@ -1545,6 +1545,14 @@ static AstNode* parse_statement(OnyxParser* parser) {
                 break;
             }
 
+            if (parse_possible_directive(parser, "remove")) {
+                // :LinearTokenDependent
+                AstDirectiveRemove *remove = make_node(AstDirectiveRemove, Ast_Kind_Directive_Remove);
+                remove->token = parser->curr - 2;
+                retval = (AstNode *) remove;
+                break;
+            }
+
             if (next_tokens_are(parser, 2, '#', Token_Type_Symbol)) {
                 retval = (AstNode *) parse_factor(parser);
                 break;
index 88b7e5b8291ef037c39d8116d0d975b3c5ed15a5..9b501acab32f5d89a2e89ec11167eeb367c37fa3 100644 (file)
@@ -849,6 +849,7 @@ static SymresStatus symres_statement(AstNode** stmt, b32 *remove) {
         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_Directive_Remove: break;
 
         case Ast_Kind_Local:
             // if (remove) *remove = 1;
index 5b076fb4615c2c4ac3985a7b50fe6f497e9effe3..237507d9f078c77f583b64513bf452aa2a2995a9 100644 (file)
@@ -243,6 +243,7 @@ EMIT_FUNC(defer,                         AstDefer* defer);
 EMIT_FUNC(defer_code,                    WasmInstruction* deferred_code, u32 code_count);
 EMIT_FUNC(deferred_stmt,                 DeferredStmt deferred_stmt);
 EMIT_FUNC_NO_ARGS(deferred_stmts);
+EMIT_FUNC(remove_directive,              AstDirectiveRemove* remove);
 EMIT_FUNC(binop,                         AstBinaryOp* binop);
 EMIT_FUNC(unaryop,                       AstUnaryOp* unop);
 EMIT_FUNC(call,                          AstCall* call);
@@ -430,6 +431,7 @@ EMIT_FUNC(statement, AstNode* stmt) {
         case Ast_Kind_Defer:      emit_defer(mod, &code, (AstDefer *) stmt); break;
         case Ast_Kind_Local:      emit_local_allocation(mod, &code, (AstTyped *) stmt); break;
 
+        case Ast_Kind_Directive_Remove: emit_remove_directive(mod, &code, (AstDirectiveRemove *) stmt); break;
         case Ast_Kind_Directive_Insert: break;
 
         default:                  emit_expression(mod, &code, (AstTyped *) stmt); break;
@@ -1024,14 +1026,32 @@ EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
     bh_arr(WasmInstruction) code = *pcode;
 
     // Allocate temporaries for iterator contents
-    u64 iterator_data_ptr   = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
-    u64 iterator_next_func  = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
-    u64 iterator_close_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
-    u64 iterator_done_bool  = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+    u64 iterator_data_ptr    = local_raw_allocate(mod->local_alloc, WASM_TYPE_PTR);
+    u64 iterator_next_func   = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+    u64 iterator_close_func  = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+    u64 iterator_remove_func = local_raw_allocate(mod->local_alloc, WASM_TYPE_FUNC);
+    u64 iterator_done_bool   = local_raw_allocate(mod->local_alloc, WASM_TYPE_INT32);
+    WIL(WI_LOCAL_SET, iterator_remove_func);
     WIL(WI_LOCAL_SET, iterator_close_func);
     WIL(WI_LOCAL_SET, iterator_next_func);
     WIL(WI_LOCAL_SET, iterator_data_ptr);
 
+
+    {
+        //
+        // This pushes an entry onto the stack of for loops that have
+        // are iterator that can have a '#remove' directive in them.
+        ForRemoveInfo remove_info;
+        remove_info.iterator_data_ptr = iterator_data_ptr;
+        remove_info.iterator_remove_func = iterator_remove_func;
+
+        TypeWithOffset remove_func_type;
+        type_linear_member_lookup(for_node->iter->type, 3, &remove_func_type);
+        remove_info.remove_func_type_idx = generate_type_idx(mod, remove_func_type.type);
+
+        bh_arr_push(mod->for_remove_info, remove_info);
+    }
+
     AstLocal* var = for_node->var;
     b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
     u64 offset = 0;
@@ -1115,6 +1135,8 @@ EMIT_FUNC(for_iterator, AstFor* for_node, u64 iter_local) {
     emit_deferred_stmts(mod, &code);
     emit_leave_structured_block(mod, &code);
 
+    bh_arr_pop(mod->for_remove_info);
+
     local_raw_free(mod->local_alloc, WASM_TYPE_PTR);
     local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
     local_raw_free(mod->local_alloc, WASM_TYPE_FUNC);
@@ -1296,6 +1318,25 @@ EMIT_FUNC_NO_ARGS(deferred_stmts) {
     }
 }
 
+EMIT_FUNC(remove_directive, AstDirectiveRemove* remove) {
+    assert(bh_arr_length(mod->for_remove_info) > 0);
+
+    bh_arr(WasmInstruction) code = *pcode;
+
+    ForRemoveInfo remove_info = bh_arr_last(mod->for_remove_info);
+
+    WIL(WI_LOCAL_GET, remove_info.iterator_remove_func);
+    WIL(WI_I32_CONST, mod->null_proc_func_idx);
+    WI(WI_I32_NE);
+    WID(WI_IF_START, 0x40);
+    WIL(WI_LOCAL_GET, remove_info.iterator_data_ptr);
+    WIL(WI_LOCAL_GET, remove_info.iterator_remove_func);
+    WIL(WI_CALL_INDIRECT, remove_info.remove_func_type_idx);
+    WI(WI_IF_END);
+
+    *pcode = code;
+}
+
 // NOTE: These need to be in the same order as
 // the OnyxBinaryOp enum
 static const WasmInstructionType binop_map[][4] = {