added custom iterators! and some error checking
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 16 Apr 2021 18:50:22 +0000 (13:50 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 16 Apr 2021 18:50:22 +0000 (13:50 -0500)
bin/onyx
core/builtin.onyx
core/list.onyx [new file with mode: 0644]
core/std.onyx
include/onyxastnodes.h
src/onyxastnodes.c
src/onyxbuiltins.c
src/onyxchecker.c
src/onyxwasm.c

index a5b6e88e669ab7d3e62c67f9b0d0ffa3db41d620..07807238c4c7f8c9b88e36280f72b296f4b1e4b8 100755 (executable)
Binary files a/bin/onyx and b/bin/onyx differ
index e0ba37041b95a6433f4bf96e5bf9001102761ee2..3e40cb7c66e3d31c98d3dd57844607159f278114 100644 (file)
@@ -150,3 +150,10 @@ cfree   :: (ptr: rawptr) do raw_free(context.allocator, ptr);
         return res;
     }
 }
+
+
+Iterator :: struct (T: type_expr) {
+    data:  rawptr;
+    next:  (data: rawptr) -> (T, bool);
+    close: (data: rawptr) -> void;
+}
diff --git a/core/list.onyx b/core/list.onyx
new file mode 100644 (file)
index 0000000..bcfc166
--- /dev/null
@@ -0,0 +1,96 @@
+package core.list
+
+ListElem :: struct (T: type_expr) {
+    next: ^ListElem(T) = null;
+    prev: ^ListElem(T) = null;
+    data: T;
+}
+
+List :: struct (T: type_expr) {
+    allocator: Allocator;
+
+    first: ^ListElem(T) = null;
+    last:  ^ListElem(T) = null;
+}
+
+make :: ($T: type_expr, allocator := context.allocator) -> List(T) {
+    return .{ allocator = allocator };
+}
+
+push_end :: (list: ^List($T), x: T) { 
+    new_elem := allocate_elem(list);
+    new_elem.data = x;
+
+    new_elem.prev = list.last;
+    list.last.next = new_elem;
+    list.last = new_elem;
+
+    if list.first == null do list.first = new_elem;
+}
+
+push_begin :: (list: ^List($T), x: T) {
+    new_elem := allocate_elem(list);
+    new_elem.data = x;
+
+    new_elem.next = list.first;
+    list.first.prev = new_elem;
+    list.first = new_elem;
+
+    if list.last == null do list.last = new_elem;
+}
+
+pop_end :: (list: ^List($T), default: T = 0) -> T {
+    if list.last == null do return default;
+
+    end := list.last;
+    list.last = list.last.prev;
+    list.last.next = null;
+
+    defer raw_free(list.allocator, end);
+    return end.data;
+}
+
+pop_begin :: (list: ^List($T), default: T = 0) -> T {
+    if list.last == null do return default;
+
+    begin := list.first;
+    list.first = list.first.next;
+    list.first.prev = null;
+
+    defer raw_free(list.allocator, begin);
+    return begin.data;
+}
+
+contains :: (list: ^List($T), x: T) -> bool {
+    elem := list.first;
+    while elem != null {
+        if elem.data == x do return true;
+        elem = elem.next;
+    }
+
+    return false;
+}
+
+get_iterator :: (list: ^List($T)) -> Iterator(T) {
+    iterator_impl :: ($T: type_expr, data: rawptr) -> (T, bool) {
+        elem_ptr := cast(^^ListElem(T)) data;
+        elem := *elem_ptr;
+
+        use package core.intrinsics.onyx { __zero_value }
+        if elem == null do return __zero_value(T), false;
+
+        *elem_ptr = elem.next;
+        return elem.data, true;
+    }
+
+    return .{
+        data = ^list.first,
+        next = #solidify iterator_impl { T = T },
+        close = null_proc,
+    };
+}
+
+#private_file
+allocate_elem :: (list: ^List($T)) -> ^ListElem(T) {
+    return new(#type ListElem(T), allocator=list.allocator);
+}
index 2632c73c3187bf84d90a62733c4808b9da5055b6..7109a87ab2d98a6ef3c5f0afc947222399cd4e60 100644 (file)
@@ -6,6 +6,7 @@ package core
 
 #load "core/array"
 #load "core/map"
+#load "core/list"
 
 #load "core/conv"
 #load "core/math"
index cfd7572494aa1c516fe76f65a2237a3037efc4ba..d6f9aa2f795f1a9b9849f818692ac9ee9b8ddb82 100644 (file)
@@ -438,6 +438,7 @@ typedef enum ForLoopType {
     For_Loop_Array,
     For_Loop_Slice,
     For_Loop_DynArr,
+    For_Loop_Iterator,
 } ForLoopType;
 
 typedef enum ParamPassType {
@@ -1126,6 +1127,7 @@ extern AstType  *builtin_vararg_type;
 extern Type     *builtin_vararg_type_type;
 extern AstTyped *builtin_context_variable;
 extern AstType  *builtin_allocator_type;
+extern AstType  *builtin_iterator_type;
 
 typedef struct BuiltinSymbol {
     char*    package;
index 0f4c83e50dc65ef67c3e18c1d106b6db4e411614..c168c1117df3dbc055c2bcbcce7fb812e3cc9448 100644 (file)
@@ -636,6 +636,9 @@ b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) {
         if      (from->Basic.size == 4) fromidx = 8;
         else if (from->Basic.size == 8) fromidx = 9;
     }
+    else if (from->Basic.flags & Basic_Flag_Boolean) {
+        fromidx = 0;
+    }
 
     if (to->Basic.flags & Basic_Flag_Pointer || to->kind == Type_Kind_Array) {
         toidx = 10;
@@ -649,12 +652,18 @@ b32 cast_is_legal(Type* from_, Type* to_, char** err_msg) {
         if      (to->Basic.size == 4) toidx = 8;
         else if (to->Basic.size == 8) toidx = 9;
     }
+    else if (to->Basic.flags & Basic_Flag_Boolean) {
+        toidx = 0;
+    }
 
     if (fromidx != -1 && toidx != -1) {
         if (!cast_legality[fromidx][toidx]) {
             *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
             return 0;
         }
+    } else {
+        *err_msg = bh_aprintf(global_heap_allocator, "Cast from '%s' to '%s' is not allowed.", type_get_name(from_), type_get_name(to_));
+        return 0;
     }
 
     *err_msg = NULL;
index e9fe9f094f8317d3808bad885596ccd6d1afa044..76e4e14871aeb0aa43f628588bccd5aa42bb2c12 100644 (file)
@@ -48,6 +48,7 @@ AstType  *builtin_vararg_type;
 Type     *builtin_vararg_type_type;
 AstTyped *builtin_context_variable;
 AstType  *builtin_allocator_type;
+AstType  *builtin_iterator_type;
 
 const BuiltinSymbol builtin_symbols[] = {
     { NULL, "void",       (AstNode *) &basic_type_void },
@@ -378,6 +379,13 @@ void initialize_builtins(bh_allocator a) {
         return;
     }
 
+    builtin_iterator_type = (AstType *) symbol_raw_resolve(p->scope, "Iterator");
+    if (builtin_iterator_type == NULL) {
+        onyx_report_error((OnyxFilePos) { 0 }, "'Iterator' struct not found in builtin package.");
+        return;
+    }
+
+
     fori (i, 0, Binary_Op_Count) {
         bh_arr_new(global_heap_allocator, operator_overloads[i], 4); 
     }
index b98e05531239a935359762283cc6f36c4287bbc2..a15e17fee8fc09a685d4a7a5370205c9587b801d 100644 (file)
@@ -214,6 +214,16 @@ CheckStatus check_for(AstFor* fornode) {
 
         fornode->loop_type = For_Loop_DynArr;
     }
+    else if (type_struct_constructed_from_poly_struct(iter_type, builtin_iterator_type)) {
+        if (fornode->by_pointer) {
+            onyx_report_error(fornode->var->token->pos, "Cannot iterate by pointer over an iterator.");
+            return Check_Error;
+        }
+
+        // HACK: This assumes the Iterator type only has a single type argument.
+        fornode->var->type = iter_type->Struct.poly_sln[0].type;
+        fornode->loop_type = For_Loop_Iterator;
+    }
 
     if (fornode->by_pointer)
         fornode->var->flags |= Ast_Flag_Cannot_Take_Addr;
index d427298a97b7b5a27e9b13f28050d15152927024..80b53d6f8414c7508d7098860e8375a5955f49e8 100644 (file)
@@ -8,6 +8,7 @@
 #define WASM_TYPE_FLOAT64 0x7C
 #define WASM_TYPE_VAR128  0x7B
 #define WASM_TYPE_PTR     WASM_TYPE_INT32
+#define WASM_TYPE_FUNC    WASM_TYPE_INT32
 #define WASM_TYPE_VOID    0x00
 
 static WasmType onyx_type_to_wasm_type(Type* type) {
@@ -32,7 +33,7 @@ static WasmType onyx_type_to_wasm_type(Type* type) {
     }
 
     if (type->kind == Type_Kind_Function) {
-        return WASM_TYPE_INT32;
+        return WASM_TYPE_FUNC;
     }
 
     if (type->kind == Type_Kind_Basic) {
@@ -240,7 +241,7 @@ EMIT_FUNC(return,                        AstReturn* ret);
 EMIT_FUNC(stack_enter,                   u64 stacksize);
 EMIT_FUNC(stack_leave,                   u32 unused);
 EMIT_FUNC(zero_value,                    WasmType wt);
-EMIT_FUNC(zero_value_for_type,           Type* type);
+EMIT_FUNC(zero_value_for_type,           Type* type, OnyxToken* where);
 
 EMIT_FUNC(enter_structured_block,        StructuredBlockType sbt);
 EMIT_FUNC_NO_ARGS(leave_structured_block);
@@ -887,6 +888,87 @@ EMIT_FUNC(for_slice, AstFor* for_node, u64 iter_local) {
     *pcode = code;
 }
 
+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);
+    WIL(WI_LOCAL_SET, iterator_close_func);
+    WIL(WI_LOCAL_SET, iterator_next_func);
+    WIL(WI_LOCAL_SET, iterator_data_ptr);
+
+    AstLocal* var = for_node->var;
+    b32 it_is_local = (b32) ((iter_local & LOCAL_IS_WASM) != 0);
+    u64 offset = 0;
+
+    // Enter a deferred statement for the auto-close
+    
+    emit_enter_structured_block(mod, &code, SBT_Breakable_Block);
+    emit_enter_structured_block(mod, &code, SBT_Continue_Loop);
+
+    if (!it_is_local) emit_local_location(mod, &code, var, &offset);
+
+    {
+        WIL(WI_LOCAL_GET, iterator_data_ptr);
+        WIL(WI_LOCAL_GET, iterator_next_func);
+
+        // CLEANUP: Calling a function is way too f-ing complicated. FACTOR IT!!
+        u64 stack_top_idx = bh_imap_get(&mod->index_map, (u64) &builtin_stack_top);
+        
+        TypeWithOffset next_func_type;
+        type_linear_member_lookup(for_node->iter->type, 1, &next_func_type);
+        Type* return_type = next_func_type.type->Function.return_type;
+
+        u32 return_size = type_size_of(return_type);
+        u32 return_align = type_alignment_of(return_type);
+        bh_align(return_size, return_align);
+
+        u64 stack_grow_amm = return_size;
+        bh_align(stack_grow_amm, 16);
+        
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        WID(WI_PTR_CONST, stack_grow_amm);
+        WI(WI_PTR_ADD);
+        WID(WI_GLOBAL_SET, stack_top_idx);
+
+        i32 type_idx = generate_type_idx(mod, next_func_type.type);
+        WID(WI_CALL_INDIRECT, ((WasmInstructionData) { type_idx, 0x00 }));
+
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        WID(WI_PTR_CONST, stack_grow_amm);
+        WI(WI_PTR_SUB);
+        WID(WI_GLOBAL_SET, stack_top_idx);
+
+        WID(WI_GLOBAL_GET, stack_top_idx);
+        emit_load_instruction(mod, &code, return_type, stack_grow_amm - return_size);
+    }
+
+    WIL(WI_LOCAL_SET, iterator_done_bool);
+
+    if (!it_is_local) emit_store_instruction(mod, &code, var->type, offset);
+    else              WIL(WI_LOCAL_SET, iter_local);
+
+    WIL(WI_LOCAL_GET, iterator_done_bool);
+    WI(WI_I32_EQZ);
+    WID(WI_COND_JUMP, 0x01);
+
+    emit_block(mod, &code, for_node->stmt, 0);
+    WID(WI_JUMP, 0x00); 
+
+    emit_leave_structured_block(mod, &code);
+    emit_leave_structured_block(mod, &code);
+
+    // Flush deferred statements
+    
+    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);
+    *pcode = code;
+}
+
 EMIT_FUNC(for, AstFor* for_node) {
     bh_arr(WasmInstruction) code = *pcode;
 
@@ -897,14 +979,15 @@ EMIT_FUNC(for, AstFor* for_node) {
     emit_expression(mod, &code, for_node->iter);
 
     switch (for_node->loop_type) {
-        case For_Loop_Range:  emit_for_range(mod, &code, for_node, iter_local); break;
-        case For_Loop_Array:  emit_for_array(mod, &code, for_node, iter_local); break;
+        case For_Loop_Range:    emit_for_range(mod, &code, for_node, iter_local); break;
+        case For_Loop_Array:    emit_for_array(mod, &code, for_node, iter_local); break;
         // NOTE: A dynamic array is just a slice with a capacity and allocator on the end.
         // Just dropping the extra fields will mean we can just use the slice implementation.
         //                                                  - brendanfh   2020/09/04
         //                                                  - brendanfh   2021/04/13
-        case For_Loop_DynArr: WI(WI_DROP); WI(WI_DROP); WI(WI_DROP);
-        case For_Loop_Slice:  emit_for_slice(mod, &code, for_node, iter_local); break;
+        case For_Loop_DynArr:   WI(WI_DROP); WI(WI_DROP); WI(WI_DROP);
+        case For_Loop_Slice:    emit_for_slice(mod, &code, for_node, iter_local); break;
+        case For_Loop_Iterator: emit_for_iterator(mod, &code, for_node, iter_local); break;
         default: onyx_report_error(for_node->token->pos, "Invalid for loop type. You should probably not be seeing this...");
     }
 
@@ -1396,8 +1479,8 @@ EMIT_FUNC(intrinsic_call, AstCall* call) {
 
         case ONYX_INTRINSIC_ZERO_VALUE: {
             // NOTE: This probably will not have to make an allocation.
-            Type* zero_type = type_build_from_ast(context.ast_alloc, (AstType *) ((AstArgument *) call->args.values[0])->value);
-            emit_zero_value_for_type(mod, &code, zero_type);
+            Type* zero_type = type_build_from_ast(context.ast_alloc, (AstType *) ((AstArgument *) call->original_args.values[0])->value);
+            emit_zero_value_for_type(mod, &code, zero_type, call->token);
             break;
         }
 
@@ -2458,7 +2541,7 @@ EMIT_FUNC(zero_value, WasmType wt) {
     *pcode = code;
 }
 
-EMIT_FUNC(zero_value_for_type, Type* type) {
+EMIT_FUNC(zero_value_for_type, Type* type, OnyxToken* where) {
     bh_arr(WasmInstruction) code = *pcode;
     
     if (type_is_structlike_strict(type)) {
@@ -2467,11 +2550,20 @@ EMIT_FUNC(zero_value_for_type, Type* type) {
 
         fori (i, 0, mem_count) {
             type_linear_member_lookup(type, i, &two);
-            emit_zero_value_for_type(mod, &code, two.type);
+            emit_zero_value_for_type(mod, &code, two.type, where);
         }
 
-    } else {
+    }
+    else if (type->kind == Type_Kind_Function) {
+        // CLEANUP ROBUSTNESS: This should use the 'null_proc' instead of whatever is at
+        // function index 0.
+        WID(WI_I32_CONST, 0);
+    }
+    else {
         WasmType wt = onyx_type_to_wasm_type(type);
+        if (wt == WASM_TYPE_VOID) {
+            onyx_report_error(where->pos, "Cannot produce a zero-value for this type.");
+        }
         emit_zero_value(mod, &code, wt);
     }