about summary refs log tree commit diff
path: root/lang/zig
diff options
context:
space:
mode:
Diffstat (limited to 'lang/zig')
-rw-r--r--lang/zig/align.zig48
-rw-r--r--lang/zig/allowzero.zig7
-rw-r--r--lang/zig/anon-list.zig9
-rw-r--r--lang/zig/arrays.zig100
-rw-r--r--lang/zig/assign.zig25
-rw-r--r--lang/zig/blocks.zig18
-rw-r--r--lang/zig/c-string.zig12
-rw-r--r--lang/zig/comments.zig11
-rw-r--r--lang/zig/comptime-vars.zig16
-rw-r--r--lang/zig/defer.zig62
-rw-r--r--lang/zig/enum-literal.zig35
-rw-r--r--lang/zig/enums-extern.zig2
-rw-r--r--lang/zig/enums-packed.zig7
-rw-r--r--lang/zig/enums.zig86
-rw-r--r--lang/zig/errdefer.zig17
-rw-r--r--lang/zig/error-set.zig22
-rw-r--r--lang/zig/error-trace.zig44
-rw-r--r--lang/zig/error-union.zig115
-rw-r--r--lang/zig/fn.zig78
-rw-r--r--lang/zig/for.zig100
-rw-r--r--lang/zig/hello-again.zig5
-rw-r--r--lang/zig/hello.zig6
-rw-r--r--lang/zig/if.zig141
-rw-r--r--lang/zig/infer-list-literal.zig13
-rw-r--r--lang/zig/multidim.zig20
-rw-r--r--lang/zig/namespaced-global.zig15
-rw-r--r--lang/zig/noreturn.zig10
-rw-r--r--lang/zig/opaque.zig12
-rw-r--r--lang/zig/pointers.zig93
-rw-r--r--lang/zig/sentinel-terminated.zig10
-rw-r--r--lang/zig/shadow.zig10
-rw-r--r--lang/zig/slices.zig77
-rw-r--r--lang/zig/string.zig14
-rw-r--r--lang/zig/struct-anon.zig20
-rw-r--r--lang/zig/struct-default.zig11
-rw-r--r--lang/zig/struct-name.zig14
-rw-r--r--lang/zig/struct-packed.zig73
-rw-r--r--lang/zig/struct-result.zig7
-rw-r--r--lang/zig/structs.zig133
-rw-r--r--lang/zig/switch-enum-literal.zig13
-rw-r--r--lang/zig/switch-exhaust.zig9
-rw-r--r--lang/zig/switch.zig86
-rw-r--r--lang/zig/tls.zig22
-rw-r--r--lang/zig/union-anon.zig13
-rw-r--r--lang/zig/union-tag.zig68
-rw-r--r--lang/zig/union.zig15
-rw-r--r--lang/zig/unreachable.zig22
-rw-r--r--lang/zig/values.zig54
-rw-r--r--lang/zig/while-inline.zig20
-rw-r--r--lang/zig/while-unpack.zig43
-rw-r--r--lang/zig/while.zig73
51 files changed, 1936 insertions, 0 deletions
diff --git a/lang/zig/align.zig b/lang/zig/align.zig
new file mode 100644
index 0000000..b81740a
--- /dev/null
+++ b/lang/zig/align.zig
@@ -0,0 +1,48 @@
+const arch = std.Target.current.cpu.arch;
+const bytesAsSlice = std.mem.bytesAsSlice;
+const expect = std.testing.expect;
+const sliceAsBytes = std.mem.sliceAsBytes;
+const std = @import("std");
+
+test "variable alignment" {
+    var x: i32 = 1234;
+    const align_of_i32 = @alignOf(@TypeOf(x));
+    expect(@TypeOf(&x) == *i32);
+    expect(*i32 == *align(align_of_i32) i32);
+    if (arch == .x86_64)
+        expect(@typeInfo(*i32).Pointer.alignment == 4);
+}
+
+var foo: u8 align(4) = 100;
+
+test "global variable alignment" {
+    expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
+    expect(@TypeOf(&foo) == *align(4) u8);
+    const as_pointer_to_array: *[1]u8 = &foo;
+    const as_slice: []u8 = as_pointer_to_array;
+    expect(@TypeOf(as_slice) == []align(4) u8);
+}
+
+fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; }
+fn noop1() align(1) void {}
+fn noop4() align(4) void {}
+
+test "function alignment" {
+    expect(derp() == 1234);
+    expect(@TypeOf(noop1) == fn() align(1) void);
+    expect(@TypeOf(noop4) == fn() align(4) void);
+    noop1();
+    noop4();
+}
+
+test "pointer alignment safety" {
+    var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
+    const bytes = sliceAsBytes(array[0..]);
+    expect(bar(bytes) == 0x11111111);
+}
+
+fn bar(bytes: []u8) u32 {
+    const slice4 = bytes[1..5];
+    const int_slice = bytesAsSlice(u32, @alignCast(4, slice4));
+    return int_slice[0];
+}
diff --git a/lang/zig/allowzero.zig b/lang/zig/allowzero.zig
new file mode 100644
index 0000000..39233dc
--- /dev/null
+++ b/lang/zig/allowzero.zig
@@ -0,0 +1,7 @@
+const expect = @import("std").testing.expect;
+
+test "allowzero" {
+    var zero: usize = 0;
+    var ptr = @intToPtr(*allowzero i32, zero);
+    expect(@ptrToInt(ptr) == 0);
+}
diff --git a/lang/zig/anon-list.zig b/lang/zig/anon-list.zig
new file mode 100644
index 0000000..f71bce8
--- /dev/null
+++ b/lang/zig/anon-list.zig
@@ -0,0 +1,9 @@
+const expect = @import("std").testing.expect;
+
+test "anonymous list literal syntax" {
+    var array: [4]u8 = .{11, 22, 33, 44};
+    expect(array[0] == 11);
+    expect(array[1] == 22);
+    expect(array[2] == 33);
+    expect(array[3] == 44);
+}
diff --git a/lang/zig/arrays.zig b/lang/zig/arrays.zig
new file mode 100644
index 0000000..d2658c2
--- /dev/null
+++ b/lang/zig/arrays.zig
@@ -0,0 +1,100 @@
+const expect = @import("std").testing.expect;
+const mem = @import("std").mem;
+
+// array literal
+const message = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
+
+// get the size of an array
+comptime {
+    expect(message.len == 5);
+}
+
+// A string literal is a pointer to an array literal.
+const same_message = "hello";
+
+comptime {
+    expect(mem.eql(u8, &message, same_message));
+}
+
+test "iterate over an array" {
+    var sum: usize = 0;
+    for (message) |byte|
+        sum += byte;
+    expect(sum == 'h' + 'e' + 'l' * 2 + 'o');
+}
+
+// modifiable array
+var some_integers: [100]i32 = undefined;
+
+test "modify an array" {
+    for (some_integers) |*item, i|
+        item.* = @intCast(i32, i);
+    expect(some_integers[10] == 10);
+    expect(some_integers[99] == 99);
+}
+
+// array concatenation works if the values are known
+// at compile time
+const part_one = [_]i32{ 1, 2, 3, 4 };
+const part_two = [_]i32{ 5, 6, 7, 8 };
+const all_of_it = part_one ++ part_two;
+comptime {
+    expect(mem.eql(i32, &all_of_it, &[_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }));
+}
+
+// remember that string literals are arrays
+const hello = "hello";
+const world = "world";
+const hello_world = hello ++ " " ++ world;
+comptime {
+    expect(mem.eql(u8, hello_world, "hello world"));
+}
+
+// ** does repeating patterns
+const pattern = "ab" ** 3;
+comptime {
+    expect(mem.eql(u8, pattern, "ababab"));
+}
+
+// initialize an array to zero
+const all_zero = [_]u16{0} ** 10;
+
+comptime {
+    expect(all_zero.len == 10);
+    expect(all_zero[5] == 0);
+}
+
+// use compile-time code to initialize an array
+var fancy_array = init: {
+    var initial_value: [10]Point = undefined;
+    for (initial_value) |*pt, i| {
+        pt.* = Point{
+            .x = @intCast(i32, i),
+            .y = @intCast(i32, i) * 2,
+        };
+    }
+    break :init initial_value;
+};
+const Point = struct {
+    x: i32,
+    y: i32,
+};
+
+test "compile-time array initialization" {
+    expect(fancy_array[4].x == 4);
+    expect(fancy_array[4].y == 8);
+}
+
+// call a function to initialize an array
+var more_points = [_]Point{makePoint(3)} ** 10;
+fn makePoint(x: i32) Point {
+    return Point{
+        .x = x,
+        .y = x * 2,
+    };
+}
+test "array initialization with function calls" {
+    expect(more_points[4].x == 3);
+    expect(more_points[4].y == 6);
+    expect(more_points.len == 10);
+}
diff --git a/lang/zig/assign.zig b/lang/zig/assign.zig
new file mode 100644
index 0000000..9ce2400
--- /dev/null
+++ b/lang/zig/assign.zig
@@ -0,0 +1,25 @@
+const x = 1234;
+var b: u6 = add(10, a);
+const a = add(12, 34);
+
+fn add(l: u6, r: u6) u6 {
+    return l + r;
+}
+
+test "var" {
+    var y: u13 = 5678;
+    y += 1;
+    expect(y == 5679);
+}
+
+test "init" {
+    var z: i1 = undefined;
+    z = -1;
+}
+
+test "global" {
+    expect(a == 46);
+    expect(b == 56);
+}
+
+const expect = @import("std").testing.expect;
diff --git a/lang/zig/blocks.zig b/lang/zig/blocks.zig
new file mode 100644
index 0000000..5f6f0e8
--- /dev/null
+++ b/lang/zig/blocks.zig
@@ -0,0 +1,18 @@
+const expect = @import("std").testing.expect;
+
+test "labeled break from labeled block expression" {
+    var y: i32 = 123;
+
+    const x = blk: {
+        y += 1;
+        break :blk y;
+    };
+    expect(x == y);
+    expect(y == 124);
+}
+
+test "access variable after block scope" {
+    { var x: i32 = 1; }
+    // undeclared
+    // x += 1;
+}
diff --git a/lang/zig/c-string.zig b/lang/zig/c-string.zig
new file mode 100644
index 0000000..537fa3b
--- /dev/null
+++ b/lang/zig/c-string.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+
+// This is also available as `std.c.printf`.
+pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
+
+pub fn main() anyerror!void {
+    _ = printf("Hello, world!\n"); // OK
+
+    const msg = "Hello, world!\n";
+    const non_null_terminated_msg: [msg.len]u8 = msg.*;
+    _ = printf(&non_null_terminated_msg);
+}
diff --git a/lang/zig/comments.zig b/lang/zig/comments.zig
new file mode 100644
index 0000000..3d1c2e2
--- /dev/null
+++ b/lang/zig/comments.zig
@@ -0,0 +1,11 @@
+const expect = @import("std").testing.expect;
+
+test "comments" {
+    // Comments in Zig start with "//" and end at the next LF byte
+    // (end of line).  The below line is a comment, and won't be executed.
+
+    //expect(false);
+
+    const x = true;  // another comment
+    expect(x);
+}
diff --git a/lang/zig/comptime-vars.zig b/lang/zig/comptime-vars.zig
new file mode 100644
index 0000000..c20193d
--- /dev/null
+++ b/lang/zig/comptime-vars.zig
@@ -0,0 +1,16 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "comptime vars" {
+    var x: i32 = 1;
+    comptime var y: i32 = 1;
+
+    x += 1;
+    y += 1;
+
+    expect(x == 2);
+    expect(y == 2);
+
+    if (y != 2)
+        @compileError("wrong y value");
+}
diff --git a/lang/zig/defer.zig b/lang/zig/defer.zig
new file mode 100644
index 0000000..754b3af
--- /dev/null
+++ b/lang/zig/defer.zig
@@ -0,0 +1,62 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const print = std.debug.print;
+
+// defer will execute an expression at the end of the current scope.
+fn deferExample() usize {
+    var a: usize = 1;
+
+    {
+        defer a = 2;
+        a = 1;
+    }
+    expect(a == 2);
+
+    a = 5;
+    return a;
+}
+
+test "defer basics" {
+    expect(deferExample() == 5);
+}
+
+// If multiple defer statements are specified, they will be executed in
+// the reverse order they were run.
+fn deferUnwindExample() void {
+    print("\n", .{});
+
+    defer print("1 ", .{});
+    defer print("2 ", .{});
+    // defers are not run if they are never executed.
+    if (false) {
+        defer print("3 ", .{});
+    }
+    // somehow this isn't a scope though?
+    if (true) {
+        defer print("4 ", .{});
+    }
+}
+
+test "defer unwinding" {
+    deferUnwindExample();
+    print("\n", .{});
+}
+
+// The errdefer keyword is similar to defer,
+// but will only execute if the scope returns with an error.
+//
+// This is especially useful in allowing a function to clean up properly
+// on error, and replaces goto error handling tactics as seen in C.
+fn deferErrorExample(is_error: bool) !void {
+    print("\nstart of function\n", .{});
+    // This will always be executed on exit
+    defer print("end of function\n", .{});
+    errdefer print("encountered an error!\n", .{});
+    if (is_error)
+        return error.DeferError;
+}
+
+test "errdefer unwinding" {
+    deferErrorExample(false) catch {};
+    deferErrorExample(true) catch {};
+}
diff --git a/lang/zig/enum-literal.zig b/lang/zig/enum-literal.zig
new file mode 100644
index 0000000..587cfb2
--- /dev/null
+++ b/lang/zig/enum-literal.zig
@@ -0,0 +1,35 @@
+const expect = @import("std").testing.expect;
+
+const Color = enum { auto, off, on };
+
+test "enum literals" {
+    const color1: Color = .auto;
+    const color2 = Color.auto;
+    expect(color1 == color2);
+    expect(color1 == .auto);
+}
+
+test "switch using enum literals" {
+    expect(switch (Color.on) {
+        .auto => false,
+        .on => true,
+        .off => false,
+    });
+}
+
+const Number = enum(u8) { one, two, three, _ };
+
+test "switch on non-exhaustive enum" {
+    const number = Number.one;
+    expect(switch (number) {
+        .one => true,
+        .two,
+        .three => false,
+        _ => false,
+    });
+    const is_one = switch (number) {
+        .one => true,
+        else => false,
+    };
+    expect(is_one);
+}
diff --git a/lang/zig/enums-extern.zig b/lang/zig/enums-extern.zig
new file mode 100644
index 0000000..58610f1
--- /dev/null
+++ b/lang/zig/enums-extern.zig
@@ -0,0 +1,2 @@
+const Foo = extern enum { a, b, c };
+export fn entry(foo: Foo) void { }
diff --git a/lang/zig/enums-packed.zig b/lang/zig/enums-packed.zig
new file mode 100644
index 0000000..b5baaab
--- /dev/null
+++ b/lang/zig/enums-packed.zig
@@ -0,0 +1,7 @@
+const expect = @import("std").testing.expect;
+
+test "packed enum" {
+    // Warranty enum size.
+    const Number = packed enum(u2) { one, two, three };
+    expect(@sizeOf(Number) == @sizeOf(u8));
+}
diff --git a/lang/zig/enums.zig b/lang/zig/enums.zig
new file mode 100644
index 0000000..4b6a7a8
--- /dev/null
+++ b/lang/zig/enums.zig
@@ -0,0 +1,86 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const eql = std.mem.eql;
+
+// Declare an enum.
+const Type = enum { ok, not_ok };
+
+// Declare a specific instance of the enum variant.
+const c = Type.ok;
+
+test "enum ordinal value" {
+    // If you want access to the ordinal value of an enum,
+    // you can specify the tag type.
+    const Value = enum(u2) { zero, one, two };
+    const Value1 = enum { zero, one, two };
+
+    // Now you can cast between u2 and Value.
+    // The ordinal value starts from 0, counting up for each member.
+    expect(@enumToInt(Value.zero) == 0);
+    expect(@enumToInt(Value.one) == 1);
+    expect(@enumToInt(Value.two) == 2);
+
+    // Tag type needs not be explicitly specified:
+    expect(@enumToInt(Value1.zero) == 0);
+    expect(@enumToInt(Value1.one) == 1);
+    expect(@enumToInt(Value1.two) == 2);
+}
+
+// You can override the ordinal value for an enum.
+const Value2 = enum(u32) { hundred = 100, thousand = 1000, million = 1000000 };
+test "set enum ordinal value" {
+    expect(@enumToInt(Value2.hundred) == 100);
+    expect(@enumToInt(Value2.thousand) == 1000);
+    expect(@enumToInt(Value2.million) == 1000000);
+}
+
+// Enums can have methods, the same as structs and unions.
+// Enum methods are not special, they are only namespaced
+// functions that you can call with dot syntax.
+const Suit = enum {
+    clubs,
+    spades,
+    diamonds,
+    hearts,
+
+    pub fn isClubs(self: Suit) bool {
+        return self == Suit.clubs;
+    }
+};
+test "enum method" {
+    const p = Suit.spades;
+    expect(!p.isClubs());
+}
+
+// An enum variant of different types can be switched upon.
+const Foo = enum {
+    string,
+    number,
+    none,
+};
+test "enum variant switch" {
+    const p = Foo.number;
+    const what_is_it = switch (p) {
+        Foo.string => "this is a string",
+        Foo.number => "this is a number",
+        Foo.none => "this is a none",
+    };
+    expect(eql(u8, what_is_it, "this is a number"));
+}
+
+// @TagType can be used to access the integer tag type of an enum.
+const Small = enum { one, two, three, four };
+test "@TagType" {
+    expect(@TagType(Small) == u2);
+}
+
+// @typeInfo tells us the field count and the fields names:
+test "@typeInfo" {
+    expect(@typeInfo(Small).Enum.fields.len == 4);
+    expect(eql(u8, @typeInfo(Small).Enum.fields[1].name, "two"));
+}
+
+// @tagName gives a []const u8 representation of an enum value:
+test "@tagName" {
+    expect(eql(u8, @tagName(Small.three), "three"));
+}
diff --git a/lang/zig/errdefer.zig b/lang/zig/errdefer.zig
new file mode 100644
index 0000000..812b0f7
--- /dev/null
+++ b/lang/zig/errdefer.zig
@@ -0,0 +1,17 @@
+fn createFoo(param: i32) !Foo {
+    const foo = try tryToAllocateFoo();
+    // now we have allocated foo. we need to free it if the function fails.
+    // but we want to return it if the function succeeds.
+    errdefer deallocateFoo(foo);
+
+    const tmp_buf = allocateTmpBuffer() orelse return error.OutOfMemory;
+    // tmp_buf is truly a temporary resource, and we for sure want to clean it up
+    // before this block leaves scope
+    defer deallocateTmpBuffer(tmp_buf);
+
+    if (param > 1337) return error.InvalidParam;
+
+    // here the errdefer will not run since we're returning success from the function.
+    // but the defer will run!
+    return foo;
+}
diff --git a/lang/zig/error-set.zig b/lang/zig/error-set.zig
new file mode 100644
index 0000000..60b01aa
--- /dev/null
+++ b/lang/zig/error-set.zig
@@ -0,0 +1,22 @@
+const expect = @import("std").testing.expect;
+const FileOpenError = error { AccessDenied, OutOfMemory, FileNotFound };
+const AllocationError = error { OutOfMemory };
+
+fn foo(err: AllocationError) FileOpenError {
+    return err;
+}
+
+test "coerce subset to superset" {
+    const err = foo(AllocationError.OutOfMemory);
+    expect(err == FileOpenError.OutOfMemory);
+    expect(err == AllocationError.OutOfMemory);
+    expect(err == error.OutOfMemory);
+}
+
+fn bar(err: FileOpenError) AllocationError {
+    return err;
+}
+
+test "coerce superset to subset" {
+    bar(FileOpenError.OutOfMemory) catch {};
+}
diff --git a/lang/zig/error-trace.zig b/lang/zig/error-trace.zig
new file mode 100644
index 0000000..822f235
--- /dev/null
+++ b/lang/zig/error-trace.zig
@@ -0,0 +1,44 @@
+test "error return traces" {
+    try foo(12);
+}
+
+fn foo(x: i32) !void {
+    if (x >= 5) {
+        try bar();
+    } else {
+        try bang2();
+    }
+}
+
+fn bar() !void {
+    if (baz()) {
+        try quux();
+    } else |err| switch (err) {
+        error.FileNotFound => try hello(),
+        else => try another(),
+    }
+}
+
+fn baz() !void {
+    try bang1();
+}
+
+fn quux() !void {
+    try bang2();
+}
+
+fn hello() !void {
+    try bang2();
+}
+
+fn another() !void {
+    try bang1();
+}
+
+fn bang1() !void {
+    return error.FileNotFound;
+}
+
+fn bang2() !void {
+    return error.PermissionDenied;
+}
diff --git a/lang/zig/error-union.zig b/lang/zig/error-union.zig
new file mode 100644
index 0000000..8ec75b2
--- /dev/null
+++ b/lang/zig/error-union.zig
@@ -0,0 +1,115 @@
+const std = @import("std");
+const maxInt = std.math.maxInt;
+const expect = std.testing.expect;
+
+fn charToDigit(c: u8) u8 {
+    return switch (c) {
+        '0'...'9' => c - '0',
+        'A'...'Z' => c - 'A' + 10,
+        'a'...'z' => c - 'a' + 10,
+        else => maxInt(u8),
+    };
+}
+
+const ParseError = error{ InvalidChar, Overflow };
+
+pub fn parseU64(buf: []const u8, radix: u8) ParseError!u64 {
+    var x: u64 = 0;
+
+    return for (buf) |c| {
+        const digit = charToDigit(c);
+        if (digit >= radix)
+            break error.InvalidChar;
+        // x *= radix
+        if (@mulWithOverflow(u64, x, radix, &x))
+            break error.Overflow;
+        // x += digit
+        if (@addWithOverflow(u64, x, digit, &x))
+            break error.Overflow;
+    } else x;
+}
+
+test "parse u64" {
+    const result = try parseU64("1234", 10);
+    expect(result == 1234);
+}
+
+fn doAThing(str: []u8) !void {
+    const number = parseU64(str, 10) catch |err| return err;
+    // The same as
+    const number = try parseU64(str, 10);
+}
+
+test "error switch" {
+    if (parseU64("42069", 10)) |number| {
+        expect(number == 42069);
+    } else |err| switch (err) {
+        error.Overflow => {
+            // handle overflow...
+        },
+        // we promise that InvalidChar won't happen
+        // (or crash in debug mode if it does)
+        error.InvalidChar => unreachable,
+    }
+}
+
+test "error union" {
+    var foo: anyerror!i32 = undefined;
+
+    // Use compile-time reflection to access the type of an error union:
+    comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.payload == i32);
+    comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.error_set == anyerror);
+
+    // Coerce values
+    foo = 1234;
+    foo = error.SomeError;
+}
+
+const A = error{
+    NotDir,
+
+    /// A doc comment
+    PathNotFound,
+};
+const B = error{
+    OutOfMemory,
+
+    /// B doc comment
+    PathNotFound,
+};
+
+const C = A || B;
+
+fn bar() C!void {
+    return error.NotDir;
+}
+
+test "merge error sets" {
+    if (bar()) {
+        @panic("unexpected");
+    } else |err| switch (err) {
+        error.OutOfMemory => @panic("unexpected"),
+        error.PathNotFound => @panic("unexpected"),
+        error.NotDir => {},
+    }
+}
+
+const Error = error { Overflow };
+
+// With an inferred error set
+pub fn add_inferred(comptime T: type, a: T, b: T) !T {
+    var answer: T = undefined;
+    return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
+}
+
+// With an explicit error set
+pub fn add_explicit(comptime T: type, a: T, b: T) Error!T {
+    var answer: T = undefined;
+    return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
+}
+
+test "inferred error set" {
+    if (add_inferred(u8, 255, 1)) |_| unreachable else |err| switch (err) {
+        error.Overflow => {}, // ok
+    }
+}
diff --git a/lang/zig/fn.zig b/lang/zig/fn.zig
new file mode 100644
index 0000000..0fb3002
--- /dev/null
+++ b/lang/zig/fn.zig
@@ -0,0 +1,78 @@
+const expect = @import("std").testing.expect;
+
+// Functions are declared like this
+fn add(a: i8, b: i8) i8 {
+    return if (a == 0) b else a + b;
+}
+
+// The export specifier makes a function externally visible in the generated
+// object file, and makes it use the C ABI.
+export fn sub(a: i8, b: i8) i8 { return a - b; }
+
+// The extern specifier is used to declare a function that will be resolved
+// at link time, when linking statically, or at runtime, when linking
+// dynamically.
+extern "c" fn atan2(a: f64, b: f64) f64;
+
+// The @setCold builtin tells the optimizer that a function is rarely called.
+fn abort() noreturn {
+    @setCold(true);
+    while (true) {}
+}
+
+// The naked calling convention makes a function not have any function prologue
+// or epilogue.  This can be useful when integrating with assembly.
+fn _start() callconv(.Naked) noreturn {
+    abort();
+}
+
+// The inline specifier forces a function to be inlined at all call sites.
+// If the function cannot be inlined, it is a compile-time error.
+inline fn shiftLeftOne(a: u32) u32 {
+    return a << 1;
+}
+
+// The pub specifier allows the function to be visible when importing.
+// Another file can use @import and call sub2
+pub fn sub2(a: i8, b: i8) i8 { return a - b; }
+
+// Functions can be used as values and are equivalent to pointers.
+fn do_op(fn_call: fn (a: i8, b: i8) i8, op1: i8, op2: i8) i8 {
+    return fn_call(op1, op2);
+}
+
+test "function" {
+    expect(do_op(add, 5, 6) == 11);
+    expect(do_op(sub2, 5, 6) == -1);
+}
+
+const Point = struct { x: i32, y: i32 };
+
+fn foo(point: Point) i32 {
+    // Here, `point` could be a reference, or a copy. The function body
+    // can ignore the difference and treat it as a value. Be very careful
+    // taking the address of the parameter - it should be treated as if
+    // the address will become invalid when the function returns.
+    return point.x + point.y;
+}
+
+test "pass struct to function" {
+    expect(foo(Point{ .x = 1, .y = 2 }) == 3);
+}
+
+fn addFortyTwo(x: anytype) @TypeOf(x) {
+    return x + 42;
+}
+
+test "fn type inference" {
+    expect(addFortyTwo(1) == 43);
+    expect(@TypeOf(addFortyTwo(1)) == comptime_int);
+    const y: i64 = 2;
+    expect(addFortyTwo(y) == 44);
+    expect(@TypeOf(addFortyTwo(y)) == i64);
+}
+
+test "fn reflection" {
+    expect(@typeInfo(@TypeOf(expect)).Fn.return_type.? == void);
+    expect(@typeInfo(@TypeOf(expect)).Fn.is_var_args == false);
+}
diff --git a/lang/zig/for.zig b/lang/zig/for.zig
new file mode 100644
index 0000000..e816fcc
--- /dev/null
+++ b/lang/zig/for.zig
@@ -0,0 +1,100 @@
+const expect = @import("std").testing.expect;
+
+test "for basics" {
+    const items = [_]i32{ 4, 5, 3, 4, 0 };
+    var sum: i32 = 0;
+
+    // For loops iterate over slices and arrays.
+    for (items) |value| {
+        // Break and continue are supported.
+        if (value == 0)
+            continue;
+        sum += value;
+    }
+    expect(sum == 16);
+
+    // To iterate over a portion of a slice, reslice.
+    for (items[0..1]) |value|
+        sum += value;
+    expect(sum == 20);
+
+    // To access the index of iteration, specify a second capture value.
+    // This is zero-indexed.
+    var sum2: i32 = 0;
+    for (items) |value, i| {
+        expect(@TypeOf(i) == usize);
+        sum2 += @intCast(i32, i);
+    }
+    expect(sum2 == 10);
+}
+
+test "for reference" {
+    var items = [_]i32{ 3, 4, 2 };
+
+    // Iterate over the slice by reference by
+    // specifying that the capture value is a pointer.
+    for (items) |*value|
+        value.* += 1;
+
+    expect(items[0] == 4);
+    expect(items[1] == 5);
+    expect(items[2] == 3);
+}
+
+test "for else" {
+    // For allows an else attached to it, the same as a while loop.
+    var items = [_]?i32{ 3, 4, null, 5 };
+
+    // For loops can also be used as expressions.
+    // Similar to while loops, when you break from a for loop,
+    // the else branch is not evaluated.
+    var sum: i32 = 0;
+    const result = for (items) |value| {
+        if (value != null)
+            sum += value.?;
+    } else blk: {
+        expect(sum == 12);
+        break :blk sum;
+    };
+    expect(result == 12);
+}
+
+test "nested break" {
+    var count: usize = 0;
+    outer: for ([_]i32{ 1, 2, 3, 4, 5 }) |_| {
+        for ([_]i32{ 1, 2, 3, 4, 5 }) |_| {
+            count += 1;
+            break :outer;
+        }
+    }
+    expect(count == 1);
+}
+
+test "nested continue" {
+    var count: usize = 0;
+    outer: for ([_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }) |_| {
+        for ([_]i32{ 1, 2, 3, 4, 5 }) |_| {
+            count += 1;
+            continue :outer;
+        }
+    }
+    expect(count == 8);
+}
+
+fn typeNameLength(comptime T: type) usize {
+    return @typeName(T).len;
+}
+
+test "inline for loop" {
+    var sum: usize = 0;
+    inline for ([_]i32{ 2, 4, 6 }) |i| {
+        const T = switch (i) {
+            2 => f32,
+            4 => i8,
+            6 => bool,
+            else => unreachable,
+        };
+        sum += typeNameLength(T);
+    }
+    expect(sum == 9);
+}
diff --git a/lang/zig/hello-again.zig b/lang/zig/hello-again.zig
new file mode 100644
index 0000000..1ef4af6
--- /dev/null
+++ b/lang/zig/hello-again.zig
@@ -0,0 +1,5 @@
+const print = @import("std").debug.print;
+
+pub fn main() void {
+    print("Hello, {s}!\n", .{"world"});
+}
diff --git a/lang/zig/hello.zig b/lang/zig/hello.zig
new file mode 100644
index 0000000..56b2066
--- /dev/null
+++ b/lang/zig/hello.zig
@@ -0,0 +1,6 @@
+const std = @import("std");
+
+pub fn main() !void {
+    const stdout = std.io.getStdOut().writer();
+    try stdout.print("Hello, {}!\n", .{"world"});
+}
diff --git a/lang/zig/if.zig b/lang/zig/if.zig
new file mode 100644
index 0000000..99de2b5
--- /dev/null
+++ b/lang/zig/if.zig
@@ -0,0 +1,141 @@
+// If expressions have three uses, corresponding to the three types:
+// * bool
+// * ?T
+// * anyerror!T
+
+const expect = @import("std").testing.expect;
+
+test "if expression" {
+    // If expressions are used instead of a ternary expression.
+    const a: u32 = 5;
+    const b: u32 = 4;
+    const result = if (a != b) 47 else 3089;
+    expect(result == 47);
+}
+
+test "if boolean" {
+    // If expressions test boolean conditions.
+    const a: u32 = 5;
+    const b: u32 = 4;
+    if (a != b) {
+        expect(true);
+    } else if (a == 9) {
+        unreachable;
+    } else {
+        unreachable;
+    }
+}
+
+// If expressions test for null.
+test "if optional" {
+    if (@as(?u32, 0)) |value| {
+        expect(value == 0);
+    } else {
+        unreachable;
+    }
+
+    const b: ?u32 = null;
+    if (b) |value| {
+        unreachable;
+    } else {
+        expect(true);
+    }
+    // To test against null only, use the binary equality operator.
+    if (b == null)
+        expect(true);
+
+    // Access the value by reference using a pointer capture.
+    var c: ?u32 = 3;
+    if (c) |*value|
+        value.* = 2;
+    // The else is not required.
+    if (c) |value|
+        expect(value == 2);
+}
+
+// If expressions test for errors.  Note the |err| capture on the else.
+test "if error union" {
+    const a: anyerror!u32 = 0;
+    if (a) |value| {
+        expect(value == 0);
+    } else |err| {
+        unreachable;
+    }
+
+    const b: anyerror!u32 = error.BadValue;
+    if (b) |value| {
+        unreachable;
+    } else |err| {
+        expect(err == error.BadValue);
+    }
+
+    // The else and |err| capture is strictly required.
+    if (a) |value| {
+        expect(value == 0);
+    } else |_| {}
+
+    // To check only the error value, use an empty block expression.
+    if (b) |_| {} else |err| {
+        expect(err == error.BadValue);
+    }
+
+    // Access the value by reference using a pointer capture.
+    var c: anyerror!u32 = 3;
+    if (c) |*value| {
+        value.* = 9;
+    } else |err| {
+        unreachable;
+    }
+
+    if (c) |value| {
+        expect(value == 9);
+    } else |err| {
+        unreachable;
+    }
+}
+
+// If expressions test for errors before unwrapping optionals.
+// The |optional_value| capture's type is ?u32.
+test "if error union with optional" {
+    const a: anyerror!?u32 = 0;
+    if (a) |optional_value| {
+        expect(optional_value.? == 0);
+    } else |err| {
+        unreachable;
+    }
+
+    const b: anyerror!?u32 = null;
+    if (b) |optional_value| {
+        expect(optional_value == null);
+    } else |err| {
+        unreachable;
+    }
+    if (b) |*optional_value| {
+        if (optional_value.*) |_|
+            unreachable;
+    } else |err| {
+        unreachable;
+    }
+
+    const c: anyerror!?u32 = error.BadValue;
+    if (c) |optional_value| {
+        unreachable;
+    } else |err| {
+        expect(err == error.BadValue);
+    }
+
+    // Access the value by reference by using a pointer capture each time.
+    var d: anyerror!?u32 = 3;
+    if (d) |*optional_value| {
+        if (optional_value.*) |*value| {
+            value.* = 9;
+        }
+    } else |err| {
+        unreachable;
+    }
+    if (d) |optional_value| {
+        expect(optional_value.? == 9);
+    } else |err| {
+        unreachable;
+    }
+}
diff --git a/lang/zig/infer-list-literal.zig b/lang/zig/infer-list-literal.zig
new file mode 100644
index 0000000..ab5c770
--- /dev/null
+++ b/lang/zig/infer-list-literal.zig
@@ -0,0 +1,13 @@
+const expect = @import("std").testing.expect;
+
+test "fully anonymous list literal" {
+    dump(.{ @as(u32, 1234), @as(f64, 12.34), true, "hi"});
+}
+
+fn dump(args: anytype) void {
+    expect(args.@"0" == 1234);
+    expect(args.@"1" == 12.34);
+    expect(args.@"2");
+    expect(args.@"3"[0] == 'h');
+    expect(args.@"3"[1] == 'i');
+}
diff --git a/lang/zig/multidim.zig b/lang/zig/multidim.zig
new file mode 100644
index 0000000..bf30c80
--- /dev/null
+++ b/lang/zig/multidim.zig
@@ -0,0 +1,20 @@
+const expect = @import("std").testing.expect;
+
+const mat4x4 = [4][4]f32{
+    [_]f32{ 1.0, 0.0, 0.0, 0.0 },
+    [_]f32{ 0.0, 1.0, 0.0, 1.0 },
+    [_]f32{ 0.0, 0.0, 1.0, 0.0 },
+    [_]f32{ 0.0, 0.0, 0.0, 1.0 },
+};
+
+test "multidimensional arrays" {
+    // Access the 2D array by indexing the outer array,
+    // and then the inner array.
+    expect(mat4x4[1][1] == 1.0);
+
+    // Here we iterate with for loops.
+    for (mat4x4) |row, row_index|
+        for (row) |cell, column_index|
+            if (row_index == column_index)
+                expect(cell == 1.0);
+}
diff --git a/lang/zig/namespaced-global.zig b/lang/zig/namespaced-global.zig
new file mode 100644
index 0000000..2d06ced
--- /dev/null
+++ b/lang/zig/namespaced-global.zig
@@ -0,0 +1,15 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "namespaced global variable" {
+    expect(foo() == 1235);
+    expect(foo() == 1236);
+}
+
+fn foo() i32 {
+    const S = struct {
+        var x: i32 = 1234;
+    };
+    S.x += 1;
+    return S.x;
+}
diff --git a/lang/zig/noreturn.zig b/lang/zig/noreturn.zig
new file mode 100644
index 0000000..6f88de1
--- /dev/null
+++ b/lang/zig/noreturn.zig
@@ -0,0 +1,10 @@
+const expect = @import("std").testing.expect;
+
+fn foo(condition: bool, b: u32) void {
+    const a = if (condition) b else return;
+    @panic("do something with a");
+}
+
+test "noreturn" {
+    foo(false, 1);
+}
diff --git a/lang/zig/opaque.zig b/lang/zig/opaque.zig
new file mode 100644
index 0000000..803d19b
--- /dev/null
+++ b/lang/zig/opaque.zig
@@ -0,0 +1,12 @@
+// Stuff for C-compat
+const Derp = opaque {};
+const Wat = opaque {};
+
+extern fn bar(d: *Derp) void;
+fn foo(w: *Wat) callconv(.C) void {
+    bar(w);
+}
+
+test "call foo" {
+    foo(undefined);
+}
diff --git a/lang/zig/pointers.zig b/lang/zig/pointers.zig
new file mode 100644
index 0000000..92c0d9d
--- /dev/null
+++ b/lang/zig/pointers.zig
@@ -0,0 +1,93 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const bytesAsSlice = std.mem.bytesAsSlice;
+
+test "address of syntax" {
+    // Get the address of a variable:
+    const x: i32 = 1234;
+    const x_ptr = &x;
+
+    // Dereference a pointer:
+    expect(x_ptr.* == 1234);
+
+    // When you get the address of a const variable,
+    // you get a const pointer to a single item.
+    expect(@TypeOf(x_ptr) == *const i32);
+
+    // If you want to mutate the value,
+    // you'd need an address of a mutable variable:
+    var y: i32 = 5678;
+    const y_ptr = &y;
+    expect(@TypeOf(y_ptr) == *i32);
+    y_ptr.* += 1;
+    expect(y_ptr.* == 5679);
+}
+
+test "pointer array access" {
+    // Taking an address of an individual element gives a
+    // pointer to a single item. This kind of pointer
+    // does not support pointer arithmetic.
+    var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    const ptr = &array[2];
+    expect(@TypeOf(ptr) == *u8);
+
+    expect(array[2] == 3);
+    ptr.* += 1;
+    expect(array[2] == 4);
+}
+
+test "pointer slicing" {
+    var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+    const slice = array[2..4];
+    expect(slice.len == 2);
+
+    expect(array[3] == 4);
+    slice[1] += 1;
+    expect(array[3] == 5);
+}
+
+test "comptime pointers" {
+    comptime {
+        var x: i32 = 1;
+        const ptr = &x;
+        ptr.* += 1;
+        x += 1;
+        expect(ptr.* == 3);
+    }
+}
+
+test "@ptrToInt and @intToPtr" {
+    const ptr = @intToPtr(*i32, 0xdeadbee0);
+    const addr = @ptrToInt(ptr);
+    expect(@TypeOf(addr) == usize);
+    expect(addr == 0xdeadbee0);
+}
+
+test "comptime @intToPtr" {
+    comptime {
+        const ptr = @intToPtr(*i32, 0xdeadbee0);
+        const addr = @ptrToInt(ptr);
+        expect(@TypeOf(addr) == usize);
+        expect(addr == 0xdeadbee0);
+    }
+}
+
+test "volatile" {
+    const mmio_ptr = @intToPtr(*volatile u8, 0x12345678);
+    expect(@TypeOf(mmio_ptr) == *volatile u8);
+}
+
+test "pointer casting" {
+    const bytes align(@alignOf(u32)) = [_]u8{ 0x12 } ** 4;
+    const u32_ptr = @ptrCast(*const u32, &bytes);
+    expect(u32_ptr.* == 0x12121212);
+
+    // Even this example is contrived,
+    // there are better ways to do the above than pointer casting.
+    // For example, using a slice narrowing cast:
+    const u32_value = bytesAsSlice(u32, bytes[0..])[0];
+    expect(u32_value == 0x12121212);
+
+    // And even another way, the most straightforward way to do it:
+    expect(@bitCast(u32, bytes) == 0x12121212);
+}
diff --git a/lang/zig/sentinel-terminated.zig b/lang/zig/sentinel-terminated.zig
new file mode 100644
index 0000000..dd9775c
--- /dev/null
+++ b/lang/zig/sentinel-terminated.zig
@@ -0,0 +1,10 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+test "null terminated array" {
+    const array = [_:0]u8 {1, 2, 3, 4};
+
+    expect(@TypeOf(array) == [4:0]u8);
+    expect(array.len == 4);
+    expect(array[4] == 0);
+}
diff --git a/lang/zig/shadow.zig b/lang/zig/shadow.zig
new file mode 100644
index 0000000..9a37722
--- /dev/null
+++ b/lang/zig/shadow.zig
@@ -0,0 +1,10 @@
+test "separate scopes" {
+    { const pi = 3.14; }
+    { var pi: bool = true; }
+}
+
+test "inside test block" {
+    // That's right, no shadowing.
+    const pi = 3.14;
+    { var pi: i32 = 1234; }
+}
diff --git a/lang/zig/slices.zig b/lang/zig/slices.zig
new file mode 100644
index 0000000..8356c26
--- /dev/null
+++ b/lang/zig/slices.zig
@@ -0,0 +1,77 @@
+const bufPrint = std.fmt.bufPrint;
+const eql = std.mem.eql;
+const expect = std.testing.expect;
+const std = @import("std");
+
+test "basic slices" {
+    var array = [_]i32{ 1, 2, 3, 4 };
+    // A slice is a pointer and a length. The difference between an array and
+    // a slice is that the array's length is part of the type and known at
+    // compile-time, whereas the slice's length is known at runtime.
+    // Both can be accessed with the `len` field.
+    var known_at_runtime_zero: usize = 0;
+    const slice = array[known_at_runtime_zero..array.len];
+    expect(&slice[0] == &array[0]);
+    expect(slice.len == array.len);
+
+    // Using the address-of operator on a slice gives a pointer to a single
+    // item, while using the `ptr` field gives an unknown length pointer.
+    expect(@TypeOf(slice.ptr) == [*]i32);
+    expect(@TypeOf(&slice[0]) == *i32);
+    expect(@ptrToInt(slice.ptr) == @ptrToInt(&slice[0]));
+
+    // Slices have array bounds checking. If you try to access something out
+    // of bounds, you'll get a safety check failure:
+    // slice[10] += 1;
+
+    // Note that `slice.ptr` does not invoke safety checking, while `&slice[0]`
+    // asserts that the slice has len >= 1.
+}
+
+test "using slices for strings" {
+    // Zig has no concept of strings. String literals are const pointers to
+    // arrays of u8, and by convention parameters that are "strings" are
+    // expected to be UTF-8 encoded slices of u8.
+    // Here we coerce [5]u8 to []const u8
+    const hello: []const u8 = "hello";
+    const world: []const u8 = "δΈ–η•Œ";
+
+    var all_together: [100]u8 = undefined;
+    // String concatenation example.
+    const hello_world = try bufPrint(
+        all_together[0..], "{} {}", .{ hello, world });
+
+    // Generally, you can use UTF-8 and not worry about whether something is a
+    // string. If you don't need to deal with individual characters, no need
+    // to decode.
+    expect(eql(u8, hello_world, "hello δΈ–η•Œ"));
+}
+
+test "slice pointer" {
+    var array: [10]u8 = undefined;
+    const ptr = &array;
+
+    // You can use slicing syntax to convert a pointer into a slice:
+    const slice = ptr[0..5];
+    slice[2] = 3;
+    expect(slice[2] == 3);
+    // The slice is mutable because we sliced a mutable pointer.
+    // Furthermore, it is actually a pointer to an array, since the start
+    // and end indexes were both comptime-known.
+    expect(@TypeOf(slice) == *[5]u8);
+
+    // You can also slice a slice:
+    const slice2 = slice[2..3];
+    expect(slice2.len == 1);
+    expect(slice2[0] == 3);
+}
+
+test "null terminated slice" {
+    const string = "hello";
+    const slice: [:0]const u8 = string;
+    expect(@TypeOf(string) == *const [5:0]u8);
+    expect(@TypeOf(slice) == [:0]const u8);
+
+    expect(slice.len == 5);
+    expect(slice[5] == 0);
+}
diff --git a/lang/zig/string.zig b/lang/zig/string.zig
new file mode 100644
index 0000000..af6bdc1
--- /dev/null
+++ b/lang/zig/string.zig
@@ -0,0 +1,14 @@
+const expect = @import("std").testing.expect;
+const mem = @import("std").mem;
+
+test "string literals" {
+    const bytes = "hello";
+    expect(@TypeOf(bytes) == *const [5:0]u8);
+    expect(bytes.len == 5);
+    expect(bytes[1] == 'e');
+    expect(bytes[5] == 0);
+    expect('e' == '\x65');
+    expect('\u{1f4a9}' == 128169);
+    expect('πŸ’―' == 128175);
+    expect(mem.eql(u8, "hello", "h\x65llo"));
+}
diff --git a/lang/zig/struct-anon.zig b/lang/zig/struct-anon.zig
new file mode 100644
index 0000000..a666bd0
--- /dev/null
+++ b/lang/zig/struct-anon.zig
@@ -0,0 +1,20 @@
+const expect = @import("std").testing.expect;
+
+test "fully anonymous struct" {
+    dump(.{
+        .int = @as(u32, 1234),
+        .float = @as(f64, 12.34),
+        .b = true,
+        .s = "hi",
+    });
+}
+
+fn dump(args: anytype) void {
+    expect(args.int == 1234);
+    expect(args.float == 12.34);
+    expect(args.b);
+    expect(args.s[0] == 'h');
+    expect(args.s[1] == 'i');
+    // Type is well infered, i.e. the following fail at comp time:
+    // expect(args.a);
+}
diff --git a/lang/zig/struct-default.zig b/lang/zig/struct-default.zig
new file mode 100644
index 0000000..941d201
--- /dev/null
+++ b/lang/zig/struct-default.zig
@@ -0,0 +1,11 @@
+const expect = @import("std").testing.expect;
+
+const Foo = struct {
+    a: i32 = 1234,
+    b: i32,
+};
+
+test "default struct initialization fields" {
+    const x = Foo{ .b = 5 };
+    expect(x.a + x.b == 1239);
+}
diff --git a/lang/zig/struct-name.zig b/lang/zig/struct-name.zig
new file mode 100644
index 0000000..8c3affc
--- /dev/null
+++ b/lang/zig/struct-name.zig
@@ -0,0 +1,14 @@
+const print = @import("std").debug.print;
+
+pub fn main() void {
+    const Foo = struct {};
+    print("variable: {}\n", .{@typeName(Foo)});
+    print("anonymous: {}\n", .{@typeName(struct {})});
+    print("function: {}\n", .{@typeName(List(i32))});
+}
+
+fn List(comptime T: type) type {
+    return struct {
+        x: T,
+    };
+}
diff --git a/lang/zig/struct-packed.zig b/lang/zig/struct-packed.zig
new file mode 100644
index 0000000..f493661
--- /dev/null
+++ b/lang/zig/struct-packed.zig
@@ -0,0 +1,73 @@
+const std = @import("std");
+const endian = std.builtin.endian;
+const expect = std.testing.expect;
+
+const Full = packed struct {
+    number: u16,
+};
+const Divided = packed struct {
+    half1: u8,
+    quarter3: u4,
+    quarter4: u4,
+};
+
+fn doTheTest() void {
+    expect(@sizeOf(Full) == 2);
+    expect(@sizeOf(Divided) == 2);
+    var full = Full{ .number = 0x1234 };
+    var divided = @bitCast(Divided, full);
+    switch (endian) {
+        .Big => {
+            expect(divided.half1 == 0x12);
+            expect(divided.quarter3 == 0x3);
+            expect(divided.quarter4 == 0x4);
+        },
+        .Little => {
+            expect(divided.half1 == 0x34);
+            expect(divided.quarter3 == 0x2);
+            expect(divided.quarter4 == 0x1);
+        },
+    }
+}
+
+test "@bitCast between packed structs" {
+    doTheTest();
+    comptime doTheTest();
+}
+
+const BitField = packed struct {
+    a: u3,
+    b: u3,
+    c: u2,
+};
+
+var foo = BitField{
+    .a = 1,
+    .b = 2,
+    .c = 3,
+};
+
+fn bar(x: *const u3) u3 {
+    return x.*;
+}
+
+test "pointer to non-byte-aligned field" {
+    const ptr = &foo.b;
+    expect(ptr.* == 2);
+    // The pointer to a non-byte-aligned field has special properties
+    // and cannot be passed when a normal pointer is expected:
+    // expect(bar(ptr) == 2);
+    // Here's why:
+    expect(@ptrToInt(&foo.a) == @ptrToInt(&foo.b));
+    expect(@ptrToInt(&foo.a) == @ptrToInt(&foo.c));
+
+    comptime {
+        expect(@bitOffsetOf(BitField, "a") == 0);
+        expect(@bitOffsetOf(BitField, "b") == 3);
+        expect(@bitOffsetOf(BitField, "c") == 6);
+
+        expect(@byteOffsetOf(BitField, "a") == 0);
+        expect(@byteOffsetOf(BitField, "b") == 0);
+        expect(@byteOffsetOf(BitField, "c") == 0);
+    }
+}
diff --git a/lang/zig/struct-result.zig b/lang/zig/struct-result.zig
new file mode 100644
index 0000000..fcd556a
--- /dev/null
+++ b/lang/zig/struct-result.zig
@@ -0,0 +1,7 @@
+const expect = @import("std").testing.expect;
+const Point = struct { x: i32, y: i32 };
+
+test "anonymous struct literal" {
+    const pt: Point = .{ .x = 13, .y = 67 };
+    expect(pt.x + pt.y == 80);
+}
diff --git a/lang/zig/structs.zig b/lang/zig/structs.zig
new file mode 100644
index 0000000..d90f4ff
--- /dev/null
+++ b/lang/zig/structs.zig
@@ -0,0 +1,133 @@
+// Declare a struct.
+// Zig gives no guarantees about the order of fields and the size of
+// the struct but the fields are guaranteed to be ABI-aligned.
+const Point = struct {
+    x: f32,
+    y: f32,
+};
+
+// Maybe we want to pass it to OpenGL so we want to be particular about
+// how the bytes are arranged.
+const Point2 = packed struct {
+    x: f32,
+    y: f32,
+};
+
+// Declare an instance of a struct.
+const p = Point {
+    .x = 0.12,
+    .y = 0.34,
+};
+
+// Maybe we're not ready to fill out some of the fields.
+var p2 = Point {
+    .x = 0.12,
+    .y = undefined,
+};
+
+// Structs can have methods
+// Struct methods are not special, they are only namespaced
+// functions that you can call with dot syntax.
+const Vec3 = struct {
+    x: f32,
+    y: f32,
+    z: f32,
+
+    pub fn init(x: f32, y: f32, z: f32) Vec3 {
+        return Vec3 {
+            .x = x,
+            .y = y,
+            .z = z,
+        };
+    }
+
+    pub fn dot(self: Vec3, other: Vec3) f32 {
+        return self.x * other.x + self.y * other.y + self.z * other.z;
+    }
+};
+
+const expect = @import("std").testing.expect;
+test "dot product" {
+    const v1 = Vec3.init(1.0, 0.0, 0.0);
+    const v2 = Vec3.init(0.0, 1.0, 0.0);
+    expect(v1.dot(v2) == 0.0);
+
+    // Other than being available to call with dot syntax, struct methods are
+    // not special. You can reference them as any other declaration inside
+    // the struct:
+    expect(Vec3.dot(v1, v2) == 0.0);
+}
+
+// Structs can have global declarations.
+// Structs can have 0 fields.
+const Empty = struct {
+    pub const PI = 3.14;
+};
+test "struct namespaced variable" {
+    expect(Empty.PI == 3.14);
+    expect(@sizeOf(Empty) == 0);
+
+    // you can still instantiate an empty struct
+    const does_nothing = Empty {};
+}
+
+// struct field order is determined by the compiler for optimal performance.
+// however, you can still calculate a struct base pointer given a field pointer:
+fn setYBasedOnX(x: *f32, y: f32) void {
+    const point = @fieldParentPtr(Point, "x", x);
+    point.y = y;
+}
+test "field parent pointer" {
+    var point = Point {
+        .x = 0.1234,
+        .y = 0.5678,
+    };
+    setYBasedOnX(&point.x, 0.9);
+    expect(point.y == 0.9);
+}
+
+// You can return a struct from a function.
+// This is how we do generics in Zig:
+fn LinkedList(comptime T: type) type {
+    return struct {
+        pub const Node = struct {
+            prev: ?*Node,
+            next: ?*Node,
+            data: T,
+        };
+
+        first: ?*Node,
+        last:  ?*Node,
+        len:   usize,
+    };
+}
+
+test "linked list" {
+    // Functions called at compile-time are memoized.
+    // This means you can do this:
+    expect(LinkedList(i32) == LinkedList(i32));
+
+    var list = LinkedList(i32) {
+        .first = null,
+        .last = null,
+        .len = 0,
+    };
+    expect(list.len == 0);
+
+    // Since types are first class values you can instantiate the type
+    // by assigning it to a variable:
+    const ListOfInts = LinkedList(i32);
+    expect(ListOfInts == LinkedList(i32));
+
+    var node = ListOfInts.Node {
+        .prev = null,
+        .next = null,
+        .data = 1234,
+    };
+    var list2 = LinkedList(i32) {
+        .first = &node,
+        .last = &node,
+        .len = 1,
+    };
+    expect(list2.first.?.data == 1234);
+}
diff --git a/lang/zig/switch-enum-literal.zig b/lang/zig/switch-enum-literal.zig
new file mode 100644
index 0000000..550de93
--- /dev/null
+++ b/lang/zig/switch-enum-literal.zig
@@ -0,0 +1,13 @@
+const std = @import("std");
+const expect = std.testing.expect;
+
+const Color = enum { auto, off, on };
+
+test "enum literals with switch" {
+    const color = Color.off;
+    const result = switch (color) {
+        .auto, .on => false,
+        .off => true,
+    };
+    expect(result);
+}
diff --git a/lang/zig/switch-exhaust.zig b/lang/zig/switch-exhaust.zig
new file mode 100644
index 0000000..4777f19
--- /dev/null
+++ b/lang/zig/switch-exhaust.zig
@@ -0,0 +1,9 @@
+const Color = enum { auto, off, on };
+
+test "exhaustive switching" {
+    const color = Color.off;
+    switch (color) {
+        Color.auto => {},
+        Color.on => {},
+    }
+}
diff --git a/lang/zig/switch.zig b/lang/zig/switch.zig
new file mode 100644
index 0000000..7fd3da5
--- /dev/null
+++ b/lang/zig/switch.zig
@@ -0,0 +1,86 @@
+const std = @import("std");
+const expect = std.testing.expect;
+const os = std.Target.current.os.tag;
+
+test "switch simple" {
+    const a: u64 = 10;
+    const zz: u64 = 103;
+
+    // All branches of a switch expression must be able to be coerced
+    // to a common type.  Branches cannot fallthrough. If fallthrough behavior
+    // is desired, combine the cases and use an if.
+    const b = switch (a) {
+        // Multiple cases can be combined via a ','
+        1, 2, 3 => 0,
+
+        // Ranges can be specified using the ... syntax.
+        // These are inclusive both ends.
+        5...100 => 1,
+
+        // Branches can be arbitrarily complex.
+        101 => blk: {
+            const c: u64 = 5;
+            break :blk c * 2 + 1;
+        },
+
+        // Switching on arbitrary expressions is allowed as long as the
+        // expression is known at compile-time.
+        zz => zz,
+        blk: {
+            const d: u32 = 5;
+            const e: u32 = 100;
+            break :blk d + e;
+        } => 107,
+
+        // The else branch catches everything not already captured.
+        // Else branches are mandatory unless the entire range of values
+        // is handled.
+        else => 9,
+    };
+
+    expect(b == 1);
+}
+
+// Switch expressions can be used outside a function:
+const os_msg = switch (os) {
+    .linux => "we found a linux user",
+    else => "not a linux user",
+};
+
+// Inside a function, switch statements implicitly are compile-time
+// evaluated if the target expression is compile-time known.
+test "switch inside function" {
+    switch (os) {
+        // On an OS other than fuchsia, block is not even analyzed,
+        // so this compile error is not triggered.
+        // On fuchsia this compile error would be triggered.
+        .fuchsia => @compileError("fuchsia not supported"),
+        else => {},
+    }
+}
+
+test "switch on tagged union" {
+    const Point = struct { x: u8, y: u8 };
+    const Item = union(enum) { a: u32, c: Point, d, e: u32 };
+    var a = Item{ .c = Point{ .x = 1, .y = 2 } };
+
+    // Switching on more complex enums is allowed.
+    const b = switch (a) {
+        // A capture group is allowed on a match, and will return the enum
+        // value matched.  If the payload types of both cases are the same
+        // they can be put into the same switch prong.
+        Item.a, Item.e => |item| item,
+
+        // A reference to the matched value can be obtained using `*` syntax.
+        Item.c => |*item| blk: {
+            item.*.x += 1;
+            break :blk 6;
+        },
+
+        // No else is required if the types cases was exhaustively handled
+        Item.d => 8,
+    };
+
+    expect(b == 6);
+    expect(a.c.x == 2);
+}
diff --git a/lang/zig/tls.zig b/lang/zig/tls.zig
new file mode 100644
index 0000000..b47b17c
--- /dev/null
+++ b/lang/zig/tls.zig
@@ -0,0 +1,22 @@
+const assert = std.debug.assert;
+const std = @import("std");
+const spawn = std.Thread.spawn;
+
+threadlocal var x: i32 = 1234;
+
+test "thread local storage" {
+    const thread1 = try spawn({}, testTls);
+    defer thread1.wait();
+
+    const thread2 = try spawn({}, testTls);
+    defer thread2.wait();
+
+    // Main thread
+    testTls({});
+}
+
+fn testTls(context: void) void {
+    assert(x == 1234);
+    x += 1;
+    assert(x == 1235);
+}
diff --git a/lang/zig/union-anon.zig b/lang/zig/union-anon.zig
new file mode 100644
index 0000000..d26dcae
--- /dev/null
+++ b/lang/zig/union-anon.zig
@@ -0,0 +1,13 @@
+const expect = @import("std").testing.expect;
+const Number = union { int: i32, float: f64 };
+
+fn makeNumber() Number {
+    return .{.float = 12.34};
+}
+
+test "anonymous union literal syntax" {
+    var i: Number = .{.int = 42};
+    var f = makeNumber();
+    expect(i.int == 42);
+    expect(f.float == 12.34);
+}
diff --git a/lang/zig/union-tag.zig b/lang/zig/union-tag.zig
new file mode 100644
index 0000000..6954c73
--- /dev/null
+++ b/lang/zig/union-tag.zig
@@ -0,0 +1,68 @@
+const eql = std.mem.eql;
+const expect = std.testing.expect;
+const std = @import("std");
+
+const ComplexTypeTag = enum { ok, not_ok };
+const ComplexType = union(ComplexTypeTag) { ok: u8, not_ok: void };
+
+test "switch on tagged union" {
+    const c = ComplexType{ .ok = 42 };
+    expect(@as(ComplexTypeTag, c) == ComplexTypeTag.ok);
+
+    switch (c) {
+        ComplexTypeTag.ok => |value| expect(value == 42),
+        ComplexTypeTag.not_ok => unreachable,
+    }
+}
+
+test "@TagType" {
+    expect(@TagType(ComplexType) == ComplexTypeTag);
+}
+
+test "coerce to enum" {
+    const c1: ComplexType = ComplexType{ .ok = 42 };
+    const c2: ComplexTypeTag = ComplexType.not_ok;
+    expect(c1 == .ok);
+    expect(c2 == .not_ok);
+}
+
+test "modify tagged union in switch" {
+    var c = ComplexType{ .ok = 42 };
+    expect(c == .ok);
+
+    switch (c) {
+        ComplexTypeTag.ok => |*value| value.* += 1,
+        ComplexTypeTag.not_ok => unreachable,
+    }
+
+    expect(c.ok == 43);
+}
+
+const Variant = union(enum) {
+    int: i32,
+    boolean: bool,
+
+    // void can be omitted when inferring enum tag type.
+    none,
+
+    fn truthy(self: Variant) bool {
+        return switch (self) {
+            Variant.int => |x_int| x_int != 0,
+            Variant.boolean => |x_bool| x_bool,
+            Variant.none => false,
+        };
+    }
+};
+
+test "union method" {
+    var v1 = Variant{ .int = 420 };
+    var v2 = Variant{ .boolean = false };
+
+    expect(v1.truthy());
+    expect(!v2.truthy());
+}
+
+const Small2 = union(enum) { a: i32, b: bool, c: u8 };
+test "@tagName" {
+    expect(eql(u8, @tagName(Small2.a), "a"));
+}
diff --git a/lang/zig/union.zig b/lang/zig/union.zig
new file mode 100644
index 0000000..a9445fd
--- /dev/null
+++ b/lang/zig/union.zig
@@ -0,0 +1,15 @@
+const expect = @import("std").testing.expect;
+const Payload = union {
+    int: i64,
+    float: f64,
+    boolean: bool,
+};
+
+test "simple union" {
+    var payload = Payload{ .int = 1234 };
+    expect(payload.int == 1234);
+    // Nope this is not how it works.
+    // payload.float = 12.34;
+    payload = Payload{ .float = 12.34 };
+    expect(payload.float == 12.34);
+}
diff --git a/lang/zig/unreachable.zig b/lang/zig/unreachable.zig
new file mode 100644
index 0000000..26463a9
--- /dev/null
+++ b/lang/zig/unreachable.zig
@@ -0,0 +1,22 @@
+test "basic math" {
+    const x = 1;
+    const y = 2;
+    if (x + y != 3)
+        unreachable;
+}
+
+fn assert(ok: bool) void {
+    if (!ok) unreachable; // assertion failure
+}
+
+// This test will fail because we hit unreachable.
+test "this will fail" {
+    assert(false);
+}
+
+// The type of unreachable is noreturn.
+test "type of unreachable" {
+    // However this assertion will still fail because
+    // evaluating unreachable at compile-time is a compile error.
+    comptime assert(@TypeOf(unreachable) == noreturn);
+}
diff --git a/lang/zig/values.zig b/lang/zig/values.zig
new file mode 100644
index 0000000..4095da5
--- /dev/null
+++ b/lang/zig/values.zig
@@ -0,0 +1,54 @@
+// Top-level declarations are order-independent:
+const print = std.debug.print;
+const std = @import("std");
+const os = std.os;
+const assert = std.debug.assert;
+
+pub fn main() void {
+    // integers
+    const one_plus_one: i32 = 1 + 1;
+    print("1 + 1 = {}\n", .{one_plus_one});
+
+    // floats
+    const seven_div_three: f32 = 7.0 / 3.0;
+    print("7.0 / 3.0 = {}\n", .{seven_div_three});
+
+    // boolean
+    print("{}\n{}\n{}\n", .{
+        true and false,
+        true or false,
+        !true,
+    });
+
+    // optional
+    var optional_value: ?[]const u8 = null;
+    assert(optional_value == null);
+
+    print("\noptional 1\ntype: {}\nvalue: {}\n", .{
+        @typeName(@TypeOf(optional_value)),
+        optional_value,
+    });
+
+    optional_value = "hi";
+    assert(optional_value != null);
+
+    print("\noptional 2\ntype: {}\nvalue: {}\n", .{
+        @typeName(@TypeOf(optional_value)),
+        optional_value,
+    });
+
+    // error union
+    var number_or_error: anyerror!i32 = error.ArgNotFound;
+
+    print("\nerror union 1\ntype: {}\nvalue: {}\n", .{
+        @typeName(@TypeOf(number_or_error)),
+        number_or_error,
+    });
+
+    number_or_error = 1234;
+
+    print("\nerror union 2\ntype: {}\nvalue: {}\n", .{
+        @typeName(@TypeOf(number_or_error)),
+        number_or_error,
+    });
+}
diff --git a/lang/zig/while-inline.zig b/lang/zig/while-inline.zig
new file mode 100644
index 0000000..2af1160
--- /dev/null
+++ b/lang/zig/while-inline.zig
@@ -0,0 +1,20 @@
+const expect = @import("std").testing.expect;
+
+fn typeNameLength(comptime T: type) usize {
+    return @typeName(T).len;
+}
+
+test "inline while loop" {
+    comptime var i = 0;
+    var sum: usize = 0;
+    inline while (i < 3) : (i += 1) {
+        const T = switch (i) {
+            0 => f32,
+            1 => i8,
+            2 => bool,
+            else => unreachable,
+        };
+        sum += typeNameLength(T);
+    }
+    expect(sum == 9);
+}
diff --git a/lang/zig/while-unpack.zig b/lang/zig/while-unpack.zig
new file mode 100644
index 0000000..d10c385
--- /dev/null
+++ b/lang/zig/while-unpack.zig
@@ -0,0 +1,43 @@
+const expect = @import("std").testing.expect;
+
+var numbers_left: u32 = undefined;
+
+fn eventuallyNullSequence() ?u32 {
+    return if (numbers_left == 0) null else blk: {
+        numbers_left -= 1;
+        break :blk numbers_left;
+    };
+}
+
+fn eventuallyErrorSequence() anyerror!u32 {
+    return if (numbers_left == 0) error.ReachedZero else blk: {
+        numbers_left -= 1;
+        break :blk numbers_left;
+    };
+}
+
+test "while null capture" {
+    var sum1: u32 = 0;
+    numbers_left = 3;
+    while (eventuallyNullSequence()) |value|
+        sum1 += value;
+    expect(sum1 == 3);
+
+    var sum2: u32 = 0;
+    numbers_left = 3;
+    while (eventuallyNullSequence()) |value| {
+        sum2 += value;
+    } else {
+        expect(sum2 == 3);
+    }
+}
+
+test "while error union capture" {
+    var sum: u32 = 0;
+    numbers_left = 3;
+    while (eventuallyErrorSequence()) |value| {
+        sum += value;
+    } else |err| {
+        expect(err == error.ReachedZero);
+    }
+}
diff --git a/lang/zig/while.zig b/lang/zig/while.zig
new file mode 100644
index 0000000..9fe87e9
--- /dev/null
+++ b/lang/zig/while.zig
@@ -0,0 +1,73 @@
+const expect = @import("std").testing.expect;
+
+test "while basic" {
+    var i: usize = 0;
+    while (i < 10)
+        i += 1;
+    expect(i == 10);
+}
+
+test "while break" {
+    var i: usize = 0;
+    while (true) {
+        if (i == 10)
+            break;
+        i += 1;
+    }
+    expect(i == 10);
+}
+
+test "while continue" {
+    var i: usize = 0;
+    while (true) {
+        i += 1;
+        if (i < 10)
+            continue;
+        break;
+    }
+    expect(i == 10);
+}
+
+test "while loop continue expression" {
+    var i: usize = 0;
+    while (i < 10) : (i += 1) {}
+    expect(i == 10);
+}
+
+test "while loop continue expression, more complicated" {
+    var i: usize = 1;
+    var j: usize = 1;
+    while (i * j < 2000) : ({ i *= 2; j *= 3; })
+        expect(i * j < 2000);
+}
+
+test "while else" {
+    expect(rangeHasNumber(0, 10, 5));
+    expect(!rangeHasNumber(0, 10, 15));
+}
+
+fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
+    var i = begin;
+    return while (i < end) : (i += 1) {
+        if (i == number) {
+            break true;
+        }
+    } else false;
+}
+
+test "nested break" {
+    outer: while (true) {
+        while (true) {
+            break :outer;
+        }
+    }
+}
+
+test "nested continue" {
+    var i: usize = 0;
+    outer: while (i < 10) : (i += 1) {
+        while (true) {
+            continue :outer;
+        }
+    }
+}