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) {
} -> 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
#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,
);
-#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;
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;
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(
}
});
+
+//
+// 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);
return initial_value;
}
+
+//
+// Returns how many times the `cond` was true.
count :: #match #local {}
#overload
return c;
}
+
+
+//
+// Returns if `cond` returned true for *any* yielded value.
some :: #match #local {}
#overload
return false;
}
+
+//
+// Returns if `cond` returned true for *all* yielded values.
every :: #match #local {}
#overload
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(
}
-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.
#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.
#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
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