Skip to content

Instantly share code, notes, and snippets.

@alexnask
Last active April 16, 2018 13:29
Show Gist options
  • Select an option

  • Save alexnask/0f844931ab6a3d9ebd9868d9ccffd4ca to your computer and use it in GitHub Desktop.

Select an option

Save alexnask/0f844931ab6a3d9ebd9868d9ccffd4ca to your computer and use it in GitHub Desktop.
const std = @import("std");
const warn = std.debug.warn;
const TypeId = @import("builtin").TypeId;
fn Iterator(comptime T: type) type {
return struct {
const Self = this;
const ResetError = error {
NotResetable,
};
const IterType = T;
next_fn: fn(self: &Self) ?T,
reset_fn: ?fn(self: &Self) ResetError!void,
fn next(self: &Self) ?T {
return self.next_fn(self);
}
fn reset(self: &Self) !void {
const rfn = self.reset_fn ?? return error.NotResetable;
return rfn(self);
}
};
}
fn UnwrapArg(comptime ArgType: type) type {
if (@typeId(ArgType) == TypeId.Pointer) {
return ArgType.Child;
}
return ArgType;
}
fn Filter(comptime filter_fn: var) type {
const FnType = @typeOf(filter_fn);
if (@typeId(FnType) != TypeId.Fn) {
@compileError("Filter expects a function parameter.");
}
if (FnType.arg_count != 1) {
@compileError("Filter expects a function with one argument.");
}
if (FnType.ReturnType != bool) {
@compileError("Filter expects a function that returns bool.");
}
const T = UnwrapArg(@ArgType(FnType, 0));
const It = Iterator(T);
return struct {
const Self = this;
it: It,
input_it: &It,
fn init(input_it: &It) Self {
return Self {
.it = It {
.next_fn = next,
.reset_fn = reset,
},
.input_it = input_it,
};
}
fn next(it: &It) ?T {
const self = @fieldParentPtr(Self, "it", it);
while (self.input_it.next()) |*val| {
if (filter_fn(*val)) {
return *val;
}
}
return null;
}
fn reset(it: &It) !void {
const self = @fieldParentPtr(Self, "it", it);
return self.input_it.reset();
}
};
}
fn Map(comptime map_fn: var) type {
const FnType = @typeOf(map_fn);
if (@typeId(FnType) != TypeId.Fn) {
@compileError("Map expects a function parameter.");
}
if (FnType.arg_count != 1) {
@compileError("Map expects a function with one argument.");
}
const T = UnwrapArg(@ArgType(FnType, 0));
const V = FnType.ReturnType;
const InputIt = Iterator(T);
const OutputIt = Iterator(V);
return struct {
const Self = this;
it: OutputIt,
input_it: &InputIt,
fn init(input_it: &InputIt) Self {
return Self {
.it = OutputIt {
.next_fn = next,
.reset_fn = reset,
},
.input_it = input_it,
};
}
fn next(it: &OutputIt) ?V {
const self = @fieldParentPtr(Self, "it", it);
const val = self.input_it.next() ?? return null;
return map_fn(val);
}
fn reset(it: &OutputIt) !void {
const self = @fieldParentPtr(Self, "it", it);
return self.input_it.reset();
}
};
}
fn Range(comptime start: usize, comptime end: usize) type {
if (start > end) {
@compileError("Invalid range.");
}
const It = Iterator(usize);
return struct {
const Self = this;
it: It,
current: usize,
fn init() Self {
return Self {
.it = It {
.next_fn = next,
.reset_fn = reset,
},
.current = start,
};
}
fn next(it: &It) ?usize {
const self = @fieldParentPtr(Self, "it", it);
if (self.current >= end) {
return null;
}
var ret = self.current;
self.current += 1;
return ret;
}
fn reset(it: &It) It.ResetError!void {
const self = @fieldParentPtr(Self, "it", it);
self.current = start;
}
};
}
fn Repeat(comptime val: var, comptime times: i64) type {
const T = @typeOf(val);
const It = Iterator(T);
return struct {
const Self = this;
it: It,
current: if (times == -1) void else if (times > 0) i64 else unreachable,
fn init() Self {
return Self {
.it = It {
.next_fn = next,
.reset_fn = if (times == -1) null else reset,
},
.current = if (times == -1) {} else 0,
};
}
fn next(it: &It) ?T {
if (times > 0) {
const self = @fieldParentPtr(Self, "it", it);
if (self.current >= times) {
return null;
}
self.current += 1;
}
return val;
}
fn reset(it: &It) !void {
if (times > 0) {
const self = @fieldParentPtr(Self, "it", it);
self.current = 0;
}
return error.NotResetable;
}
};
}
fn addOne(x: usize) usize {
return x + 1;
}
fn isEven(x: usize) bool {
return x % 2 == 0;
}
fn consume(it: var) void {
while (it.next()) |*val| {
warn("Next value: {}\n", *val);
}
}
const Foo = struct {
data: usize,
};
const FooIterator = struct {
const Self = this;
const It = Iterator(Foo);
it: It,
current: usize,
fn init() Self {
return Self {
.it = It {
.next_fn = next,
.reset_fn = null,
},
.current = 0,
};
}
fn next(it: &It) ?Foo {
const self = @fieldParentPtr(Self, "it", it);
const current = self.current;
self.current += 1;
if (self.current > 30) {
return null;
}
return Foo {
.data = current,
};
}
};
fn filter_foo(foo: &const Foo) bool {
return isEven(foo.data);
}
pub fn main() !void {
var map_it = &Map(addOne).init(&Range(0, 10).init().it).it;
consume(map_it);
var filter_it = &Filter(isEven).init(map_it).it;
// Reset iterator
try filter_it.reset();
consume(filter_it);
var filter_foos = &Filter(filter_foo).init(&FooIterator.init().it).it;
while (filter_foos.next()) |*val| {
warn("Foo data: {}\n", val.data);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment