Skip to content

Commit f99fe81

Browse files
Marlermarler8997
Marler
authored andcommitted
Add iterators module
1 parent d422d57 commit f99fe81

File tree

4 files changed

+204
-0
lines changed

4 files changed

+204
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ set(ZIG_STD_FILES
530530
"io.zig"
531531
"io/c_out_stream.zig"
532532
"io/seekable_stream.zig"
533+
"iterators.zig"
533534
"json.zig"
534535
"lazy_init.zig"
535536
"linked_list.zig"

std/iterators.zig

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
const builtin = @import("builtin");
2+
const std = @import("std");
3+
const debug = std.debug;
4+
5+
/// Given a pointer/array type, return the pointer type `[*]` variation.
6+
fn ArrayPointerType(comptime T: type) type {
7+
const typeInfo = @typeInfo(T);
8+
switch (typeInfo) {
9+
builtin.TypeId.Array => {
10+
return [*]const typeInfo.Array.child;
11+
},
12+
builtin.TypeId.Pointer => {
13+
if (typeInfo.Pointer.is_const)
14+
return [*]const typeInfo.Pointer.child;
15+
return [*]typeInfo.Pointer.child;
16+
},
17+
else => @compileError("Can't interpret this type as an ArrayPointerType"),
18+
}
19+
}
20+
21+
22+
23+
24+
fn ArrayIterator(comptime T: type) type {
25+
return struct {
26+
ptr: T,
27+
limit: T,
28+
pub fn initPointers(ptr: T, limit: T) @This() {
29+
return @This() {
30+
.ptr = ptr,
31+
.limit = limit,
32+
};
33+
}
34+
pub fn initSlice(array: SliceType(T)) @This() {
35+
return @This() {
36+
.ptr = array.ptr,
37+
.limit = array.ptr + array.len,
38+
};
39+
}
40+
pub fn next(self: *@This()) ?T.Child {
41+
if (self.ptr != self.limit) {
42+
self.ptr += 1;
43+
return (self.ptr - 1)[0];
44+
}
45+
return null;
46+
}
47+
};
48+
}
49+
fn arrayIterator(array: var) ArrayIterator(ArrayPointerType(@typeOf(array))) {
50+
const typeInfo = @typeInfo(@typeOf(array));
51+
switch (typeInfo) {
52+
builtin.TypeId.Array => {
53+
//@compileError("not implemented");
54+
return ArrayIterator([*]const typeInfo.Array.child).initSlice(array[0..]);
55+
},
56+
builtin.TypeId.Pointer => {
57+
if (typeInfo.Pointer.is_const)
58+
return ArrayIterator([*]const typeInfo.Pointer.child).initSlice(array);
59+
return ArrayIterator([*]typeInfo.Pointer.child).initSlice(array);
60+
},
61+
else => @compileError("arrayIterator does not accept this type"),
62+
}
63+
}
64+
65+
fn testIterator(expected: var, iterator: var) void {
66+
var expectedIndex : usize = 0;
67+
var mutableIterator = iterator;
68+
while (mutableIterator.next()) |actual| {
69+
debug.assert(expectedIndex < expected.len);
70+
debug.assert(expected[expectedIndex] == actual);
71+
expectedIndex += 1;
72+
}
73+
debug.assert(expectedIndex == expected.len);
74+
}
75+
76+
test "ArrayIterator" {
77+
testIterator("a", arrayIterator("a"));
78+
testIterator("abcd", arrayIterator("abcd"));
79+
testIterator([_]u8 {9,1,4}, arrayIterator([_]u8 {9,1,4}));
80+
}
81+
82+
// TODO: accept multiple arguments when language supports it
83+
fn ArgsIterator(comptime T: type) type {
84+
return struct {
85+
arg: T,
86+
nextIndex: usize,
87+
pub fn init(arg: T) @This() {
88+
return @This() {
89+
.arg = arg,
90+
.nextIndex = 0,
91+
};
92+
}
93+
pub fn next(self: *@This()) ?T {
94+
if (self.nextIndex < 1) {
95+
self.nextIndex += 1;
96+
//return self.arg[self.nextIndex - 1];
97+
return self.arg;
98+
}
99+
return null;
100+
}
101+
};
102+
}
103+
104+
/// Return an iterator that loops through the given arguments.
105+
/// TODO: accept multiple arguments when language supports it
106+
pub fn argsIterator(arg: var) ArgsIterator(@typeOf(arg)) {
107+
return ArgsIterator(@typeOf(arg)).init(arg);
108+
}
109+
110+
test "argsIterator" {
111+
testIterator([_]usize {0}, argsIterator(@intCast(usize, 0)));
112+
}
113+
114+
// TODO: accept 2 or more iterators once the language can support it
115+
fn ChainIterator(comptime T: type, comptime U: type) type {
116+
return struct {
117+
t: T,
118+
u: U,
119+
onU: bool,
120+
pub fn init(t: T, u: U) @This() {
121+
return @This() {
122+
.t = t,
123+
.u = u,
124+
.onU = false,
125+
};
126+
}
127+
pub fn next(self: *@This()) @typeOf(T.next).ReturnType {
128+
if (!self.onU) {
129+
if (self.t.next()) |tvalue| {
130+
return tvalue;
131+
}
132+
self.onU = true;
133+
}
134+
return self.u.next();
135+
}
136+
};
137+
}
138+
139+
/// Chain multiple iterators into one.
140+
/// TODO: accept 2 or more iterators once the language can support it
141+
pub fn chain(a: var, b: var) ChainIterator(@typeOf(a), @typeOf(b)) {
142+
return ChainIterator(@typeOf(a), @typeOf(b)).init(a, b);
143+
}
144+
145+
test "chain" {
146+
// testIterator(
147+
// {
148+
// var nextExpected : usize = 0;
149+
// var it = chain(argsIterator(@intCast(usize, 0)), argsIterator(@intCast(usize, 1)));
150+
// while (it.next()) |nextActual| {
151+
// debug.assert(nextExpected == nextActual);
152+
// nextExpected += 1;
153+
// }
154+
// }
155+
// {
156+
// var nextExpected : usize = 0;
157+
// var it = chain([_]usize {0,1,2}, argsIterator(@intCast(usize, 3)));
158+
// while (it.next()) |nextActual| {
159+
// debug.assert(nextExpected == nextActual);
160+
// nextExpected += 1;
161+
// }
162+
// }
163+
}

std/meta.zig

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,44 @@ test "std.meta.Child" {
117117
testing.expect(Child(promise->u8) == u8);
118118
}
119119

120+
/// Given an array/pointer type, return the slice type `[]Child`.
121+
pub fn SliceType(comptime T: type) type {
122+
return switch (@typeInfo(T)) {
123+
TypeId.Array => |info| []const info.child,
124+
TypeId.Pointer => |info| if (info.is_const) []const info.child else []info.child,
125+
else => @compileError("Expected pointer or array type, " ++ "found '" ++ @typeName(T) ++ "'"),
126+
};
127+
}
128+
129+
test "std.meta.SliceType" {
130+
testing.expect(SliceType([]u8) == []u8);
131+
testing.expect(SliceType([]const u8) == []const u8);
132+
testing.expect(SliceType(*u8) == []u8);
133+
testing.expect(SliceType(*const u8) == []const u8);
134+
testing.expect(SliceType([*]u8) == []u8);
135+
testing.expect(SliceType([*]const u8) == []const u8);
136+
testing.expect(SliceType([10]u8) == []const u8);
137+
}
138+
139+
/// Given an array/pointer type, return the pointer type `[*]Child`.
140+
fn ArrayPointerType(comptime T: type) type {
141+
return switch (@typeInfo(T)) {
142+
TypeId.Array => |info| [*]const info.child,
143+
TypeId.Pointer => |info| if (info.is_const) [*]const info.child else [*]info.child,
144+
else => @compileError("Expected pointer or array type, " ++ "found '" ++ @typeName(T) ++ "'"),
145+
};
146+
}
147+
148+
test "std.meta.ArrayPointerType" {
149+
testing.expect(ArrayPointerType([]u8) == [*]u8);
150+
testing.expect(ArrayPointerType([]const u8) == [*]const u8);
151+
testing.expect(ArrayPointerType(*u8) == [*]u8);
152+
testing.expect(ArrayPointerType(*const u8) == [*]const u8);
153+
testing.expect(ArrayPointerType([*]u8) == [*]u8);
154+
testing.expect(ArrayPointerType([*]const u8) == [*]const u8);
155+
testing.expect(ArrayPointerType([10]u8) == [*]const u8);
156+
}
157+
120158
pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
121159
return switch (@typeInfo(T)) {
122160
TypeId.Struct => |info| info.layout,

std/std.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub const hash_map = @import("hash_map.zig");
3939
pub const heap = @import("heap.zig");
4040
pub const http = @import("http.zig");
4141
pub const io = @import("io.zig");
42+
pub const iterators = @import("iterators.zig");
4243
pub const json = @import("json.zig");
4344
pub const lazyInit = @import("lazy_init.zig").lazyInit;
4445
pub const macho = @import("macho.zig");
@@ -92,6 +93,7 @@ test "std" {
9293
_ = @import("heap.zig");
9394
_ = @import("http.zig");
9495
_ = @import("io.zig");
96+
_ = @import("iterators.zig");
9597
_ = @import("json.zig");
9698
_ = @import("lazy_init.zig");
9799
_ = @import("macho.zig");

0 commit comments

Comments
 (0)