re-wrote much of core.iter to be cleaner
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 7 Feb 2023 02:52:43 +0000 (20:52 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Tue, 7 Feb 2023 02:52:43 +0000 (20:52 -0600)
compiler/src/checker.c
compiler/src/symres.c
compiler/src/types.c
compiler/src/utils.c
core/container/iter.onyx
core/container/map.onyx

index 87f1bd14b0b66d21c5f8fc4bd58bbd7f0dded4a9..ae5cdbde0a1d5620b2d5dbb952591ab5288b265e 100644 (file)
@@ -2534,7 +2534,29 @@ CheckStatus check_overloaded_function(AstOverloadedFunction* ofunc) {
     }
 
     if (ofunc->expected_return_node) {
-        ofunc->expected_return_type = type_build_from_ast(context.ast_alloc, ofunc->expected_return_node);
+        AstType *expected_return_node = (AstType *) strip_aliases((AstNode *) ofunc->expected_return_node);
+
+        if (expected_return_node->kind == Ast_Kind_Poly_Struct_Type) {
+            //
+            // When you declare the expected return type of a #match'ed procedure to
+            // be a polymorphic structure type, a special case has to happen. By trying
+            // to build the type, the polymorphic structure will be given a type-id.
+            // type_build_from_ast() will never return a polymorphic structure type
+            // because that is never valid in the type system. However, we can by-pass
+            // this and look it up directly using type_lookup_by_id.
+            type_build_from_ast(context.ast_alloc, expected_return_node);
+
+            if (expected_return_node->type_id) {
+                ofunc->expected_return_type = type_lookup_by_id(expected_return_node->type_id);
+                
+                // Return early here because the following code does not work with a
+                // polymorphic expected return type.
+                bh_imap_free(&all_overloads);
+                return Check_Success;
+            }
+        }
+
+        ofunc->expected_return_type = type_build_from_ast(context.ast_alloc, expected_return_node);
         if (!ofunc->expected_return_type) YIELD(ofunc->token->pos, "Waiting to construct expected return type.");
 
         bh_arr_each(bh__imap_entry, entry, all_overloads.entries) {
index fbca2f71f4735e8a143a254069524299c9f7aedc..bbd01ace524731b27fe3b830d8db8dc71e0976dc 100644 (file)
@@ -1493,8 +1493,12 @@ static SymresStatus symres_process_directive(AstNode* directive) {
 
             Scope *scope = get_scope_from_node_or_create((AstNode *) inject->dest);
             if (scope == NULL) {
-                onyx_report_error(inject->token->pos, Error_Critical, "Cannot #inject here.");
-                return Symres_Error;
+                if (context.cycle_almost_detected >= 1) {
+                    onyx_report_error(inject->token->pos, Error_Critical, "Cannot #inject here.");
+                    return Symres_Error;
+                }
+
+                return Symres_Yield_Macro;
             }
 
             AstBinding *binding = onyx_ast_node_new(context.ast_alloc, sizeof(AstBinding), Ast_Kind_Binding);
index e0db986b6d7eba4ddfd22c0868ce633e5b1c202b..296bcaec4bb8652eba0a44a2d89a7111e176b622 100644 (file)
@@ -1068,11 +1068,16 @@ const char* type_get_name(Type* type) {
         case Type_Kind_Basic: return type->Basic.name;
         case Type_Kind_Pointer: return bh_aprintf(global_scratch_allocator, "^%s", type_get_name(type->Pointer.elem));
         case Type_Kind_Array: return bh_aprintf(global_scratch_allocator, "[%d] %s", type->Array.count, type_get_name(type->Array.elem));
+
+        case Type_Kind_PolyStruct:
+            return type->PolyStruct.name;
+
         case Type_Kind_Struct:
             if (type->Struct.name)
                 return type->Struct.name;
             else
                 return "<anonymous struct>";
+
         case Type_Kind_Enum:
             if (type->Enum.name)
                 return type->Enum.name;
index 608a63f2d614d046fec36e352c660a5bdfd16bfa..fe06c8dbf5c088cf345a9ebc96f2e0a589265940 100644 (file)
@@ -620,6 +620,19 @@ static TypeMatch ensure_overload_returns_correct_type_job(void *raw_data) {
     Type *return_type = func->type->Function.return_type;
     if (return_type == &type_auto_return) return TYPE_MATCH_YIELD;
 
+    // See the note about using Polymorphic Structures as expected return types,
+    // in check_overloaded_function().
+    if (expected_type->kind == Type_Kind_PolyStruct) {
+        if (   return_type->kind == Type_Kind_Struct
+            && return_type->Struct.constructed_from
+            && return_type->Struct.constructed_from->type_id == expected_type->id) {
+            return TYPE_MATCH_SUCCESS;
+        }
+
+        report_incorrect_overload_expected_type(return_type, expected_type, func->token, data->group);
+        return TYPE_MATCH_FAILED;
+    }
+
     if (!types_are_compatible(return_type, expected_type)) {
         report_incorrect_overload_expected_type(return_type, expected_type, func->token, data->group);
         return TYPE_MATCH_FAILED;
index f9bd4e9ecdb2c8bc638aef906067340586c98994..06cac02f9b2e0104e1872168f409d04b3d05e40c 100644 (file)
@@ -1,20 +1,75 @@
 package core.iter
 
 use core {memory, alloc, array, Pair}
+use core.intrinsics.types {type_is_struct}
+
+
+//
+// Iterator is a builtin type known by the compiler, as Iterators
+// can be used in for-loops natively without any translation.
+#inject Iterator {
+    filter :: filter;
+    map :: map;
+    zip :: zip;
+
+    take_one :: take_one;
+    take :: take;
+    take_while :: take_while;
+    skip :: skip;
+    skip_while :: skip_while;
+    
+    enumerate :: enumerate;
+
+    fold :: fold;
+    count :: count;
+    some :: some;
+    every :: every;
+}
 
-as_iter :: #match {}
 
+
+//
+// Thge standard function to convert something to an Iterator.
+// For-loops currently do not use this function to determine
+// how to iterate over something unknown, but that could be
+// a feature down the line.
+as_iter :: #match -> Iterator {}
+
+//
+// Helper interface to test if something can be passed to
+// as_iter successfully.
 Iterable :: interface (t: $T) {
     { as_iter(t) } -> Iterator;
 }
 
-close :: (it: Iterator($T)) {
+//
+// Helper function to get the next value out of an iterator.
+next :: (it: Iterator) -> (it.Iter_Type, bool) {
+    return it.next(it.data);
+}
+
+//
+// Helper function to close an iterator, if a close function
+// is defined.
+close :: (it: Iterator) {
     if it.close != null_proc {
         it.close(it.data);
     }
 }
 
-// Implicit iterator
+//
+// Implicit iterator creation
+//
+// The following overloads of as_iter all for an automatic
+// definition of how to declare an iterator, provided the
+// type has the necessary methods.
+//
+
+#overload #precedence 10000
+as_iter :: (x: ^$T/ImplicitIterator) => {
+    x->iter_open();
+    return generator_no_copy(x, T.iter_next, T.iter_close);
+}
 
 #local
 ImplicitIterator :: interface (t: $T) {
@@ -30,497 +85,284 @@ ImplicitIterator :: interface (t: $T) {
     } -> bool;
 }
 
-#overload #precedence 10000
-as_iter :: (x: ^$T/ImplicitIterator) => {
-    x->iter_open();
-    return generator_no_copy(x, T.iter_next, T.iter_close);
-}
-
-// nocheckin
-// Does it make sense for the language to have
-// the iterator "methods" as actual methods
-// on every iterator type? Or should you
-// just have to use the `iter.` variants
-// instead?
-#inject Iterator {
-    filter :: filter;
-    map :: map;
-    zip :: zip;
 
-    take_one :: take_one;
-    take :: take;
-    take_while :: take_while;
-    skip :: skip;
-    skip_while :: skip_while;
-    
-    enumerate :: enumerate;
+#overload #precedence 10000
+as_iter :: macro (x: $T/HasAsIter) => x->as_iter();
 
-    fold :: fold;
-    count :: count;
-    some :: some;
-    every :: every;
+#local
+HasAsIter :: interface (t: $T) {
+    { t->as_iter() } -> Iterator;
 }
 
 
-// Iterator Modifiers
 
+//
+// Iterator Transformers
+//
+// Most of these procedures come in two variants,
+// one that takes a context paramter, and one that does not.
+
+//
+// Only yields the values for which the predicate is true.
 filter :: #match #local {}
+
 #overload
-filter :: (it: Iterator($T), predicate: (T) -> bool, allocator := context.temp_allocator) -> Iterator(T) {
-    FilterIterator :: struct (T: type_expr) {
-        iterator:  Iterator(T);
-        predicate: (T) -> bool;
-        allocator: Allocator;
-    }
+filter :: (it: Iterator($T), predicate: (T) -> bool) =>
+    generator(
+        ^.{ iterator = it, predicate = predicate },
+
+        fi => {
+            value, cont := next(fi.iterator);
+            if cont {
+                while !fi.predicate(value) {
+                    value, cont = next(fi.iterator);
+                    if !cont do return value, false;
+                }
 
-    filter_iterator := new(FilterIterator(T), allocator=allocator);
-    filter_iterator.iterator = it;
-    filter_iterator.predicate = predicate;
-    filter_iterator.allocator = allocator;
-
-    next :: (fi: ^FilterIterator($T)) -> (T, bool) {
-        value, cont := fi.iterator.next(fi.iterator.data);
-        if cont {
-            while !fi.predicate(value) {
-                value, cont = fi.iterator.next(fi.iterator.data);
-                if !cont do return value, false;
+                return value, true;
             }
 
-            return value, true;
-        } else {
             return value, false;
-        }
-    }
-
-    close :: (fi: ^FilterIterator($T)) {
-        if fi.iterator.close != null_proc do fi.iterator.close(fi.iterator.data);
-        raw_free(fi.allocator, fi);
-    }
+        },
 
-    return .{
-        data  = filter_iterator,
-        next  = #solidify next  { T=T },
-        close = #solidify close { T=T },
-    };
-}
+        fi => { close(fi.iterator); });
 
 #overload
-filter :: (it: Iterator($T), ctx: $Ctx, predicate: (T, Ctx) -> bool, allocator := context.temp_allocator) -> Iterator(T) {
-    FilterIterator :: struct (T: type_expr, Ctx: type_expr) {
-        iterator:  Iterator(T);
-        predicate: (T, Ctx) -> bool;
-        ctx: Ctx;
-        allocator: Allocator;
-    }
+filter :: (it: Iterator($T), ctx: $Ctx, predicate: (T, Ctx) -> bool) =>
+    generator(
+        ^.{ iterator = it, predicate = predicate, ctx = ctx },
+
+        fi => {
+            value, cont := next(fi.iterator);
+            if cont {
+                while !fi.predicate(value, fi.ctx) {
+                    value, cont = next(fi.iterator);
+                    if !cont do return value, false;
+                }
 
-    filter_iterator := new(FilterIterator(T, Ctx), allocator=allocator);
-    filter_iterator.iterator = it;
-    filter_iterator.predicate = predicate;
-    filter_iterator.ctx = ctx;
-    filter_iterator.allocator = allocator;
-
-    next :: (fi: ^FilterIterator($T, $_)) -> (T, bool) {
-        value, cont := fi.iterator.next(fi.iterator.data);
-        if cont {
-            while !fi.predicate(value, fi.ctx) {
-                value, cont = fi.iterator.next(fi.iterator.data);
-                if !cont do return value, false;
+                return value, true;
             }
 
-            return value, true;
-        } else {
             return value, false;
-        }
-    }
+        },
 
-    close :: (fi: ^FilterIterator($T, $_)) {
-        if fi.iterator.close != null_proc do fi.iterator.close(fi.iterator.data);
-        raw_free(fi.allocator, fi);
-    }
+        fi => { close(fi.iterator); });
 
-    return .{
-        data  = filter_iterator,
-        next  = #solidify next  { T=T, _=Ctx },
-        close = #solidify close { T=T, _=Ctx },
-    };
-}
 
+//
+// Transforms every value that comes out of an iterator
+// using the transform function.
 map :: #match #local {}
 
 #overload
-map :: (it: Iterator($T), transform: (T) -> $R, allocator := context.temp_allocator) -> Iterator(R) {
-    MapIterator :: struct (T: type_expr, R: type_expr) {
-        iterator:  Iterator(T);
-        transform: (T) -> R;
-        allocator: Allocator;
-    }
+map :: (it: Iterator($T), transform: (T) -> $R) =>
+    generator(
+        ^.{ iterator = it, transform = transform },
 
-    map_iterator := new(MapIterator(T, R), allocator=allocator);
-    map_iterator.iterator = it;
-    map_iterator.transform = transform;
-    map_iterator.allocator = allocator;
-
-    next :: (mi: ^MapIterator($T, $R)) -> (R, bool) {
-        value, cont := mi.iterator.next(mi.iterator.data);
-        if !cont do return .{}, false;
-
-        return mi.transform(value), true;
-    }
+        mi => {
+            value, cont := next(mi.iterator);
+            if cont {
+                return mi.transform(value), true;
+            }
 
-    close :: (mi: ^MapIterator($T, $R)) {
-        if mi.iterator.close != null_proc do mi.iterator.close(mi.iterator.data);
-        raw_free(mi.allocator, mi);
-    }
+            return .{}, false;
+        },
 
-    return .{
-        data  = map_iterator,
-        next  = #solidify next  { T=T, R=R },
-        close = #solidify close { T=T, R=R },
-    };
-}
+        mi => { close(mi.iterator); })
 
 #overload
-map :: (it: Iterator($T), ctx: $Ctx, transform: (T, Ctx) -> $R, allocator := context.temp_allocator) -> Iterator(R) {
-    MapIterator :: struct (T: type_expr, R: type_expr, Ctx: type_expr) {
-        iterator:  Iterator(T);
-        transform: (T, Ctx) -> R;
-        ctx: Ctx;
-        allocator: Allocator;
-    }
-
-    map_iterator := new(MapIterator(T, R, Ctx), allocator=allocator);
-    map_iterator.iterator = it;
-    map_iterator.transform = transform;
-    map_iterator.ctx = ctx;
-    map_iterator.allocator = allocator;
-
-    next :: (mi: ^MapIterator($T, $R, $Ctx)) -> (R, bool) {
-        value, cont := mi.iterator.next(mi.iterator.data);
-        if !cont do return .{}, false;
+map :: (it: Iterator($T), ctx: $Ctx, transform: (T, Ctx) -> $R) =>
+    generator(
+        ^.{ iterator = it, transform = transform, ctx = ctx },
 
-        return mi.transform(value, mi.ctx), true;
-    }
+        mi => {
+            value, cont := next(mi.iterator);
+            if cont {
+                return mi.transform(value, mi.ctx), true;
+            }
 
-    close :: (mi: ^MapIterator($T, $R, $Ctx)) {
-        if mi.iterator.close != null_proc do mi.iterator.close(mi.iterator.data);
-        raw_free(mi.allocator, mi);
-    }
+            return .{}, false;
+        },
 
-    return .{
-        data  = map_iterator,
-        next  = #solidify next  { T=T, R=R, Ctx=Ctx },
-        close = #solidify close { T=T, R=R, Ctx=Ctx },
-    };
-}
+        mi => { close(mi.iterator); })
 
-take_one :: (it: Iterator($T), no_close := false) -> (T, bool) {
-    ret, cont := it.next(it.data);
-    if !cont && !no_close {
-        if it.close != null_proc do it.close(it.data);
-    }
-    return ret, cont;
-}
 
-// Macro that allows you to extract elements from an iterator in a simple way:
-//
-// value: i32;
-// iterator: Iterator(i32) = ...;
 //
-// if #(value) << iterator {
-//     ...iterater closed...
-// }
-#operator << macro (dest: Code, it: Iterator($T)) -> bool {
-    take_one :: take_one
-
-    cont: bool;
-    (#unquote dest), cont = take_one(it);
-    return !cont;
-}
-
-take :: (it: Iterator($T), count: u32, allocator := context.temp_allocator) -> Iterator(T) {
-    TakeIterator :: struct (T: type_expr) {
-        iterator:  Iterator(T);
-        remaining: u32;
-        allocator: Allocator;
-    }
-
-    take_iterator := new(TakeIterator(T), allocator=allocator);
-    take_iterator.iterator = it;
-    take_iterator.remaining = count;
-    take_iterator.allocator = allocator;
-
-    next :: ($T: type_expr, ti: ^TakeIterator(T)) -> (T, bool) {
-        if ti.remaining == 0 do return .{}, false;
-
-        ti.remaining -= 1;
-        return ti.iterator.next(ti.iterator.data);
-    }
+// Only yields the first `count` values, then closes.
+take :: (it: Iterator($T), count: u32) -> Iterator(T) {
+    return generator(
+        ^.{ iterator = it, remaining = count },
 
-    close :: ($T: type_expr, ti: ^TakeIterator(T)) {
-        if ti.iterator.close != null_proc do ti.iterator.close(ti.iterator.data);
-        raw_free(ti.allocator, ti);
-    }
+        ti => {
+            if ti.remaining > 0 {
+                ti.remaining -= 1;
+                return next(ti.iterator);
+            }
+            
+            return .{}, false;
+        },
 
-    return .{
-        data  = take_iterator,
-        next  = #solidify next  { T=T },
-        close = #solidify close { T=T },
-    };
+        ti => { close(ti.iterator); });
 }
 
-take_while :: (it: Iterator($T), predicate: (T) -> bool, allocator := context.temp_allocator) -> Iterator(T) {
-    TakeIterator :: struct (T: type_expr) {
-        iterator:  Iterator(T);
-        predicate: (T) -> bool;
-        allocator: Allocator;
-    }
 
-    take_iterator := new(TakeIterator(T), allocator=allocator);
-    take_iterator.iterator = it;
-    take_iterator.predicate = predicate;
-    take_iterator.allocator = allocator;
+//
+// Yields values while the predicate returns true.
+take_while :: (it: Iterator($T), predicate: (T) -> bool) -> Iterator(T) {
+    return generator(
+        ^.{ iterator = it, predicate = predicate },
 
-    next :: ($T: type_expr, ti: ^TakeIterator(T)) -> (T, bool) {
-        value, cont := ti.iterator.next(ti.iterator.data);
-        if !cont do return value, false;
+        ti => {
+            value, cont := next(ti.iterator);
+            if !cont do return value, false;
 
-        return value, ti.predicate(value);
-    }
-
-    close :: ($T: type_expr, ti: ^TakeIterator(T)) {
-        if ti.iterator.close != null_proc do ti.iterator.close(ti.iterator.data);
-        raw_free(ti.allocator, ti);
-    }
+            return value, ti.predicate(value);
+        },
 
-    return .{
-        data  = take_iterator,
-        next  = #solidify next  { T=T },
-        close = #solidify close { T=T },
-    };
+        ti => { close(ti.iterator); });
 }
 
-skip :: (it: Iterator($T), count: u32, allocator := context.temp_allocator) -> Iterator(T) {
-    SkipIterator :: struct (T: type_expr) {
-        iterator: Iterator(T);
-        to_skip:  i32;
-        skipped:  bool = false;
-        allocator: Allocator;
-    }
 
-    skip_iterator := new(SkipIterator(T), allocator=allocator);
-    skip_iterator.iterator = it;
-    skip_iterator.to_skip = count;
-    skip_iterator.allocator = allocator;
+//
+// Discards the first `count` values and yields all remaining values,
+skip :: (it: Iterator($T), count: u32) -> Iterator(T) {
+    return generator(
+        ^.{ iterator = it, to_skip = count, skipped = false },
 
-    next :: (si: ^SkipIterator($T)) -> (T, bool) {
-        while !si.skipped && si.to_skip > 0 {
-            si.to_skip -= 1;
-            value, cont := si.iterator.next(si.iterator.data);
+        si => {
+            while !si.skipped && si.to_skip > 0 {
+                si.to_skip -= 1;
+                value, cont := next(si.iterator);
 
-            if !cont {
-                si.skipped = true;
-                return value, false;
+                if !cont {
+                    si.skipped = true;
+                    return value, false;
+                }
             }
-        }
-
-        return si.iterator.next(si.iterator.data);
-    }
 
-    close :: (si: ^SkipIterator($T)) {
-        if si.iterator.close != null_proc do si.iterator.close(si.iterator.data);
-        raw_free(si.allocator, si);
-    }
+            return next(si.iterator);
+        },
 
-    return .{
-        data  = skip_iterator,
-        next  = #solidify next  { T=T },
-        close = #solidify close { T=T },
-    };
+        si => { close(si.iterator); });
 }
 
+
+//
+// Discards values while the predicate is true, then yields all values.
 skip_while :: #match #local {}
 
 #overload
-skip_while :: (it: Iterator($T), predicate: (T) -> bool, allocator := context.temp_allocator) -> Iterator(T) {
-    SkipIterator :: struct (T: type_expr) {
-        iterator: Iterator(T);
-        allocator: Allocator;
-        predicate: (T) -> bool;
-        skipped := false;
-    }
-
-    skip_iterator := new(SkipIterator(T), allocator=allocator);
-    skip_iterator.iterator = it;
-    skip_iterator.allocator = allocator;
-    skip_iterator.predicate = predicate;
+skip_while :: (it: Iterator($T), predicate: (T) -> bool) -> Iterator(T) {
+    return generator(
+        ^.{ iterator = it, predicate = predicate, skipped = false },
 
-    next :: (si: ^SkipIterator($T)) -> (T, bool) {
-        while !si.skipped {
-            value, cont := si.iterator.next(si.iterator.data);
+        si => {
+            while !si.skipped {
+                value, cont := next(si.iterator);
 
-            if !cont {
-                si.skipped = true;
-                return value, false;
-            }
+                if !cont {
+                    si.skipped = true;
+                    return value, false;
+                }
 
-            if !si.predicate(value) {
-                si.skipped = true;
-                return value, true;
+                if !si.predicate(value) {
+                    si.skipped = true;
+                    return value, true;
+                }
             }
-        }
-
-        return si.iterator.next(si.iterator.data);
-    }
 
-    close :: (si: ^SkipIterator($T)) {
-        if si.iterator.close != null_proc do si.iterator.close(si.iterator.data);
-        raw_free(si.allocator, si);
-    }
+            return next(si.iterator);
+        },
 
-    return .{
-        data  = skip_iterator,
-        next  = #solidify next  { T=T },
-        close = #solidify close { T=T },
-    };
+        si => { close(si.iterator); });
 }
 
 #overload
-skip_while :: (it: Iterator($T), ctx: $Ctx, predicate: (T, Ctx) -> bool, allocator := context.temp_allocator) -> Iterator(T) {
-    SkipIterator :: struct (T: type_expr, Ctx: type_expr) {
-        iterator: Iterator(T);
-        allocator: Allocator;
-        predicate: (T, Ctx) -> bool;
-        ctx: Ctx;
-        skipped := false;
-    }
+skip_while :: (it: Iterator($T), ctx: $Ctx, predicate: (T, Ctx) -> bool) -> Iterator(T) {
+    return generator(
+        ^.{ iterator = it, ctx = ctx, predicate = predicate },
 
-    skip_iterator := new(SkipIterator(T, Ctx), allocator=allocator);
-    skip_iterator.iterator = it;
-    skip_iterator.allocator = allocator;
-    skip_iterator.predicate = predicate;
-    skip_iterator.ctx = ctx;
+        si => {
+            while !si.skipped {
+                value, cont := next(si.iterator);
 
-    next :: (si: ^SkipIterator($T, $Ctx)) -> (T, bool) {
-        while !si.skipped {
-            value, cont := si.iterator.next(si.iterator.data);
+                if !cont {
+                    si.skipped = true;
+                    return value, false;
+                }
 
-            if !cont {
-                si.skipped = true;
-                return value, false;
+                if !si.predicate(value, si.ctx) {
+                    si.skipped = true;
+                    return value, true;
+                }
             }
 
-            if !si.predicate(value, si.ctx) {
-                si.skipped = true;
-                return value, true;
-            }
-        }
-
-        return si.iterator.next(si.iterator.data);
-    }
-
-    close :: (si: ^SkipIterator($T, $Ctx)) {
-        if si.iterator.close != null_proc do si.iterator.close(si.iterator.data);
-        raw_free(si.allocator, si);
-    }
+            return next(si.iterator);
+        },
 
-    return .{
-        data  = skip_iterator,
-        next  = #solidify next  { T=T, Ctx=Ctx },
-        close = #solidify close { T=T, Ctx=Ctx },
-    };
+        si => { close(si.iterator); });
 }
 
-zip :: (left_iterator: Iterator($T), right_iterator: Iterator($R), allocator := context.temp_allocator) -> Iterator(Pair(T, R)) {
-    ZippedIterator :: struct (T: type_expr, R: type_expr) {
-        iterator1: Iterator(T);
-        iterator2: Iterator(R);
-        allocator: Allocator;
-    }
-
-    zipped_iterator := new(ZippedIterator(T, R), allocator=allocator);
-    zipped_iterator.iterator1 = left_iterator;
-    zipped_iterator.iterator2 = right_iterator;
-    zipped_iterator.allocator = allocator;
 
-    next :: (zi: ^ZippedIterator($T, $R)) -> (Pair(T, R), bool) {
-        v1, cont1 := zi.iterator1.next(zi.iterator1.data);
-        v2, cont2 := zi.iterator2.next(zi.iterator2.data);
+//
+// Combines two iterators into one by yielding a Pair of
+// the value from each of the iterators.
+zip :: (left_iterator: Iterator($T), right_iterator: Iterator($R)) -> Iterator(Pair(T, R)) {
+    return generator(
+        ^.{ left_iter = left_iterator, right_iter = right_iterator },
 
-        return .{ v1, v2 }, cont1 && cont2;
-    }
+        zi => {
+            v1, cont1 := next(zi.left_iter);
+            v2, cont2 := next(zi.right_iter);
 
-    close :: (zi: ^ZippedIterator($T, $R)) {
-        if zi.iterator1.close != null_proc do zi.iterator1.close(zi.iterator1.data);
-        if zi.iterator2.close != null_proc do zi.iterator2.close(zi.iterator2.data);
-        raw_free(zi.allocator, zi);
-    }
+            return Pair.make(v1, v2), cont1 && cont2;
+        },
 
-    return .{
-        data  = zipped_iterator,
-        next  = #solidify next  { T=T, R=R },
-        close = #solidify close { T=T, R=R },
-    };
+        zi => { close(zi.left_iter); close(zi.right_iter); });
 }
 
-concat :: (iters: ..Iterator($T)) -> Iterator(T) {
-    Context :: struct (T: type_expr) {
-        iters: [] Iterator(T);
-        idx:   u32;
-    }
 
-    c := new(Context(T), allocator=context.temp_allocator);
-    c.iters = memory.copy_slice(iters, allocator=context.temp_allocator);
-    c.idx = 0;
+//
+// Combines iterators by first yielding all values from
+// one, then yielding all values from the next, and so on.
+concat :: (iters: ..Iterator($T)) -> Iterator(T) {
+    return generator(
+        ^.{
+            iters = memory.copy_slice(iters, context.temp_allocator),
+            idx = 0
+        },
 
-    next :: (use c: ^Context($T)) -> (T, bool) {
-        while true {
-            if idx >= iters.count do return .{}, false;        
+        c => {
+            while c.idx < c.iters.count {
+                curr_iter := c.iters[c.idx];
+                value, valid := next(curr_iter);
+                if valid {
+                    return value, true;
+                }
 
-            curr_iter := ^iters[idx];
-            value, valid := curr_iter.next(curr_iter.data);
-            if valid {
-                return value, true;
+                c.idx += 1;
             }
 
-            idx += 1;
-        }
-    }
-
-    close :: (use c: ^Context($T)) {
-        // I don't feel like this should always close ALL the iterators...
-        // But I don't know what the semantics should be for specifying which
-        // if any iterators to close.
-        for^ iters {
-            if it.close != null_proc do it.close(it.data);
-        }
-    }
-
-    return .{
-        c,
-        #solidify next {T=T},
-        #solidify close {T=T}
-    };
+            return .{}, false;
+        },
+        
+        c => {
+            for^ c.iters {
+                close(*it);
+            }
+        });
 }
 
+//
+// Yields the same value indefinitely. Useful with iter.zip.
 const :: (value: $T) -> Iterator(T) {
-    next :: (data: ^$T) -> (T, bool) {
-        return *(cast(^T) data), true;
-    }
-
-    allocated := cast(^T) raw_alloc(context.temp_allocator, sizeof T);
-    *allocated = value;
-
-    return .{
-        data = allocated,
-        next = #solidify next { T=T },
-    };
+    return generator(^.{ v = value }, c => (c.v, true));
 }
 
-#local Enumeration_Value :: struct (T: type_expr) {
-    index: i32;
-    value: T;
-}
 
+//
+// Yields a value that contains: 1) the value from the iterator,
+// and 2) an incrementing integer.
 enumerate :: #match #local {}
 
 #overload
@@ -529,36 +371,63 @@ enumerate :: macro (it: $T/Iterable, start_index: i32 = 0) =>
 
 #overload
 enumerate :: (it: Iterator($T), start_index: i32 = 0) -> Iterator(Enumeration_Value(T)) {
-    Enumeration_Context :: struct (T: type_expr) {
-        iterator:      Iterator(T);
-        current_index: i32;
-    }
+    return generator(
+        ^.{ iterator = it, current_index = start_index },
 
-    ec := make(Enumeration_Context(T), allocator=context.temp_allocator);
-    ec.iterator = it;
-    ec.current_index = start_index;
+        ec => {
+            value, cont := next(ec.iterator);
+            if cont {
+                defer ec.current_index += 1;
+                return Enumeration_Value(typeof value).{ current_index, value }, true;
+            }
 
-    next :: (use data: ^Enumeration_Context($T)) -> (Enumeration_Value(T), bool) {
-        value, cont := iterator.next(iterator.data);
+            return .{}, false;
+        },
 
-        if !cont do return .{ current_index, .{} }, false;
+        ec => { close(ec.iterator); });
+}
 
-        defer current_index += 1;
-        return .{ current_index, value }, true;
-    }
+#local Enumeration_Value :: struct (T: type_expr) {
+    index: i32;
+    value: T;
+}
 
-    close :: (use data: ^Enumeration_Context($T)) {
-        if iterator.close != null_proc do iterator.close(iterator.data);
-    }
 
-    return .{
-        data  = ec,
-        next  = #solidify next  { T = T },
-        close = #solidify close { T = T },
-    };
+
+//
+// Extract the next value out of an iterator. Closes it when
+// the iterator is out of values, if no_close is false.
+take_one :: (it: Iterator($T), no_close := false) -> (T, bool) {
+    ret, cont := next(it);
+    if !cont && !no_close { close(it); }
+    return ret, cont;
 }
 
-use core.intrinsics.types {type_is_struct}
+// Macro that allows you to extract elements from an iterator in a simple way:
+//
+// value: i32;
+// iterator: Iterator(i32) = ...;
+//
+// if #(value) << iterator {
+//     ...iterater closed...
+// }
+#operator << macro (dest: Code, it: Iterator($T)) -> bool {
+    take_one :: take_one
+
+    cont: bool;
+    (#unquote dest), cont = take_one(it);
+    return !cont;
+}
+
+
+//
+// Iterator creations
+//
+// Sensible defaults for creating an iterator out of primitive types.
+//
+
+#overload
+as_iter :: from_array
 
 //
 // `from_array` has two almost identical implementations,
@@ -600,11 +469,11 @@ from_array :: (arr: [] $T) => generator(
 );
 
 
-#overload
-as_iter :: from_array
-
-#overload
-as_iter :: (x: ^[..] $T) -> Iterator(T) {
+//
+// Iterators created from pointers to dynamic arrays are
+// special, because they support the #remove directive.
+#local
+generic_dynamic_array_as_iter :: (x: ^[..] $T, $access: Code, $return_type: type_expr) => {
     Context :: struct (T: type_expr) {
         arr: ^[..] T;
         current: u32;
@@ -614,10 +483,10 @@ as_iter :: (x: ^[..] $T) -> Iterator(T) {
     c.arr = x;
     c.current = 0;
 
-    next :: (use _: ^Context($T)) -> (T, bool) {
+    next :: (use _: ^Context($T), $access: Code) => {
         if current < arr.count {
             defer current += 1;
-            return arr.data[current], true;
+            return (#unquote access), true;
 
         } else {
             return .{}, false;
@@ -632,48 +501,27 @@ as_iter :: (x: ^[..] $T) -> Iterator(T) {
         current -= 1;
     }
 
-    return .{
+    return return_type.{
         data  = c,
-        next  = #solidify next { T = T },
+        next  = #solidify next { T = T, access = access },
         remove = #solidify remove { T = T },
     };
 }
 
-#overload
-as_iter :: (x: ^[..] $T, by_pointer: bool) -> Iterator(^T) {
-    Context :: struct (T: type_expr) {
-        arr: ^[..] T;
-        current: u32;
-    }
-
-    c := make(Context(T), allocator=context.temp_allocator);
-    c.arr = x;
-    c.current = 0;
 
-    next :: (use _: ^Context($T)) -> (^T, bool) {
-        if current < arr.count {
-            defer current += 1;
-            return ^arr.data[current], true;
+#overload
+as_iter :: macro (x: ^[..] $T) => {
+    G :: generic_dynamic_array_as_iter
+    return G(x, #(arr.data[current]), Iterator(T));
+}
 
-        } else {
-            return null, false;
-        }
-    }
+#overload
+as_iter :: macro (x: ^[..] $T, by_pointer: bool) => {
+    G :: generic_dynamic_array_as_iter
+    return G(x, #(^arr.data[current]), Iterator(^T));
+}
 
-    remove :: (use _: ^Context($T)) {
-        //
-        // This is current - 1 because current will have already
-        // been incremented by the time this element calls #remove.
-        array.delete(arr, current - 1);
-        current -= 1;
-    }
 
-    return .{
-        data  = c,
-        next  = #solidify next { T = T },
-        remove = #solidify remove { T = T },
-    };
-}
 
 #overload
 as_iter :: (r: range) => generator(
@@ -697,9 +545,17 @@ as_iter :: (r: range) => generator(
         }
     });
 
+
+//
+// Iterator reducing
+//
+
+//
+// Incremently calls `combine` on the yielded value and the
+// accumulated value, producing a new accumulated value. Returns
+// the final accumulated value.
 fold :: #match #local {}
 
-// @Cleanup // some way to shorten this would be nice
 #overload
 fold :: macro (it: $T/Iterable, init: $R, combine: $S) =>
     #this_package.fold(#this_package.as_iter(it), init, combine);
@@ -713,6 +569,9 @@ fold :: (it: Iterator($T), initial_value: $R, combine: (T, R) -> R) -> R {
     return initial_value;
 }
 
+
+//
+// Returns how many times the `cond` was true.
 count :: #match #local {}
 
 #overload
@@ -726,6 +585,10 @@ count :: (it: Iterator($T), cond: (T) -> bool) -> i32 {
     return c;
 }
 
+
+
+//
+// Returns if `cond` returned true for *any* yielded value.
 some :: #match #local {}
 
 #overload
@@ -738,6 +601,9 @@ some :: (it: Iterator($T), cond: (T) -> bool) -> bool {
     return false;
 }
 
+
+//
+// Returns if `cond` returned true for *all* yielded values.
 every :: #match #local {}
 
 #overload
@@ -750,8 +616,38 @@ every :: (it: Iterator($T), cond: (T) -> bool) -> bool {
     return true;
 }
 
-prod :: (x: $I1/Iterable, y: $I2/Iterable) => {
-    y_iter   := as_iter(y);
+
+//
+// Places all yielded values into a dynamically allocated array,
+// using the allocator provided (context.allocator by default).
+to_array :: (it: Iterator($T), allocator := context.allocator) -> [..] T {
+    arr := array.make(T, allocator=allocator);
+    for v: it do array.push(^arr, v);
+
+    return arr;
+}
+
+
+//
+// Produces an iterator that first yields all values from the
+// first iterable, combined with the first yield value from the
+// second iterable. Then, steps the second iterable, and repeats.
+//
+// For example,
+//
+//      iter.prod(1 .. 4, 1 .. 3)
+//
+// Would yield:
+//      (1, 1), (2, 1), (3, 1), (1, 2), (2, 2), (3, 2)
+prod :: #match #local {}
+
+#overload
+prod :: macro (x: $I/Iterable, y: $I2/Iterable) => {
+    return #this_package.prod(x, #this_package.as_iter(y));
+}
+
+#overload
+prod :: (x: $I1/Iterable, y_iter: Iterator(y)) => {
     y_val, _ := take_one(y_iter);
 
     return generator(
@@ -782,13 +678,6 @@ prod :: (x: $I1/Iterable, y: $I2/Iterable) => {
 }
 
 
-to_array :: (it: Iterator($T), allocator := context.allocator) -> [..] T {
-    arr := array.make(T, allocator=allocator);
-    for v: it do array.push(^arr, v);
-
-    return arr;
-}
-
 //
 // Simple iterator comprehensions, in the same vein
 // as Pythons comprehension syntax.
@@ -825,6 +714,10 @@ comp :: macro (i: $I/Iterable, value: Code) =>
     #this_package.comp(#this_package.as_iter(i), value);
 
 
+//
+// Using the polymorph solving system, you can write type
+// free versions of arbitrary iterators. This is used
+// heavily by many of the functions defined above.
 //
 // Maybe at some point an alternate allocator would be good
 // for this? For now, I think the temporary allocator is sufficient.
@@ -868,6 +761,9 @@ generator_no_copy :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool), close: (^Ctx) -> vo
 #if runtime.Multi_Threading_Enabled {
     #local sync :: core.sync
 
+    // A simple iterator transformer that protects
+    // the retrieving of the next value by using
+    // a mutex, making the iterator thread-safe.
     distributor :: #match #local {}
 
     #overload
@@ -911,6 +807,14 @@ generator_no_copy :: (ctx: ^$Ctx, gen: (^Ctx) -> ($T, bool), close: (^Ctx) -> vo
         return .{c, #solidify next {T=it.Iter_Type}, #solidify close {T=it.Iter_Type}};
     }
 
+    //
+    // Allows you to easily write a parallelized for-loop over an iterator.
+    // For example,
+    //
+    //     iter.parallel_for(1 .. 100, 4, ^.{}) {
+    //         printf("Thread {} has {}!\n", context.thread_id, it);
+    //     }
+    //
     parallel_for :: #match #local {}
 
     #overload
index d3dbf7d775d60af7175dccb6415e083be311fb9a..daeb19b67999a2f07c510d4a513df7e72e801472 100644 (file)
@@ -3,18 +3,7 @@ package core.map
 use core {array, hash, memory, math, conv, Optional}
 use core.intrinsics.onyx { __initialize }
 
-#local ValidKey :: interface (t: $T) {
-    // In order to use a certain type as a key in a Map, you must
-    // provide an implementation of core.hash.to_u32() for that type,
-    // and you must provide an operator overload for ==.
-
-    { hash.to_u32(t) } -> u32;
-    { t == t         } -> bool;
-}
-
-#tag conv.Custom_Format.{
-    #solidify format_map {K=Key_Type, V=Value_Type}
-}
+@conv.Custom_Format.{ #solidify format_map {K=Key_Type, V=Value_Type} }
 Map :: struct (Key_Type: type_expr, Value_Type: type_expr) where ValidKey(Key_Type) {
     allocator : Allocator;
 
@@ -32,6 +21,15 @@ Map :: struct (Key_Type: type_expr, Value_Type: type_expr) where ValidKey(Key_Ty
     }
 }
 
+#local ValidKey :: interface (t: $T) {
+    // In order to use a certain type as a key in a Map, you must
+    // provide an implementation of core.hash.to_u32() for that type,
+    // and you must provide an operator overload for ==.
+
+    { hash.to_u32(t) } -> u32;
+    { t == t         } -> bool;
+}
+
 #inject Map {
     init    :: init
     has     :: has
@@ -47,14 +45,15 @@ Map :: struct (Key_Type: type_expr, Value_Type: type_expr) where ValidKey(Key_Ty
     as_iter :: as_iter
 }
 
+
+#match __make_overload macro (x: ^Map($K, $V), allocator := context.allocator) => #this_package.make(K, V);
+
 make :: ($Key: type_expr, $Value: type_expr, default := Value.{}) -> Map(Key, Value) {
     map : Map(Key, Value);
     init(^map, default = default);
     return map;
 }
 
-#match __make_overload macro (x: ^Map($K, $V), allocator := context.allocator) => core.map.make(K, V);
-
 init :: (use map: ^Map($K, $V), default := V.{}) {
     __initialize(map);
 
@@ -67,13 +66,13 @@ init :: (use map: ^Map($K, $V), default := V.{}) {
     array.init(^entries, allocator=allocator);
 }
 
+#match builtin.delete core.map.free
+
 free :: (use map: ^Map) {
     if hashes.data != null  do memory.free_slice(^hashes, allocator=allocator);
     if entries.data != null do array.free(^entries);
 }
 
-#match builtin.delete core.map.free
-
 put :: (use map: ^Map, key: map.Key_Type, value: map.Value_Type) {
     lr := lookup(map, key);
 
@@ -100,10 +99,6 @@ get :: (use map: ^Map, key: map.Key_Type) -> map.Value_Type {
     return default_value;
 }
 
-#operator []  macro (map: Map($K, $V), key: K) -> V do return core.map.get(^map, key);
-#operator []= macro (map: Map($K, $V), key: K, value: V) do core.map.put(^map, key, value);
-#operator ^[] macro (map: Map($K, $V), key: K) -> ^V do return core.map.get_ptr(^map, key);
-
 get_ptr :: (use map: ^Map, key: map.Key_Type) -> ^map.Value_Type {
     lr := lookup(map, key);
     if lr.entry_index >= 0 do return ^entries[lr.entry_index].value;
@@ -132,15 +127,15 @@ delete :: (use map: ^Map, key: map.Key_Type) {
 
     array.fast_delete(^entries, lr.entry_index);
     last := lookup(map, entries[lr.entry_index].key);
+
     if last.entry_prev >= 0    do entries[last.entry_prev].next = lr.entry_index;
     else                       do hashes[last.hash_index] = lr.entry_index;
 }
 
 update :: macro (map: ^Map, key: map.Key_Type, body: Code) {
-    // @Hack // Weird hack because 'lookup' exists at file scope.
     lookup_ :: lookup
-
     lr := lookup_(map, key);
+
     if lr.entry_index >= 0 {
         it := ^map.entries[lr.entry_index].value;
         #unquote body;
@@ -189,13 +184,11 @@ literal :: ($Key: type_expr, $Value: type_expr, values: [] MapLiteralValue(Key,
     return m;
 }
 
-
-#overload core.iter.as_iter as_iter
 as_iter :: (m: ^Map) =>
     core.iter.generator(
         ^.{ m = m, i = 0 },
 
-        (ctx) => {
+        ctx => {
             if ctx.i >= ctx.m.entries.count {
                 return (typeof ctx.m.entries.data).{}, false;
             }
@@ -204,6 +197,11 @@ as_iter :: (m: ^Map) =>
             return ^ctx.m.entries.data[ctx.i], true;
         });
 
+
+#operator []  macro (map: Map($K, $V), key: K) -> V      { return #this_package.get(^map, key); }
+#operator ^[] macro (map: Map($K, $V), key: K) -> ^V     { return #this_package.get_ptr(^map, key); }
+#operator []= macro (map: Map($K, $V), key: K, value: V) { #this_package.put(^map, key, value); }
+
 //
 // Private symbols
 //
@@ -220,7 +218,7 @@ as_iter :: (m: ^Map) =>
         if hashes.data == null do init(map);
         lr := MapLookupResult.{};
 
-        hash_value: u32 = hash.to_u32(key); // You cannot use this type for the key, unless you add an overload.
+        hash_value: u32 = hash.to_u32(key);
         lr.hash = hash_value;
 
         lr.hash_index = hash_value % hashes.count;