├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── build.zig ├── fun.zig ├── fun ├── comptime_dynamic_typing.zig ├── functional.zig ├── handle.zig ├── interface.zig ├── iterators.zig └── reify.zig └── src ├── bits.zig ├── generic.zig ├── generic ├── compare.zig └── slice.zig ├── loop.zig ├── match.zig ├── math.zig ├── math ├── interval.zig └── safe.zig ├── parser.zig ├── parser ├── common.zig ├── json.zig └── string.zig ├── platform.zig ├── struct.zig └── union.zig /.gitattributes: -------------------------------------------------------------------------------- 1 | *.zig text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: push 3 | jobs: 4 | test: 5 | strategy: 6 | matrix: 7 | os: [ubuntu-latest, macos-latest] 8 | runs-on: ${{matrix.os}} 9 | steps: 10 | - uses: actions/checkout@v1 11 | with: 12 | submodules: recursive 13 | - uses: goto-bus-stop/setup-zig@v1.0.0 14 | with: 15 | version: 0.6.0 16 | - run: zig build 17 | lint: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v1 21 | - uses: goto-bus-stop/setup-zig@v1.0.0 22 | with: 23 | version: 0.6.0 24 | - run: zig fmt --check . 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jimmi Holst Christensen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fun-with-zig 2 | 3 | `fun-with-zig` is a Zig library full of random utilities. 4 | 5 | ## Why? 6 | 7 | The project started as just me playing with the features of the Zig language and its features in 8 | unconventional ways. This then lead to a few utilites that where actually useful as libraries. 9 | 10 | Now, this repo works by first playing with something in the `fun` directory. Nothing in that directory 11 | is actually useful, as files might just play around with the oddities of Zig, or the file might be 12 | just an idea with no user friendly api. 13 | 14 | If something in `fun` then turns into anything useful, it'll be moved to `src` and an effort will 15 | be made to finish up the idea, testing it, and providing a nice api. 16 | 17 | Files from `src` might even be moved into its own repo if the ideas a file or files explore 18 | become bigger standalone concepts. 19 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const builtin = @import("builtin"); 2 | const std = @import("std"); 3 | 4 | const Builder = std.build.Builder; 5 | const Mode = builtin.Mode; 6 | 7 | pub fn build(b: *Builder) void { 8 | const test_all_step = b.step("test", "Run all tests in all modes."); 9 | inline for ([_]Mode{ Mode.Debug, Mode.ReleaseFast, Mode.ReleaseSafe, Mode.ReleaseSmall }) |test_mode| { 10 | const mode_str = comptime modeToString(test_mode); 11 | 12 | const t = b.addTest("fun.zig"); 13 | t.setBuildMode(test_mode); 14 | t.setNamePrefix(mode_str ++ " "); 15 | 16 | const test_step = b.step("test-" ++ mode_str, "Run all tests in " ++ mode_str ++ "."); 17 | test_step.dependOn(&t.step); 18 | test_all_step.dependOn(test_step); 19 | } 20 | 21 | const fmt_step = b.addFmt(&[_][]const u8{ 22 | "build.zig", 23 | "fun", 24 | "fun.zig", 25 | "src", 26 | }); 27 | 28 | b.default_step.dependOn(&fmt_step.step); 29 | b.default_step.dependOn(test_all_step); 30 | } 31 | 32 | fn modeToString(mode: Mode) []const u8 { 33 | return switch (mode) { 34 | .Debug => "debug", 35 | .ReleaseFast => "release-fast", 36 | .ReleaseSafe => "release-safe", 37 | .ReleaseSmall => "release-small", 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /fun.zig: -------------------------------------------------------------------------------- 1 | pub const @"struct" = @import("src/struct.zig"); 2 | pub const @"union" = @import("src/union.zig"); 3 | pub const ascii = @import("src/ascii.zig"); 4 | pub const bits = @import("src/bits.zig"); 5 | pub const generic = @import("src/generic.zig"); 6 | pub const loop = @import("src/loop.zig"); 7 | pub const match = @import("src/match.zig"); 8 | pub const math = @import("src/math.zig"); 9 | pub const parser = @import("src/parser.zig"); 10 | pub const platform = @import("src/platform.zig"); 11 | pub const searcher = @import("src/searcher.zig"); 12 | 13 | test "fun-with-zig" { 14 | _ = @import("fun/comptime_dynamic_typing.zig"); 15 | _ = @import("fun/functional.zig"); 16 | _ = @import("fun/handle.zig"); 17 | _ = @import("fun/interface.zig"); 18 | _ = @import("fun/iterators.zig"); 19 | _ = @import("fun/reify.zig"); 20 | 21 | // These tests break the compiler, so I'll just leave them off 22 | //_ = @"struct"; 23 | //_ = parser; 24 | 25 | _ = @"union"; 26 | _ = bits; 27 | _ = generic; 28 | _ = loop; 29 | _ = match; 30 | _ = math; 31 | _ = platform; 32 | } 33 | -------------------------------------------------------------------------------- /fun/comptime_dynamic_typing.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | 4 | const Opaque = @OpaqueType(); 5 | 6 | pub const Dynamic = struct { 7 | v: *const Opaque, 8 | Type: type, 9 | 10 | pub fn init(comptime Type: type, v: *const Type) Dynamic { 11 | return Dynamic{ 12 | .v = @ptrCast(*const Opaque, v), 13 | .Type = Type, 14 | }; 15 | } 16 | 17 | // TODO: Change to pass-by-value 18 | pub fn value(comptime dyn: *const Dynamic) dyn.Type { 19 | return @ptrCast(*const dyn.Type, dyn.v).*; 20 | } 21 | 22 | // TODO: Change to pass-by-value 23 | pub fn field(comptime dyn: *const Dynamic, comptime field_name: []const u8) (@TypeOf(@field(dyn.Type{}, field_name))) { 24 | return @field(dyn.value(), field_name); 25 | } 26 | 27 | // TODO: Change to pass-by-value 28 | pub fn call(comptime dyn: *const Dynamic, args: ...) dyn.Type.ReturnType { 29 | return switch (args.len) { 30 | 0 => dyn.value()(), 31 | 1 => dyn.value()(args[0]), 32 | 2 => dyn.value()(args[0], args[1]), 33 | 3 => dyn.value()(args[0], args[1], args[2]), 34 | 4 => dyn.value()(args[0], args[1], args[2], args[3]), 35 | 5 => dyn.value()(args[0], args[1], args[2], args[3], args[4]), 36 | 6 => dyn.value()(args[0], args[1], args[2], args[3], args[4], args[5]), 37 | 7 => dyn.value()(args[0], args[1], args[2], args[3], args[4], args[5], args[6]), 38 | 8 => dyn.value()(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]), 39 | 9 => dyn.value()(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]), 40 | else => comptime unreachable, 41 | }; 42 | } 43 | }; 44 | 45 | test "Dynamic.value" { 46 | comptime { 47 | var a: u8 = 0; 48 | var b: f32 = 1.0; 49 | var c: []const u8 = "Hello World!"; 50 | const dyn_int = Dynamic.init(u8, &a); 51 | const dyn_float = Dynamic.init(f32, &b); 52 | const dyn_string = Dynamic.init([]const u8, &c); 53 | 54 | // They are all the same static type, just like in dynamic typed languages 55 | testing.expectEqual(@TypeOf(dyn_int), @TypeOf(dyn_float)); 56 | testing.expectEqual(@TypeOf(dyn_int), @TypeOf(dyn_string)); 57 | testing.expectEqual(@TypeOf(dyn_float), @TypeOf(dyn_string)); 58 | 59 | // Their values, are not the same dynamic type though. 60 | testing.expect(@TypeOf(dyn_int.value()) != @TypeOf(dyn_float.value())); 61 | testing.expect(@TypeOf(dyn_int.value()) != @TypeOf(dyn_string.value())); 62 | testing.expect(@TypeOf(dyn_float.value()) != @TypeOf(dyn_string.value())); 63 | 64 | testing.expectEqual(dyn_int.value(), 0); 65 | testing.expectEqual(dyn_float.value(), 1.0); 66 | testing.expectEqualSlices(u8, "Hello World!", dyn_string.value()); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /fun/functional.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | 4 | /// Composes two functions at compile time. 5 | pub fn compose( 6 | comptime X: type, 7 | comptime Y: type, 8 | comptime Z: type, 9 | comptime f: fn (X) Y, 10 | comptime g: fn (Y) Z, 11 | ) fn (X) Z { 12 | return struct { 13 | fn composed(x: X) Y { 14 | return g(f(x)); 15 | } 16 | }.composed; 17 | } 18 | 19 | fn firstHalf(s: []const u8) []const u8 { 20 | return s[0 .. s.len / 2]; 21 | } 22 | 23 | fn secondHalf(s: []const u8) []const u8 { 24 | return s[s.len / 2 ..]; 25 | } 26 | 27 | test "functional.Example: functional.compose" { 28 | const mem = @import("std").mem; 29 | const str = "12345678"; 30 | const firstOneForth = compose([]const u8, []const u8, []const u8, firstHalf, firstHalf); 31 | const secondOneForth = compose([]const u8, []const u8, []const u8, firstHalf, secondHalf); 32 | const thirdOneForth = compose([]const u8, []const u8, []const u8, secondHalf, firstHalf); 33 | const forthOneForth = compose([]const u8, []const u8, []const u8, secondHalf, secondHalf); 34 | 35 | testing.expectEqualSlices(u8, "12", firstOneForth(str)); 36 | testing.expectEqualSlices(u8, "34", secondOneForth(str)); 37 | testing.expectEqualSlices(u8, "56", thirdOneForth(str)); 38 | testing.expectEqualSlices(u8, "78", forthOneForth(str)); 39 | } 40 | 41 | pub fn reverse(comptime X: type, comptime Y: type, comptime f: fn (X, X) Y) fn (X, X) Y { 42 | return struct { 43 | fn reversed(a: X, b: X) Y { 44 | return f(b, a); 45 | } 46 | }.reversed; 47 | } 48 | 49 | pub fn reverseSimple(comptime T: type, comptime f: fn (T, T) T) fn (T, T) T { 50 | return reverse(T, T, f); 51 | } 52 | 53 | fn lt(a: i32, b: i32) bool { 54 | return a < b; 55 | } 56 | 57 | test "functional.Example: functional.reverse" { 58 | const sort = @import("std").sort; 59 | const mem = @import("std").mem; 60 | 61 | var iarr = [_]i32{ 5, 3, 1, 2, 4 }; 62 | sort.sort(i32, iarr[0..], comptime reverse(i32, bool, lt)); 63 | 64 | testing.expectEqualSlices(i32, &[_]i32{ 5, 4, 3, 2, 1 }, &iarr); 65 | } 66 | -------------------------------------------------------------------------------- /fun/handle.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | 4 | pub fn OpaqueHandle(comptime T: type, comptime hack_around_comptime_cache: type) type { 5 | return packed struct { 6 | // We could store this variable as a @IntType(false, @sizeOf(T) * 8) 7 | // but we lose the exact size in bits this way. If we had @sizeOfBits, 8 | // this would work better. 9 | ____________: T, 10 | 11 | pub fn init(v: T) @This() { 12 | return @This(){ .____________ = v }; 13 | } 14 | 15 | pub fn cast(self: @This()) T { 16 | return self.____________; 17 | } 18 | }; 19 | } 20 | 21 | test "OpaqueHandle" { 22 | const A = OpaqueHandle(u64, @OpaqueType()); 23 | const B = OpaqueHandle(u64, @OpaqueType()); 24 | testing.expect(A != B); 25 | const a = A.init(10); 26 | const b = B.init(10); 27 | testing.expectEqual(a.cast(), b.cast()); 28 | } 29 | -------------------------------------------------------------------------------- /fun/interface.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const debug = std.debug; 4 | const mem = std.mem; 5 | const testing = std.testing; 6 | 7 | pub const Self = @OpaqueType(); 8 | 9 | pub fn Interface(comptime T: type) type { 10 | const info = @typeInfo(T).Struct; 11 | const VTable = struct { 12 | funcs: [info.decls.len]fn () void, 13 | 14 | fn init(comptime Funcs: type, comptime State: type) @This() { 15 | var res: @This() = undefined; 16 | 17 | inline for (info.decls) |def, i| { 18 | const DefType = @field(T, def.name); 19 | comptime debug.assert(@TypeOf(DefType) == type); 20 | 21 | const func_info = @typeInfo(DefType).Fn; 22 | comptime debug.assert(func_info.args[0].arg_type.? == *Self); 23 | 24 | const Type = FnType(State, func_info.args[1..], func_info.return_type.?); 25 | const func: Type = @field(Funcs, def.name); 26 | res.funcs[i] = @ptrCast(fn () void, func); 27 | } 28 | 29 | return res; 30 | } 31 | 32 | fn dispatch(vtable: @This(), comptime fn_name: []const u8, self: *Self, args: var) @field(T, fn_name).ReturnType { 33 | inline for (info.decls) |decl, i| { 34 | if (comptime !mem.eql(u8, decl.name, fn_name)) 35 | continue; 36 | 37 | const func = @ptrCast(@field(T, decl.name), vtable.funcs[i]); 38 | return switch (args.len) { 39 | 0 => func(self), 40 | 1 => func(self, args[0]), 41 | 2 => func(self, args[0], args[1]), 42 | 3 => func(self, args[0], args[1], args[2]), 43 | else => comptime unreachable, 44 | }; 45 | } 46 | 47 | comptime unreachable; 48 | } 49 | 50 | fn FnType(comptime State: type, comptime args: []const builtin.TypeInfo.FnArg, comptime Return: type) type { 51 | return switch (args.len) { 52 | 0 => fn (*State) Return, 53 | 1 => fn (*State, args[0].arg_type.?) Return, 54 | 2 => fn (*State, args[0].arg_type.?, args[1].arg_type.?) Return, 55 | 3 => fn (*State, args[0].arg_type.?, args[1].arg_type.?, args[2].arg_type.?) Return, 56 | else => comptime unreachable, 57 | }; 58 | } 59 | }; 60 | 61 | return struct { 62 | state: *Self, 63 | vtable: *const VTable, 64 | 65 | pub fn init(comptime State: type, state: *State) @This() { 66 | return initWithFuncs(State, state, State); 67 | } 68 | 69 | pub fn initWithFuncs(comptime State: type, state: *State, comptime Funcs: type) @This() { 70 | return @This(){ 71 | .state = @ptrCast(*Self, state), 72 | .vtable = &comptime VTable.init(Funcs, State), 73 | }; 74 | } 75 | 76 | fn call(self: @This(), comptime fn_name: []const u8, args: var) @field(T, fn_name).ReturnType { 77 | return self.vtable.dispatch(fn_name, self.state, args); 78 | } 79 | }; 80 | } 81 | 82 | const Sb = struct { 83 | b: u8, 84 | 85 | fn a(self: *Sb, v: u8) u8 { 86 | return self.b + v; 87 | } 88 | }; 89 | 90 | const Sq = struct { 91 | q: u8, 92 | 93 | fn a(self: *Sq, v: u8) u8 { 94 | return self.q * v; 95 | } 96 | }; 97 | 98 | const IA = Interface(struct { 99 | const a = fn (*Self, u8) u8; 100 | }); 101 | 102 | test "interface" { 103 | var sb = Sb{ .b = 3 }; 104 | var sq = Sq{ .q = 3 }; 105 | const ib = IA.init(Sb, &sb); 106 | const iq = IA.init(Sq, &sq); 107 | testing.expectEqual(@as(u8, 5), ib.call("a", .{@as(u8, 2)})); 108 | testing.expectEqual(@as(u8, 6), iq.call("a", .{@as(u8, 2)})); 109 | } 110 | -------------------------------------------------------------------------------- /fun/iterators.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const debug = std.debug; 3 | const mem = std.mem; 4 | const testing = std.testing; 5 | 6 | // TODO: If we get functions with capture one day, then "comptime nextFn fn (&Context) ?Result" wont work, because then those 7 | // can't be used. We could store the function in the iterator. The iterator will then just grow a little in size every 8 | // time you construct iterators from other iterators. 9 | 10 | /// A generic iterator which uses ::nextFn to iterate over a ::TContext. 11 | pub fn Iterator(comptime TContext: type, comptime TResult: type, comptime nextFn: fn (*TContext) ?TResult) type { 12 | return struct { 13 | const Result = TResult; 14 | const Context = TContext; 15 | const Self = @This(); 16 | 17 | context: Context, 18 | 19 | pub fn init(context: Context) Self { 20 | return Self{ .context = context }; 21 | } 22 | 23 | pub fn next(it: *Self) ?Result { 24 | return nextFn(&it.context); 25 | } 26 | 27 | /// NOTE: append we can do with no allocations, because we can make a new iterator type 28 | /// and store the last element in its context. 29 | pub fn append(it: Self, item: Result) void { 30 | comptime @panic("TODO: Implement append!"); 31 | } 32 | 33 | pub fn concat(it: Self, other: var) ConcatIterator(@TypeOf(other)) { 34 | const OtherIterator = @TypeOf(other); 35 | return ConcatIterator(OtherIterator).init(IteratorPair(OtherIterator){ .it1 = it, .it2 = other }); 36 | } 37 | 38 | pub fn intersect(it: Self, other: var) void { 39 | comptime @panic("TODO: Implement except!"); 40 | } 41 | 42 | /// NOTE: prepend we can do with no allocations, because we can make a new iterator type 43 | /// and store the last element in its context. 44 | pub fn prepend(it: Self, item: Result) void { 45 | comptime @panic("TODO: Implement append!"); 46 | } 47 | 48 | pub fn select(it: Self, comptime SelectResult: type, comptime selector: fn (Result) SelectResult) SelectIterator(SelectResult, selector) { 49 | return SelectIterator(SelectResult, selector).init(it.context); 50 | } 51 | 52 | pub fn skip(it: Self, count: u64) Self { 53 | var res = it; 54 | var i = @as(u64, 0); 55 | while (i < count) : (i += 1) { 56 | _ = res.next() orelse return res; 57 | } 58 | 59 | return res; 60 | } 61 | 62 | pub fn take(it: Self, count: u64) TakeIterator() { 63 | return TakeIterator().init(TakeContext{ .it = it, .count = count }); 64 | } 65 | 66 | pub fn where(it: Self, comptime predicate: fn (Result) bool) WhereIterator(predicate) { 67 | return WhereIterator(predicate).init(it.context); 68 | } 69 | 70 | pub fn zip(it: Self, other: var) ZipIterator(@TypeOf(other)) { 71 | const OtherIterator = @TypeOf(other); 72 | return ZipIterator(OtherIterator).init(IteratorPair(OtherIterator){ .it1 = it, .it2 = other }); 73 | } 74 | 75 | fn IteratorPair(comptime OtherIterator: type) type { 76 | return struct { 77 | it1: Self, 78 | it2: OtherIterator, 79 | }; 80 | } 81 | 82 | fn ConcatIterator(comptime OtherIterator: type) type { 83 | const OtherResult = @TypeOf(OtherIterator.next).ReturnType.Child; 84 | 85 | return Iterator(IteratorPair(OtherIterator), Result, struct { 86 | fn whereNext(context: *IteratorPair(OtherIterator)) ?Result { 87 | return context.it1.next() orelse { 88 | return context.it2.next(); 89 | }; 90 | } 91 | }.whereNext); 92 | } 93 | 94 | fn SelectIterator(comptime SelectResult: type, comptime selector: fn (Result) SelectResult) type { 95 | return Iterator(Context, SelectResult, struct { 96 | fn selectNext(context: *Context) ?SelectResult { 97 | const item = nextFn(context) orelse return null; 98 | return selector(item); 99 | } 100 | }.selectNext); 101 | } 102 | 103 | const TakeContext = struct { 104 | it: Self, 105 | count: u64, 106 | }; 107 | 108 | fn TakeIterator() type { 109 | return Iterator(TakeContext, Result, struct { 110 | fn takeNext(context: *TakeContext) ?Result { 111 | if (context.count == 0) return null; 112 | 113 | context.count -= 1; 114 | return context.it.next(); 115 | } 116 | }.takeNext); 117 | } 118 | 119 | fn WhereIterator(comptime predicate: fn (Result) bool) type { 120 | return Iterator(Context, Result, struct { 121 | fn whereNext(context: *Context) ?Result { 122 | while (nextFn(context)) |item| { 123 | if (predicate(item)) return item; 124 | } 125 | 126 | return null; 127 | } 128 | }.whereNext); 129 | } 130 | 131 | fn ZipIterator(comptime OtherIterator: type) type { 132 | const OtherResult = @TypeOf(OtherIterator.next).ReturnType.Child; 133 | const ZipPair = struct { 134 | first: Result, 135 | second: OtherResult, 136 | }; 137 | 138 | return Iterator(IteratorPair(OtherIterator), ZipPair, struct { 139 | fn whereNext(context: *IteratorPair(OtherIterator)) ?ZipPair { 140 | const first = context.it1.next() orelse return null; 141 | const second = context.it2.next() orelse return null; 142 | return ZipPair{ .first = first, .second = second }; 143 | } 144 | }.whereNext); 145 | } 146 | }; 147 | } 148 | 149 | pub fn SliceIterator(comptime T: type) type { 150 | const NextFn = struct { 151 | fn next(context: *[]const T) ?T { 152 | if (context.len != 0) { 153 | defer context.* = (context.*)[1..]; 154 | return (context.*)[0]; 155 | } 156 | 157 | return null; 158 | } 159 | }; 160 | 161 | return Iterator([]const T, T, NextFn.next); 162 | } 163 | 164 | pub fn SliceMutableIterator(comptime T: type) type { 165 | const NextFn = struct { 166 | fn next(context: *[]T) ?*T { 167 | if (context.len != 0) { 168 | defer context.* = (context.*)[1..]; 169 | return &(context.*)[0]; 170 | } 171 | 172 | return null; 173 | } 174 | }; 175 | 176 | return Iterator([]T, *T, NextFn.next); 177 | } 178 | 179 | fn RangeIterator(comptime T: type) type { 180 | const RangeContext = struct { 181 | current: T, 182 | count: T, 183 | step: T, 184 | }; 185 | const NextFn = struct { 186 | fn next(context: *RangeContext) ?T { 187 | if (context.count == 0) return null; 188 | defer context.count -= 1; 189 | defer context.current += context.step; 190 | return context.current; 191 | } 192 | }; 193 | 194 | return Iterator(RangeContext, T, NextFn.next); 195 | } 196 | 197 | pub fn range(comptime T: type, start: T, count: T, step: T) RangeIterator(T) { 198 | const Context = RangeIterator(T).Context; 199 | return RangeIterator(T).init(Context{ .current = start, .count = count, .step = step }); 200 | } 201 | 202 | fn RepeatIterator(comptime T: type) type { 203 | const NextFn = struct { 204 | fn next(context: *T) ?T { 205 | return context.*; 206 | } 207 | }; 208 | 209 | return Iterator(T, T, NextFn.next); 210 | } 211 | 212 | pub fn repeat(comptime T: type, v: T) RepeatIterator(T) { 213 | return RepeatIterator(T).init(v); 214 | } 215 | 216 | fn EmptyIterator(comptime T: type) type { 217 | const NextFn = struct { 218 | fn next(context: *u8) ?T { 219 | return null; 220 | } 221 | }; 222 | 223 | // TODO: The context can't be "void" because of https://github.com/zig-lang/zig/issues/838 224 | return Iterator(u8, T, NextFn.next); 225 | } 226 | 227 | pub fn empty(comptime T: type) EmptyIterator(T) { 228 | return EmptyIterator(T).init(0); 229 | } 230 | 231 | pub fn aggregate(it: var, func: fn (@TypeOf(it).Result, @TypeOf(it).Result) @TypeOf(it).Result) ?@TypeOf(it).Result { 232 | return aggregateAcc(it, it.next() orelse return null, @TypeOf(it.*).Result, func); 233 | } 234 | 235 | pub fn aggregateAcc(it: var, acc: var, func: fn (@TypeOf(acc), @TypeOf(it).Result) @TypeOf(acc)) ?@TypeOf(acc) { 236 | var _it = it; 237 | var result = acc; 238 | while (_it.next()) |item| { 239 | result = func(result, item); 240 | } 241 | 242 | return result; 243 | } 244 | 245 | pub fn all(it: var, predicate: fn (@TypeOf(it).Result) bool) bool { 246 | var _it = it; 247 | while (_it.next()) |item| { 248 | if (!predicate(item)) return false; 249 | } 250 | 251 | return true; 252 | } 253 | 254 | pub fn any(it: var, predicate: fn (@TypeOf(it).Result) bool) bool { 255 | var _it = it; 256 | while (_it.next()) |item| { 257 | if (predicate(item)) return true; 258 | } 259 | 260 | return false; 261 | } 262 | 263 | pub fn countIf(it: var, predicate: fn (@TypeOf(it).Result) bool) u64 { 264 | var _it = it; 265 | var res = @as(u64, 0); 266 | while (_it.next()) |item| { 267 | if (predicate(item)) res += 1; 268 | } 269 | 270 | return res; 271 | } 272 | 273 | test "iterators.SliceIterator" { 274 | const data = "abacad"; 275 | var it = SliceIterator(u8).init(data); 276 | 277 | var i = @as(usize, 0); 278 | while (it.next()) |item| : (i += 1) { 279 | testing.expectEqual(data[i], item); 280 | } 281 | 282 | testing.expectEqual(data.len, i); 283 | } 284 | 285 | test "iterators.SliceMutableIterator" { 286 | var data = "abacac".*; 287 | const res = "bcbdbd"; 288 | var it = SliceMutableIterator(u8).init(data[0..]); 289 | 290 | while (it.next()) |item| { 291 | item.* += 1; 292 | } 293 | 294 | testing.expectEqualSlices(u8, &data, res); 295 | } 296 | 297 | test "iterators.range" { 298 | const res = "abcd"; 299 | var it = range(u8, 'a', 4, 1); 300 | 301 | var i = @as(usize, 0); 302 | while (it.next()) |item| : (i += 1) { 303 | testing.expectEqual(res[i], item); 304 | } 305 | 306 | testing.expectEqual(res.len, i); 307 | } 308 | 309 | test "iterators.repeat" { 310 | var it = repeat(u64, 3); 311 | 312 | var i = @as(usize, 0); 313 | while (it.next()) |item| : (i += 1) { 314 | testing.expectEqual(@as(u64, 3), item); 315 | if (i == 10) break; 316 | } 317 | } 318 | 319 | test "iterators.empty" { 320 | var it = empty(u8); 321 | var i = @as(usize, 0); 322 | while (it.next()) |item| : (i += 1) { 323 | testing.expect(false); 324 | } 325 | 326 | testing.expectEqual(@as(usize, 0), i); 327 | } 328 | 329 | test "iterators.aggregateAcc" { 330 | const data = "abacad"; 331 | const countA = struct { 332 | fn f(acc: u64, char: u8) u64 { 333 | return acc + @boolToInt(char == 'a'); 334 | } 335 | }.f; 336 | 337 | testing.expectEqual(@as(u64, 3), aggregateAcc(SliceIterator(u8).init(data), @as(u64, 0), countA).?); 338 | } 339 | 340 | test "iterators.all" { 341 | const data1 = "aaaa"; 342 | const data2 = "abaa"; 343 | const isA = struct { 344 | fn f(char: u8) bool { 345 | return char == 'a'; 346 | } 347 | }.f; 348 | 349 | testing.expect(all(SliceIterator(u8).init(data1), isA)); 350 | testing.expect(!all(SliceIterator(u8).init(data2), isA)); 351 | } 352 | 353 | test "iterators.any" { 354 | const data1 = "bbbb"; 355 | const data2 = "bbab"; 356 | const isA = struct { 357 | fn f(char: u8) bool { 358 | return char == 'a'; 359 | } 360 | }.f; 361 | 362 | testing.expect(!any(SliceIterator(u8).init(data1), isA)); 363 | testing.expect(any(SliceIterator(u8).init(data2), isA)); 364 | } 365 | 366 | test "iterators.countIf" { 367 | const data = "abab"; 368 | const isA = struct { 369 | fn f(char: u8) bool { 370 | return char == 'a'; 371 | } 372 | }.f; 373 | 374 | testing.expectEqual(@as(usize, 2), countIf(SliceIterator(u8).init(data), isA)); 375 | } 376 | 377 | test "iterators.SliceIterator" { 378 | const data = "abc"; 379 | 380 | var it = SliceIterator(u8).init(data); 381 | var i: usize = 0; 382 | while (it.next()) |item| : (i += 1) { 383 | testing.expectEqual(data[i], item); 384 | } 385 | 386 | testing.expectEqual(data.len, i); 387 | } 388 | 389 | test "iterators.append: TODO" {} 390 | 391 | test "iterators.concat" { 392 | const data1 = "abc"; 393 | const data2 = "defg"; 394 | 395 | const it1 = SliceIterator(u8).init(data1); 396 | const it2 = SliceIterator(u8).init(data2); 397 | var concatted = it1.concat(it2); 398 | 399 | var i: usize = 0; 400 | while (concatted.next()) |item| : (i += 1) { 401 | if (i < data1.len) 402 | testing.expectEqual(data1[i], item) 403 | else 404 | testing.expectEqual(data2[i - data1.len], item); 405 | } 406 | 407 | testing.expectEqual(data1.len + data2.len, i); 408 | } 409 | 410 | test "iterators.except: TODO" {} 411 | 412 | test "iterators.intersect: TODO" {} 413 | 414 | test "iterators.prepend: TODO" {} 415 | 416 | test "iterators.select" { 417 | const data = [_]f64{ 1.5, 2.5, 3.5 }; 418 | const toI64 = struct { 419 | fn f(i: f64) i64 { 420 | return @floatToInt(i64, i); 421 | } 422 | }.f; 423 | 424 | var it = SliceIterator(f64).init(&data).select(i64, toI64); 425 | 426 | var i: usize = 0; 427 | while (it.next()) |item| : (i += 1) { 428 | testing.expectEqual(@floatToInt(i64, data[i]), item); 429 | } 430 | 431 | testing.expectEqual(data.len, i); 432 | } 433 | 434 | test "iterators.skip" { 435 | const data = "abcd"; 436 | const res = "cd"; 437 | 438 | var it = SliceIterator(u8).init(data).skip(2); 439 | 440 | var i: usize = 0; 441 | while (it.next()) |item| : (i += 1) { 442 | testing.expectEqual(res[i], item); 443 | } 444 | 445 | testing.expectEqual(res.len, i); 446 | } 447 | 448 | test "iterators.skipWhile: TODO" {} 449 | 450 | test "iterators.take" { 451 | const data = "abcd"; 452 | const res = "ab"; 453 | 454 | var it = SliceIterator(u8).init(data).take(2); 455 | 456 | var i: usize = 0; 457 | while (it.next()) |item| : (i += 1) { 458 | testing.expectEqual(res[i], item); 459 | } 460 | 461 | testing.expectEqual(res.len, i); 462 | } 463 | 464 | test "iterators.takeWhile: TODO" {} 465 | 466 | test "iterators.where" { 467 | const data = "abc"; 468 | const res = "ac"; 469 | const isB = struct { 470 | fn f(i: u8) bool { 471 | return i != 'b'; 472 | } 473 | }.f; 474 | 475 | var it = SliceIterator(u8).init(data).where(isB); 476 | 477 | var i: usize = 0; 478 | while (it.next()) |item| : (i += 1) { 479 | testing.expectEqual(res[i], item); 480 | } 481 | 482 | testing.expectEqual(res.len, i); 483 | } 484 | 485 | test "iterators.zip" { 486 | const data1 = "abc"; 487 | const data2 = "defg"; 488 | 489 | const it1 = SliceIterator(u8).init(data1); 490 | const it2 = SliceIterator(u8).init(data2); 491 | var zipped = it1.zip(it2); 492 | 493 | var i: usize = 0; 494 | while (zipped.next()) |item| : (i += 1) { 495 | testing.expectEqual(data1[i], item.first); 496 | testing.expectEqual(data2[i], item.second); 497 | } 498 | 499 | testing.expectEqual(data1.len, i); 500 | } 501 | -------------------------------------------------------------------------------- /fun/reify.zig: -------------------------------------------------------------------------------- 1 | const builtin = @import("builtin"); 2 | const std = @import("std"); 3 | 4 | const debug = std.debug; 5 | const testing = std.testing; 6 | 7 | const TypeInfo = builtin.TypeInfo; 8 | const TypeId = builtin.TypeId; 9 | 10 | pub fn Reify(comptime info: TypeInfo) type { 11 | switch (info) { 12 | .Type => return type, 13 | .Void => return void, 14 | .Bool => return bool, 15 | .Null => return @TypeOf(null), 16 | .Undefined => return @TypeOf(undefined), 17 | .NoReturn => return noreturn, 18 | .ComptimeInt => return comptime_int, 19 | .ComptimeFloat => return comptime_float, 20 | // TODO: Implement without using @Type 21 | .Int => |int| unreachable, 22 | .Float => |float| switch (float.bits) { 23 | 16 => return f16, 24 | 32 => return f32, 25 | 64 => return f64, 26 | 128 => return f128, 27 | else => @compileError("Float cannot be Reified with {TODO bits in error} bits"), 28 | }, 29 | .Pointer => |ptr| switch (ptr.size) { 30 | .One => { 31 | if (ptr.is_const and ptr.is_volatile) 32 | return *align(ptr.alignment) const volatile ptr.child; 33 | if (ptr.is_const) 34 | return *align(ptr.alignment) const ptr.child; 35 | if (ptr.is_volatile) 36 | return *align(ptr.alignment) volatile ptr.child; 37 | 38 | return *align(ptr.alignment) ptr.child; 39 | }, 40 | .Many => { 41 | if (ptr.is_const and ptr.is_volatile) 42 | return [*]align(ptr.alignment) const volatile ptr.child; 43 | if (ptr.is_const) 44 | return [*]align(ptr.alignment) const ptr.child; 45 | if (ptr.is_volatile) 46 | return [*]align(ptr.alignment) volatile ptr.child; 47 | 48 | return [*]align(ptr.alignment) ptr.child; 49 | }, 50 | .Slice => { 51 | if (ptr.is_const and ptr.is_volatile) 52 | return []align(ptr.alignment) const volatile ptr.child; 53 | if (ptr.is_const) 54 | return []align(ptr.alignment) const ptr.child; 55 | if (ptr.is_volatile) 56 | return []align(ptr.alignment) volatile ptr.child; 57 | 58 | return []align(ptr.alignment) ptr.child; 59 | }, 60 | .C => { 61 | if (ptr.is_const and ptr.is_volatile) 62 | return [*c]align(ptr.alignment) const volatile ptr.child; 63 | if (ptr.is_const) 64 | return [*c]align(ptr.alignment) const ptr.child; 65 | if (ptr.is_volatile) 66 | return [*c]align(ptr.alignment) volatile ptr.child; 67 | 68 | return [*c]align(ptr.alignment) ptr.child; 69 | }, 70 | }, 71 | .Array => |arr| return [arr.len]arr.child, 72 | .Struct => |str| @compileError("TODO"), 73 | .Optional => |opt| return ?opt.child, 74 | .ErrorUnion => |err_union| return err_union.error_set!err_union.payload, 75 | .ErrorSet => |err_set| { 76 | var Res = error{}; 77 | inline for (err_set.errors) |err| { 78 | Res = Res || @field(anyerror, err.name); 79 | } 80 | 81 | return Res; 82 | }, 83 | .Opaque => return @OpaqueType(), 84 | .AnyFrame => return anyframe, 85 | .Enum => |enu| @compileError("TODO"), 86 | .Union => |unio| @compileError("TODO"), 87 | .Fn => |func| @compileError("TODO"), 88 | .BoundFn => |func| @compileError("TODO"), 89 | .Frame => @compileError("TODO"), 90 | .Vector => @compileError("TODO"), 91 | .EnumLiteral => @compileError("TODO"), 92 | } 93 | } 94 | 95 | test "reify: type" { 96 | const T = Reify(@typeInfo(type)); 97 | testing.expectEqual(T, type); 98 | } 99 | 100 | test "reify: void" { 101 | const T = Reify(@typeInfo(void)); 102 | testing.expectEqual(T, void); 103 | } 104 | 105 | test "reify: bool" { 106 | const T = Reify(@typeInfo(bool)); 107 | testing.expectEqual(T, bool); 108 | } 109 | 110 | test "reify: noreturn" { 111 | const T = Reify(@typeInfo(noreturn)); 112 | testing.expectEqual(T, noreturn); 113 | } 114 | 115 | test "reify: ix/ux" { 116 | //@setEvalBranchQuota(10000); 117 | //inline for ([_]bool{ true, false }) |signed| { 118 | // comptime var i = 0; 119 | // inline while (i < 256) : (i += 1) { 120 | // const T1 = @IntType(signed, i); 121 | // const T2 = Reify(@typeInfo(T1)); 122 | // comptime testing.expectEqual(T1, T2); 123 | // } 124 | //} 125 | } 126 | 127 | test "reify: fx" { 128 | testing.expectEqual(Reify(@typeInfo(f16)), f16); 129 | // TODO: All these fail for some reason 130 | //testing.expectEqual(Reify(@typeInfo(f32)), f32); 131 | //testing.expectEqual(Reify(@typeInfo(f64)), f64); 132 | //testing.expectEqual(Reify(@typeInfo(f128)), f128); 133 | } 134 | 135 | test "reify: *X" { 136 | const types = [_]type{ 137 | *u8, 138 | *const u8, 139 | *volatile u8, 140 | *align(4) u8, 141 | *const volatile u8, 142 | *align(4) volatile u8, 143 | *align(4) const u8, 144 | *align(4) const volatile u8, 145 | }; 146 | inline for (types) |P| { 147 | const T = Reify(@typeInfo(P)); 148 | testing.expectEqual(T, P); 149 | } 150 | } 151 | 152 | test "reify: [*]X" { 153 | const types = [_]type{ 154 | [*]u8, 155 | [*]const u8, 156 | [*]volatile u8, 157 | [*]align(4) u8, 158 | [*]const volatile u8, 159 | [*]align(4) volatile u8, 160 | [*]align(4) const u8, 161 | [*]align(4) const volatile u8, 162 | }; 163 | inline for (types) |P| { 164 | const T = Reify(@typeInfo(P)); 165 | testing.expectEqual(T, P); 166 | } 167 | } 168 | 169 | test "reify: []X" { 170 | const types = [_]type{ 171 | []u8, 172 | []const u8, 173 | []volatile u8, 174 | []align(4) u8, 175 | []const volatile u8, 176 | []align(4) volatile u8, 177 | []align(4) const u8, 178 | []align(4) const volatile u8, 179 | }; 180 | inline for (types) |P| { 181 | const T = comptime Reify(@typeInfo(P)); 182 | testing.expectEqual(T, P); 183 | } 184 | } 185 | 186 | test "reify: [n]X" { 187 | testing.expectEqual([1]u8, Reify(@typeInfo([1]u8))); 188 | // TODO: This fails for some reason 189 | //testing.expectEqual([10]u8, Reify(@typeInfo([10]u8))); 190 | } 191 | 192 | test "reify: struct" { 193 | return error.SkipZigTest; 194 | } 195 | 196 | test "reify: ?X" { 197 | const T = Reify(@typeInfo(?u8)); 198 | testing.expectEqual(T, ?u8); 199 | } 200 | 201 | test "reify: X!Y" { 202 | const Set = error{}; 203 | const T = Reify(@typeInfo(Set!u8)); 204 | testing.expectEqual(T, Set!u8); 205 | } 206 | 207 | test "reify: error sets" { 208 | return error.SkipZigTest; 209 | } 210 | 211 | test "reify: enum" { 212 | return error.SkipZigTest; 213 | } 214 | 215 | test "reify: union" { 216 | return error.SkipZigTest; 217 | } 218 | 219 | test "reify: fn" { 220 | return error.SkipZigTest; 221 | } 222 | 223 | test "reify: boundfn" { 224 | return error.SkipZigTest; 225 | } 226 | 227 | test "reify: ..." { 228 | return error.SkipZigTest; 229 | } 230 | 231 | test "reify: @OpaqueType()" { 232 | const T = Reify(@typeInfo(@OpaqueType())); 233 | testing.expectEqual(@as(TypeId, @typeInfo(T)), .Opaque); 234 | } 235 | 236 | test "reify: anyframe" { 237 | const T = Reify(@typeInfo(anyframe)); 238 | testing.expectEqual(T, anyframe); 239 | } 240 | 241 | test "reify: @Frame" { 242 | return error.SkipZigTest; 243 | } 244 | -------------------------------------------------------------------------------- /src/bits.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const math = std.math; 4 | const testing = std.testing; 5 | 6 | pub fn set(comptime Int: type, num: Int, bit: math.Log2Int(Int)) Int { 7 | return num | (@as(Int, 1) << bit); 8 | } 9 | 10 | test "bits.set" { 11 | const v = @as(u8, 0b10); 12 | testing.expectEqual(@as(u8, 0b11), set(u8, v, 0)); 13 | testing.expectEqual(@as(u8, 0b10), set(u8, v, 1)); 14 | } 15 | 16 | pub fn clear(comptime Int: type, num: Int, bit: math.Log2Int(Int)) Int { 17 | return num & ~(@as(Int, 1) << bit); 18 | } 19 | 20 | test "bits.clear" { 21 | const v = @as(u8, 0b10); 22 | testing.expectEqual(@as(u8, 0b10), clear(u8, v, 0)); 23 | testing.expectEqual(@as(u8, 0b00), clear(u8, v, 1)); 24 | } 25 | 26 | pub fn isSet(comptime Int: type, num: Int, bit: math.Log2Int(Int)) bool { 27 | return ((num >> bit) & 1) != 0; 28 | } 29 | 30 | test "bits.isSet" { 31 | const v = @as(u8, 0b10); 32 | testing.expect(!isSet(u8, v, 0)); 33 | testing.expect(isSet(u8, v, 1)); 34 | } 35 | 36 | pub fn toggle(comptime Int: type, num: Int, bit: math.Log2Int(Int)) Int { 37 | return num ^ (@as(Int, 1) << bit); 38 | } 39 | 40 | test "bits.toggle" { 41 | const v = @as(u8, 0b10); 42 | testing.expectEqual(@as(u8, 0b11), toggle(u8, v, 0)); 43 | testing.expectEqual(@as(u8, 0b00), toggle(u8, v, 1)); 44 | } 45 | 46 | pub fn count(comptime Int: type, num: Int) usize { 47 | var tmp = num; 48 | var res: usize = 0; 49 | while (tmp != 0) : (res += 1) 50 | tmp &= tmp - 1; 51 | 52 | return res; 53 | } 54 | 55 | test "bits.count" { 56 | testing.expectEqual(@as(usize, 0), count(u8, 0b0)); 57 | testing.expectEqual(@as(usize, 1), count(u8, 0b1)); 58 | testing.expectEqual(@as(usize, 2), count(u8, 0b101)); 59 | testing.expectEqual(@as(usize, 4), count(u8, 0b11011)); 60 | } 61 | -------------------------------------------------------------------------------- /src/generic.zig: -------------------------------------------------------------------------------- 1 | pub const compare = @import("generic/compare.zig"); 2 | pub const slice = @import("generic/slice.zig"); 3 | 4 | test "generic" { 5 | _ = compare; 6 | _ = slice; 7 | } 8 | -------------------------------------------------------------------------------- /src/generic/compare.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | 4 | const mem = std.mem; 5 | const testing = std.testing; 6 | 7 | const TypeId = builtin.TypeId; 8 | const TypeInfo = builtin.TypeInfo; 9 | 10 | pub fn lessThan(comptime T: type, a: T, b: T) bool { 11 | const info = @typeInfo(T); 12 | switch (info) { 13 | .Int, .Float, .ComptimeFloat, .ComptimeInt => return a < b, 14 | .Bool => return @boolToInt(a) < @boolToInt(b), 15 | 16 | .Optional => |optional| { 17 | const a_value = a orelse { 18 | return if (b) |_| true else false; 19 | }; 20 | const b_value = b orelse return false; 21 | 22 | return lessThan(optional.child, a_value, b_value); 23 | }, 24 | .ErrorUnion => |err_union| { 25 | const a_value = a catch |a_err| { 26 | if (b) |_| { 27 | return true; 28 | } else |b_err| { 29 | return lessThan(err_union.error_set, a_err, b_err); 30 | } 31 | }; 32 | const b_value = b catch return false; 33 | 34 | return lessThan(err_union.payload, a_value, b_value); 35 | }, 36 | 37 | // TODO: mem.lessThan is wrong 38 | .Array => |arr| return mem.lessThan(arr.child, &a, &b), 39 | .Enum => |e| return @enumToInt(a) < @enumToInt(b), 40 | .ErrorSet => return @errorToInt(a) < @errorToInt(b), 41 | 42 | .Null, .Void => return false, 43 | 44 | .Vector, 45 | .Undefined, 46 | .Type, 47 | .NoReturn, 48 | .Fn, 49 | .BoundFn, 50 | .Opaque, 51 | .AnyFrame, 52 | .Frame, 53 | .Struct, 54 | .Union, 55 | .Pointer, 56 | .EnumLiteral, 57 | => { 58 | @compileError("Cannot get a default less than for " ++ @typeName(T)); 59 | return false; 60 | }, 61 | } 62 | } 63 | 64 | test "generic.compare.lessThan(u64)" { 65 | testing.expect(lessThan(u64, 1, 2)); 66 | testing.expect(!lessThan(u64, 1, 1)); 67 | testing.expect(!lessThan(u64, 1, 0)); 68 | } 69 | 70 | test "generic.compare.lessThan(i64)" { 71 | testing.expect(lessThan(i64, 0, 1)); 72 | testing.expect(!lessThan(i64, 0, 0)); 73 | testing.expect(!lessThan(i64, 0, -1)); 74 | } 75 | 76 | test "generic.compare.lessThan(comptime_int)" { 77 | testing.expect(lessThan(comptime_int, 0, 1)); 78 | testing.expect(!lessThan(comptime_int, 0, 0)); 79 | testing.expect(!lessThan(comptime_int, 0, -1)); 80 | } 81 | 82 | test "generic.compare.lessThan(f64)" { 83 | testing.expect(lessThan(f64, 0, 1)); 84 | testing.expect(!lessThan(f64, 0, 0)); 85 | testing.expect(!lessThan(f64, 0, -1)); 86 | } 87 | 88 | test "generic.compare.lessThan(comptime_float)" { 89 | testing.expect(lessThan(comptime_float, 0.0, 1.0)); 90 | testing.expect(!lessThan(comptime_float, 0.0, 0.0)); 91 | testing.expect(!lessThan(comptime_float, 0.0, -1.0)); 92 | } 93 | 94 | test "generic.compare.lessThan(bool)" { 95 | testing.expect(lessThan(bool, false, true)); 96 | testing.expect(!lessThan(bool, true, true)); 97 | testing.expect(!lessThan(bool, true, false)); 98 | } 99 | 100 | test "generic.compare.lessThan(?i64)" { 101 | const nul: ?i64 = null; 102 | testing.expect(lessThan(?i64, 0, 1)); 103 | testing.expect(!lessThan(?i64, 0, 0)); 104 | testing.expect(!lessThan(?i64, 0, -1)); 105 | testing.expect(lessThan(?i64, nul, 0)); 106 | testing.expect(!lessThan(?i64, nul, nul)); 107 | testing.expect(!lessThan(?i64, 0, nul)); 108 | } 109 | 110 | //TODO implement @typeInfo for global error set 111 | //test "generic.compare.lessThan(error!i64)" { 112 | // const err : error!i64 = error.No; 113 | // testing.expect( lessThan(error!i64, 0, 1)); 114 | // testing.expect(!lessThan(error!i64, 0, 0)); 115 | // testing.expect(!lessThan(error!i64, 0, -1)); 116 | // testing.expect( lessThan(error!i64, err, 0 )); 117 | // testing.expect(!lessThan(error!i64, err, err)); 118 | // testing.expect(!lessThan(error!i64, 0 , err)); 119 | //} 120 | 121 | test "generic.compare.lessThan([1]u8)" { 122 | testing.expect(lessThan([1]u8, "1".*, "2".*)); 123 | testing.expect(!lessThan([1]u8, "1".*, "1".*)); 124 | testing.expect(!lessThan([1]u8, "1".*, "0".*)); 125 | } 126 | 127 | test "generic.compare.lessThan(enum)" { 128 | const E = enum { 129 | A = 0, 130 | B = 1, 131 | }; 132 | testing.expect(lessThan(E, E.A, E.B)); 133 | testing.expect(!lessThan(E, E.B, E.B)); 134 | testing.expect(!lessThan(E, E.B, E.A)); 135 | } 136 | 137 | //TODO implement @typeInfo for global error set 138 | //test "generic.compare.lessThan(error)" { 139 | // testing.expect( lessThan(error, error.A, error.B)); 140 | // testing.expect(!lessThan(error, error.B, error.B)); 141 | // testing.expect(!lessThan(error, error.B, error.A)); 142 | //} 143 | 144 | //test "generic.compare.lessThan(null)" { 145 | // comptime testing.expect(!lessThan(@typeOf(null), null, null)); 146 | //} 147 | 148 | test "generic.compare.lessThan(void)" { 149 | testing.expect(!lessThan(void, void{}, void{})); 150 | } 151 | 152 | pub fn equal(comptime T: type, a: T, b: T) bool { 153 | const info = @typeInfo(T); 154 | switch (info) { 155 | .Int, 156 | .Float, 157 | .ComptimeInt, 158 | .ComptimeFloat, 159 | .Enum, 160 | .ErrorSet, 161 | .Type, 162 | .Void, 163 | .Fn, 164 | .Null, 165 | .Bool, 166 | .EnumLiteral, 167 | .AnyFrame, 168 | => return a == b, 169 | // We don't follow pointers, as this would `lessThan` recursive on recursive types (like LinkedList 170 | .Pointer => |ptr| switch (ptr.size) { 171 | .Slice => { 172 | return a.ptr == b.ptr and a.len == b.len; 173 | }, 174 | else => return a == b, 175 | }, 176 | .Array => { 177 | if (a.len != b.len) 178 | return false; 179 | 180 | for (a) |_, i| { 181 | if (!equal(T.Child, a[i], b[i])) 182 | return false; 183 | } 184 | 185 | return true; 186 | }, 187 | .Optional => |optional| { 188 | const a_value = a orelse { 189 | return if (b) |_| false else true; 190 | }; 191 | const b_value = b orelse return false; 192 | 193 | return equal(optional.child, a_value, b_value); 194 | }, 195 | .ErrorUnion => |err_union| { 196 | const a_value = a catch |a_err| { 197 | if (b) |_| { 198 | return false; 199 | } else |b_err| { 200 | return equal(err_union.error_set, a_err, b_err); 201 | } 202 | }; 203 | const b_value = b catch return false; 204 | 205 | return equal(err_union.payload, a_value, b_value); 206 | }, 207 | .Struct => |struct_info| { 208 | inline for (struct_info.fields) |field| { 209 | if (!fieldsEql(T, field.name, a, b)) 210 | return false; 211 | } 212 | 213 | return true; 214 | }, 215 | 216 | .Vector, 217 | .Undefined, 218 | .NoReturn, 219 | .BoundFn, 220 | .Opaque, 221 | .Frame, 222 | .Union, 223 | => { 224 | @compileError("Cannot get a default equal for " ++ @typeName(T)); 225 | return false; 226 | }, 227 | } 228 | } 229 | 230 | fn fieldsEql(comptime T: type, comptime field: []const u8, a: T, b: T) bool { 231 | const af = @field(a, field); 232 | const bf = @field(b, field); 233 | return equal(@TypeOf(af), af, bf); 234 | } 235 | 236 | test "generic.compare.equal(i32)" { 237 | testing.expect(equal(i32, 1, 1)); 238 | testing.expect(!equal(i32, 0, 1)); 239 | } 240 | 241 | test "generic.compare.equal(comptime_int)" { 242 | testing.expect(equal(comptime_int, 1, 1)); 243 | testing.expect(!equal(comptime_int, 0, 1)); 244 | } 245 | 246 | test "generic.compare.equal(f32)" { 247 | testing.expect(equal(f32, 1, 1)); 248 | testing.expect(!equal(f32, 0, 1)); 249 | } 250 | 251 | test "generic.compare.equal(comptime_float)" { 252 | testing.expect(equal(comptime_float, 1.1, 1.1)); 253 | testing.expect(!equal(comptime_float, 0.0, 1.1)); 254 | } 255 | 256 | test "generic.compare.equal(bool)" { 257 | testing.expect(equal(bool, true, true)); 258 | testing.expect(!equal(bool, true, false)); 259 | } 260 | 261 | test "generic.compare.equal(type)" { 262 | comptime { 263 | testing.expect(equal(type, u8, u8)); 264 | testing.expect(!equal(type, u16, u8)); 265 | } 266 | } 267 | 268 | test "generic.compare.equal(enum)" { 269 | const E = enum { 270 | A, 271 | B, 272 | }; 273 | testing.expect(equal(E, E.A, E.A)); 274 | testing.expect(!equal(E, E.A, E.B)); 275 | } 276 | 277 | //TODO implement @typeInfo for global error set 278 | //test "generic.compare.equal(error)" { 279 | // testing.expect( equal(error, error.A, error.A)); 280 | // testing.expect(!equal(error, error.A, error.B)); 281 | //} 282 | 283 | test "generic.compare.equal(&i64)" { 284 | var a: i64 = undefined; 285 | var b: i64 = undefined; 286 | testing.expect(equal(*i64, &a, &a)); 287 | testing.expect(!equal(*i64, &a, &b)); 288 | } 289 | 290 | test "generic.compare.equal(?i64)" { 291 | var nul: ?i64 = null; 292 | testing.expect(equal(?i64, 1, 1)); 293 | testing.expect(equal(?i64, nul, nul)); 294 | testing.expect(!equal(?i64, 1, 0)); 295 | testing.expect(!equal(?i64, 1, nul)); 296 | } 297 | 298 | //TODO implement @typeInfo for global error set 299 | //test "generic.compare.equal(%i32)" { 300 | // const a : error!i32 = 1; 301 | // const b : error!i32 = error.TestError1; 302 | // const errorEqual = equal(error!i32); 303 | // testing.expect( errorEqual(a, (error!i32)(1))); 304 | // testing.expect(!errorEqual(a, (error!i32)(0))); 305 | // testing.expect(!errorEqual(a, (error!i32)(error.TestError1))); 306 | // testing.expect( errorEqual(b, (error!i32)(error.TestError1))); 307 | // testing.expect(!errorEqual(b, (error!i32)(error.TestError2))); 308 | // testing.expect(!errorEqual(b, (error!i32)(0))); 309 | //} 310 | 311 | test "generic.compare.equal([1]u8)" { 312 | testing.expect(equal([1]u8, "1".*, "1".*)); 313 | testing.expect(!equal([1]u8, "1".*, "0".*)); 314 | } 315 | 316 | test "generic.compare.equal(null)" { 317 | comptime testing.expect(equal(@TypeOf(null), null, null)); 318 | } 319 | 320 | test "generic.compare.equal(void)" { 321 | testing.expect(equal(void, void{}, void{})); 322 | } 323 | 324 | test "generic.compare.equal(struct)" { 325 | const Struct = packed struct { 326 | a: u3, 327 | b: u3, 328 | }; 329 | testing.expect(equal(Struct, Struct{ .a = 1, .b = 1 }, Struct{ .a = 1, .b = 1 })); 330 | testing.expect(!equal(Struct, Struct{ .a = 0, .b = 0 }, Struct{ .a = 1, .b = 1 })); 331 | } 332 | 333 | test "generic.compare.equal([]const u8)" { 334 | const a = "1"; 335 | const b = "0"; 336 | testing.expect(equal([]const u8, a, a)); 337 | testing.expect(!equal([]const u8, a, b)); 338 | } 339 | 340 | //unreachable 341 | //[1] 6911 abort (core dumped) zig test src/index.zig 342 | //test "equal(fn()void)" { 343 | // const T = struct { 344 | // fn a() void {} 345 | // fn b() void {} 346 | // }; 347 | // 348 | // testing.expect( equal(fn()void, T.a, T.a)); 349 | // testing.expect(!equal(fn()void, T.a, T.b)); 350 | //} 351 | -------------------------------------------------------------------------------- /src/generic/slice.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | const mem = std.mem; 4 | const meta = std.meta; 5 | const debug = std.debug; 6 | const testing = std.testing; 7 | 8 | const TypeInfo = builtin.TypeInfo; 9 | const TypeId = builtin.TypeId; 10 | 11 | fn ByteToSliceResult(comptime Elem: type, comptime SliceOrArray: type) type { 12 | if (@typeInfo(SliceOrArray) != .Pointer) 13 | @compileError("Cannot bytesToSlice " ++ @typeName(SliceOrArray)); 14 | 15 | const ptr = @typeInfo(SliceOrArray).Pointer; 16 | if (ptr.is_const and ptr.is_volatile) 17 | return []align(ptr.alignment) const volatile Elem; 18 | if (ptr.is_const) 19 | return []align(ptr.alignment) const Elem; 20 | if (ptr.is_volatile) 21 | return []align(ptr.alignment) volatile Elem; 22 | 23 | return []align(ptr.alignment) Elem; 24 | } 25 | 26 | pub fn bytesToSlice(comptime Element: type, bytes: var) error{SizeMismatch}!ByteToSliceResult(Element, @TypeOf(bytes)) { 27 | if (bytes.len % @sizeOf(Element) != 0) 28 | return error.SizeMismatch; 29 | 30 | return std.mem.bytesAsSlice(Element, bytes); 31 | } 32 | 33 | test "generic.slice.bytesToSlice" { 34 | const S = packed struct { 35 | a: u8, 36 | b: u8, 37 | }; 38 | 39 | { 40 | const a = [_]u8{1}; 41 | const b = [_]u8{ 1, 2 }; 42 | 43 | if (bytesToSlice(S, a[0..])) |_| unreachable else |_| {} 44 | const v = bytesToSlice(S, b[0..]) catch unreachable; 45 | comptime testing.expectEqual([]align(1) const S, @TypeOf(v)); 46 | testing.expectEqual(@as(usize, 1), v.len); 47 | testing.expectEqual(@as(u8, 1), v[0].a); 48 | testing.expectEqual(@as(u8, 2), v[0].b); 49 | 50 | const v2 = bytesToSlice(S, &b) catch unreachable; 51 | comptime testing.expectEqual([]align(1) const S, @TypeOf(v2)); 52 | testing.expectEqual(@as(usize, 1), v2.len); 53 | testing.expectEqual(@as(u8, 1), v2[0].a); 54 | testing.expectEqual(@as(u8, 2), v2[0].b); 55 | } 56 | 57 | { 58 | var a = [_]u8{1}; 59 | var b = [_]u8{ 1, 2 }; 60 | 61 | if (bytesToSlice(S, a[0..])) |_| unreachable else |_| {} 62 | const v = bytesToSlice(S, b[0..]) catch unreachable; 63 | comptime testing.expectEqual([]align(1) S, @TypeOf(v)); 64 | testing.expectEqual(@as(usize, 1), v.len); 65 | testing.expectEqual(@as(u8, 1), v[0].a); 66 | testing.expectEqual(@as(u8, 2), v[0].b); 67 | 68 | const v2 = bytesToSlice(S, &b) catch unreachable; 69 | comptime testing.expectEqual([]align(1) S, @TypeOf(v2)); 70 | testing.expectEqual(@as(usize, 1), v2.len); 71 | testing.expectEqual(@as(u8, 1), v2[0].a); 72 | testing.expectEqual(@as(u8, 2), v2[0].b); 73 | } 74 | } 75 | 76 | pub fn bytesToSliceTrim(comptime Element: type, bytes: var) ByteToSliceResult(Element, @TypeOf(bytes)) { 77 | var rem = bytes.len % @sizeOf(Element); 78 | return std.mem.bytesAsSlice(Element, bytes[0 .. bytes.len - rem]); 79 | } 80 | 81 | test "generic.slice.bytesToSliceTrim" { 82 | const S = packed struct { 83 | a: u8, 84 | b: u8, 85 | }; 86 | 87 | { 88 | const a = [_]u8{1}; 89 | const b = [_]u8{ 1, 2 }; 90 | const v1 = bytesToSliceTrim(S, a[0..]); 91 | const v2 = bytesToSliceTrim(S, b[0..]); 92 | const v3 = bytesToSliceTrim(S, &a); 93 | const v4 = bytesToSliceTrim(S, &b); 94 | 95 | comptime testing.expect([]align(1) const S == @TypeOf(v1)); 96 | comptime testing.expect([]align(1) const S == @TypeOf(v2)); 97 | comptime testing.expect([]const S == @TypeOf(v3)); 98 | comptime testing.expect([]const S == @TypeOf(v4)); 99 | testing.expectEqual(@as(usize, 0), v1.len); 100 | testing.expectEqual(@as(usize, 1), v2.len); 101 | testing.expectEqual(@as(usize, 0), v3.len); 102 | testing.expectEqual(@as(usize, 1), v4.len); 103 | testing.expectEqual(@as(u8, 1), v2[0].a); 104 | testing.expectEqual(@as(u8, 2), v2[0].b); 105 | testing.expectEqual(@as(u8, 1), v4[0].a); 106 | testing.expectEqual(@as(u8, 2), v4[0].b); 107 | } 108 | 109 | { 110 | var a = [_]u8{1}; 111 | var b = [_]u8{ 1, 2 }; 112 | const v1 = bytesToSliceTrim(S, a[0..]); 113 | const v2 = bytesToSliceTrim(S, b[0..]); 114 | const v3 = bytesToSliceTrim(S, &a); 115 | const v4 = bytesToSliceTrim(S, &b); 116 | 117 | comptime testing.expectEqual([]S, @TypeOf(v1)); 118 | comptime testing.expectEqual([]S, @TypeOf(v2)); 119 | comptime testing.expectEqual([]S, @TypeOf(v3)); 120 | comptime testing.expectEqual([]S, @TypeOf(v4)); 121 | testing.expectEqual(@as(usize, 0), v1.len); 122 | testing.expectEqual(@as(usize, 1), v2.len); 123 | testing.expectEqual(@as(usize, 0), v3.len); 124 | testing.expectEqual(@as(usize, 1), v4.len); 125 | testing.expectEqual(@as(usize, 1), v2[0].a); 126 | testing.expectEqual(@as(usize, 2), v2[0].b); 127 | testing.expectEqual(@as(usize, 1), v4[0].a); 128 | testing.expectEqual(@as(usize, 2), v4[0].b); 129 | } 130 | } 131 | 132 | fn SliceResult(comptime SliceOrArray: type) type { 133 | if (@typeInfo(SliceOrArray) != .Pointer) 134 | @compileError("Cannot slice " ++ @typeName(SliceOrArray)); 135 | 136 | const ptr = @typeInfo(SliceOrArray).Pointer; 137 | const Child = switch (ptr.size) { 138 | .One => @typeInfo(ptr.child).Array.child, 139 | else => ptr.child, 140 | }; 141 | 142 | if (ptr.is_const and ptr.is_volatile) 143 | return []align(ptr.alignment) const volatile Child; 144 | if (ptr.is_const) 145 | return []align(ptr.alignment) const Child; 146 | if (ptr.is_volatile) 147 | return []align(ptr.alignment) volatile Child; 148 | 149 | return []align(ptr.alignment) Child; 150 | } 151 | 152 | /// Slices ::s from ::start to ::end. 153 | /// Returns errors instead of doing runtime asserts when ::start or ::end are out of bounds, 154 | /// or when ::end is less that ::start. 155 | pub fn slice(s: var, start: usize, end: usize) !SliceResult(@TypeOf(s)) { 156 | if (end < start) 157 | return error.EndLessThanStart; 158 | if (s.len < start or s.len < end) 159 | return error.OutOfBound; 160 | 161 | return s[start..end]; 162 | } 163 | 164 | test "generic.slice.slice" { 165 | const a = [_]u8{ 1, 2 }; 166 | const b = slice(a[0..], 0, 1) catch unreachable; 167 | const c = slice(a[0..], 1, 2) catch unreachable; 168 | const d = slice(a[0..], 0, 2) catch unreachable; 169 | const e = slice(a[0..], 2, 2) catch unreachable; 170 | const f = slice(&a, 1, 2) catch unreachable; 171 | 172 | testing.expectEqualSlices(u8, &[_]u8{1}, b); 173 | testing.expectEqualSlices(u8, &[_]u8{2}, c); 174 | testing.expectEqualSlices(u8, &[_]u8{ 1, 2 }, d); 175 | testing.expectEqualSlices(u8, &[_]u8{}, e); 176 | testing.expectEqualSlices(u8, &[_]u8{2}, f); 177 | 178 | testing.expectError(error.OutOfBound, slice(a[0..], 0, 3)); 179 | testing.expectError(error.OutOfBound, slice(a[0..], 3, 3)); 180 | testing.expectError(error.EndLessThanStart, slice(a[0..], 1, 0)); 181 | 182 | const q1 = [_]u8{ 1, 2 }; 183 | var q2 = [_]u8{ 1, 2 }; 184 | 185 | const q11 = slice(q1[0..], 0, 2) catch unreachable; 186 | const q21 = slice(q2[0..], 0, 2) catch unreachable; 187 | comptime testing.expectEqual([]const u8, @TypeOf(q11)); 188 | comptime testing.expectEqual([]u8, @TypeOf(q21)); 189 | 190 | const q12 = slice(&q1, 0, 2) catch unreachable; 191 | const q22 = slice(&q2, 0, 2) catch unreachable; 192 | comptime testing.expectEqual([]const u8, @TypeOf(q12)); 193 | comptime testing.expectEqual([]u8, @TypeOf(q22)); 194 | } 195 | 196 | /// Returns a pointer to the item at ::index in ::s. 197 | /// Returns an error instead of doing a runtime assert when ::index is out of bounds. 198 | pub fn at(s: var, index: usize) !@TypeOf(&s[0]) { 199 | if (s.len <= index) 200 | return error.OutOfBound; 201 | 202 | return &s[index]; 203 | } 204 | 205 | test "generic.slice.at" { 206 | const a = [_]u8{ 1, 2 }; 207 | const b = at(a[0..], 0) catch unreachable; 208 | const c = at(a[0..], 1) catch unreachable; 209 | const d = at(a[0..], 1) catch unreachable; 210 | 211 | testing.expectEqual(@as(u8, 1), b.*); 212 | testing.expectEqual(@as(u8, 2), c.*); 213 | testing.expectEqual(@as(u8, 2), d.*); 214 | testing.expectError(error.OutOfBound, at(a[0..], 2)); 215 | 216 | const q1 = [_]u8{ 1, 2 }; 217 | var q2 = [_]u8{ 1, 2 }; 218 | 219 | const q11 = at(q1[0..], 0) catch unreachable; 220 | const q21 = at(q2[0..], 0) catch unreachable; 221 | comptime testing.expectEqual(*const u8, @TypeOf(q11)); 222 | comptime testing.expectEqual(*u8, @TypeOf(q21)); 223 | 224 | const q31 = at(&q1, 0) catch unreachable; 225 | const q41 = at(&q2, 0) catch unreachable; 226 | comptime testing.expectEqual(*const u8, @TypeOf(q31)); 227 | comptime testing.expectEqual(*u8, @TypeOf(q41)); 228 | } 229 | 230 | pub fn all(s: var, predicate: fn (@TypeOf(s[0])) bool) bool { 231 | for (s) |v| { 232 | if (!predicate(v)) return false; 233 | } 234 | 235 | return true; 236 | } 237 | 238 | test "generic.slice.all" { 239 | const s = "aaa"[0..]; 240 | testing.expect(all(s, struct { 241 | fn l(c: u8) bool { 242 | return c == 'a'; 243 | } 244 | }.l)); 245 | testing.expect(!all(s, struct { 246 | fn l(c: u8) bool { 247 | return c != 'a'; 248 | } 249 | }.l)); 250 | } 251 | 252 | pub fn any(s: var, predicate: fn (@TypeOf(s[0])) bool) bool { 253 | for (s) |v| { 254 | if (predicate(v)) return true; 255 | } 256 | 257 | return false; 258 | } 259 | 260 | test "generic.slice.any" { 261 | const s = "abc"; 262 | testing.expect(any(s, struct { 263 | fn l(c: u8) bool { 264 | return c == 'a'; 265 | } 266 | }.l)); 267 | testing.expect(!any(s, struct { 268 | fn l(c: u8) bool { 269 | return c == 'd'; 270 | } 271 | }.l)); 272 | } 273 | 274 | pub fn populate(s: var, value: @TypeOf(s[0])) void { 275 | for (s) |*v| { 276 | v.* = value; 277 | } 278 | } 279 | 280 | test "generic.slice.populate" { 281 | var arr: [4]u8 = undefined; 282 | populate(arr[0..], 'a'); 283 | testing.expectEqualSlices(u8, "aaaa", &arr); 284 | } 285 | 286 | pub fn transform(s: var, transformer: fn (@TypeOf(s[0])) @TypeOf(s[0])) void { 287 | for (s) |*v| { 288 | v.* = transformer(v.*); 289 | } 290 | } 291 | 292 | test "generic.slice.transform" { 293 | var arr = "abcd".*; 294 | transform(arr[0..], struct { 295 | fn l(c: u8) u8 { 296 | return if ('a' <= c and c <= 'z') c - ('a' - 'A') else c; 297 | } 298 | }.l); 299 | testing.expectEqualSlices(u8, "ABCD", &arr); 300 | } 301 | -------------------------------------------------------------------------------- /src/loop.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const debug = std.debug; 3 | const testing = std.testing; 4 | 5 | pub fn to(n: usize) []void { 6 | return @as([*]void, undefined)[0..n]; 7 | } 8 | 9 | test "loop.to" { 10 | var j: usize = 0; 11 | for (to(10)) |_, i| { 12 | testing.expectEqual(j, i); 13 | j += 1; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/match.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const debug = std.debug; 4 | const mem = std.mem; 5 | const rand = std.rand; 6 | const sort = std.sort; 7 | 8 | pub fn StringSwitch(comptime strings: []const []const u8) type { 9 | for (strings[1..]) |_, i| { 10 | if (mem.lessThan(u8, strings[i], strings[i + 1])) 11 | continue; 12 | 13 | @compileError("Input not sorted (assert(\"" ++ strings[i] ++ "\" < \"" ++ strings[i + 1] ++ "\"))"); 14 | } 15 | 16 | return struct { 17 | pub fn match(str: []const u8) usize { 18 | var curr: usize = 0; 19 | next: for (strings) |s, i| { 20 | while (curr < s.len) : (curr += 1) { 21 | const a = str[curr]; 22 | const b = s[curr]; 23 | if (a != b) 24 | continue :next; 25 | } 26 | 27 | if (s.len == str.len) 28 | return i; 29 | } 30 | 31 | return strings.len; 32 | } 33 | 34 | pub fn case(comptime str: []const u8) usize { 35 | const i = match(str); 36 | debug.assert(i < strings.len); 37 | return i; 38 | } 39 | }; 40 | } 41 | 42 | test "match.StringSwitch" { 43 | @setEvalBranchQuota(1000000); 44 | const strings = [_][]const u8{ 45 | "A", 46 | "AA", 47 | "AAA", 48 | "AAAA", 49 | "AAAAA", 50 | "AAAAAA", 51 | "AAAAAAA", 52 | "AAAAAAAA", 53 | }; 54 | const sw = comptime StringSwitch(&strings); 55 | 56 | inline for (strings) |str| 57 | switch (sw.match(str)) { 58 | sw.case(str) => {}, 59 | else => unreachable, 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /src/math.zig: -------------------------------------------------------------------------------- 1 | pub const interval = @import("math/interval.zig"); 2 | pub const safe = @import("math/safe.zig"); 3 | 4 | const std = @import("std"); 5 | const math = std.math; 6 | const debug = std.debug; 7 | const testing = std.testing; 8 | 9 | test "math" { 10 | _ = interval; 11 | _ = safe; 12 | } 13 | 14 | fn digits(comptime N: type, comptime base: comptime_int, n: N) usize { 15 | comptime var res = 1; 16 | comptime var check = base; 17 | 18 | inline while (check <= math.maxInt(N)) : ({ 19 | check *= base; 20 | res += 1; 21 | }) { 22 | if (n < check) 23 | return res; 24 | } 25 | 26 | return res; 27 | } 28 | 29 | test "math.digits" { 30 | testing.expectEqual(digits(u1, 10, 0), 1); 31 | testing.expectEqual(digits(u1, 10, 1), 1); 32 | testing.expectEqual(digits(u2, 10, 3), 1); 33 | testing.expectEqual(digits(u8, 10, 255), 3); 34 | testing.expectEqual(digits(u1, 8, 0o0), 1); 35 | testing.expectEqual(digits(u1, 8, 0o1), 1); 36 | testing.expectEqual(digits(u2, 8, 0o3), 1); 37 | testing.expectEqual(digits(u8, 8, 0o255), 3); 38 | testing.expectEqual(digits(u1, 2, 0b0), 1); 39 | testing.expectEqual(digits(u1, 2, 0b1), 1); 40 | testing.expectEqual(digits(u2, 2, 0b11), 2); 41 | testing.expectEqual(digits(u8, 2, 0b1111), 4); 42 | } 43 | -------------------------------------------------------------------------------- /src/math/interval.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | 4 | const math = std.math; 5 | const mem = std.mem; 6 | const debug = std.debug; 7 | const testing = std.testing; 8 | 9 | const min = @import("index.zig").min; 10 | const max = @import("index.zig").max; 11 | 12 | pub fn Interval(comptime T: type) type { 13 | const info = @typeInfo(T); 14 | debug.assert(info == .Int or 15 | info == .Float or 16 | info == .ComptimeInt or 17 | info == .ComptimeFloat); 18 | 19 | return struct { 20 | const Self = @This(); 21 | 22 | min: T, 23 | max: T, 24 | 25 | pub fn fromSlice(nums: []const T) Self { 26 | return Self{ 27 | .min = mem.min(T, nums), 28 | .max = mem.max(T, nums), 29 | }; 30 | } 31 | 32 | pub fn add(a: Self, b: Self) Self { 33 | return Self{ 34 | .min = a.min + b.min, 35 | .max = a.max + b.max, 36 | }; 37 | } 38 | 39 | pub fn sub(a: Self, b: Self) Self { 40 | return Self{ 41 | .min = a.min - b.max, 42 | .max = a.max - b.min, 43 | }; 44 | } 45 | 46 | pub fn mul(a: Self, b: Self) Self { 47 | return fromSlice(&[_]T{ 48 | a.min * b.min, 49 | a.min * b.max, 50 | a.max * b.min, 51 | a.max * b.max, 52 | }); 53 | } 54 | 55 | pub fn div(a: Self, b: Self) Self { 56 | debug.assert(b.min != 0 and b.max != 0); 57 | return fromSlice(&[_]T{ 58 | a.min / b.min, 59 | a.min / b.max, 60 | a.max / b.min, 61 | a.max / b.max, 62 | }); 63 | } 64 | 65 | pub fn mod(a: Self, b: Self) Self { 66 | debug.assert(b.min != 0 and b.max != 0); 67 | return fromSlice([_]T{ 68 | a.min % b.min, 69 | a.min % b.max, 70 | a.max % b.min, 71 | a.max % b.max, 72 | }); 73 | } 74 | 75 | pub fn shiftLeft(a: Self, b: Self) Self { 76 | return fromSlice([_]T{ 77 | a.min << b.min, 78 | a.min << b.max, 79 | a.max << b.min, 80 | a.max << b.max, 81 | }); 82 | } 83 | 84 | pub fn shiftRight(a: Self, b: Self) Self { 85 | return fromSlice([_]T{ 86 | a.min >> b.min, 87 | a.min >> b.max, 88 | a.max >> b.min, 89 | a.max >> b.max, 90 | }); 91 | } 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /src/math/safe.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const interval = @import("interval.zig"); 3 | 4 | const debug = std.debug; 5 | const math = std.math; 6 | const testing = std.testing; 7 | 8 | // TODO: We can't use comptime_int, because then all the methods on Interval don't compile 9 | const Interval = interval.Interval(i128); 10 | 11 | fn toInterval(comptime T: type) Interval { 12 | return Interval{ 13 | .min = math.minInt(T), 14 | .max = math.maxInt(T), 15 | }; 16 | } 17 | 18 | fn Result(comptime A: type, comptime B: type, comptime operation: @TypeOf(Interval.add)) type { 19 | const a = toInterval(A); 20 | const b = toInterval(B); 21 | const res = operation(a, b); 22 | return math.IntFittingRange(res.min, res.max); 23 | } 24 | 25 | pub fn add(a: var, b: var) Result(@TypeOf(a), @TypeOf(b), Interval.add) { 26 | const Res = Result(@TypeOf(a), @TypeOf(b), Interval.add); 27 | return @as(Res, a) + @as(Res, b); 28 | } 29 | 30 | fn testAdd() void { 31 | const u64_max: u64 = math.maxInt(u64); 32 | const u64_min: u64 = math.minInt(u64); 33 | const i64_max: i64 = math.maxInt(i64); 34 | const i64_min: i64 = math.minInt(i64); 35 | testing.expectEqual(add(u64_max, u64_max), math.maxInt(u64) + math.maxInt(u64)); 36 | testing.expectEqual(add(u64_max, u64_min), math.maxInt(u64) + math.minInt(u64)); 37 | testing.expectEqual(add(u64_max, i64_max), math.maxInt(u64) + math.maxInt(i64)); 38 | testing.expectEqual(add(u64_max, i64_min), math.maxInt(u64) + math.minInt(i64)); 39 | 40 | testing.expectEqual(add(u64_min, u64_min), math.minInt(u64) + math.minInt(u64)); 41 | testing.expectEqual(add(u64_min, i64_max), math.minInt(u64) + math.maxInt(i64)); 42 | testing.expectEqual(add(u64_min, i64_min), math.minInt(u64) + math.minInt(i64)); 43 | 44 | testing.expectEqual(add(i64_max, i64_max), math.maxInt(i64) + math.maxInt(i64)); 45 | testing.expectEqual(add(i64_max, i64_min), math.maxInt(i64) + math.minInt(i64)); 46 | 47 | testing.expectEqual(add(i64_min, i64_min), math.minInt(i64) + math.minInt(i64)); 48 | } 49 | 50 | test "math.safe.add" { 51 | comptime testAdd(); 52 | testAdd(); 53 | } 54 | 55 | pub fn sub(a: var, b: var) Result(@TypeOf(a), @TypeOf(b), Interval.sub) { 56 | const Res = Result(@TypeOf(a), @TypeOf(b), Interval.sub); 57 | return @as(Res, a) - @as(Res, b); 58 | } 59 | 60 | fn testSub() void { 61 | const u64_max: u64 = math.maxInt(u64); 62 | const u64_min: u64 = math.minInt(u64); 63 | const i64_max: i64 = math.maxInt(i64); 64 | const i64_min: i64 = math.minInt(i64); 65 | testing.expectEqual(sub(u64_max, u64_max), math.maxInt(u64) - math.maxInt(u64)); 66 | testing.expectEqual(sub(u64_max, u64_min), math.maxInt(u64) - math.minInt(u64)); 67 | testing.expectEqual(sub(u64_max, i64_max), math.maxInt(u64) - math.maxInt(i64)); 68 | testing.expectEqual(sub(u64_max, i64_min), math.maxInt(u64) - math.minInt(i64)); 69 | 70 | testing.expectEqual(sub(u64_min, u64_min), math.minInt(u64) - math.minInt(u64)); 71 | testing.expectEqual(sub(u64_min, i64_max), math.minInt(u64) - math.maxInt(i64)); 72 | testing.expectEqual(sub(u64_min, i64_min), math.minInt(u64) - math.minInt(i64)); 73 | 74 | testing.expectEqual(sub(i64_max, i64_max), math.maxInt(i64) - math.maxInt(i64)); 75 | testing.expectEqual(sub(i64_max, i64_min), math.maxInt(i64) - math.minInt(i64)); 76 | 77 | testing.expectEqual(sub(i64_min, i64_min), math.minInt(i64) - math.minInt(i64)); 78 | } 79 | 80 | test "math.safe.sub" { 81 | comptime testSub(); 82 | testSub(); 83 | } 84 | 85 | pub fn mul(a: var, b: var) Result(@TypeOf(a), @TypeOf(b), Interval.mul) { 86 | const Res = Result(@TypeOf(a), @TypeOf(b), Interval.mul); 87 | return @as(Res, a) * @as(Res, b); 88 | } 89 | 90 | fn testMul() void { 91 | // TODO: Because we can only have Interval(i128), then u64 values might overflow the 92 | // Interval. 93 | const u32_max: u32 = math.maxInt(u32); 94 | const u32_min: u32 = math.minInt(u32); 95 | const i32_max: i32 = math.maxInt(i32); 96 | const i32_min: i32 = math.minInt(i32); 97 | testing.expectEqual(mul(u32_max, u32_max), math.maxInt(u32) * math.maxInt(u32)); 98 | testing.expectEqual(mul(u32_max, u32_min), math.maxInt(u32) * math.minInt(u32)); 99 | //testing.expectEqual(mul(u32_max, i32_max) , math.maxInt(u32) * math.maxInt(i32)); 100 | //testing.expectEqual(mul(u32_max, i32_min) , math.maxInt(u32) * math.minInt(i32)); 101 | 102 | testing.expectEqual(mul(u32_min, u32_min), math.minInt(u32) * math.minInt(u32)); 103 | //testing.expectEqual(mul(u32_min, i32_max) , math.minInt(u32) * math.maxInt(i32)); 104 | //testing.expectEqual(mul(u32_min, i32_min) , math.minInt(u32) * math.minInt(i32)); 105 | 106 | //testing.expectEqual(mul(i32_max, i32_max) , math.maxInt(i32) * math.maxInt(i32)); 107 | //testing.expectEqual(mul(i32_max, i32_min) , math.maxInt(i32) * math.minInt(i32)); 108 | 109 | //testing.expectEqual(mul(i32_min, i32_min) , math.minInt(i32) * math.minInt(i32)); 110 | } 111 | 112 | test "math.safe.mul" { 113 | comptime testMul(); 114 | testMul(); 115 | } 116 | -------------------------------------------------------------------------------- /src/parser.zig: -------------------------------------------------------------------------------- 1 | pub const string = @import("parser/string.zig"); 2 | pub const json = @import("parser/json.zig"); 3 | pub usingnamespace @import("parser/common.zig"); 4 | 5 | test "parser" { 6 | _ = string; 7 | _ = json; 8 | } 9 | -------------------------------------------------------------------------------- /src/parser/common.zig: -------------------------------------------------------------------------------- 1 | const string = @import("string.zig"); 2 | 3 | const std = @import("std"); 4 | const @"struct" = @import("../struct.zig"); 5 | const @"union" = @import("../union.zig"); 6 | 7 | const StructField = @"struct".Field; 8 | const Struct = @"struct".Struct; 9 | const UnionField = @"union".Field; 10 | const Union = @"union".Union; 11 | 12 | const debug = std.debug; 13 | const mem = std.mem; 14 | const testing = std.testing; 15 | 16 | pub fn ParseResult(comptime Input: type, comptime Result: type) type { 17 | return struct { 18 | input: Input, 19 | result: Result, 20 | }; 21 | } 22 | 23 | fn Func(comptime Result: type) type { 24 | return @TypeOf(struct { 25 | fn func(input: var) ?ParseResult(@TypeOf(input), Result) { 26 | unreachable; 27 | } 28 | }.func); 29 | } 30 | 31 | pub fn eatIf(comptime Token: type, comptime predicate: fn (Token) bool) type { 32 | return struct { 33 | pub const Result = Token; 34 | 35 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 36 | const curr = input.curr() orelse return null; 37 | if (!predicate(curr)) 38 | return null; 39 | 40 | return ParseResult(@TypeOf(input), Result){ 41 | .input = input.next(), 42 | .result = curr, 43 | }; 44 | } 45 | }; 46 | } 47 | 48 | pub fn end() type { 49 | return struct { 50 | pub const Result = void; 51 | 52 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 53 | if (input.curr() == null) { 54 | return ParseResult(@TypeOf(input), void){ 55 | .input = input, 56 | .result = {}, 57 | }; 58 | } 59 | 60 | return null; 61 | } 62 | }; 63 | } 64 | 65 | pub fn sequence(comptime parsers: []const type) type { 66 | return struct { 67 | pub const Result = SeqParserResult(parsers); 68 | 69 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 70 | var res: Result = undefined; 71 | var next = input; 72 | 73 | inline for (@TypeOf(res).fields) |field, i| { 74 | const r = parsers[i].parse(next) orelse return null; 75 | next = r.input; 76 | res.ptr(i).* = r.result; 77 | } 78 | 79 | return ParseResult(@TypeOf(input), Result){ 80 | .input = next, 81 | .result = res, 82 | }; 83 | } 84 | }; 85 | } 86 | 87 | fn SeqParserResult(comptime parsers: []const type) type { 88 | var results: [parsers.len]StructField(usize) = undefined; 89 | for (parsers) |Par, i| 90 | results[i] = StructField(usize).init(i, Par.Result); 91 | 92 | return Struct(usize, results); 93 | } 94 | 95 | pub fn options(comptime parsers: []const type) type { 96 | return struct { 97 | pub const Result = OptParserResult(parsers); 98 | 99 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 100 | inline for (parsers) |Par| { 101 | if (Par.parse(input)) |res| { 102 | return ParseResult(@TypeOf(input), Result){ 103 | .input = res.input, 104 | .result = res.result, 105 | }; 106 | } 107 | } 108 | 109 | return null; 110 | } 111 | }; 112 | } 113 | 114 | fn OptParserResult(comptime parsers: []const type) type { 115 | debug.assert(parsers.len != 0); 116 | const Res = parsers[0].Result; 117 | for (parsers[1..]) |Par| { 118 | //@compileLog(Par.Result, Res); 119 | debug.assert(Par.Result == Res); 120 | } 121 | 122 | return Res; 123 | } 124 | 125 | pub fn then( 126 | comptime Parser: type, 127 | comptime Res: type, 128 | comptime func: fn (Parser.Result) Res, 129 | ) type { 130 | return struct { 131 | pub const Result = Res; 132 | 133 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 134 | const parsed = Parser.parse(input) orelse return null; 135 | const res = func(parsed.result); 136 | return ParseResult(@TypeOf(input), Result){ 137 | .input = parsed.input, 138 | .result = res, 139 | }; 140 | } 141 | }; 142 | } 143 | 144 | pub fn toVoid(comptime T: type) fn (T) void { 145 | return struct { 146 | fn toVoid(arg: T) void {} 147 | }.toVoid; 148 | } 149 | 150 | pub const nothing = struct { 151 | pub const Result = void; 152 | 153 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 154 | return ParseResult(@TypeOf(input), Result){ 155 | .input = input, 156 | .result = {}, 157 | }; 158 | } 159 | }; 160 | 161 | fn refFunc() type { 162 | unreachable; 163 | } 164 | 165 | pub fn ref(comptime Res: type, comptime f: @TypeOf(refFunc)) type { 166 | return struct { 167 | pub const Result = Res; 168 | 169 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 170 | return f().parse(input); 171 | } 172 | }; 173 | } 174 | 175 | fn isPred(comptime c: u8) fn (u8) bool { 176 | return struct { 177 | fn predicate(char: u8) bool { 178 | return char == c; 179 | } 180 | }.predicate; 181 | } 182 | 183 | fn testSuccess(comptime P: type, str: []const u8, result: var) void { 184 | const res = P.parse(string.Input.init(str)) orelse unreachable; 185 | comptime testing.expectEqual(@sizeOf(P.Result), @sizeOf(@TypeOf(result))); 186 | if (@sizeOf(P.Result) != 0) 187 | testing.expectEqualSlices(u8, &mem.toBytes(result), &mem.toBytes(res.result)); 188 | } 189 | 190 | fn testFail(comptime P: type, str: []const u8) void { 191 | if (P.parse(string.Input.init(str))) |res| { 192 | testing.expect(res.input.str.len != 0); 193 | } 194 | } 195 | 196 | test "parser.eatIf" { 197 | const P = eatIf(u8, comptime isPred('a')); 198 | 199 | testSuccess(P, "a", @as(u8, 'a')); 200 | testFail(P, "b"); 201 | } 202 | 203 | test "parser.end" { 204 | const P = end(); 205 | 206 | testSuccess(P, "", {}); 207 | testFail(P, "b"); 208 | } 209 | 210 | test "parser.sequence" { 211 | const A = eatIf(u8, comptime isPred('a')); 212 | const B = eatIf(u8, comptime isPred('b')); 213 | const C = eatIf(u8, comptime isPred('c')); 214 | const P = sequence([_]type{ A, B, C }); 215 | 216 | testSuccess(P, "abc", "abc"); 217 | testFail(P, "cba"); 218 | } 219 | 220 | test "parser.options" { 221 | const A = eatIf(u8, comptime isPred('a')); 222 | const B = eatIf(u8, comptime isPred('b')); 223 | const C = eatIf(u8, comptime isPred('c')); 224 | const P = options(&[_]type{ A, B, C }); 225 | 226 | testSuccess(P, "a", @as(u8, 'a')); 227 | testSuccess(P, "b", @as(u8, 'b')); 228 | testSuccess(P, "c", @as(u8, 'c')); 229 | testFail(P, "d"); 230 | } 231 | 232 | test "parser.options" { 233 | const A = eatIf(u8, comptime isPred('a')); 234 | const B = eatIf(u8, comptime isPred('b')); 235 | const C = eatIf(u8, comptime isPred('c')); 236 | const P = options(&[_]type{ A, B, C }); 237 | 238 | testSuccess(P, "a", @as(u8, 'a')); 239 | testSuccess(P, "b", @as(u8, 'b')); 240 | testSuccess(P, "c", @as(u8, 'c')); 241 | testFail(P, "d"); 242 | } 243 | 244 | test "parser.then" { 245 | const A = eatIf(u8, comptime isPred('a')); 246 | const B = eatIf(u8, comptime isPred('b')); 247 | const S = sequence(&[_]type{ A, B }); 248 | const P = options(&[_]type{ 249 | then(S, void, comptime toVoid(S.Result)), 250 | then(A, void, comptime toVoid(A.Result)), 251 | }); 252 | 253 | testSuccess(P, "a", {}); 254 | testSuccess(P, "ab", {}); 255 | testFail(P, "ba"); 256 | } 257 | 258 | test "parser.nothing" { 259 | testSuccess(nothing, "a", {}); 260 | testSuccess(nothing, "aaa", {}); 261 | testSuccess(nothing, "qqq", {}); 262 | testSuccess(nothing, "", {}); 263 | testSuccess(nothing, "2", {}); 264 | testSuccess(nothing, "10", {}); 265 | } 266 | -------------------------------------------------------------------------------- /src/parser/json.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | usingnamespace @import("common.zig"); 3 | usingnamespace @import("string.zig"); 4 | 5 | const debug = std.debug; 6 | const math = std.math; 7 | const mem = std.mem; 8 | const fmt = std.fmt; 9 | 10 | fn valueRef() type { 11 | return value; 12 | } 13 | 14 | const value = options([]type{ 15 | object, 16 | array, 17 | jstring, 18 | number, 19 | string("true"), 20 | string("false"), 21 | string("null"), 22 | }); 23 | 24 | const object = options([]type{ 25 | sequence([]type{ char('{'), members, char('}') }), 26 | sequence([]type{ char('{'), ws, char('}') }), 27 | }); 28 | 29 | const members = options([]type{ 30 | sequence([]type{ member, char(','), members }), 31 | member, 32 | }); 33 | 34 | const member = sequence([]type{ ws, string, ws, char(':'), element }); 35 | 36 | const array = options([]type{ 37 | sequence([]type{ char('['), elements, char(']') }), 38 | sequence([]type{ char('['), ws, char(']') }), 39 | }); 40 | 41 | const elements = options([]type{ 42 | sequence([]type{ element, char(','), elements }), 43 | element, 44 | }); 45 | 46 | const element = sequence([]type{ ws, ref(void, valueRef), ws }); 47 | 48 | const jstring = sequence([]type{ char('"'), characters, char('"') }); 49 | 50 | const characters = options([]type{ 51 | sequence([]type{ character, characters }), 52 | character, 53 | }); 54 | 55 | // TODO: Unicode 56 | const character = options([]type{ 57 | range(' ', '!'), 58 | range('#', '['), 59 | range(']', '~'), 60 | sequence([]type{ char('\\'), escape }), 61 | }); 62 | 63 | const escape = options([]type{ 64 | char('"'), 65 | char('\\'), 66 | char('/'), 67 | char('b'), 68 | char('n'), 69 | char('r'), 70 | char('t'), 71 | sequence([]type{ char('u'), hex, hex, hex, hex }), 72 | }); 73 | 74 | const hex = options([]type{ 75 | digit, 76 | range('A', 'F'), 77 | range('a', 'f'), 78 | }); 79 | 80 | const number = sequence([]type{ int, frac, exp }); 81 | 82 | const int = options([]type{ 83 | sequence([]type{ char('-'), onenine, digits }), 84 | sequence([]type{ char('-'), digit }), 85 | sequence([]type{ onenine, digits }), 86 | sequence([]type{digit}), 87 | }); 88 | 89 | const digits = options([]type{ 90 | sequence([]type{ digit, digits }), 91 | digit, 92 | }); 93 | 94 | const digit = options([]type{ 95 | char('0'), 96 | onenine, 97 | }); 98 | 99 | const onenine = range('1', '9'); 100 | 101 | const frac = options([]type{ 102 | sequence([]type{ char('.'), digits }), 103 | string(""), 104 | }); 105 | 106 | const exp = options([]type{ 107 | sequence([]type{ char('E'), sign, digits }), 108 | sequence([]type{ char('e'), sign, digits }), 109 | string(""), 110 | }); 111 | 112 | const sign = options([]type{ 113 | char('+'), 114 | char('-'), 115 | string(""), 116 | }); 117 | 118 | fn wsRef() type { 119 | return ws; 120 | } 121 | 122 | const ws0x09 = sequence([]type{ char(0x09), ref(u8, wsRef) }); 123 | const ws0x0a = sequence([]type{ char(0x0a), ref(u8, wsRef) }); 124 | const ws0x0d = sequence([]type{ char(0x0d), ref(u8, wsRef) }); 125 | const ws0x20 = sequence([]type{ char(0x20), ref(u8, wsRef) }); 126 | 127 | const ws = options([]type{ 128 | then(ws0x09, void, toVoid(ws0x09.Result)), 129 | then(ws0x0a, void, toVoid(ws0x0a.Result)), 130 | then(ws0x0d, void, toVoid(ws0x0d.Result)), 131 | then(ws0x20, void, toVoid(ws0x20.Result)), 132 | nothing, 133 | }); 134 | 135 | //test "parser.json" { 136 | // _ = element.parse(Input.init("{}")) orelse unreachable; 137 | //} 138 | -------------------------------------------------------------------------------- /src/parser/string.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const parser = @import("common.zig"); 3 | 4 | const debug = std.debug; 5 | const math = std.math; 6 | const mem = std.mem; 7 | const fmt = std.fmt; 8 | const testing = std.testing; 9 | 10 | const ParseResult = parser.ParseResult; 11 | 12 | pub const Input = struct { 13 | str: []const u8, 14 | 15 | pub fn init(str: []const u8) Input { 16 | return Input{ .str = str }; 17 | } 18 | 19 | pub fn curr(input: Input) ?u8 { 20 | if (input.str.len != 0) 21 | return input.str[0]; 22 | 23 | return null; 24 | } 25 | 26 | pub fn next(input: Input) Input { 27 | return Input{ .str = if (input.str.len != 0) input.str[1..] else input.str }; 28 | } 29 | }; 30 | 31 | pub fn char(comptime c1: u8) type { 32 | return parser.eatIf(u8, struct { 33 | fn predicate(c2: u8) bool { 34 | return c1 == c2; 35 | } 36 | }.predicate); 37 | } 38 | 39 | pub fn range(comptime a: u8, comptime b: u8) type { 40 | return parser.eatIf(u8, struct { 41 | fn predicate(c: u8) bool { 42 | return a <= c and c <= b; 43 | } 44 | }.predicate); 45 | } 46 | 47 | pub fn uint(comptime Int: type, comptime base: u8) type { 48 | return struct { 49 | pub const Result = Int; 50 | 51 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 52 | const first = input.curr() orelse return null; 53 | const first_digit = fmt.charToDigit(first, base) catch return null; 54 | var res = math.cast(Result, first_digit) catch return null; 55 | 56 | var next = input.next(); 57 | while (next.curr()) |curr| : (next = next.next()) { 58 | const digit = fmt.charToDigit(curr, base) catch break; 59 | res = math.mul(Result, res, base) catch return null; 60 | res = math.add(Result, res, digit) catch return null; 61 | } 62 | 63 | return ParseResult(@TypeOf(input), Result){ 64 | .input = next, 65 | .result = res, 66 | }; 67 | } 68 | }; 69 | } 70 | 71 | pub fn string(comptime s: []const u8) type { 72 | return struct { 73 | pub const Result = []const u8; 74 | 75 | pub fn parse(input: var) ?ParseResult(@TypeOf(input), Result) { 76 | var next = input; 77 | for (s) |c| { 78 | const curr = next.curr() orelse return null; 79 | if (c != curr) 80 | return null; 81 | 82 | next = next.next(); 83 | } 84 | 85 | return ParseResult(@TypeOf(input), Result){ 86 | .input = next, 87 | .result = s, 88 | }; 89 | } 90 | }; 91 | } 92 | 93 | fn testSuccess(comptime P: type, str: []const u8, result: var) void { 94 | const res = P.parse(Input.init(str)) orelse unreachable; 95 | testing.expectEqual(res.input.str.len, 0); 96 | comptime testing.expectEqual(@sizeOf(P.Result), @sizeOf(@TypeOf(result))); 97 | if (@sizeOf(P.Result) != 0) 98 | testing.expectEqualSlices(u8, &mem.toBytes(result), &mem.toBytes(res.result)); 99 | } 100 | 101 | fn testFail(comptime P: type, str: []const u8) void { 102 | if (P.parse(Input.init(str))) |res| { 103 | testing.expect(res.input.str.len != 0); 104 | } 105 | } 106 | 107 | test "parser.string.char" { 108 | const P = char('a'); 109 | 110 | comptime var i = 0; 111 | inline while (i < 'a') : (i += 1) 112 | testFail(P, &[_]u8{i}); 113 | inline while (i <= 'a') : (i += 1) 114 | testSuccess(P, &[_]u8{i}, @as(u8, i)); 115 | inline while (i <= math.maxInt(u8)) : (i += 1) 116 | testFail(P, &[_]u8{i}); 117 | } 118 | 119 | test "parser.string.range" { 120 | const P = range('a', 'z'); 121 | 122 | comptime var i = 0; 123 | inline while (i < 'a') : (i += 1) 124 | testFail(P, &[_]u8{i}); 125 | inline while (i <= 'z') : (i += 1) 126 | testSuccess(P, &[_]u8{i}, @as(u8, i)); 127 | inline while (i <= math.maxInt(u8)) : (i += 1) 128 | testFail(P, &[_]u8{i}); 129 | } 130 | 131 | test "parser.string.uint" { 132 | for ([_][]const u8{ 133 | "0000", "1111", "7777", "9999", 134 | "aaaa", "AAAA", "ffff", "FFFF", 135 | "zzzz", "ZZZZ", "0123", "4567", 136 | "89AB", "CDEF", "GHIJ", "KLMN", 137 | "OPQR", "STUV", "WYZa", "bcde", 138 | "fghi", "jklm", "nopq", "rstu", 139 | "bwyz", 140 | }) |str| { 141 | comptime var base = 1; 142 | inline while (base <= 36) : (base += 1) { 143 | const P = uint(u64, base); 144 | if (fmt.parseUnsigned(u64, str, base)) |res| { 145 | testSuccess(P, str, res); 146 | } else |err| { 147 | testFail(P, str); 148 | } 149 | } 150 | } 151 | } 152 | 153 | test "parser.string.string" { 154 | const s: []const u8 = "1234"; 155 | const P = string(s); 156 | 157 | testSuccess(P, s, s); 158 | testFail(P, "1235"); 159 | } 160 | -------------------------------------------------------------------------------- /src/platform.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const builtin = @import("builtin"); 3 | 4 | const debug = std.debug; 5 | const mem = std.mem; 6 | const testing = std.testing; 7 | 8 | pub const lu16 = Int(u16, builtin.Endian.Little); 9 | pub const lu32 = Int(u32, builtin.Endian.Little); 10 | pub const lu64 = Int(u64, builtin.Endian.Little); 11 | pub const lu128 = Int(u128, builtin.Endian.Little); 12 | pub const li16 = Int(i16, builtin.Endian.Little); 13 | pub const li32 = Int(i32, builtin.Endian.Little); 14 | pub const li64 = Int(i64, builtin.Endian.Little); 15 | pub const li128 = Int(i128, builtin.Endian.Little); 16 | 17 | pub const bu16 = Int(u16, builtin.Endian.Big); 18 | pub const bu32 = Int(u32, builtin.Endian.Big); 19 | pub const bu64 = Int(u64, builtin.Endian.Big); 20 | pub const bu128 = Int(u128, builtin.Endian.Big); 21 | pub const bi16 = Int(i16, builtin.Endian.Big); 22 | pub const bi32 = Int(i32, builtin.Endian.Big); 23 | pub const bi128 = Int(i128, builtin.Endian.Big); 24 | 25 | /// A data structure representing an integer of a specific endianess 26 | pub fn Int(comptime Inner: type, comptime endian: builtin.Endian) type { 27 | comptime debug.assert(@typeInfo(Inner) == .Int); 28 | 29 | return packed struct { 30 | const Self = @This(); 31 | 32 | bytes: [@sizeOf(Inner)]u8, 33 | 34 | pub fn init(v: Inner) Self { 35 | var res: Self = undefined; 36 | mem.writeInt(Inner, &res.bytes, v, endian); 37 | 38 | return res; 39 | } 40 | 41 | pub fn value(int: Self) Inner { 42 | return mem.readInt(Inner, &int.bytes, endian); 43 | } 44 | }; 45 | } 46 | 47 | test "platform.Int" { 48 | const value: u32 = 0x12345678; 49 | const numLittle = Int(u32, builtin.Endian.Little).init(value); 50 | const numBig = Int(u32, builtin.Endian.Big).init(value); 51 | testing.expectEqual(value, numLittle.value()); 52 | testing.expectEqual(value, numBig.value()); 53 | testing.expectEqualSlices(u8, &[_]u8{ 0x78, 0x56, 0x34, 0x12 }, &numLittle.bytes); 54 | testing.expectEqualSlices(u8, &[_]u8{ 0x12, 0x34, 0x56, 0x78 }, &numBig.bytes); 55 | } 56 | -------------------------------------------------------------------------------- /src/struct.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const debug = std.debug; 3 | const mem = std.mem; 4 | const testing = std.testing; 5 | 6 | /// Determin the runtime size requirement of N types continues in memory (in bytes). 7 | fn runtimeSize(comptime fields: var) comptime_int { 8 | var res = 0; 9 | for (fields) |field| { 10 | res += @sizeOf(field.Value); 11 | } 12 | 13 | return res; 14 | } 15 | 16 | pub fn Field(comptime T: type) type { 17 | return struct { 18 | key: T, 19 | Value: type, 20 | 21 | pub fn init(key: T, comptime Value: type) @This() { 22 | return @This(){ 23 | .key = key, 24 | .Value = Value, 25 | }; 26 | } 27 | }; 28 | } 29 | 30 | pub fn Struct(comptime Key: type, comptime field_array: var) type { 31 | for (field_array) |a, i| { 32 | for (field_array[i + 1 ..]) |b| { 33 | // TODO: Abitrary key equal 34 | debug.assert(a.key != b.key); 35 | } 36 | } 37 | 38 | return struct { 39 | pub const fields = field_array; 40 | 41 | // In order for us to store the tuples values, we have 42 | // to type erase away the values, and store them as bytes. 43 | data: [runtimeSize(fields)]u8, 44 | 45 | pub fn field(s: @This(), comptime key: Key) GetField(key).Value { 46 | return s.ptrConst(key).*; 47 | } 48 | 49 | pub fn ptr(s: *@This(), comptime key: Key) *align(1) GetField(key).Value { 50 | const i = comptime index(key); 51 | const offset = comptime runtimeSize(fields[0..i]); 52 | return &std.mem.bytesAsSlice(GetField(key).Value, s.data[offset..])[0]; 53 | } 54 | 55 | pub fn ptrConst(s: *const @This(), comptime key: Key) *align(1) const GetField(key).Value { 56 | const i = comptime index(key); 57 | const offset = comptime runtimeSize(fields[0..i]); 58 | return &std.mem.bytesAsSlice(GetField(key).Value, s.data[offset..])[0]; 59 | } 60 | 61 | fn GetField(comptime key: Key) Field(Key) { 62 | return fields[index(key)]; 63 | } 64 | 65 | fn index(comptime key: Key) usize { 66 | inline for (fields) |f, i| { 67 | if (f.key == key) 68 | return i; 69 | } 70 | 71 | unreachable; 72 | } 73 | }; 74 | } 75 | 76 | test "struct" { 77 | const T = Struct(u8, [_]Field(u8){ 78 | Field(u8).init(0, u8), 79 | Field(u8).init(1, u16), 80 | Field(u8).init(2, f32), 81 | }); 82 | 83 | const s = blk: { 84 | var res: T = undefined; 85 | res.ptr(0).* = 11; 86 | res.ptr(1).* = 22; 87 | res.ptr(2).* = 33; 88 | break :blk res; 89 | }; 90 | testing.expectEqual(s.field(0), 11); 91 | testing.expectEqual(s.field(1), 22); 92 | testing.expectEqual(s.field(2), 33); 93 | } 94 | -------------------------------------------------------------------------------- /src/union.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const compare = @import("../src/generic"); 3 | const debug = std.debug; 4 | const mem = std.mem; 5 | const math = std.math; 6 | const testing = std.testing; 7 | 8 | /// Determin the max runtime size requirement to a union of N types. 9 | fn runtimeSize(comptime fields: var) comptime_int { 10 | var res = 0; 11 | for (fields) |field| { 12 | if (res < @sizeOf(field.Payload)) 13 | res = @sizeOf(field.Payload); 14 | } 15 | 16 | return res; 17 | } 18 | 19 | pub fn Field(comptime T: type) type { 20 | return struct { 21 | key: T, 22 | Payload: type, 23 | 24 | pub fn init(key: T, comptime Payload: type) @This() { 25 | return @This(){ 26 | .key = key, 27 | .Payload = Payload, 28 | }; 29 | } 30 | }; 31 | } 32 | 33 | pub fn Union(comptime Key: type, comptime field_array: var) type { 34 | for (field_array) |a, i| { 35 | for (field_array[i + 1 ..]) |b| { 36 | // TODO: Abitrary key equal 37 | debug.assert(a.key != b.key); 38 | } 39 | } 40 | 41 | return struct { 42 | pub const fields = field_array; 43 | 44 | // In order for us to store the eithers values, we have 45 | // to type erase away the values, and store them as bytes. 46 | payload: [runtimeSize(fields)]u8, 47 | 48 | key: usize, // TODO: Log2Int 49 | 50 | pub fn init(comptime key: Key, value: GetField(key).Payload) @This() { 51 | var res: @This() = undefined; 52 | res.key = comptime index(key); 53 | res.ptr(key).?.* = value; 54 | return res; 55 | } 56 | 57 | pub fn field(u: @This(), comptime key: Key) ?GetField(key).Payload { 58 | if (u.ptrConst(key)) |p| 59 | return p.*; 60 | 61 | return null; 62 | } 63 | 64 | pub fn ptr(u: *@This(), comptime key: Key) ?*align(1) GetField(key).Payload { 65 | const i = comptime index(key); 66 | if (u.key != i) 67 | return null; 68 | 69 | return &std.mem.bytesAsSlice(GetField(key).Payload, u.payload[0..])[0]; 70 | } 71 | 72 | pub fn ptrConst(u: *const @This(), comptime key: Key) ?*align(1) const GetField(key).Payload { 73 | const i = comptime index(key); 74 | if (u.key != i) 75 | return null; 76 | 77 | return &std.mem.bytesAsSlice(GetField(key).Payload, u.payload[0..])[0]; 78 | } 79 | 80 | fn GetField(comptime key: Key) Field(Key) { 81 | return fields[index(key)]; 82 | } 83 | 84 | fn index(comptime key: Key) usize { 85 | inline for (fields) |f, i| { 86 | if (f.key == key) 87 | return i; 88 | } 89 | 90 | unreachable; 91 | } 92 | }; 93 | } 94 | 95 | test "union" { 96 | const T = Union(u8, [_]Field(u8){ 97 | Field(u8).init(0, u8), 98 | Field(u8).init(1, u16), 99 | Field(u8).init(2, f32), 100 | }); 101 | const a = T.init(0, 11); 102 | const b = T.init(1, 22); 103 | const c = T.init(2, 33); 104 | testing.expectEqual(a.field(0).?, 11); 105 | testing.expectEqual(b.field(0), null); 106 | testing.expectEqual(c.field(0), null); 107 | testing.expectEqual(a.field(1), null); 108 | testing.expectEqual(b.field(1).?, 22); 109 | testing.expectEqual(c.field(1), null); 110 | testing.expectEqual(a.field(2), null); 111 | testing.expectEqual(b.field(2), null); 112 | testing.expectEqual(c.field(2).?, 33); 113 | } 114 | --------------------------------------------------------------------------------