aboutsummaryrefslogtreecommitdiff
path: root/std
diff options
context:
space:
mode:
authorAndrea Orru <andrea@orru.io>2018-04-11 00:33:19 -0700
committerAndrea Orru <andrea@orru.io>2018-04-11 00:33:19 -0700
commit135a335ce12a33666e44cb13e0be4bb893877565 (patch)
tree23b10bed3d36e3fb244c6a2e5d5765d8b7a71fb5 /std
parentb01c5a95c468650f143e0ae96f6c3865852fdcda (diff)
parentf43711e5fbbedafa1c28c933fdca0949427c77cd (diff)
downloadzig-135a335ce12a33666e44cb13e0be4bb893877565.tar.gz
zig-135a335ce12a33666e44cb13e0be4bb893877565.zip
Merge branch 'master' into zen_stdlib
Diffstat (limited to 'std')
-rw-r--r--std/buf_map.zig51
-rw-r--r--std/build.zig11
-rw-r--r--std/c/darwin.zig18
-rw-r--r--std/c/index.zig1
-rw-r--r--std/crypto/blake2.zig22
-rw-r--r--std/crypto/hmac.zig81
-rw-r--r--std/crypto/index.zig6
-rw-r--r--std/crypto/md5.zig11
-rw-r--r--std/crypto/sha1.zig11
-rw-r--r--std/crypto/sha2.zig22
-rw-r--r--std/crypto/sha3.zig18
-rw-r--r--std/endian.zig25
-rw-r--r--std/event.zig235
-rw-r--r--std/fmt/index.zig2
-rw-r--r--std/hash/adler.zig112
-rw-r--r--std/hash/crc.zig180
-rw-r--r--std/hash/fnv.zig60
-rw-r--r--std/hash/index.zig22
-rw-r--r--std/hash/siphash.zig320
-rw-r--r--std/hash_map.zig6
-rw-r--r--std/index.zig12
-rw-r--r--std/io.zig30
-rw-r--r--std/io_test.zig6
-rw-r--r--std/linked_list.zig1
-rw-r--r--std/math/atan2.zig2
-rw-r--r--std/math/index.zig17
-rw-r--r--std/mem.zig26
-rw-r--r--std/net.zig283
-rw-r--r--std/os/child_process.zig35
-rw-r--r--std/os/darwin.zig35
-rw-r--r--std/os/file.zig2
-rw-r--r--std/os/index.zig638
-rw-r--r--std/os/linux/i386.zig505
-rw-r--r--std/os/linux/index.zig752
-rw-r--r--std/os/linux/x86_64.zig8
-rw-r--r--std/os/test.zig25
-rw-r--r--std/rand.zig240
-rw-r--r--std/rand/index.zig652
-rw-r--r--std/rand_test.zig507
-rw-r--r--std/sort.zig156
-rw-r--r--std/special/build_file_template.zig2
-rw-r--r--std/special/builtin.zig33
-rw-r--r--std/zig/ast.zig1400
-rw-r--r--std/zig/parser.zig4304
-rw-r--r--std/zig/tokenizer.zig472
45 files changed, 9179 insertions, 2178 deletions
diff --git a/std/buf_map.zig b/std/buf_map.zig
index a58df4b2db..3e12d9a7d9 100644
--- a/std/buf_map.zig
+++ b/std/buf_map.zig
@@ -1,6 +1,8 @@
-const HashMap = @import("hash_map.zig").HashMap;
-const mem = @import("mem.zig");
+const std = @import("index.zig");
+const HashMap = std.HashMap;
+const mem = std.mem;
const Allocator = mem.Allocator;
+const assert = std.debug.assert;
/// BufMap copies keys and values before they go into the map, and
/// frees them when they get removed.
@@ -28,18 +30,12 @@ pub const BufMap = struct {
}
pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void {
- if (self.hash_map.get(key)) |entry| {
- const value_copy = try self.copy(value);
- errdefer self.free(value_copy);
- _ = try self.hash_map.put(key, value_copy);
- self.free(entry.value);
- } else {
- const key_copy = try self.copy(key);
- errdefer self.free(key_copy);
- const value_copy = try self.copy(value);
- errdefer self.free(value_copy);
- _ = try self.hash_map.put(key_copy, value_copy);
- }
+ self.delete(key);
+ const key_copy = try self.copy(key);
+ errdefer self.free(key_copy);
+ const value_copy = try self.copy(value);
+ errdefer self.free(value_copy);
+ _ = try self.hash_map.put(key_copy, value_copy);
}
pub fn get(self: &BufMap, key: []const u8) ?[]const u8 {
@@ -66,8 +62,29 @@ pub const BufMap = struct {
}
fn copy(self: &BufMap, value: []const u8) ![]const u8 {
- const result = try self.hash_map.allocator.alloc(u8, value.len);
- mem.copy(u8, result, value);
- return result;
+ return mem.dupe(self.hash_map.allocator, u8, value);
}
};
+
+test "BufMap" {
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var bufmap = BufMap.init(&direct_allocator.allocator);
+ defer bufmap.deinit();
+
+ try bufmap.set("x", "1");
+ assert(mem.eql(u8, ??bufmap.get("x"), "1"));
+ assert(1 == bufmap.count());
+
+ try bufmap.set("x", "2");
+ assert(mem.eql(u8, ??bufmap.get("x"), "2"));
+ assert(1 == bufmap.count());
+
+ try bufmap.set("x", "3");
+ assert(mem.eql(u8, ??bufmap.get("x"), "3"));
+ assert(1 == bufmap.count());
+
+ bufmap.delete("x");
+ assert(0 == bufmap.count());
+}
diff --git a/std/build.zig b/std/build.zig
index e6b6676261..a4d745e450 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -1627,6 +1627,7 @@ pub const TestStep = struct {
filter: ?[]const u8,
target: Target,
exec_cmd_args: ?[]const ?[]const u8,
+ include_dirs: ArrayList([]const u8),
pub fn init(builder: &Builder, root_src: []const u8) TestStep {
const step_name = builder.fmt("test {}", root_src);
@@ -1641,6 +1642,7 @@ pub const TestStep = struct {
.link_libs = BufSet.init(builder.allocator),
.target = Target { .Native = {} },
.exec_cmd_args = null,
+ .include_dirs = ArrayList([]const u8).init(builder.allocator),
};
}
@@ -1648,6 +1650,10 @@ pub const TestStep = struct {
self.verbose = value;
}
+ pub fn addIncludeDir(self: &TestStep, path: []const u8) void {
+ self.include_dirs.append(path) catch unreachable;
+ }
+
pub fn setBuildMode(self: &TestStep, mode: builtin.Mode) void {
self.build_mode = mode;
}
@@ -1746,6 +1752,11 @@ pub const TestStep = struct {
}
}
+ for (self.include_dirs.toSliceConst()) |include_path| {
+ try zig_args.append("-isystem");
+ try zig_args.append(builder.pathFromRoot(include_path));
+ }
+
for (builder.include_paths.toSliceConst()) |include_path| {
try zig_args.append("-isystem");
try zig_args.append(builder.pathFromRoot(include_path));
diff --git a/std/c/darwin.zig b/std/c/darwin.zig
index f0890d4ec0..feb689cdc5 100644
--- a/std/c/darwin.zig
+++ b/std/c/darwin.zig
@@ -1,6 +1,7 @@
extern "c" fn __error() &c_int;
pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;
+pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;
pub use @import("../os/darwin_errno.zig");
@@ -45,3 +46,20 @@ pub const Sigaction = extern struct {
sa_mask: sigset_t,
sa_flags: c_int,
};
+
+pub const dirent = extern struct {
+ d_ino: usize,
+ d_seekoff: usize,
+ d_reclen: u16,
+ d_namlen: u16,
+ d_type: u8,
+ d_name: u8, // field address is address of first byte of name
+};
+
+pub const sockaddr = extern struct {
+ sa_len: u8,
+ sa_family: sa_family_t,
+ sa_data: [14]u8,
+};
+
+pub const sa_family_t = u8;
diff --git a/std/c/index.zig b/std/c/index.zig
index ce9f3c473a..369ea2b358 100644
--- a/std/c/index.zig
+++ b/std/c/index.zig
@@ -44,6 +44,7 @@ pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias o
pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;
pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
+pub extern "c" fn rmdir(path: &const u8) c_int;
pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?&c_void;
pub extern "c" fn malloc(usize) ?&c_void;
diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig
index ea0a68b184..99f0e629cd 100644
--- a/std/crypto/blake2.zig
+++ b/std/crypto/blake2.zig
@@ -84,7 +84,7 @@ fn Blake2s(comptime out_len: usize) type { return struct {
}
// Full middle blocks.
- while (off + 64 < b.len) : (off += 64) {
+ while (off + 64 <= b.len) : (off += 64) {
d.t += 64;
d.round(b[off..off + 64], false);
}
@@ -229,6 +229,15 @@ test "blake2s256 streaming" {
htest.assertEqual(h2, out[0..]);
}
+test "blake2s256 aligned final" {
+ var block = []u8 {0} ** Blake2s256.block_size;
+ var out: [Blake2s256.digest_size]u8 = undefined;
+
+ var h = Blake2s256.init();
+ h.update(block);
+ h.final(out[0..]);
+}
+
/////////////////////
// Blake2b
@@ -305,7 +314,7 @@ fn Blake2b(comptime out_len: usize) type { return struct {
}
// Full middle blocks.
- while (off + 128 < b.len) : (off += 128) {
+ while (off + 128 <= b.len) : (off += 128) {
d.t += 128;
d.round(b[off..off + 128], false);
}
@@ -447,3 +456,12 @@ test "blake2b512 streaming" {
h.final(out[0..]);
htest.assertEqual(h2, out[0..]);
}
+
+test "blake2b512 aligned final" {
+ var block = []u8 {0} ** Blake2b512.block_size;
+ var out: [Blake2b512.digest_size]u8 = undefined;
+
+ var h = Blake2b512.init();
+ h.update(block);
+ h.final(out[0..]);
+}
diff --git a/std/crypto/hmac.zig b/std/crypto/hmac.zig
new file mode 100644
index 0000000000..2a36f15b71
--- /dev/null
+++ b/std/crypto/hmac.zig
@@ -0,0 +1,81 @@
+const std = @import("../index.zig");
+const crypto = std.crypto;
+const debug = std.debug;
+const mem = std.mem;
+
+pub const HmacMd5 = Hmac(crypto.Md5);
+pub const HmacSha1 = Hmac(crypto.Sha1);
+pub const HmacSha256 = Hmac(crypto.Sha256);
+
+pub fn Hmac(comptime H: type) type {
+ return struct {
+ const digest_size = H.digest_size;
+
+ pub fn hash(output: []u8, key: []const u8, message: []const u8) void {
+ debug.assert(output.len >= H.digest_size);
+ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
+ var scratch: [H.block_size]u8 = undefined;
+
+ // Normalize key length to block size of hash
+ if (key.len > H.block_size) {
+ H.hash(key, scratch[0..H.digest_size]);
+ mem.set(u8, scratch[H.digest_size..H.block_size], 0);
+ } else if (key.len < H.block_size) {
+ mem.copy(u8, scratch[0..key.len], key);
+ mem.set(u8, scratch[key.len..H.block_size], 0);
+ } else {
+ mem.copy(u8, scratch[0..], key);
+ }
+
+ var o_key_pad: [H.block_size]u8 = undefined;
+ for (o_key_pad) |*b, i| {
+ *b = scratch[i] ^ 0x5c;
+ }
+
+ var i_key_pad: [H.block_size]u8 = undefined;
+ for (i_key_pad) |*b, i| {
+ *b = scratch[i] ^ 0x36;
+ }
+
+ // HMAC(k, m) = H(o_key_pad | H(i_key_pad | message)) where | is concatenation
+ var hmac = H.init();
+ hmac.update(i_key_pad[0..]);
+ hmac.update(message);
+ hmac.final(scratch[0..H.digest_size]);
+
+ hmac.reset();
+ hmac.update(o_key_pad[0..]);
+ hmac.update(scratch[0..H.digest_size]);
+ hmac.final(output[0..H.digest_size]);
+ }
+ };
+}
+
+const htest = @import("test.zig");
+
+test "hmac md5" {
+ var out: [crypto.Md5.digest_size]u8 = undefined;
+ HmacMd5.hash(out[0..], "", "");
+ htest.assertEqual("74e6f7298a9c2d168935f58c001bad88", out[0..]);
+
+ HmacMd5.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
+ htest.assertEqual("80070713463e7749b90c2dc24911e275", out[0..]);
+}
+
+test "hmac sha1" {
+ var out: [crypto.Sha1.digest_size]u8 = undefined;
+ HmacSha1.hash(out[0..], "", "");
+ htest.assertEqual("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d", out[0..]);
+
+ HmacSha1.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
+ htest.assertEqual("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9", out[0..]);
+}
+
+test "hmac sha256" {
+ var out: [crypto.Sha256.digest_size]u8 = undefined;
+ HmacSha256.hash(out[0..], "", "");
+ htest.assertEqual("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad", out[0..]);
+
+ HmacSha256.hash(out[0..], "key", "The quick brown fox jumps over the lazy dog");
+ htest.assertEqual("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8", out[0..]);
+}
diff --git a/std/crypto/index.zig b/std/crypto/index.zig
index ee7dc2aa0e..2f39020228 100644
--- a/std/crypto/index.zig
+++ b/std/crypto/index.zig
@@ -19,10 +19,16 @@ pub const Blake2s256 = blake2.Blake2s256;
pub const Blake2b384 = blake2.Blake2b384;
pub const Blake2b512 = blake2.Blake2b512;
+const hmac = @import("hmac.zig");
+pub const HmacMd5 = hmac.HmacMd5;
+pub const HmacSha1 = hmac.Sha1;
+pub const HmacSha256 = hmac.Sha256;
+
test "crypto" {
_ = @import("md5.zig");
_ = @import("sha1.zig");
_ = @import("sha2.zig");
_ = @import("sha3.zig");
_ = @import("blake2.zig");
+ _ = @import("hmac.zig");
}
diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig
index 26700cd65b..705b2428a7 100644
--- a/std/crypto/md5.zig
+++ b/std/crypto/md5.zig
@@ -59,7 +59,7 @@ pub const Md5 = struct {
}
// Full middle blocks.
- while (off + 64 < b.len) : (off += 64) {
+ while (off + 64 <= b.len) : (off += 64) {
d.round(b[off..off + 64]);
}
@@ -253,3 +253,12 @@ test "md5 streaming" {
htest.assertEqual("900150983cd24fb0d6963f7d28e17f72", out[0..]);
}
+
+test "md5 aligned final" {
+ var block = []u8 {0} ** Md5.block_size;
+ var out: [Md5.digest_size]u8 = undefined;
+
+ var h = Md5.init();
+ h.update(block);
+ h.final(out[0..]);
+}
diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig
index f0dd3c3377..333597b12d 100644
--- a/std/crypto/sha1.zig
+++ b/std/crypto/sha1.zig
@@ -60,7 +60,7 @@ pub const Sha1 = struct {
}
// Full middle blocks.
- while (off + 64 < b.len) : (off += 64) {
+ while (off + 64 <= b.len) : (off += 64) {
d.round(b[off..off + 64]);
}
@@ -284,3 +284,12 @@ test "sha1 streaming" {
h.final(out[0..]);
htest.assertEqual("a9993e364706816aba3e25717850c26c9cd0d89d", out[0..]);
}
+
+test "sha1 aligned final" {
+ var block = []u8 {0} ** Sha1.block_size;
+ var out: [Sha1.digest_size]u8 = undefined;
+
+ var h = Sha1.init();
+ h.update(block);
+ h.final(out[0..]);
+}
diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig
index 113bab926b..b70450c0ad 100644
--- a/std/crypto/sha2.zig
+++ b/std/crypto/sha2.zig
@@ -105,7 +105,7 @@ fn Sha2_32(comptime params: Sha2Params32) type { return struct {
}
// Full middle blocks.
- while (off + 64 < b.len) : (off += 64) {
+ while (off + 64 <= b.len) : (off += 64) {
d.round(b[off..off + 64]);
}
@@ -319,6 +319,15 @@ test "sha256 streaming" {
htest.assertEqual("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", out[0..]);
}
+test "sha256 aligned final" {
+ var block = []u8 {0} ** Sha256.block_size;
+ var out: [Sha256.digest_size]u8 = undefined;
+
+ var h = Sha256.init();
+ h.update(block);
+ h.final(out[0..]);
+}
+
/////////////////////
// Sha384 + Sha512
@@ -420,7 +429,7 @@ fn Sha2_64(comptime params: Sha2Params64) type { return struct {
}
// Full middle blocks.
- while (off + 128 < b.len) : (off += 128) {
+ while (off + 128 <= b.len) : (off += 128) {
d.round(b[off..off + 128]);
}
@@ -669,3 +678,12 @@ test "sha512 streaming" {
h.final(out[0..]);
htest.assertEqual(h2, out[0..]);
}
+
+test "sha512 aligned final" {
+ var block = []u8 {0} ** Sha512.block_size;
+ var out: [Sha512.digest_size]u8 = undefined;
+
+ var h = Sha512.init();
+ h.update(block);
+ h.final(out[0..]);
+}
diff --git a/std/crypto/sha3.zig b/std/crypto/sha3.zig
index 6e6a86b3d5..f92f56d68f 100644
--- a/std/crypto/sha3.zig
+++ b/std/crypto/sha3.zig
@@ -217,6 +217,15 @@ test "sha3-256 streaming" {
htest.assertEqual("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", out[0..]);
}
+test "sha3-256 aligned final" {
+ var block = []u8 {0} ** Sha3_256.block_size;
+ var out: [Sha3_256.digest_size]u8 = undefined;
+
+ var h = Sha3_256.init();
+ h.update(block);
+ h.final(out[0..]);
+}
+
test "sha3-384 single" {
const h1 = "0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004";
htest.assertEqualHash(Sha3_384, h1 , "");
@@ -278,3 +287,12 @@ test "sha3-512 streaming" {
h.final(out[0..]);
htest.assertEqual(h2, out[0..]);
}
+
+test "sha3-512 aligned final" {
+ var block = []u8 {0} ** Sha3_512.block_size;
+ var out: [Sha3_512.digest_size]u8 = undefined;
+
+ var h = Sha3_512.init();
+ h.update(block);
+ h.final(out[0..]);
+}
diff --git a/std/endian.zig b/std/endian.zig
deleted file mode 100644
index 121505d24d..0000000000
--- a/std/endian.zig
+++ /dev/null
@@ -1,25 +0,0 @@
-const mem = @import("mem.zig");
-const builtin = @import("builtin");
-
-pub fn swapIfLe(comptime T: type, x: T) T {
- return swapIf(builtin.Endian.Little, T, x);
-}
-
-pub fn swapIfBe(comptime T: type, x: T) T {
- return swapIf(builtin.Endian.Big, T, x);
-}
-
-pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) T {
- return if (builtin.endian == endian) swap(T, x) else x;
-}
-
-pub fn swap(comptime T: type, x: T) T {
- var buf: [@sizeOf(T)]u8 = undefined;
- mem.writeInt(buf[0..], x, builtin.Endian.Little);
- return mem.readInt(buf, T, builtin.Endian.Big);
-}
-
-test "swap" {
- const debug = @import("debug/index.zig");
- debug.assert(swap(u32, 0xDEADBEEF) == 0xEFBEADDE);
-}
diff --git a/std/event.zig b/std/event.zig
new file mode 100644
index 0000000000..bdad7fcc18
--- /dev/null
+++ b/std/event.zig
@@ -0,0 +1,235 @@
+const std = @import("index.zig");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const event = this;
+const mem = std.mem;
+const posix = std.os.posix;
+
+pub const TcpServer = struct {
+ handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void,
+
+ loop: &Loop,
+ sockfd: i32,
+ accept_coro: ?promise,
+ listen_address: std.net.Address,
+
+ waiting_for_emfile_node: PromiseNode,
+
+ const PromiseNode = std.LinkedList(promise).Node;
+
+ pub fn init(loop: &Loop) !TcpServer {
+ const sockfd = try std.os.posixSocket(posix.AF_INET,
+ posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK,
+ posix.PROTO_tcp);
+ errdefer std.os.close(sockfd);
+
+ // TODO can't initialize handler coroutine here because we need well defined copy elision
+ return TcpServer {
+ .loop = loop,
+ .sockfd = sockfd,
+ .accept_coro = null,
+ .handleRequestFn = undefined,
+ .waiting_for_emfile_node = undefined,
+ .listen_address = undefined,
+ };
+ }
+
+ pub fn listen(self: &TcpServer, address: &const std.net.Address,
+ handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void
+ {
+ self.handleRequestFn = handleRequestFn;
+
+ try std.os.posixBind(self.sockfd, &address.os_addr);
+ try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
+ self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
+
+ self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);
+ errdefer cancel ??self.accept_coro;
+
+ try self.loop.addFd(self.sockfd, ??self.accept_coro);
+ errdefer self.loop.removeFd(self.sockfd);
+
+ }
+
+ pub fn deinit(self: &TcpServer) void {
+ self.loop.removeFd(self.sockfd);
+ if (self.accept_coro) |accept_coro| cancel accept_coro;
+ std.os.close(self.sockfd);
+ }
+
+ pub async fn handler(self: &TcpServer) void {
+ while (true) {
+ var accepted_addr: std.net.Address = undefined;
+ if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr,
+ posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd|
+ {
+ var socket = std.os.File.openHandle(accepted_fd);
+ _ = async<self.loop.allocator> self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) {
+ error.OutOfMemory => {
+ socket.close();
+ continue;
+ },
+ };
+ } else |err| switch (err) {
+ error.WouldBlock => {
+ suspend; // we will get resumed by epoll_wait in the event loop
+ continue;
+ },
+ error.ProcessFdQuotaExceeded => {
+ errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
+ suspend |p| {
+ self.waiting_for_emfile_node = PromiseNode.init(p);
+ std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
+ }
+ continue;
+ },
+ error.ConnectionAborted,
+ error.FileDescriptorClosed => continue,
+
+ error.PageFault => unreachable,
+ error.InvalidSyscall => unreachable,
+ error.FileDescriptorNotASocket => unreachable,
+ error.OperationNotSupported => unreachable,
+
+ error.SystemFdQuotaExceeded,
+ error.SystemResources,
+ error.ProtocolFailure,
+ error.BlockedByFirewall,
+ error.Unexpected => {
+ @panic("TODO handle this error");
+ },
+ }
+ }
+ }
+};
+
+pub const Loop = struct {
+ allocator: &mem.Allocator,
+ epollfd: i32,
+ keep_running: bool,
+
+ fn init(allocator: &mem.Allocator) !Loop {
+ const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
+ return Loop {
+ .keep_running = true,
+ .allocator = allocator,
+ .epollfd = epollfd,
+ };
+ }
+
+ pub fn addFd(self: &Loop, fd: i32, prom: promise) !void {
+ var ev = std.os.linux.epoll_event {
+ .events = std.os.linux.EPOLLIN|std.os.linux.EPOLLOUT|std.os.linux.EPOLLET,
+ .data = std.os.linux.epoll_data {
+ .ptr = @ptrToInt(prom),
+ },
+ };
+ try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
+ }
+
+ pub fn removeFd(self: &Loop, fd: i32) void {
+ std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
+ }
+
+ async fn waitFd(self: &Loop, fd: i32) !void {
+ defer self.removeFd(fd);
+ suspend |p| {
+ try self.addFd(fd, p);
+ }
+ }
+
+ pub fn stop(self: &Loop) void {
+ // TODO make atomic
+ self.keep_running = false;
+ // TODO activate an fd in the epoll set
+ }
+
+ pub fn run(self: &Loop) void {
+ while (self.keep_running) {
+ var events: [16]std.os.linux.epoll_event = undefined;
+ const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1);
+ for (events[0..count]) |ev| {
+ const p = @intToPtr(promise, ev.data.ptr);
+ resume p;
+ }
+ }
+ }
+};
+
+pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File {
+ var address = *_address; // TODO https://github.com/zig-lang/zig/issues/733
+
+ const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK, posix.PROTO_tcp);
+ errdefer std.os.close(sockfd);
+
+ try std.os.posixConnectAsync(sockfd, &address.os_addr);
+ try await try async loop.waitFd(sockfd);
+ try std.os.posixGetSockOptConnectError(sockfd);
+
+ return std.os.File.openHandle(sockfd);
+}
+
+test "listen on a port, send bytes, receive bytes" {
+ if (builtin.os != builtin.Os.linux) {
+ // TODO build abstractions for other operating systems
+ return;
+ }
+ const MyServer = struct {
+ tcp_server: TcpServer,
+
+ const Self = this;
+
+ async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address,
+ _socket: &const std.os.File) void
+ {
+ const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
+ var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
+ defer socket.close();
+ const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
+ error.OutOfMemory => @panic("unable to handle connection: out of memory"),
+ };
+ (await next_handler) catch |err| {
+ std.debug.panic("unable to handle connection: {}\n", err);
+ };
+ suspend |p| { cancel p; }
+ }
+
+ async fn errorableHandler(self: &Self, _addr: &const std.net.Address,
+ _socket: &const std.os.File) !void
+ {
+ const addr = *_addr; // TODO https://github.com/zig-lang/zig/issues/733
+ var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
+
+ var adapter = std.io.FileOutStream.init(&socket);
+ var stream = &adapter.stream;
+ try stream.print("hello from server\n");
+ }
+ };
+
+ const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
+ const addr = std.net.Address.initIp4(ip4addr, 0);
+
+ var loop = try Loop.init(std.debug.global_allocator);
+ var server = MyServer {
+ .tcp_server = try TcpServer.init(&loop),
+ };
+ defer server.tcp_server.deinit();
+ try server.tcp_server.listen(addr, MyServer.handler);
+
+ const p = try async<std.debug.global_allocator> doAsyncTest(&loop, server.tcp_server.listen_address);
+ defer cancel p;
+ loop.run();
+}
+
+async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void {
+ errdefer @panic("test failure");
+
+ var socket_file = try await try async event.connect(loop, address);
+ defer socket_file.close();
+
+ var buf: [512]u8 = undefined;
+ const amt_read = try socket_file.read(buf[0..]);
+ const msg = buf[0..amt_read];
+ assert(mem.eql(u8, msg, "hello from server\n"));
+ loop.stop();
+}
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index bd5b5710e0..cfdd70e95b 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -465,7 +465,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned
return x;
}
-fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
+pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
const value = switch (c) {
'0' ... '9' => c - '0',
'A' ... 'Z' => c - 'A' + 10,
diff --git a/std/hash/adler.zig b/std/hash/adler.zig
new file mode 100644
index 0000000000..c77a5aaf50
--- /dev/null
+++ b/std/hash/adler.zig
@@ -0,0 +1,112 @@
+// Adler32 checksum.
+//
+// https://tools.ietf.org/html/rfc1950#section-9
+// https://github.com/madler/zlib/blob/master/adler32.c
+
+const std = @import("../index.zig");
+const debug = std.debug;
+
+pub const Adler32 = struct {
+ const base = 65521;
+ const nmax = 5552;
+
+ adler: u32,
+
+ pub fn init() Adler32 {
+ return Adler32 {
+ .adler = 1,
+ };
+ }
+
+ // This fast variant is taken from zlib. It reduces the required modulos and unrolls longer
+ // buffer inputs and should be much quicker.
+ pub fn update(self: &Adler32, input: []const u8) void {
+ var s1 = self.adler & 0xffff;
+ var s2 = (self.adler >> 16) & 0xffff;
+
+ if (input.len == 1) {
+ s1 +%= input[0];
+ if (s1 >= base) {
+ s1 -= base;
+ }
+ s2 +%= s1;
+ if (s2 >= base) {
+ s2 -= base;
+ }
+ }
+ else if (input.len < 16) {
+ for (input) |b| {
+ s1 +%= b;
+ s2 +%= s1;
+ }
+ if (s1 >= base) {
+ s1 -= base;
+ }
+
+ s2 %= base;
+ }
+ else {
+ var i: usize = 0;
+ while (i + nmax <= input.len) : (i += nmax) {
+ const n = nmax / 16; // note: 16 | nmax
+
+ var rounds: usize = 0;
+ while (rounds < n) : (rounds += 1) {
+ comptime var j: usize = 0;
+ inline while (j < 16) : (j += 1) {
+ s1 +%= input[i + n * j];
+ s2 +%= s1;
+ }
+ }
+ }
+
+ if (i < input.len) {
+ while (i + 16 <= input.len) : (i += 16) {
+ comptime var j: usize = 0;
+ inline while (j < 16) : (j += 1) {
+ s1 +%= input[i + j];
+ s2 +%= s1;
+ }
+ }
+ while (i < input.len) : (i += 1) {
+ s1 +%= input[i];
+ s2 +%= s1;
+ }
+
+ s1 %= base;
+ s2 %= base;
+ }
+ }
+
+ self.adler = s1 | (s2 << 16);
+ }
+
+ pub fn final(self: &Adler32) u32 {
+ return self.adler;
+ }
+
+ pub fn hash(input: []const u8) u32 {
+ var c = Adler32.init();
+ c.update(input);
+ return c.final();
+ }
+};
+
+test "adler32 sanity" {
+ debug.assert(Adler32.hash("a") == 0x620062);
+ debug.assert(Adler32.hash("example") == 0xbc002ed);
+}
+
+test "adler32 long" {
+ const long1 = []u8 {1} ** 1024;
+ debug.assert(Adler32.hash(long1[0..]) == 0x06780401);
+
+ const long2 = []u8 {1} ** 1025;
+ debug.assert(Adler32.hash(long2[0..]) == 0x0a7a0402);
+}
+
+test "adler32 very long" {
+ const long = []u8 {1} ** 5553;
+ debug.assert(Adler32.hash(long[0..]) == 0x707f15b2);
+}
+
diff --git a/std/hash/crc.zig b/std/hash/crc.zig
new file mode 100644
index 0000000000..f88069ce3c
--- /dev/null
+++ b/std/hash/crc.zig
@@ -0,0 +1,180 @@
+// There are two implementations of CRC32 implemented with the following key characteristics:
+//
+// - Crc32WithPoly uses 8Kb of tables but is ~10x faster than the small method.
+//
+// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is
+// still moderately fast just slow relative to the slicing approach.
+
+const std = @import("../index.zig");
+const debug = std.debug;
+
+pub const Polynomial = struct {
+ const IEEE = 0xedb88320;
+ const Castagnoli = 0x82f63b78;
+ const Koopman = 0xeb31d82e;
+};
+
+// IEEE is by far the most common CRC and so is aliased by default.
+pub const Crc32 = Crc32WithPoly(Polynomial.IEEE);
+
+// slicing-by-8 crc32 implementation.
+pub fn Crc32WithPoly(comptime poly: u32) type {
+ return struct {
+ const Self = this;
+ const lookup_tables = comptime block: {
+ @setEvalBranchQuota(20000);
+ var tables: [8][256]u32 = undefined;
+
+ for (tables[0]) |*e, i| {
+ var crc = u32(i);
+ var j: usize = 0; while (j < 8) : (j += 1) {
+ if (crc & 1 == 1) {
+ crc = (crc >> 1) ^ poly;
+ } else {
+ crc = (crc >> 1);
+ }
+ }
+ *e = crc;
+ }
+
+ var i: usize = 0;
+ while (i < 256) : (i += 1) {
+ var crc = tables[0][i];
+ var j: usize = 1; while (j < 8) : (j += 1) {
+ const index = @truncate(u8, crc);
+ crc = tables[0][index] ^ (crc >> 8);
+ tables[j][i] = crc;
+ }
+ }
+
+ break :block tables;
+ };
+
+ crc: u32,
+
+ pub fn init() Self {
+ return Self {
+ .crc = 0xffffffff,
+ };
+ }
+
+ pub fn update(self: &Self, input: []const u8) void {
+ var i: usize = 0;
+ while (i + 8 <= input.len) : (i += 8) {
+ const p = input[i..i+8];
+
+ // Unrolling this way gives ~50Mb/s increase
+ self.crc ^= (u32(p[0]) << 0);
+ self.crc ^= (u32(p[1]) << 8);
+ self.crc ^= (u32(p[2]) << 16);
+ self.crc ^= (u32(p[3]) << 24);
+
+ self.crc =
+ lookup_tables[0][p[7]] ^
+ lookup_tables[1][p[6]] ^
+ lookup_tables[2][p[5]] ^
+ lookup_tables[3][p[4]] ^
+ lookup_tables[4][@truncate(u8, self.crc >> 24)] ^
+ lookup_tables[5][@truncate(u8, self.crc >> 16)] ^
+ lookup_tables[6][@truncate(u8, self.crc >> 8)] ^
+ lookup_tables[7][@truncate(u8, self.crc >> 0)];
+ }
+
+ while (i < input.len) : (i += 1) {
+ const index = @truncate(u8, self.crc) ^ input[i];
+ self.crc = (self.crc >> 8) ^ lookup_tables[0][index];
+ }
+ }
+
+ pub fn final(self: &Self) u32 {
+ return ~self.crc;
+ }
+
+ pub fn hash(input: []const u8) u32 {
+ var c = Self.init();
+ c.update(input);
+ return c.final();
+ }
+ };
+}
+
+test "crc32 ieee" {
+ const Crc32Ieee = Crc32WithPoly(Polynomial.IEEE);
+
+ debug.assert(Crc32Ieee.hash("") == 0x00000000);
+ debug.assert(Crc32Ieee.hash("a") == 0xe8b7be43);
+ debug.assert(Crc32Ieee.hash("abc") == 0x352441c2);
+}
+
+test "crc32 castagnoli" {
+ const Crc32Castagnoli = Crc32WithPoly(Polynomial.Castagnoli);
+
+ debug.assert(Crc32Castagnoli.hash("") == 0x00000000);
+ debug.assert(Crc32Castagnoli.hash("a") == 0xc1d04330);
+ debug.assert(Crc32Castagnoli.hash("abc") == 0x364b3fb7);
+}
+
+// half-byte lookup table implementation.
+pub fn Crc32SmallWithPoly(comptime poly: u32) type {
+ return struct {
+ const Self = this;
+ const lookup_table = comptime block: {
+ var table: [16]u32 = undefined;
+
+ for (table) |*e, i| {
+ var crc = u32(i * 16);
+ var j: usize = 0; while (j < 8) : (j += 1) {
+ if (crc & 1 == 1) {
+ crc = (crc >> 1) ^ poly;
+ } else {
+ crc = (crc >> 1);
+ }
+ }
+ *e = crc;
+ }
+
+ break :block table;
+ };
+
+ crc: u32,
+
+ pub fn init() Self {
+ return Self {
+ .crc = 0xffffffff,
+ };
+ }
+
+ pub fn update(self: &Self, input: []const u8) void {
+ for (input) |b| {
+ self.crc = lookup_table[@truncate(u4, self.crc ^ (b >> 0))] ^ (self.crc >> 4);
+ self.crc = lookup_table[@truncate(u4, self.crc ^ (b >> 4))] ^ (self.crc >> 4);
+ }
+ }
+
+ pub fn final(self: &Self) u32 {
+ return ~self.crc;
+ }
+
+ pub fn hash(input: []const u8) u32 {
+ var c = Self.init();
+ c.update(input);
+ return c.final();
+ }
+ };
+}
+
+test "small crc32 ieee" {
+ const Crc32Ieee = Crc32SmallWithPoly(Polynomial.IEEE);
+
+ debug.assert(Crc32Ieee.hash("") == 0x00000000);
+ debug.assert(Crc32Ieee.hash("a") == 0xe8b7be43);
+ debug.assert(Crc32Ieee.hash("abc") == 0x352441c2);
+}
+
+test "small crc32 castagnoli" {
+ const Crc32Castagnoli = Crc32SmallWithPoly(Polynomial.Castagnoli);
+
+ debug.assert(Crc32Castagnoli.hash("") == 0x00000000);
+ debug.assert(Crc32Castagnoli.hash("a") == 0xc1d04330);
+ debug.assert(Crc32Castagnoli.hash("abc") == 0x364b3fb7);
+}
diff --git a/std/hash/fnv.zig b/std/hash/fnv.zig
new file mode 100644
index 0000000000..88b965b76a
--- /dev/null
+++ b/std/hash/fnv.zig
@@ -0,0 +1,60 @@
+// FNV1a - Fowler-Noll-Vo hash function
+//
+// FNV1a is a fast, non-cryptographic hash function with fairly good distribution properties.
+//
+// https://tools.ietf.org/html/draft-eastlake-fnv-14
+
+const std = @import("../index.zig");
+const debug = std.debug;
+
+pub const Fnv1a_32 = Fnv1a(u32, 0x01000193 , 0x811c9dc5);
+pub const Fnv1a_64 = Fnv1a(u64, 0x100000001b3, 0xcbf29ce484222325);
+pub const Fnv1a_128 = Fnv1a(u128, 0x1000000000000000000013b, 0x6c62272e07bb014262b821756295c58d);
+
+fn Fnv1a(comptime T: type, comptime prime: T, comptime offset: T) type {
+ return struct {
+ const Self = this;
+
+ value: T,
+
+ pub fn init() Self {
+ return Self {
+ .value = offset,
+ };
+ }
+
+ pub fn update(self: &Self, input: []const u8) void {
+ for (input) |b| {
+ self.value ^= b;
+ self.value *%= prime;
+ }
+ }
+
+ pub fn final(self: &Self) T {
+ return self.value;
+ }
+
+ pub fn hash(input: []const u8) T {
+ var c = Self.init();
+ c.update(input);
+ return c.final();
+ }
+ };
+}
+
+test "fnv1a-32" {
+ debug.assert(Fnv1a_32.hash("") == 0x811c9dc5);
+ debug.assert(Fnv1a_32.hash("a") == 0xe40c292c);
+ debug.assert(Fnv1a_32.hash("foobar") == 0xbf9cf968);
+}
+
+test "fnv1a-64" {
+ debug.assert(Fnv1a_64.hash("") == 0xcbf29ce484222325);
+ debug.assert(Fnv1a_64.hash("a") == 0xaf63dc4c8601ec8c);
+ debug.assert(Fnv1a_64.hash("foobar") == 0x85944171f73967e8);
+}
+
+test "fnv1a-128" {
+ debug.assert(Fnv1a_128.hash("") == 0x6c62272e07bb014262b821756295c58d);
+ debug.assert(Fnv1a_128.hash("a") == 0xd228cb696f1a8caf78912b704e4a8964);
+}
diff --git a/std/hash/index.zig b/std/hash/index.zig
new file mode 100644
index 0000000000..8cce35f3c5
--- /dev/null
+++ b/std/hash/index.zig
@@ -0,0 +1,22 @@
+const adler = @import("adler.zig");
+pub const Adler32 = adler.Adler32;
+
+// pub for polynomials + generic crc32 construction
+pub const crc = @import("crc.zig");
+pub const Crc32 = crc.Crc32;
+
+const fnv = @import("fnv.zig");
+pub const Fnv1a_32 = fnv.Fnv1a_32;
+pub const Fnv1a_64 = fnv.Fnv1a_64;
+pub const Fnv1a_128 = fnv.Fnv1a_128;
+
+const siphash = @import("siphash.zig");
+pub const SipHash64 = siphash.SipHash64;
+pub const SipHash128 = siphash.SipHash128;
+
+test "hash" {
+ _ = @import("adler.zig");
+ _ = @import("crc.zig");
+ _ = @import("fnv.zig");
+ _ = @import("siphash.zig");
+}
diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig
new file mode 100644
index 0000000000..301c35cf05
--- /dev/null
+++ b/std/hash/siphash.zig
@@ -0,0 +1,320 @@
+// Siphash
+//
+// SipHash is a moderately fast, non-cryptographic keyed hash function designed for resistance
+// against hash flooding DoS attacks.
+//
+// https://131002.net/siphash/
+
+const std = @import("../index.zig");
+const debug = std.debug;
+const math = std.math;
+const mem = std.mem;
+
+const Endian = @import("builtin").Endian;
+
+pub fn SipHash64(comptime c_rounds: usize, comptime d_rounds: usize) type {
+ return SipHash(u64, c_rounds, d_rounds);
+}
+
+pub fn SipHash128(comptime c_rounds: usize, comptime d_rounds: usize) type {
+ return SipHash(u128, c_rounds, d_rounds);
+}
+
+fn SipHash(comptime T: type, comptime c_rounds: usize, comptime d_rounds: usize) type {
+ debug.assert(T == u64 or T == u128);
+ debug.assert(c_rounds > 0 and d_rounds > 0);
+
+ return struct {
+ const Self = this;
+ const digest_size = 64;
+ const block_size = 64;
+
+ v0: u64,
+ v1: u64,
+ v2: u64,
+ v3: u64,
+
+ // streaming cache
+ buf: [8]u8,
+ buf_len: usize,
+ msg_len: u8,
+
+ pub fn init(key: []const u8) Self {
+ debug.assert(key.len >= 16);
+
+ const k0 = mem.readInt(key[0..8], u64, Endian.Little);
+ const k1 = mem.readInt(key[8..16], u64, Endian.Little);
+
+ var d = Self {
+ .v0 = k0 ^ 0x736f6d6570736575,
+ .v1 = k1 ^ 0x646f72616e646f6d,
+ .v2 = k0 ^ 0x6c7967656e657261,
+ .v3 = k1 ^ 0x7465646279746573,
+
+ .buf = undefined,
+ .buf_len = 0,
+ .msg_len = 0,
+ };
+
+ if (T == u128) {
+ d.v1 ^= 0xee;
+ }
+
+ return d;
+ }
+
+ pub fn update(d: &Self, b: []const u8) void {
+ var off: usize = 0;
+
+ // Partial from previous.
+ if (d.buf_len != 0 and d.buf_len + b.len > 8) {
+ off += 8 - d.buf_len;
+ mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
+ d.round(d.buf[0..]);
+ d.buf_len = 0;
+ }
+
+ // Full middle blocks.
+ while (off + 8 <= b.len) : (off += 8) {
+ d.round(b[off..off + 8]);
+ }
+
+ // Remainder for next pass.
+ mem.copy(u8, d.buf[d.buf_len..], b[off..]);
+ d.buf_len += u8(b[off..].len);
+ d.msg_len +%= @truncate(u8, b.len);
+ }
+
+ pub fn final(d: &Self) T {
+ // Padding
+ mem.set(u8, d.buf[d.buf_len..], 0);
+ d.buf[7] = d.msg_len;
+ d.round(d.buf[0..]);
+
+ if (T == u128) {
+ d.v2 ^= 0xee;
+ } else {
+ d.v2 ^= 0xff;
+ }
+
+ comptime var i: usize = 0;
+ inline while (i < d_rounds) : (i += 1) {
+ @inlineCall(sipRound, d);
+ }
+
+ const b1 = d.v0 ^ d.v1 ^ d.v2 ^ d.v3;
+ if (T == u64) {
+ return b1;
+ }
+
+ d.v1 ^= 0xdd;
+
+ comptime var j: usize = 0;
+ inline while (j < d_rounds) : (j += 1) {
+ @inlineCall(sipRound, d);
+ }
+
+ const b2 = d.v0 ^ d.v1 ^ d.v2 ^ d.v3;
+ return (u128(b2) << 64) | b1;
+ }
+
+ fn round(d: &Self, b: []const u8) void {
+ debug.assert(b.len == 8);
+
+ const m = mem.readInt(b[0..], u64, Endian.Little);
+ d.v3 ^= m;
+
+ comptime var i: usize = 0;
+ inline while (i < c_rounds) : (i += 1) {
+ @inlineCall(sipRound, d);
+ }
+
+ d.v0 ^= m;
+ }
+
+ fn sipRound(d: &Self) void {
+ d.v0 +%= d.v1;
+ d.v1 = math.rotl(u64, d.v1, u64(13));
+ d.v1 ^= d.v0;
+ d.v0 = math.rotl(u64, d.v0, u64(32));
+ d.v2 +%= d.v3;
+ d.v3 = math.rotl(u64, d.v3, u64(16));
+ d.v3 ^= d.v2;
+ d.v0 +%= d.v3;
+ d.v3 = math.rotl(u64, d.v3, u64(21));
+ d.v3 ^= d.v0;
+ d.v2 +%= d.v1;
+ d.v1 = math.rotl(u64, d.v1, u64(17));
+ d.v1 ^= d.v2;
+ d.v2 = math.rotl(u64, d.v2, u64(32));
+ }
+
+ pub fn hash(key: []const u8, input: []const u8) T {
+ var c = Self.init(key);
+ c.update(input);
+ return c.final();
+ }
+ };
+}
+
+// Test vectors from reference implementation.
+// https://github.com/veorq/SipHash/blob/master/vectors.h
+const test_key = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
+
+test "siphash64-2-4 sanity" {
+ const vectors = [][]const u8 {
+ "\x31\x0e\x0e\xdd\x47\xdb\x6f\x72", // ""
+ "\xfd\x67\xdc\x93\xc5\x39\xf8\x74", // "\x00"
+ "\x5a\x4f\xa9\xd9\x09\x80\x6c\x0d", // "\x00\x01" ... etc
+ "\x2d\x7e\xfb\xd7\x96\x66\x67\x85",
+ "\xb7\x87\x71\x27\xe0\x94\x27\xcf",
+ "\x8d\xa6\x99\xcd\x64\x55\x76\x18",
+ "\xce\xe3\xfe\x58\x6e\x46\xc9\xcb",
+ "\x37\xd1\x01\x8b\xf5\x00\x02\xab",
+ "\x62\x24\x93\x9a\x79\xf5\xf5\x93",
+ "\xb0\xe4\xa9\x0b\xdf\x82\x00\x9e",
+ "\xf3\xb9\xdd\x94\xc5\xbb\x5d\x7a",
+ "\xa7\xad\x6b\x22\x46\x2f\xb3\xf4",
+ "\xfb\xe5\x0e\x86\xbc\x8f\x1e\x75",
+ "\x90\x3d\x84\xc0\x27\x56\xea\x14",
+ "\xee\xf2\x7a\x8e\x90\xca\x23\xf7",
+ "\xe5\x45\xbe\x49\x61\xca\x29\xa1",
+ "\xdb\x9b\xc2\x57\x7f\xcc\x2a\x3f",
+ "\x94\x47\xbe\x2c\xf5\xe9\x9a\x69",
+ "\x9c\xd3\x8d\x96\xf0\xb3\xc1\x4b",
+ "\xbd\x61\x79\xa7\x1d\xc9\x6d\xbb",
+ "\x98\xee\xa2\x1a\xf2\x5c\xd6\xbe",
+ "\xc7\x67\x3b\x2e\xb0\xcb\xf2\xd0",
+ "\x88\x3e\xa3\xe3\x95\x67\x53\x93",
+ "\xc8\xce\x5c\xcd\x8c\x03\x0c\xa8",
+ "\x94\xaf\x49\xf6\xc6\x50\xad\xb8",
+ "\xea\xb8\x85\x8a\xde\x92\xe1\xbc",
+ "\xf3\x15\xbb\x5b\xb8\x35\xd8\x17",
+ "\xad\xcf\x6b\x07\x63\x61\x2e\x2f",
+ "\xa5\xc9\x1d\xa7\xac\xaa\x4d\xde",
+ "\x71\x65\x95\x87\x66\x50\xa2\xa6",
+ "\x28\xef\x49\x5c\x53\xa3\x87\xad",
+ "\x42\xc3\x41\xd8\xfa\x92\xd8\x32",
+ "\xce\x7c\xf2\x72\x2f\x51\x27\x71",
+ "\xe3\x78\x59\xf9\x46\x23\xf3\xa7",
+ "\x38\x12\x05\xbb\x1a\xb0\xe0\x12",
+ "\xae\x97\xa1\x0f\xd4\x34\xe0\x15",
+ "\xb4\xa3\x15\x08\xbe\xff\x4d\x31",
+ "\x81\x39\x62\x29\xf0\x90\x79\x02",
+ "\x4d\x0c\xf4\x9e\xe5\xd4\xdc\xca",
+ "\x5c\x73\x33\x6a\x76\xd8\xbf\x9a",
+ "\xd0\xa7\x04\x53\x6b\xa9\x3e\x0e",
+ "\x92\x59\x58\xfc\xd6\x42\x0c\xad",
+ "\xa9\x15\xc2\x9b\xc8\x06\x73\x18",
+ "\x95\x2b\x79\xf3\xbc\x0a\xa6\xd4",
+ "\xf2\x1d\xf2\xe4\x1d\x45\x35\xf9",
+ "\x87\x57\x75\x19\x04\x8f\x53\xa9",
+ "\x10\xa5\x6c\xf5\xdf\xcd\x9a\xdb",
+ "\xeb\x75\x09\x5c\xcd\x98\x6c\xd0",
+ "\x51\xa9\xcb\x9e\xcb\xa3\x12\xe6",
+ "\x96\xaf\xad\xfc\x2c\xe6\x66\xc7",
+ "\x72\xfe\x52\x97\x5a\x43\x64\xee",
+ "\x5a\x16\x45\xb2\x76\xd5\x92\xa1",
+ "\xb2\x74\xcb\x8e\xbf\x87\x87\x0a",
+ "\x6f\x9b\xb4\x20\x3d\xe7\xb3\x81",
+ "\xea\xec\xb2\xa3\x0b\x22\xa8\x7f",
+ "\x99\x24\xa4\x3c\xc1\x31\x57\x24",
+ "\xbd\x83\x8d\x3a\xaf\xbf\x8d\xb7",
+ "\x0b\x1a\x2a\x32\x65\xd5\x1a\xea",
+ "\x13\x50\x79\xa3\x23\x1c\xe6\x60",
+ "\x93\x2b\x28\x46\xe4\xd7\x06\x66",
+ "\xe1\x91\x5f\x5c\xb1\xec\xa4\x6c",
+ "\xf3\x25\x96\x5c\xa1\x6d\x62\x9f",
+ "\x57\x5f\xf2\x8e\x60\x38\x1b\xe5",
+ "\x72\x45\x06\xeb\x4c\x32\x8a\x95",
+ };
+
+ const siphash = SipHash64(2, 4);
+
+ var buffer: [64]u8 = undefined;
+ for (vectors) |vector, i| {
+ buffer[i] = u8(i);
+
+ const expected = mem.readInt(vector, u64, Endian.Little);
+ debug.assert(siphash.hash(test_key, buffer[0..i]) == expected);
+ }
+}
+
+test "siphash128-2-4 sanity" {
+ const vectors = [][]const u8 {
+ "\xa3\x81\x7f\x04\xba\x25\xa8\xe6\x6d\xf6\x72\x14\xc7\x55\x02\x93",
+ "\xda\x87\xc1\xd8\x6b\x99\xaf\x44\x34\x76\x59\x11\x9b\x22\xfc\x45",
+ "\x81\x77\x22\x8d\xa4\xa4\x5d\xc7\xfc\xa3\x8b\xde\xf6\x0a\xff\xe4",
+ "\x9c\x70\xb6\x0c\x52\x67\xa9\x4e\x5f\x33\xb6\xb0\x29\x85\xed\x51",
+ "\xf8\x81\x64\xc1\x2d\x9c\x8f\xaf\x7d\x0f\x6e\x7c\x7b\xcd\x55\x79",
+ "\x13\x68\x87\x59\x80\x77\x6f\x88\x54\x52\x7a\x07\x69\x0e\x96\x27",
+ "\x14\xee\xca\x33\x8b\x20\x86\x13\x48\x5e\xa0\x30\x8f\xd7\xa1\x5e",
+ "\xa1\xf1\xeb\xbe\xd8\xdb\xc1\x53\xc0\xb8\x4a\xa6\x1f\xf0\x82\x39",
+ "\x3b\x62\xa9\xba\x62\x58\xf5\x61\x0f\x83\xe2\x64\xf3\x14\x97\xb4",
+ "\x26\x44\x99\x06\x0a\xd9\xba\xab\xc4\x7f\x8b\x02\xbb\x6d\x71\xed",
+ "\x00\x11\x0d\xc3\x78\x14\x69\x56\xc9\x54\x47\xd3\xf3\xd0\xfb\xba",
+ "\x01\x51\xc5\x68\x38\x6b\x66\x77\xa2\xb4\xdc\x6f\x81\xe5\xdc\x18",
+ "\xd6\x26\xb2\x66\x90\x5e\xf3\x58\x82\x63\x4d\xf6\x85\x32\xc1\x25",
+ "\x98\x69\xe2\x47\xe9\xc0\x8b\x10\xd0\x29\x93\x4f\xc4\xb9\x52\xf7",
+ "\x31\xfc\xef\xac\x66\xd7\xde\x9c\x7e\xc7\x48\x5f\xe4\x49\x49\x02",
+ "\x54\x93\xe9\x99\x33\xb0\xa8\x11\x7e\x08\xec\x0f\x97\xcf\xc3\xd9",
+ "\x6e\xe2\xa4\xca\x67\xb0\x54\xbb\xfd\x33\x15\xbf\x85\x23\x05\x77",
+ "\x47\x3d\x06\xe8\x73\x8d\xb8\x98\x54\xc0\x66\xc4\x7a\xe4\x77\x40",
+ "\xa4\x26\xe5\xe4\x23\xbf\x48\x85\x29\x4d\xa4\x81\xfe\xae\xf7\x23",
+ "\x78\x01\x77\x31\xcf\x65\xfa\xb0\x74\xd5\x20\x89\x52\x51\x2e\xb1",
+ "\x9e\x25\xfc\x83\x3f\x22\x90\x73\x3e\x93\x44\xa5\xe8\x38\x39\xeb",
+ "\x56\x8e\x49\x5a\xbe\x52\x5a\x21\x8a\x22\x14\xcd\x3e\x07\x1d\x12",
+ "\x4a\x29\xb5\x45\x52\xd1\x6b\x9a\x46\x9c\x10\x52\x8e\xff\x0a\xae",
+ "\xc9\xd1\x84\xdd\xd5\xa9\xf5\xe0\xcf\x8c\xe2\x9a\x9a\xbf\x69\x1c",
+ "\x2d\xb4\x79\xae\x78\xbd\x50\xd8\x88\x2a\x8a\x17\x8a\x61\x32\xad",
+ "\x8e\xce\x5f\x04\x2d\x5e\x44\x7b\x50\x51\xb9\xea\xcb\x8d\x8f\x6f",
+ "\x9c\x0b\x53\xb4\xb3\xc3\x07\xe8\x7e\xae\xe0\x86\x78\x14\x1f\x66",
+ "\xab\xf2\x48\xaf\x69\xa6\xea\xe4\xbf\xd3\xeb\x2f\x12\x9e\xeb\x94",
+ "\x06\x64\xda\x16\x68\x57\x4b\x88\xb9\x35\xf3\x02\x73\x58\xae\xf4",
+ "\xaa\x4b\x9d\xc4\xbf\x33\x7d\xe9\x0c\xd4\xfd\x3c\x46\x7c\x6a\xb7",
+ "\xea\x5c\x7f\x47\x1f\xaf\x6b\xde\x2b\x1a\xd7\xd4\x68\x6d\x22\x87",
+ "\x29\x39\xb0\x18\x32\x23\xfa\xfc\x17\x23\xde\x4f\x52\xc4\x3d\x35",
+ "\x7c\x39\x56\xca\x5e\xea\xfc\x3e\x36\x3e\x9d\x55\x65\x46\xeb\x68",
+ "\x77\xc6\x07\x71\x46\xf0\x1c\x32\xb6\xb6\x9d\x5f\x4e\xa9\xff\xcf",
+ "\x37\xa6\x98\x6c\xb8\x84\x7e\xdf\x09\x25\xf0\xf1\x30\x9b\x54\xde",
+ "\xa7\x05\xf0\xe6\x9d\xa9\xa8\xf9\x07\x24\x1a\x2e\x92\x3c\x8c\xc8",
+ "\x3d\xc4\x7d\x1f\x29\xc4\x48\x46\x1e\x9e\x76\xed\x90\x4f\x67\x11",
+ "\x0d\x62\xbf\x01\xe6\xfc\x0e\x1a\x0d\x3c\x47\x51\xc5\xd3\x69\x2b",
+ "\x8c\x03\x46\x8b\xca\x7c\x66\x9e\xe4\xfd\x5e\x08\x4b\xbe\xe7\xb5",
+ "\x52\x8a\x5b\xb9\x3b\xaf\x2c\x9c\x44\x73\xcc\xe5\xd0\xd2\x2b\xd9",
+ "\xdf\x6a\x30\x1e\x95\xc9\x5d\xad\x97\xae\x0c\xc8\xc6\x91\x3b\xd8",
+ "\x80\x11\x89\x90\x2c\x85\x7f\x39\xe7\x35\x91\x28\x5e\x70\xb6\xdb",
+ "\xe6\x17\x34\x6a\xc9\xc2\x31\xbb\x36\x50\xae\x34\xcc\xca\x0c\x5b",
+ "\x27\xd9\x34\x37\xef\xb7\x21\xaa\x40\x18\x21\xdc\xec\x5a\xdf\x89",
+ "\x89\x23\x7d\x9d\xed\x9c\x5e\x78\xd8\xb1\xc9\xb1\x66\xcc\x73\x42",
+ "\x4a\x6d\x80\x91\xbf\x5e\x7d\x65\x11\x89\xfa\x94\xa2\x50\xb1\x4c",
+ "\x0e\x33\xf9\x60\x55\xe7\xae\x89\x3f\xfc\x0e\x3d\xcf\x49\x29\x02",
+ "\xe6\x1c\x43\x2b\x72\x0b\x19\xd1\x8e\xc8\xd8\x4b\xdc\x63\x15\x1b",
+ "\xf7\xe5\xae\xf5\x49\xf7\x82\xcf\x37\x90\x55\xa6\x08\x26\x9b\x16",
+ "\x43\x8d\x03\x0f\xd0\xb7\xa5\x4f\xa8\x37\xf2\xad\x20\x1a\x64\x03",
+ "\xa5\x90\xd3\xee\x4f\xbf\x04\xe3\x24\x7e\x0d\x27\xf2\x86\x42\x3f",
+ "\x5f\xe2\xc1\xa1\x72\xfe\x93\xc4\xb1\x5c\xd3\x7c\xae\xf9\xf5\x38",
+ "\x2c\x97\x32\x5c\xbd\x06\xb3\x6e\xb2\x13\x3d\xd0\x8b\x3a\x01\x7c",
+ "\x92\xc8\x14\x22\x7a\x6b\xca\x94\x9f\xf0\x65\x9f\x00\x2a\xd3\x9e",
+ "\xdc\xe8\x50\x11\x0b\xd8\x32\x8c\xfb\xd5\x08\x41\xd6\x91\x1d\x87",
+ "\x67\xf1\x49\x84\xc7\xda\x79\x12\x48\xe3\x2b\xb5\x92\x25\x83\xda",
+ "\x19\x38\xf2\xcf\x72\xd5\x4e\xe9\x7e\x94\x16\x6f\xa9\x1d\x2a\x36",
+ "\x74\x48\x1e\x96\x46\xed\x49\xfe\x0f\x62\x24\x30\x16\x04\x69\x8e",
+ "\x57\xfc\xa5\xde\x98\xa9\xd6\xd8\x00\x64\x38\xd0\x58\x3d\x8a\x1d",
+ "\x9f\xec\xde\x1c\xef\xdc\x1c\xbe\xd4\x76\x36\x74\xd9\x57\x53\x59",
+ "\xe3\x04\x0c\x00\xeb\x28\xf1\x53\x66\xca\x73\xcb\xd8\x72\xe7\x40",
+ "\x76\x97\x00\x9a\x6a\x83\x1d\xfe\xcc\xa9\x1c\x59\x93\x67\x0f\x7a",
+ "\x58\x53\x54\x23\x21\xf5\x67\xa0\x05\xd5\x47\xa4\xf0\x47\x59\xbd",
+ "\x51\x50\xd1\x77\x2f\x50\x83\x4a\x50\x3e\x06\x9a\x97\x3f\xbd\x7c",
+ };
+
+ const siphash = SipHash128(2, 4);
+
+ var buffer: [64]u8 = undefined;
+ for (vectors) |vector, i| {
+ buffer[i] = u8(i);
+
+ const expected = mem.readInt(vector, u128, Endian.Little);
+ debug.assert(siphash.hash(test_key, buffer[0..i]) == expected);
+ }
+}
diff --git a/std/hash_map.zig b/std/hash_map.zig
index becced64ff..29dd233753 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -114,6 +114,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
pub fn remove(hm: &Self, key: K) ?&Entry {
+ if (hm.entries.len == 0) return null;
hm.incrementModificationCount();
const start_index = hm.keyToIndex(key);
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
@@ -236,7 +237,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
test "basic hash map usage" {
- var map = HashMap(i32, i32, hash_i32, eql_i32).init(debug.global_allocator);
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
defer map.deinit();
assert((map.put(1, 11) catch unreachable) == null);
diff --git a/std/index.zig b/std/index.zig
index 179eae159e..07c4360aab 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -17,8 +17,9 @@ pub const debug = @import("debug/index.zig");
pub const dwarf = @import("dwarf.zig");
pub const elf = @import("elf.zig");
pub const empty_import = @import("empty.zig");
-pub const endian = @import("endian.zig");
+pub const event = @import("event.zig");
pub const fmt = @import("fmt/index.zig");
+pub const hash = @import("hash/index.zig");
pub const heap = @import("heap.zig");
pub const io = @import("io.zig");
pub const macho = @import("macho.zig");
@@ -26,7 +27,7 @@ pub const math = @import("math/index.zig");
pub const mem = @import("mem.zig");
pub const net = @import("net.zig");
pub const os = @import("os/index.zig");
-pub const rand = @import("rand.zig");
+pub const rand = @import("rand/index.zig");
pub const sort = @import("sort.zig");
pub const unicode = @import("unicode.zig");
pub const zig = @import("zig/index.zig");
@@ -49,16 +50,17 @@ test "std" {
_ = @import("dwarf.zig");
_ = @import("elf.zig");
_ = @import("empty.zig");
- _ = @import("endian.zig");
+ _ = @import("event.zig");
_ = @import("fmt/index.zig");
+ _ = @import("hash/index.zig");
_ = @import("io.zig");
_ = @import("macho.zig");
_ = @import("math/index.zig");
_ = @import("mem.zig");
- _ = @import("heap.zig");
_ = @import("net.zig");
+ _ = @import("heap.zig");
_ = @import("os/index.zig");
- _ = @import("rand.zig");
+ _ = @import("rand/index.zig");
_ = @import("sort.zig");
_ = @import("unicode.zig");
_ = @import("zig/index.zig");
diff --git a/std/io.zig b/std/io.zig
index 94685c4d03..7b72af15e4 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -144,7 +144,7 @@ pub fn InStream(comptime ReadError: type) type {
/// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and the contents
/// read from the stream so far are lost.
pub fn readUntilDelimiterBuffer(self: &Self, buffer: &Buffer, delimiter: u8, max_size: usize) !void {
- try buf.resize(0);
+ try buffer.resize(0);
while (true) {
var byte: u8 = try self.readByte();
@@ -153,11 +153,11 @@ pub fn InStream(comptime ReadError: type) type {
return;
}
- if (buf.len() == max_size) {
+ if (buffer.len() == max_size) {
return error.StreamTooLong;
}
- try buf.appendByte(byte);
+ try buffer.appendByte(byte);
}
}
@@ -171,7 +171,7 @@ pub fn InStream(comptime ReadError: type) type {
var buf = Buffer.initNull(allocator);
defer buf.deinit();
- try self.readUntilDelimiterBuffer(self, &buf, delimiter, max_size);
+ try self.readUntilDelimiterBuffer(&buf, delimiter, max_size);
return buf.toOwnedSlice();
}
@@ -478,3 +478,25 @@ test "import io tests" {
}
}
+pub fn readLine(buf: []u8) !usize {
+ var stdin = getStdIn() catch return error.StdInUnavailable;
+ var adapter = FileInStream.init(&stdin);
+ var stream = &adapter.stream;
+ var index: usize = 0;
+ while (true) {
+ const byte = stream.readByte() catch return error.EndOfFile;
+ switch (byte) {
+ '\r' => {
+ // trash the following \n
+ _ = stream.readByte() catch return error.EndOfFile;
+ return index;
+ },
+ '\n' => return index,
+ else => {
+ if (index == buf.len) return error.InputTooLong;
+ buf[index] = byte;
+ index += 1;
+ },
+ }
+ }
+}
diff --git a/std/io_test.zig b/std/io_test.zig
index 993ec84d20..89959b7b54 100644
--- a/std/io_test.zig
+++ b/std/io_test.zig
@@ -1,7 +1,7 @@
const std = @import("index.zig");
const io = std.io;
const allocator = std.debug.global_allocator;
-const Rand = std.rand.Rand;
+const DefaultPrng = std.rand.DefaultPrng;
const assert = std.debug.assert;
const mem = std.mem;
const os = std.os;
@@ -9,8 +9,8 @@ const builtin = @import("builtin");
test "write a file, read it, then delete it" {
var data: [1024]u8 = undefined;
- var rng = Rand.init(1234);
- rng.fillBytes(data[0..]);
+ var prng = DefaultPrng.init(1234);
+ prng.random.bytes(data[0..]);
const tmp_file_name = "temp_test_file.txt";
{
var file = try os.File.openWrite(allocator, tmp_file_name);
diff --git a/std/linked_list.zig b/std/linked_list.zig
index c916a53133..45595f3efb 100644
--- a/std/linked_list.zig
+++ b/std/linked_list.zig
@@ -161,6 +161,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
}
list.len -= 1;
+ assert(list.len == 0 or (list.first != null and list.last != null));
}
/// Remove and return the last node in the list.
diff --git a/std/math/atan2.zig b/std/math/atan2.zig
index 6f29b7b18d..37c520da46 100644
--- a/std/math/atan2.zig
+++ b/std/math/atan2.zig
@@ -22,7 +22,7 @@ const std = @import("../index.zig");
const math = std.math;
const assert = std.debug.assert;
-fn atan2(comptime T: type, x: T, y: T) T {
+pub fn atan2(comptime T: type, x: T, y: T) T {
return switch (T) {
f32 => atan2_32(x, y),
f64 => atan2_64(x, y),
diff --git a/std/math/index.zig b/std/math/index.zig
index f8668cc00d..477dafcbcc 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -515,15 +515,28 @@ test "math.negateCast" {
/// Cast an integer to a different integer type. If the value doesn't fit,
/// return an error.
-pub fn cast(comptime T: type, x: var) !T {
+pub fn cast(comptime T: type, x: var) (error{Overflow}!T) {
comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer
- if (x > @maxValue(T)) {
+ comptime assert(@typeId(@typeOf(x)) == builtin.TypeId.Int); // must pass an integer
+ if (@maxValue(@typeOf(x)) > @maxValue(T) and x > @maxValue(T)) {
+ return error.Overflow;
+ } else if (@minValue(@typeOf(x)) < @minValue(T) and x < @minValue(T)) {
return error.Overflow;
} else {
return T(x);
}
}
+test "math.cast" {
+ if (cast(u8, u32(300))) |_| @panic("fail") else |err| assert(err == error.Overflow);
+ if (cast(i8, i32(-200))) |_| @panic("fail") else |err| assert(err == error.Overflow);
+ if (cast(u8, i8(-1))) |_| @panic("fail") else |err| assert(err == error.Overflow);
+ if (cast(u64, i8(-1))) |_| @panic("fail") else |err| assert(err == error.Overflow);
+
+ assert((try cast(u8, u32(255))) == u8(255));
+ assert(@typeOf(try cast(u8, u32(255))) == u8);
+}
+
pub fn floorPowerOfTwo(comptime T: type, value: T) T {
var x = value;
diff --git a/std/mem.zig b/std/mem.zig
index 97cb35ae65..8a59d6251b 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -3,6 +3,7 @@ const debug = std.debug;
const assert = debug.assert;
const math = std.math;
const builtin = @import("builtin");
+const mem = this;
pub const Allocator = struct {
const Error = error {OutOfMemory};
@@ -550,3 +551,28 @@ test "std.mem.rotate" {
assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 }));
}
+
+// TODO: When https://github.com/zig-lang/zig/issues/649 is solved these can be done by
+// endian-casting the pointer and then dereferencing
+
+pub fn endianSwapIfLe(comptime T: type, x: T) T {
+ return endianSwapIf(builtin.Endian.Little, T, x);
+}
+
+pub fn endianSwapIfBe(comptime T: type, x: T) T {
+ return endianSwapIf(builtin.Endian.Big, T, x);
+}
+
+pub fn endianSwapIf(endian: builtin.Endian, comptime T: type, x: T) T {
+ return if (builtin.endian == endian) endianSwap(T, x) else x;
+}
+
+pub fn endianSwap(comptime T: type, x: T) T {
+ var buf: [@sizeOf(T)]u8 = undefined;
+ mem.writeInt(buf[0..], x, builtin.Endian.Little);
+ return mem.readInt(buf, T, builtin.Endian.Big);
+}
+
+test "std.mem.endianSwap" {
+ assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);
+}
diff --git a/std/net.zig b/std/net.zig
index 1140b6449b..8e1b8d97b2 100644
--- a/std/net.zig
+++ b/std/net.zig
@@ -1,143 +1,120 @@
const std = @import("index.zig");
-const linux = std.os.linux;
+const builtin = @import("builtin");
const assert = std.debug.assert;
-const endian = std.endian;
+const net = this;
+const posix = std.os.posix;
+const mem = std.mem;
-// TODO don't trust this file, it bit rotted. start over
-
-const Connection = struct {
- socket_fd: i32,
-
- pub fn send(c: Connection, buf: []const u8) !usize {
- const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);
- const send_err = linux.getErrno(send_ret);
- switch (send_err) {
- 0 => return send_ret,
- linux.EINVAL => unreachable,
- linux.EFAULT => unreachable,
- linux.ECONNRESET => return error.ConnectionReset,
- linux.EINTR => return error.SigInterrupt,
- // TODO there are more possible errors
- else => return error.Unexpected,
- }
- }
-
- pub fn recv(c: Connection, buf: []u8) ![]u8 {
- const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);
- const recv_err = linux.getErrno(recv_ret);
- switch (recv_err) {
- 0 => return buf[0..recv_ret],
- linux.EINVAL => unreachable,
- linux.EFAULT => unreachable,
- linux.ENOTSOCK => return error.NotSocket,
- linux.EINTR => return error.SigInterrupt,
- linux.ENOMEM => return error.OutOfMemory,
- linux.ECONNREFUSED => return error.ConnectionRefused,
- linux.EBADF => return error.BadFd,
- // TODO more error values
- else => return error.Unexpected,
- }
- }
-
- pub fn close(c: Connection) !void {
- switch (linux.getErrno(linux.close(c.socket_fd))) {
- 0 => return,
- linux.EBADF => unreachable,
- linux.EINTR => return error.SigInterrupt,
- linux.EIO => return error.Io,
- else => return error.Unexpected,
- }
- }
+pub const TmpWinAddr = struct {
+ family: u8,
+ data: [14]u8,
};
-const Address = struct {
- family: u16,
- scope_id: u32,
- addr: [16]u8,
- sort_key: i32,
+pub const OsAddress = switch (builtin.os) {
+ builtin.Os.windows => TmpWinAddr,
+ else => posix.sockaddr,
};
-pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address {
- if (hostname.len == 0) {
-
- unreachable; // TODO
+pub const Address = struct {
+ os_addr: OsAddress,
+
+ pub fn initIp4(ip4: u32, port: u16) Address {
+ return Address {
+ .os_addr = posix.sockaddr {
+ .in = posix.sockaddr_in {
+ .family = posix.AF_INET,
+ .port = std.mem.endianSwapIfLe(u16, port),
+ .addr = ip4,
+ .zero = []u8{0} ** 8,
+ },
+ },
+ };
}
- unreachable; // TODO
-}
+ pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address {
+ return Address {
+ .family = posix.AF_INET6,
+ .os_addr = posix.sockaddr {
+ .in6 = posix.sockaddr_in6 {
+ .family = posix.AF_INET6,
+ .port = std.mem.endianSwapIfLe(u16, port),
+ .flowinfo = 0,
+ .addr = ip6.addr,
+ .scope_id = ip6.scope_id,
+ },
+ },
+ };
+ }
-pub fn connectAddr(addr: &Address, port: u16) !Connection {
- const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);
- const socket_err = linux.getErrno(socket_ret);
- if (socket_err > 0) {
- // TODO figure out possible errors from socket()
- return error.Unexpected;
+ pub fn initPosix(addr: &const posix.sockaddr) Address {
+ return Address {
+ .os_addr = *addr,
+ };
}
- const socket_fd = i32(socket_ret);
- const connect_ret = if (addr.family == linux.AF_INET) x: {
- var os_addr: linux.sockaddr_in = undefined;
- os_addr.family = addr.family;
- os_addr.port = endian.swapIfLe(u16, port);
- @memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
- @memset(&os_addr.zero[0], 0, @sizeOf(@typeOf(os_addr.zero)));
- break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in));
- } else if (addr.family == linux.AF_INET6) x: {
- var os_addr: linux.sockaddr_in6 = undefined;
- os_addr.family = addr.family;
- os_addr.port = endian.swapIfLe(u16, port);
- os_addr.flowinfo = 0;
- os_addr.scope_id = addr.scope_id;
- @memcpy(&os_addr.addr[0], &addr.addr[0], 16);
- break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6));
- } else {
- unreachable;
- };
- const connect_err = linux.getErrno(connect_ret);
- if (connect_err > 0) {
- switch (connect_err) {
- linux.ETIMEDOUT => return error.TimedOut,
- else => {
- // TODO figure out possible errors from connect()
- return error.Unexpected;
+ pub fn format(self: &const Address, out_stream: var) !void {
+ switch (self.os_addr.in.family) {
+ posix.AF_INET => {
+ const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in.port);
+ const bytes = ([]const u8)((&self.os_addr.in.addr)[0..1]);
+ try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port);
+ },
+ posix.AF_INET6 => {
+ const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in6.port);
+ try out_stream.print("[TODO render ip6 address]:{}", native_endian_port);
},
+ else => try out_stream.write("(unrecognized address family)"),
}
}
+};
- return Connection {
- .socket_fd = socket_fd,
- };
-}
-
-pub fn connect(hostname: []const u8, port: u16) !Connection {
- var addrs_buf: [1]Address = undefined;
- const addrs_slice = try lookup(hostname, addrs_buf[0..]);
- const main_addr = &addrs_slice[0];
-
- return connectAddr(main_addr, port);
-}
+pub fn parseIp4(buf: []const u8) !u32 {
+ var result: u32 = undefined;
+ const out_ptr = ([]u8)((&result)[0..1]);
-pub fn parseIpLiteral(buf: []const u8) !Address {
+ var x: u8 = 0;
+ var index: u8 = 0;
+ var saw_any_digits = false;
+ for (buf) |c| {
+ if (c == '.') {
+ if (!saw_any_digits) {
+ return error.InvalidCharacter;
+ }
+ if (index == 3) {
+ return error.InvalidEnd;
+ }
+ out_ptr[index] = x;
+ index += 1;
+ x = 0;
+ saw_any_digits = false;
+ } else if (c >= '0' and c <= '9') {
+ saw_any_digits = true;
+ const digit = c - '0';
+ if (@mulWithOverflow(u8, x, 10, &x)) {
+ return error.Overflow;
+ }
+ if (@addWithOverflow(u8, x, digit, &x)) {
+ return error.Overflow;
+ }
+ } else {
+ return error.InvalidCharacter;
+ }
+ }
+ if (index == 3 and saw_any_digits) {
+ out_ptr[index] = x;
+ return result;
+ }
- return error.InvalidIpLiteral;
+ return error.Incomplete;
}
-fn hexDigit(c: u8) u8 {
- // TODO use switch with range
- if ('0' <= c and c <= '9') {
- return c - '0';
- } else if ('A' <= c and c <= 'Z') {
- return c - 'A' + 10;
- } else if ('a' <= c and c <= 'z') {
- return c - 'a' + 10;
- } else {
- return @maxValue(u8);
- }
-}
+pub const Ip6Addr = struct {
+ scope_id: u32,
+ addr: [16]u8,
+};
-fn parseIp6(buf: []const u8) !Address {
- var result: Address = undefined;
- result.family = linux.AF_INET6;
+pub fn parseIp6(buf: []const u8) !Ip6Addr {
+ var result: Ip6Addr = undefined;
result.scope_id = 0;
const ip_slice = result.addr[0..];
@@ -156,14 +133,14 @@ fn parseIp6(buf: []const u8) !Address {
return error.Overflow;
}
} else {
- return error.InvalidChar;
+ return error.InvalidCharacter;
}
} else if (c == ':') {
if (!saw_any_digits) {
- return error.InvalidChar;
+ return error.InvalidCharacter;
}
if (index == 14) {
- return error.JunkAtEnd;
+ return error.InvalidEnd;
}
ip_slice[index] = @truncate(u8, x >> 8);
index += 1;
@@ -174,7 +151,7 @@ fn parseIp6(buf: []const u8) !Address {
saw_any_digits = false;
} else if (c == '%') {
if (!saw_any_digits) {
- return error.InvalidChar;
+ return error.InvalidCharacter;
}
if (index == 14) {
ip_slice[index] = @truncate(u8, x >> 8);
@@ -185,10 +162,7 @@ fn parseIp6(buf: []const u8) !Address {
scope_id = true;
saw_any_digits = false;
} else {
- const digit = hexDigit(c);
- if (digit == @maxValue(u8)) {
- return error.InvalidChar;
- }
+ const digit = try std.fmt.charToDigit(c, 16);
if (@mulWithOverflow(u16, x, 16, &x)) {
return error.Overflow;
}
@@ -216,42 +190,27 @@ fn parseIp6(buf: []const u8) !Address {
return error.Incomplete;
}
-fn parseIp4(buf: []const u8) !u32 {
- var result: u32 = undefined;
- const out_ptr = ([]u8)((&result)[0..1]);
+test "std.net.parseIp4" {
+ assert((try parseIp4("127.0.0.1")) == std.mem.endianSwapIfLe(u32, 0x7f000001));
- var x: u8 = 0;
- var index: u8 = 0;
- var saw_any_digits = false;
- for (buf) |c| {
- if (c == '.') {
- if (!saw_any_digits) {
- return error.InvalidChar;
- }
- if (index == 3) {
- return error.JunkAtEnd;
- }
- out_ptr[index] = x;
- index += 1;
- x = 0;
- saw_any_digits = false;
- } else if (c >= '0' and c <= '9') {
- saw_any_digits = true;
- const digit = c - '0';
- if (@mulWithOverflow(u8, x, 10, &x)) {
- return error.Overflow;
- }
- if (@addWithOverflow(u8, x, digit, &x)) {
- return error.Overflow;
- }
- } else {
- return error.InvalidChar;
- }
- }
- if (index == 3 and saw_any_digits) {
- out_ptr[index] = x;
- return result;
+ testParseIp4Fail("256.0.0.1", error.Overflow);
+ testParseIp4Fail("x.0.0.1", error.InvalidCharacter);
+ testParseIp4Fail("127.0.0.1.1", error.InvalidEnd);
+ testParseIp4Fail("127.0.0.", error.Incomplete);
+ testParseIp4Fail("100..0.1", error.InvalidCharacter);
+}
+
+fn testParseIp4Fail(buf: []const u8, expected_err: error) void {
+ if (parseIp4(buf)) |_| {
+ @panic("expected error");
+ } else |e| {
+ assert(e == expected_err);
}
+}
- return error.Incomplete;
+test "std.net.parseIp6" {
+ const addr = try parseIp6("FF01:0:0:0:0:0:0:FB");
+ assert(addr.addr[0] == 0xff);
+ assert(addr.addr[1] == 0x01);
+ assert(addr.addr[2] == 0x00);
}
diff --git a/std/os/child_process.zig b/std/os/child_process.zig
index 06802e657c..8bb8b2d7e7 100644
--- a/std/os/child_process.zig
+++ b/std/os/child_process.zig
@@ -13,8 +13,6 @@ const builtin = @import("builtin");
const Os = builtin.Os;
const LinkedList = std.LinkedList;
-var children_nodes = LinkedList(&ChildProcess).init();
-
const is_windows = builtin.os == Os.windows;
pub const ChildProcess = struct {
@@ -296,8 +294,6 @@ pub const ChildProcess = struct {
}
fn cleanupAfterWait(self: &ChildProcess, status: i32) !Term {
- children_nodes.remove(&self.llnode);
-
defer {
os.close(self.err_pipe[0]);
os.close(self.err_pipe[1]);
@@ -427,9 +423,6 @@ pub const ChildProcess = struct {
self.llnode = LinkedList(&ChildProcess).Node.init(self);
self.term = null;
- // TODO make this atomic so it works even with threads
- children_nodes.prepend(&self.llnode);
-
if (self.stdin_behavior == StdIo.Pipe) { os.close(stdin_pipe[0]); }
if (self.stdout_behavior == StdIo.Pipe) { os.close(stdout_pipe[1]); }
if (self.stderr_behavior == StdIo.Pipe) { os.close(stderr_pipe[1]); }
@@ -773,31 +766,3 @@ fn readIntFd(fd: i32) !ErrInt {
os.posixRead(fd, bytes[0..]) catch return error.SystemResources;
return mem.readInt(bytes[0..], ErrInt, builtin.endian);
}
-
-extern fn sigchld_handler(_: i32) void {
- while (true) {
- var status: i32 = undefined;
- const pid_result = posix.waitpid(-1, &status, posix.WNOHANG);
- if (pid_result == 0) {
- return;
- }
- const err = posix.getErrno(pid_result);
- if (err > 0) {
- if (err == posix.ECHILD) {
- return;
- }
- unreachable;
- }
- handleTerm(i32(pid_result), status);
- }
-}
-
-fn handleTerm(pid: i32, status: i32) void {
- var it = children_nodes.first;
- while (it) |node| : (it = node.next) {
- if (node.data.pid == pid) {
- node.data.handleWaitResult(status);
- return;
- }
- }
-}
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index ebc6f65f55..40da55315c 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -56,10 +56,32 @@ pub const O_SYMLINK = 0x200000; /// allow open of symlinks
pub const O_EVTONLY = 0x8000; /// descriptor requested for event notifications only
pub const O_CLOEXEC = 0x1000000; /// mark as close-on-exec
+pub const O_ACCMODE = 3;
+pub const O_ALERT = 536870912;
+pub const O_ASYNC = 64;
+pub const O_DIRECTORY = 1048576;
+pub const O_DP_GETRAWENCRYPTED = 1;
+pub const O_DP_GETRAWUNENCRYPTED = 2;
+pub const O_DSYNC = 4194304;
+pub const O_FSYNC = O_SYNC;
+pub const O_NOCTTY = 131072;
+pub const O_POPUP = 2147483648;
+pub const O_SYNC = 128;
+
pub const SEEK_SET = 0x0;
pub const SEEK_CUR = 0x1;
pub const SEEK_END = 0x2;
+pub const DT_UNKNOWN = 0;
+pub const DT_FIFO = 1;
+pub const DT_CHR = 2;
+pub const DT_DIR = 4;
+pub const DT_BLK = 6;
+pub const DT_REG = 8;
+pub const DT_LNK = 10;
+pub const DT_SOCK = 12;
+pub const DT_WHT = 14;
+
pub const SIG_BLOCK = 1; /// block specified signal set
pub const SIG_UNBLOCK = 2; /// unblock specified signal set
pub const SIG_SETMASK = 3; /// set specified signal set
@@ -192,6 +214,11 @@ pub fn pipe(fds: &[2]i32) usize {
return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
}
+
+pub fn getdirentries64(fd: i32, buf_ptr: &u8, buf_len: usize, basep: &i64) usize {
+ return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep)));
+}
+
pub fn mkdir(path: &const u8, mode: u32) usize {
return errnoWrap(c.mkdir(path, mode));
}
@@ -204,6 +231,10 @@ pub fn rename(old: &const u8, new: &const u8) usize {
return errnoWrap(c.rename(old, new));
}
+pub fn rmdir(path: &const u8) usize {
+ return errnoWrap(c.rmdir(path));
+}
+
pub fn chdir(path: &const u8) usize {
return errnoWrap(c.chdir(path));
}
@@ -268,6 +299,10 @@ pub const empty_sigset = sigset_t(0);
pub const timespec = c.timespec;
pub const Stat = c.Stat;
+pub const dirent = c.dirent;
+
+pub const sa_family_t = c.sa_family_t;
+pub const sockaddr = c.sockaddr;
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
pub const Sigaction = struct {
diff --git a/std/os/file.zig b/std/os/file.zig
index 772fbf7c73..eed3a443b9 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -233,7 +233,7 @@ pub const File = struct {
Unexpected,
};
- fn mode(self: &File) ModeError!FileMode {
+ fn mode(self: &File) ModeError!os.FileMode {
if (is_posix) {
var stat: posix.Stat = undefined;
const err = posix.getErrno(posix.fstat(self.handle, &stat));
diff --git a/std/os/index.zig b/std/os/index.zig
index e472908c68..b6caed6f53 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -4,6 +4,19 @@ const Os = builtin.Os;
const is_windows = builtin.os == Os.windows;
const os = this;
+test "std.os" {
+ _ = @import("child_process.zig");
+ _ = @import("darwin.zig");
+ _ = @import("darwin_errno.zig");
+ _ = @import("get_user_id.zig");
+ _ = @import("linux/errno.zig");
+ _ = @import("linux/index.zig");
+ _ = @import("linux/x86_64.zig");
+ _ = @import("path.zig");
+ _ = @import("test.zig");
+ _ = @import("windows/index.zig");
+}
+
pub const windows = @import("windows/index.zig");
pub const darwin = @import("darwin.zig");
pub const linux = @import("linux/index.zig");
@@ -14,6 +27,7 @@ pub const posix = switch(builtin.os) {
Os.zen => zen,
else => @compileError("Unsupported OS"),
};
+pub const net = @import("net.zig");
pub const ChildProcess = @import("child_process.zig").ChildProcess;
pub const path = @import("path.zig");
@@ -173,6 +187,13 @@ pub fn exit(status: u8) noreturn {
}
}
+/// When a file descriptor is closed on linux, it pops the first
+/// node from this queue and resumes it.
+/// Async functions which get the EMFILE error code can suspend,
+/// putting their coroutine handle into this list.
+/// TODO make this an atomic linked list
+pub var emfile_promise_queue = std.LinkedList(promise).init();
+
/// Closes the file handle. Keeps trying if it gets interrupted by a signal.
pub fn close(handle: FileHandle) void {
if (is_windows) {
@@ -180,10 +201,12 @@ pub fn close(handle: FileHandle) void {
} else {
while (true) {
const err = posix.getErrno(posix.close(handle));
- if (err == posix.EINTR) {
- continue;
- } else {
- return;
+ switch (err) {
+ posix.EINTR => continue,
+ else => {
+ if (emfile_promise_queue.popFirst()) |p| resume p.data;
+ return;
+ },
}
}
}
@@ -1050,15 +1073,16 @@ const DeleteTreeError = error {
};
pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!void {
start_over: while (true) {
+ var got_access_denied = false;
// First, try deleting the item as a file. This way we don't follow sym links.
if (deleteFile(allocator, full_path)) {
return;
} else |err| switch (err) {
error.FileNotFound => return,
error.IsDir => {},
+ error.AccessDenied => got_access_denied = true,
error.OutOfMemory,
- error.AccessDenied,
error.SymLinkLoop,
error.NameTooLong,
error.SystemResources,
@@ -1071,7 +1095,12 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
}
{
var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
- error.NotDir => continue :start_over,
+ error.NotDir => {
+ if (got_access_denied) {
+ return error.AccessDenied;
+ }
+ continue :start_over;
+ },
error.OutOfMemory,
error.AccessDenied,
@@ -1109,18 +1138,16 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
}
pub const Dir = struct {
- // See man getdents
fd: i32,
+ darwin_seek: darwin_seek_t,
allocator: &Allocator,
buf: []u8,
index: usize,
end_index: usize,
- const LinuxEntry = extern struct {
- d_ino: usize,
- d_off: usize,
- d_reclen: u16,
- d_name: u8, // field address is the address of first byte of name
+ const darwin_seek_t = switch (builtin.os) {
+ Os.macosx, Os.ios => i64,
+ else => void,
};
pub const Entry = struct {
@@ -1135,15 +1162,26 @@ pub const Dir = struct {
SymLink,
File,
UnixDomainSocket,
+ Whiteout,
Unknown,
};
};
pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
- const fd = try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0);
+ const fd = switch (builtin.os) {
+ Os.windows => @compileError("TODO support Dir.open for windows"),
+ Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
+ Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),
+ else => @compileError("Dir.open is not supported for this platform"),
+ };
+ const darwin_seek_init = switch (builtin.os) {
+ Os.macosx, Os.ios => 0,
+ else => {},
+ };
return Dir {
.allocator = allocator,
.fd = fd,
+ .darwin_seek = darwin_seek_init,
.index = 0,
.end_index = 0,
.buf = []u8{},
@@ -1158,6 +1196,76 @@ pub const Dir = struct {
/// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to next, as well as when this ::Dir is deinitialized.
pub fn next(self: &Dir) !?Entry {
+ switch (builtin.os) {
+ Os.linux => return self.nextLinux(),
+ Os.macosx, Os.ios => return self.nextDarwin(),
+ Os.windows => return self.nextWindows(),
+ else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
+ }
+ }
+
+ fn nextDarwin(self: &Dir) !?Entry {
+ start_over: while (true) {
+ if (self.index >= self.end_index) {
+ if (self.buf.len == 0) {
+ self.buf = try self.allocator.alloc(u8, page_size);
+ }
+
+ while (true) {
+ const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len,
+ &self.darwin_seek);
+ const err = posix.getErrno(result);
+ if (err > 0) {
+ switch (err) {
+ posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
+ posix.EINVAL => {
+ self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
+ continue;
+ },
+ else => return unexpectedErrorPosix(err),
+ }
+ }
+ if (result == 0)
+ return null;
+ self.index = 0;
+ self.end_index = result;
+ break;
+ }
+ }
+ const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
+ const next_index = self.index + darwin_entry.d_reclen;
+ self.index = next_index;
+
+ const name = (&darwin_entry.d_name)[0..darwin_entry.d_namlen];
+
+ // skip . and .. entries
+ if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
+ continue :start_over;
+ }
+
+ const entry_kind = switch (darwin_entry.d_type) {
+ posix.DT_BLK => Entry.Kind.BlockDevice,
+ posix.DT_CHR => Entry.Kind.CharacterDevice,
+ posix.DT_DIR => Entry.Kind.Directory,
+ posix.DT_FIFO => Entry.Kind.NamedPipe,
+ posix.DT_LNK => Entry.Kind.SymLink,
+ posix.DT_REG => Entry.Kind.File,
+ posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
+ posix.DT_WHT => Entry.Kind.Whiteout,
+ else => Entry.Kind.Unknown,
+ };
+ return Entry {
+ .name = name,
+ .kind = entry_kind,
+ };
+ }
+ }
+
+ fn nextWindows(self: &Dir) !?Entry {
+ @compileError("TODO support Dir.next for windows");
+ }
+
+ fn nextLinux(self: &Dir) !?Entry {
start_over: while (true) {
if (self.index >= self.end_index) {
if (self.buf.len == 0) {
@@ -1166,7 +1274,7 @@ pub const Dir = struct {
while (true) {
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
- const err = linux.getErrno(result);
+ const err = posix.getErrno(result);
if (err > 0) {
switch (err) {
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
@@ -1184,7 +1292,7 @@ pub const Dir = struct {
break;
}
}
- const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
+ const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);
const next_index = self.index + linux_entry.d_reclen;
self.index = next_index;
@@ -1668,39 +1776,29 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
assert(it.next(debug.global_allocator) == null);
}
-test "std.os" {
- _ = @import("child_process.zig");
- _ = @import("darwin_errno.zig");
- _ = @import("darwin.zig");
- _ = @import("get_user_id.zig");
- _ = @import("linux/errno.zig");
- //_ = @import("linux_i386.zig");
- _ = @import("linux/x86_64.zig");
- _ = @import("linux/index.zig");
- _ = @import("path.zig");
- _ = @import("windows/index.zig");
-}
-
-
// TODO make this a build variable that you can set
const unexpected_error_tracing = false;
+const UnexpectedError = error {
+ /// The Operating System returned an undocumented error code.
+ Unexpected,
+};
/// Call this when you made a syscall or something that sets errno
/// and you get an unexpected error.
-pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
+pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
if (unexpected_error_tracing) {
debug.warn("unexpected errno: {}\n", errno);
- debug.dumpStackTrace();
+ debug.dumpCurrentStackTrace(null);
}
return error.Unexpected;
}
/// Call this when you made a windows DLL call or something that does SetLastError
/// and you get an unexpected error.
-pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) {
+pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
if (unexpected_error_tracing) {
debug.warn("unexpected GetLastError(): {}\n", err);
- debug.dumpStackTrace();
+ debug.dumpCurrentStackTrace(null);
}
return error.Unexpected;
}
@@ -1812,3 +1910,477 @@ pub fn isTty(handle: FileHandle) bool {
}
}
}
+
+pub const PosixSocketError = error {
+ /// Permission to create a socket of the specified type and/or
+ /// pro‐tocol is denied.
+ PermissionDenied,
+
+ /// The implementation does not support the specified address family.
+ AddressFamilyNotSupported,
+
+ /// Unknown protocol, or protocol family not available.
+ ProtocolFamilyNotAvailable,
+
+ /// The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// Insufficient memory is available. The socket cannot be created until sufficient
+ /// resources are freed.
+ SystemResources,
+
+ /// The protocol type or the specified protocol is not supported within this domain.
+ ProtocolNotSupported,
+};
+
+pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {
+ const rc = posix.socket(domain, socket_type, protocol);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ posix.EACCES => return PosixSocketError.PermissionDenied,
+ posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported,
+ posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,
+ posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,
+ posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources,
+ posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub const PosixBindError = error {
+ /// The address is protected, and the user is not the superuser.
+ /// For UNIX domain sockets: Search permission is denied on a component
+ /// of the path prefix.
+ AccessDenied,
+
+ /// The given address is already in use, or in the case of Internet domain sockets,
+ /// The port number was specified as zero in the socket
+ /// address structure, but, upon attempting to bind to an ephemeral port, it was
+ /// determined that all port numbers in the ephemeral port range are currently in
+ /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
+ AddressInUse,
+
+ /// sockfd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// The socket is already bound to an address, or addrlen is wrong, or addr is not
+ /// a valid address for this socket's domain.
+ InvalidSocketOrAddress,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// A nonexistent interface was requested or the requested address was not local.
+ AddressNotAvailable,
+
+ /// addr points outside the user's accessible address space.
+ PageFault,
+
+ /// Too many symbolic links were encountered in resolving addr.
+ SymLinkLoop,
+
+ /// addr is too long.
+ NameTooLong,
+
+ /// A component in the directory prefix of the socket pathname does not exist.
+ FileNotFound,
+
+ /// Insufficient kernel memory was available.
+ SystemResources,
+
+ /// A component of the path prefix is not a directory.
+ NotDir,
+
+ /// The socket inode would reside on a read-only filesystem.
+ ReadOnlyFileSystem,
+
+ Unexpected,
+};
+
+/// addr is `&const T` where T is one of the sockaddr
+pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void {
+ const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ posix.EACCES => return PosixBindError.AccessDenied,
+ posix.EADDRINUSE => return PosixBindError.AddressInUse,
+ posix.EBADF => return PosixBindError.InvalidFileDescriptor,
+ posix.EINVAL => return PosixBindError.InvalidSocketOrAddress,
+ posix.ENOTSOCK => return PosixBindError.FileDescriptorNotASocket,
+ posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable,
+ posix.EFAULT => return PosixBindError.PageFault,
+ posix.ELOOP => return PosixBindError.SymLinkLoop,
+ posix.ENAMETOOLONG => return PosixBindError.NameTooLong,
+ posix.ENOENT => return PosixBindError.FileNotFound,
+ posix.ENOMEM => return PosixBindError.SystemResources,
+ posix.ENOTDIR => return PosixBindError.NotDir,
+ posix.EROFS => return PosixBindError.ReadOnlyFileSystem,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+const PosixListenError = error {
+ /// Another socket is already listening on the same port.
+ /// For Internet domain sockets, the socket referred to by sockfd had not previously
+ /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
+ /// was determined that all port numbers in the ephemeral port range are currently in
+ /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+ AddressInUse,
+
+ /// The argument sockfd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// The socket is not of a type that supports the listen() operation.
+ OperationNotSupported,
+
+ Unexpected,
+};
+
+pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void {
+ const rc = posix.listen(sockfd, backlog);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ posix.EADDRINUSE => return PosixListenError.AddressInUse,
+ posix.EBADF => return PosixListenError.InvalidFileDescriptor,
+ posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket,
+ posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported,
+ else => return unexpectedErrorPosix(err),
+ }
+}
+
+pub const PosixAcceptError = error {
+ /// The socket is marked nonblocking and no connections are present to be accepted.
+ WouldBlock,
+
+ /// sockfd is not an open file descriptor.
+ FileDescriptorClosed,
+
+ ConnectionAborted,
+
+ /// The addr argument is not in a writable part of the user address space.
+ PageFault,
+
+ /// Socket is not listening for connections, or addrlen is invalid (e.g., is negative),
+ /// or invalid value in flags.
+ InvalidSyscall,
+
+ /// The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// Not enough free memory. This often means that the memory allocation is limited
+ /// by the socket buffer limits, not by the system memory.
+ SystemResources,
+
+ /// The file descriptor sockfd does not refer to a socket.
+ FileDescriptorNotASocket,
+
+ /// The referenced socket is not of type SOCK_STREAM.
+ OperationNotSupported,
+
+ ProtocolFailure,
+
+ /// Firewall rules forbid connection.
+ BlockedByFirewall,
+
+ Unexpected,
+};
+
+pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!i32 {
+ while (true) {
+ var sockaddr_size = u32(@sizeOf(posix.sockaddr));
+ const rc = posix.accept4(fd, addr, &sockaddr_size, flags);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ posix.EINTR => continue,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EAGAIN => return PosixAcceptError.WouldBlock,
+ posix.EBADF => return PosixAcceptError.FileDescriptorClosed,
+ posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted,
+ posix.EFAULT => return PosixAcceptError.PageFault,
+ posix.EINVAL => return PosixAcceptError.InvalidSyscall,
+ posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,
+ posix.ENOBUFS, posix.ENOMEM => return PosixAcceptError.SystemResources,
+ posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,
+ posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,
+ posix.EPROTO => return PosixAcceptError.ProtocolFailure,
+ posix.EPERM => return PosixAcceptError.BlockedByFirewall,
+ }
+ }
+}
+
+pub const LinuxEpollCreateError = error {
+ /// Invalid value specified in flags.
+ InvalidSyscall,
+
+ /// The per-user limit on the number of epoll instances imposed by
+ /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further
+ /// details.
+ /// Or, The per-process limit on the number of open file descriptors has been reached.
+ ProcessFdQuotaExceeded,
+
+ /// The system-wide limit on the total number of open files has been reached.
+ SystemFdQuotaExceeded,
+
+ /// There was insufficient memory to create the kernel object.
+ SystemResources,
+
+ Unexpected,
+};
+
+pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 {
+ const rc = posix.epoll_create1(flags);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return i32(rc),
+ else => return unexpectedErrorPosix(err),
+
+ posix.EINVAL => return LinuxEpollCreateError.InvalidSyscall,
+ posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded,
+ posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded,
+ posix.ENOMEM => return LinuxEpollCreateError.SystemResources,
+ }
+}
+
+pub const LinuxEpollCtlError = error {
+ /// epfd or fd is not a valid file descriptor.
+ InvalidFileDescriptor,
+
+ /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered
+ /// with this epoll instance.
+ FileDescriptorAlreadyPresentInSet,
+
+ /// epfd is not an epoll file descriptor, or fd is the same as epfd, or the requested
+ /// operation op is not supported by this interface, or
+ /// An invalid event type was specified along with EPOLLEXCLUSIVE in events, or
+ /// op was EPOLL_CTL_MOD and events included EPOLLEXCLUSIVE, or
+ /// op was EPOLL_CTL_MOD and the EPOLLEXCLUSIVE flag has previously been applied to
+ /// this epfd, fd pair, or
+ /// EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance.
+ InvalidSyscall,
+
+ /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
+ /// circular loop of epoll instances monitoring one another.
+ OperationCausesCircularLoop,
+
+ /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll
+ /// instance.
+ FileDescriptorNotRegistered,
+
+ /// There was insufficient memory to handle the requested op control operation.
+ SystemResources,
+
+ /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while
+ /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.
+ /// See epoll(7) for further details.
+ UserResourceLimitReached,
+
+ /// The target file fd does not support epoll. This error can occur if fd refers to,
+ /// for example, a regular file or a directory.
+ FileDescriptorIncompatibleWithEpoll,
+
+ Unexpected,
+};
+
+pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) LinuxEpollCtlError!void {
+ const rc = posix.epoll_ctl(epfd, op, fd, event);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EBADF => return LinuxEpollCtlError.InvalidFileDescriptor,
+ posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet,
+ posix.EINVAL => return LinuxEpollCtlError.InvalidSyscall,
+ posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop,
+ posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered,
+ posix.ENOMEM => return LinuxEpollCtlError.SystemResources,
+ posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached,
+ posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll,
+ }
+}
+
+pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
+ while (true) {
+ const rc = posix.epoll_wait(epfd, events.ptr, u32(events.len), timeout);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return rc,
+ posix.EINTR => continue,
+ posix.EBADF => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ else => unreachable,
+ }
+ }
+}
+
+pub const PosixGetSockNameError = error {
+ /// Insufficient resources were available in the system to perform the operation.
+ SystemResources,
+
+ Unexpected,
+};
+
+pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr {
+ var addr: posix.sockaddr = undefined;
+ var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr);
+ const rc = posix.getsockname(sockfd, &addr, &addrlen);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return addr,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EBADF => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EINVAL => unreachable,
+ posix.ENOTSOCK => unreachable,
+ posix.ENOBUFS => return PosixGetSockNameError.SystemResources,
+ }
+}
+
+pub const PosixConnectError = error {
+ /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket
+ /// file, or search permission is denied for one of the directories in the path prefix.
+ /// or
+ /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
+ /// the connection request failed because of a local firewall rule.
+ PermissionDenied,
+
+ /// Local address is already in use.
+ AddressInUse,
+
+ /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an
+ /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
+ /// in the ephemeral port range are currently in use. See the discussion of
+ /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
+ AddressNotAvailable,
+
+ /// The passed address didn't have the correct address family in its sa_family field.
+ AddressFamilyNotSupported,
+
+ /// Insufficient entries in the routing cache.
+ SystemResources,
+
+ /// A connect() on a stream socket found no one listening on the remote address.
+ ConnectionRefused,
+
+ /// Network is unreachable.
+ NetworkUnreachable,
+
+ /// Timeout while attempting connection. The server may be too busy to accept new connections. Note
+ /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
+ ConnectionTimedOut,
+
+ Unexpected,
+};
+
+pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
+ while (true) {
+ const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
+ posix.EINTR => continue,
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ }
+ }
+}
+
+/// Same as posixConnect except it is for blocking socket file descriptors.
+/// It expects to receive EINPROGRESS.
+pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
+ while (true) {
+ const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0, posix.EINPROGRESS => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EINTR => continue,
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ }
+ }
+}
+
+pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
+ var err_code: i32 = undefined;
+ var size: u32 = @sizeOf(i32);
+ const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(&u8, &err_code), &size);
+ assert(size == 4);
+ const err = posix.getErrno(rc);
+ switch (err) {
+ 0 => switch (err_code) {
+ 0 => return,
+ else => return unexpectedErrorPosix(err),
+
+ posix.EACCES => return PosixConnectError.PermissionDenied,
+ posix.EPERM => return PosixConnectError.PermissionDenied,
+ posix.EADDRINUSE => return PosixConnectError.AddressInUse,
+ posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
+ posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
+ posix.EAGAIN => return PosixConnectError.SystemResources,
+ posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
+ posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
+ posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
+ posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
+ posix.EISCONN => unreachable, // The socket is already connected.
+ posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
+ posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
+ },
+ else => return unexpectedErrorPosix(err),
+ posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
+ posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
+ posix.EINVAL => unreachable,
+ posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
+ posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
+ }
+}
diff --git a/std/os/linux/i386.zig b/std/os/linux/i386.zig
deleted file mode 100644
index 7450ad34fa..0000000000
--- a/std/os/linux/i386.zig
+++ /dev/null
@@ -1,505 +0,0 @@
-const std = @import("../../index.zig");
-const linux = std.os.linux;
-const socklen_t = linux.socklen_t;
-const iovec = linux.iovec;
-
-pub const SYS_restart_syscall = 0;
-pub const SYS_exit = 1;
-pub const SYS_fork = 2;
-pub const SYS_read = 3;
-pub const SYS_write = 4;
-pub const SYS_open = 5;
-pub const SYS_close = 6;
-pub const SYS_waitpid = 7;
-pub const SYS_creat = 8;
-pub const SYS_link = 9;
-pub const SYS_unlink = 10;
-pub const SYS_execve = 11;
-pub const SYS_chdir = 12;
-pub const SYS_time = 13;
-pub const SYS_mknod = 14;
-pub const SYS_chmod = 15;
-pub const SYS_lchown = 16;
-pub const SYS_break = 17;
-pub const SYS_oldstat = 18;
-pub const SYS_lseek = 19;
-pub const SYS_getpid = 20;
-pub const SYS_mount = 21;
-pub const SYS_umount = 22;
-pub const SYS_setuid = 23;
-pub const SYS_getuid = 24;
-pub const SYS_stime = 25;
-pub const SYS_ptrace = 26;
-pub const SYS_alarm = 27;
-pub const SYS_oldfstat = 28;
-pub const SYS_pause = 29;
-pub const SYS_utime = 30;
-pub const SYS_stty = 31;
-pub const SYS_gtty = 32;
-pub const SYS_access = 33;
-pub const SYS_nice = 34;
-pub const SYS_ftime = 35;
-pub const SYS_sync = 36;
-pub const SYS_kill = 37;
-pub const SYS_rename = 38;
-pub const SYS_mkdir = 39;
-pub const SYS_rmdir = 40;
-pub const SYS_dup = 41;
-pub const SYS_pipe = 42;
-pub const SYS_times = 43;
-pub const SYS_prof = 44;
-pub const SYS_brk = 45;
-pub const SYS_setgid = 46;
-pub const SYS_getgid = 47;
-pub const SYS_signal = 48;
-pub const SYS_geteuid = 49;
-pub const SYS_getegid = 50;
-pub const SYS_acct = 51;
-pub const SYS_umount2 = 52;
-pub const SYS_lock = 53;
-pub const SYS_ioctl = 54;
-pub const SYS_fcntl = 55;
-pub const SYS_mpx = 56;
-pub const SYS_setpgid = 57;
-pub const SYS_ulimit = 58;
-pub const SYS_oldolduname = 59;
-pub const SYS_umask = 60;
-pub const SYS_chroot = 61;
-pub const SYS_ustat = 62;
-pub const SYS_dup2 = 63;
-pub const SYS_getppid = 64;
-pub const SYS_getpgrp = 65;
-pub const SYS_setsid = 66;
-pub const SYS_sigaction = 67;
-pub const SYS_sgetmask = 68;
-pub const SYS_ssetmask = 69;
-pub const SYS_setreuid = 70;
-pub const SYS_setregid = 71;
-pub const SYS_sigsuspend = 72;
-pub const SYS_sigpending = 73;
-pub const SYS_sethostname = 74;
-pub const SYS_setrlimit = 75;
-pub const SYS_getrlimit = 76;
-pub const SYS_getrusage = 77;
-pub const SYS_gettimeofday = 78;
-pub const SYS_settimeofday = 79;
-pub const SYS_getgroups = 80;
-pub const SYS_setgroups = 81;
-pub const SYS_select = 82;
-pub const SYS_symlink = 83;
-pub const SYS_oldlstat = 84;
-pub const SYS_readlink = 85;
-pub const SYS_uselib = 86;
-pub const SYS_swapon = 87;
-pub const SYS_reboot = 88;
-pub const SYS_readdir = 89;
-pub const SYS_mmap = 90;
-pub const SYS_munmap = 91;
-pub const SYS_truncate = 92;
-pub const SYS_ftruncate = 93;
-pub const SYS_fchmod = 94;
-pub const SYS_fchown = 95;
-pub const SYS_getpriority = 96;
-pub const SYS_setpriority = 97;
-pub const SYS_profil = 98;
-pub const SYS_statfs = 99;
-pub const SYS_fstatfs = 100;
-pub const SYS_ioperm = 101;
-pub const SYS_socketcall = 102;
-pub const SYS_syslog = 103;
-pub const SYS_setitimer = 104;
-pub const SYS_getitimer = 105;
-pub const SYS_stat = 106;
-pub const SYS_lstat = 107;
-pub const SYS_fstat = 108;
-pub const SYS_olduname = 109;
-pub const SYS_iopl = 110;
-pub const SYS_vhangup = 111;
-pub const SYS_idle = 112;
-pub const SYS_vm86old = 113;
-pub const SYS_wait4 = 114;
-pub const SYS_swapoff = 115;
-pub const SYS_sysinfo = 116;
-pub const SYS_ipc = 117;
-pub const SYS_fsync = 118;
-pub const SYS_sigreturn = 119;
-pub const SYS_clone = 120;
-pub const SYS_setdomainname = 121;
-pub const SYS_uname = 122;
-pub const SYS_modify_ldt = 123;
-pub const SYS_adjtimex = 124;
-pub const SYS_mprotect = 125;
-pub const SYS_sigprocmask = 126;
-pub const SYS_create_module = 127;
-pub const SYS_init_module = 128;
-pub const SYS_delete_module = 129;
-pub const SYS_get_kernel_syms = 130;
-pub const SYS_quotactl = 131;
-pub const SYS_getpgid = 132;
-pub const SYS_fchdir = 133;
-pub const SYS_bdflush = 134;
-pub const SYS_sysfs = 135;
-pub const SYS_personality = 136;
-pub const SYS_afs_syscall = 137;
-pub const SYS_setfsuid = 138;
-pub const SYS_setfsgid = 139;
-pub const SYS__llseek = 140;
-pub const SYS_getdents = 141;
-pub const SYS__newselect = 142;
-pub const SYS_flock = 143;
-pub const SYS_msync = 144;
-pub const SYS_readv = 145;
-pub const SYS_writev = 146;
-pub const SYS_getsid = 147;
-pub const SYS_fdatasync = 148;
-pub const SYS__sysctl = 149;
-pub const SYS_mlock = 150;
-pub const SYS_munlock = 151;
-pub const SYS_mlockall = 152;
-pub const SYS_munlockall = 153;
-pub const SYS_sched_setparam = 154;
-pub const SYS_sched_getparam = 155;
-pub const SYS_sched_setscheduler = 156;
-pub const SYS_sched_getscheduler = 157;
-pub const SYS_sched_yield = 158;
-pub const SYS_sched_get_priority_max = 159;
-pub const SYS_sched_get_priority_min = 160;
-pub const SYS_sched_rr_get_interval = 161;
-pub const SYS_nanosleep = 162;
-pub const SYS_mremap = 163;
-pub const SYS_setresuid = 164;
-pub const SYS_getresuid = 165;
-pub const SYS_vm86 = 166;
-pub const SYS_query_module = 167;
-pub const SYS_poll = 168;
-pub const SYS_nfsservctl = 169;
-pub const SYS_setresgid = 170;
-pub const SYS_getresgid = 171;
-pub const SYS_prctl = 172;
-pub const SYS_rt_sigreturn = 173;
-pub const SYS_rt_sigaction = 174;
-pub const SYS_rt_sigprocmask = 175;
-pub const SYS_rt_sigpending = 176;
-pub const SYS_rt_sigtimedwait = 177;
-pub const SYS_rt_sigqueueinfo = 178;
-pub const SYS_rt_sigsuspend = 179;
-pub const SYS_pread64 = 180;
-pub const SYS_pwrite64 = 181;
-pub const SYS_chown = 182;
-pub const SYS_getcwd = 183;
-pub const SYS_capget = 184;
-pub const SYS_capset = 185;
-pub const SYS_sigaltstack = 186;
-pub const SYS_sendfile = 187;
-pub const SYS_getpmsg = 188;
-pub const SYS_putpmsg = 189;
-pub const SYS_vfork = 190;
-pub const SYS_ugetrlimit = 191;
-pub const SYS_mmap2 = 192;
-pub const SYS_truncate64 = 193;
-pub const SYS_ftruncate64 = 194;
-pub const SYS_stat64 = 195;
-pub const SYS_lstat64 = 196;
-pub const SYS_fstat64 = 197;
-pub const SYS_lchown32 = 198;
-pub const SYS_getuid32 = 199;
-pub const SYS_getgid32 = 200;
-pub const SYS_geteuid32 = 201;
-pub const SYS_getegid32 = 202;
-pub const SYS_setreuid32 = 203;
-pub const SYS_setregid32 = 204;
-pub const SYS_getgroups32 = 205;
-pub const SYS_setgroups32 = 206;
-pub const SYS_fchown32 = 207;
-pub const SYS_setresuid32 = 208;
-pub const SYS_getresuid32 = 209;
-pub const SYS_setresgid32 = 210;
-pub const SYS_getresgid32 = 211;
-pub const SYS_chown32 = 212;
-pub const SYS_setuid32 = 213;
-pub const SYS_setgid32 = 214;
-pub const SYS_setfsuid32 = 215;
-pub const SYS_setfsgid32 = 216;
-pub const SYS_pivot_root = 217;
-pub const SYS_mincore = 218;
-pub const SYS_madvise = 219;
-pub const SYS_madvise1 = 219;
-pub const SYS_getdents64 = 220;
-pub const SYS_fcntl64 = 221;
-pub const SYS_gettid = 224;
-pub const SYS_readahead = 225;
-pub const SYS_setxattr = 226;
-pub const SYS_lsetxattr = 227;
-pub const SYS_fsetxattr = 228;
-pub const SYS_getxattr = 229;
-pub const SYS_lgetxattr = 230;
-pub const SYS_fgetxattr = 231;
-pub const SYS_listxattr = 232;
-pub const SYS_llistxattr = 233;
-pub const SYS_flistxattr = 234;
-pub const SYS_removexattr = 235;
-pub const SYS_lremovexattr = 236;
-pub const SYS_fremovexattr = 237;
-pub const SYS_tkill = 238;
-pub const SYS_sendfile64 = 239;
-pub const SYS_futex = 240;
-pub const SYS_sched_setaffinity = 241;
-pub const SYS_sched_getaffinity = 242;
-pub const SYS_set_thread_area = 243;
-pub const SYS_get_thread_area = 244;
-pub const SYS_io_setup = 245;
-pub const SYS_io_destroy = 246;
-pub const SYS_io_getevents = 247;
-pub const SYS_io_submit = 248;
-pub const SYS_io_cancel = 249;
-pub const SYS_fadvise64 = 250;
-pub const SYS_exit_group = 252;
-pub const SYS_lookup_dcookie = 253;
-pub const SYS_epoll_create = 254;
-pub const SYS_epoll_ctl = 255;
-pub const SYS_epoll_wait = 256;
-pub const SYS_remap_file_pages = 257;
-pub const SYS_set_tid_address = 258;
-pub const SYS_timer_create = 259;
-pub const SYS_timer_settime = SYS_timer_create+1;
-pub const SYS_timer_gettime = SYS_timer_create+2;
-pub const SYS_timer_getoverrun = SYS_timer_create+3;
-pub const SYS_timer_delete = SYS_timer_create+4;
-pub const SYS_clock_settime = SYS_timer_create+5;
-pub const SYS_clock_gettime = SYS_timer_create+6;
-pub const SYS_clock_getres = SYS_timer_create+7;
-pub const SYS_clock_nanosleep = SYS_timer_create+8;
-pub const SYS_statfs64 = 268;
-pub const SYS_fstatfs64 = 269;
-pub const SYS_tgkill = 270;
-pub const SYS_utimes = 271;
-pub const SYS_fadvise64_64 = 272;
-pub const SYS_vserver = 273;
-pub const SYS_mbind = 274;
-pub const SYS_get_mempolicy = 275;
-pub const SYS_set_mempolicy = 276;
-pub const SYS_mq_open = 277;
-pub const SYS_mq_unlink = SYS_mq_open+1;
-pub const SYS_mq_timedsend = SYS_mq_open+2;
-pub const SYS_mq_timedreceive = SYS_mq_open+3;
-pub const SYS_mq_notify = SYS_mq_open+4;
-pub const SYS_mq_getsetattr = SYS_mq_open+5;
-pub const SYS_kexec_load = 283;
-pub const SYS_waitid = 284;
-pub const SYS_add_key = 286;
-pub const SYS_request_key = 287;
-pub const SYS_keyctl = 288;
-pub const SYS_ioprio_set = 289;
-pub const SYS_ioprio_get = 290;
-pub const SYS_inotify_init = 291;
-pub const SYS_inotify_add_watch = 292;
-pub const SYS_inotify_rm_watch = 293;
-pub const SYS_migrate_pages = 294;
-pub const SYS_openat = 295;
-pub const SYS_mkdirat = 296;
-pub const SYS_mknodat = 297;
-pub const SYS_fchownat = 298;
-pub const SYS_futimesat = 299;
-pub const SYS_fstatat64 = 300;
-pub const SYS_unlinkat = 301;
-pub const SYS_renameat = 302;
-pub const SYS_linkat = 303;
-pub const SYS_symlinkat = 304;
-pub const SYS_readlinkat = 305;
-pub const SYS_fchmodat = 306;
-pub const SYS_faccessat = 307;
-pub const SYS_pselect6 = 308;
-pub const SYS_ppoll = 309;
-pub const SYS_unshare = 310;
-pub const SYS_set_robust_list = 311;
-pub const SYS_get_robust_list = 312;
-pub const SYS_splice = 313;
-pub const SYS_sync_file_range = 314;
-pub const SYS_tee = 315;
-pub const SYS_vmsplice = 316;
-pub const SYS_move_pages = 317;
-pub const SYS_getcpu = 318;
-pub const SYS_epoll_pwait = 319;
-pub const SYS_utimensat = 320;
-pub const SYS_signalfd = 321;
-pub const SYS_timerfd_create = 322;
-pub const SYS_eventfd = 323;
-pub const SYS_fallocate = 324;
-pub const SYS_timerfd_settime = 325;
-pub const SYS_timerfd_gettime = 326;
-pub const SYS_signalfd4 = 327;
-pub const SYS_eventfd2 = 328;
-pub const SYS_epoll_create1 = 329;
-pub const SYS_dup3 = 330;
-pub const SYS_pipe2 = 331;
-pub const SYS_inotify_init1 = 332;
-pub const SYS_preadv = 333;
-pub const SYS_pwritev = 334;
-pub const SYS_rt_tgsigqueueinfo = 335;
-pub const SYS_perf_event_open = 336;
-pub const SYS_recvmmsg = 337;
-pub const SYS_fanotify_init = 338;
-pub const SYS_fanotify_mark = 339;
-pub const SYS_prlimit64 = 340;
-pub const SYS_name_to_handle_at = 341;
-pub const SYS_open_by_handle_at = 342;
-pub const SYS_clock_adjtime = 343;
-pub const SYS_syncfs = 344;
-pub const SYS_sendmmsg = 345;
-pub const SYS_setns = 346;
-pub const SYS_process_vm_readv = 347;
-pub const SYS_process_vm_writev = 348;
-pub const SYS_kcmp = 349;
-pub const SYS_finit_module = 350;
-pub const SYS_sched_setattr = 351;
-pub const SYS_sched_getattr = 352;
-pub const SYS_renameat2 = 353;
-pub const SYS_seccomp = 354;
-pub const SYS_getrandom = 355;
-pub const SYS_memfd_create = 356;
-pub const SYS_bpf = 357;
-pub const SYS_execveat = 358;
-pub const SYS_socket = 359;
-pub const SYS_socketpair = 360;
-pub const SYS_bind = 361;
-pub const SYS_connect = 362;
-pub const SYS_listen = 363;
-pub const SYS_accept4 = 364;
-pub const SYS_getsockopt = 365;
-pub const SYS_setsockopt = 366;
-pub const SYS_getsockname = 367;
-pub const SYS_getpeername = 368;
-pub const SYS_sendto = 369;
-pub const SYS_sendmsg = 370;
-pub const SYS_recvfrom = 371;
-pub const SYS_recvmsg = 372;
-pub const SYS_shutdown = 373;
-pub const SYS_userfaultfd = 374;
-pub const SYS_membarrier = 375;
-pub const SYS_mlock2 = 376;
-
-
-pub const O_CREAT = 0o100;
-pub const O_EXCL = 0o200;
-pub const O_NOCTTY = 0o400;
-pub const O_TRUNC = 0o1000;
-pub const O_APPEND = 0o2000;
-pub const O_NONBLOCK = 0o4000;
-pub const O_DSYNC = 0o10000;
-pub const O_SYNC = 0o4010000;
-pub const O_RSYNC = 0o4010000;
-pub const O_DIRECTORY = 0o200000;
-pub const O_NOFOLLOW = 0o400000;
-pub const O_CLOEXEC = 0o2000000;
-
-pub const O_ASYNC = 0o20000;
-pub const O_DIRECT = 0o40000;
-pub const O_LARGEFILE = 0o100000;
-pub const O_NOATIME = 0o1000000;
-pub const O_PATH = 0o10000000;
-pub const O_TMPFILE = 0o20200000;
-pub const O_NDELAY = O_NONBLOCK;
-
-pub const F_DUPFD = 0;
-pub const F_GETFD = 1;
-pub const F_SETFD = 2;
-pub const F_GETFL = 3;
-pub const F_SETFL = 4;
-
-pub const F_SETOWN = 8;
-pub const F_GETOWN = 9;
-pub const F_SETSIG = 10;
-pub const F_GETSIG = 11;
-
-pub const F_GETLK = 12;
-pub const F_SETLK = 13;
-pub const F_SETLKW = 14;
-
-pub const F_SETOWN_EX = 15;
-pub const F_GETOWN_EX = 16;
-
-pub const F_GETOWNER_UIDS = 17;
-
-pub inline fn syscall0(number: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number));
-}
-
-pub inline fn syscall1(number: usize, arg1: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1));
-}
-
-pub inline fn syscall2(number: usize, arg1: usize, arg2: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2));
-}
-
-pub inline fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3));
-}
-
-pub inline fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4));
-}
-
-pub inline fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize,
- arg4: usize, arg5: usize) usize
-{
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4),
- [arg5] "{edi}" (arg5));
-}
-
-pub inline fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize,
- arg4: usize, arg5: usize, arg6: usize) usize
-{
- return asm volatile ("int $0x80"
- : [ret] "={eax}" (-> usize)
- : [number] "{eax}" (number),
- [arg1] "{ebx}" (arg1),
- [arg2] "{ecx}" (arg2),
- [arg3] "{edx}" (arg3),
- [arg4] "{esi}" (arg4),
- [arg5] "{edi}" (arg5),
- [arg6] "{ebp}" (arg6));
-}
-
-pub nakedcc fn restore() void {
- asm volatile (
- \\popl %%eax
- \\movl $119, %%eax
- \\int $0x80
- :
- :
- : "rcx", "r11");
-}
-
-pub nakedcc fn restore_rt() void {
- asm volatile ("int $0x80"
- :
- : [number] "{eax}" (usize(SYS_rt_sigreturn))
- : "rcx", "r11");
-}
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 646b1ef300..aa2a6d85da 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -1,7 +1,7 @@
const std = @import("../../index.zig");
const assert = std.debug.assert;
const builtin = @import("builtin");
-const arch = switch (builtin.arch) {
+pub use switch (builtin.arch) {
builtin.Arch.x86_64 => @import("x86_64.zig"),
builtin.Arch.i386 => @import("i386.zig"),
else => @compileError("unsupported arch"),
@@ -93,27 +93,6 @@ pub const O_RDONLY = 0o0;
pub const O_WRONLY = 0o1;
pub const O_RDWR = 0o2;
-pub const O_CREAT = arch.O_CREAT;
-pub const O_EXCL = arch.O_EXCL;
-pub const O_NOCTTY = arch.O_NOCTTY;
-pub const O_TRUNC = arch.O_TRUNC;
-pub const O_APPEND = arch.O_APPEND;
-pub const O_NONBLOCK = arch.O_NONBLOCK;
-pub const O_DSYNC = arch.O_DSYNC;
-pub const O_SYNC = arch.O_SYNC;
-pub const O_RSYNC = arch.O_RSYNC;
-pub const O_DIRECTORY = arch.O_DIRECTORY;
-pub const O_NOFOLLOW = arch.O_NOFOLLOW;
-pub const O_CLOEXEC = arch.O_CLOEXEC;
-
-pub const O_ASYNC = arch.O_ASYNC;
-pub const O_DIRECT = arch.O_DIRECT;
-pub const O_LARGEFILE = arch.O_LARGEFILE;
-pub const O_NOATIME = arch.O_NOATIME;
-pub const O_PATH = arch.O_PATH;
-pub const O_TMPFILE = arch.O_TMPFILE;
-pub const O_NDELAY = arch.O_NDELAY;
-
pub const SEEK_SET = 0;
pub const SEEK_CUR = 1;
pub const SEEK_END = 2;
@@ -122,17 +101,6 @@ pub const SIG_BLOCK = 0;
pub const SIG_UNBLOCK = 1;
pub const SIG_SETMASK = 2;
-pub const SOCK_STREAM = 1;
-pub const SOCK_DGRAM = 2;
-pub const SOCK_RAW = 3;
-pub const SOCK_RDM = 4;
-pub const SOCK_SEQPACKET = 5;
-pub const SOCK_DCCP = 6;
-pub const SOCK_PACKET = 10;
-pub const SOCK_CLOEXEC = 0o2000000;
-pub const SOCK_NONBLOCK = 0o4000;
-
-
pub const PROTO_ip = 0o000;
pub const PROTO_icmp = 0o001;
pub const PROTO_igmp = 0o002;
@@ -170,6 +138,20 @@ pub const PROTO_encap = 0o142;
pub const PROTO_pim = 0o147;
pub const PROTO_raw = 0o377;
+pub const SHUT_RD = 0;
+pub const SHUT_WR = 1;
+pub const SHUT_RDWR = 2;
+
+pub const SOCK_STREAM = 1;
+pub const SOCK_DGRAM = 2;
+pub const SOCK_RAW = 3;
+pub const SOCK_RDM = 4;
+pub const SOCK_SEQPACKET = 5;
+pub const SOCK_DCCP = 6;
+pub const SOCK_PACKET = 10;
+pub const SOCK_CLOEXEC = 0o2000000;
+pub const SOCK_NONBLOCK = 0o4000;
+
pub const PF_UNSPEC = 0;
pub const PF_LOCAL = 1;
pub const PF_UNIX = PF_LOCAL;
@@ -214,7 +196,10 @@ pub const PF_CAIF = 37;
pub const PF_ALG = 38;
pub const PF_NFC = 39;
pub const PF_VSOCK = 40;
-pub const PF_MAX = 41;
+pub const PF_KCM = 41;
+pub const PF_QIPCRTR = 42;
+pub const PF_SMC = 43;
+pub const PF_MAX = 44;
pub const AF_UNSPEC = PF_UNSPEC;
pub const AF_LOCAL = PF_LOCAL;
@@ -260,8 +245,137 @@ pub const AF_CAIF = PF_CAIF;
pub const AF_ALG = PF_ALG;
pub const AF_NFC = PF_NFC;
pub const AF_VSOCK = PF_VSOCK;
+pub const AF_KCM = PF_KCM;
+pub const AF_QIPCRTR = PF_QIPCRTR;
+pub const AF_SMC = PF_SMC;
pub const AF_MAX = PF_MAX;
+pub const SO_DEBUG = 1;
+pub const SO_REUSEADDR = 2;
+pub const SO_TYPE = 3;
+pub const SO_ERROR = 4;
+pub const SO_DONTROUTE = 5;
+pub const SO_BROADCAST = 6;
+pub const SO_SNDBUF = 7;
+pub const SO_RCVBUF = 8;
+pub const SO_KEEPALIVE = 9;
+pub const SO_OOBINLINE = 10;
+pub const SO_NO_CHECK = 11;
+pub const SO_PRIORITY = 12;
+pub const SO_LINGER = 13;
+pub const SO_BSDCOMPAT = 14;
+pub const SO_REUSEPORT = 15;
+pub const SO_PASSCRED = 16;
+pub const SO_PEERCRED = 17;
+pub const SO_RCVLOWAT = 18;
+pub const SO_SNDLOWAT = 19;
+pub const SO_RCVTIMEO = 20;
+pub const SO_SNDTIMEO = 21;
+pub const SO_ACCEPTCONN = 30;
+pub const SO_SNDBUFFORCE = 32;
+pub const SO_RCVBUFFORCE = 33;
+pub const SO_PROTOCOL = 38;
+pub const SO_DOMAIN = 39;
+
+pub const SO_SECURITY_AUTHENTICATION = 22;
+pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23;
+pub const SO_SECURITY_ENCRYPTION_NETWORK = 24;
+
+pub const SO_BINDTODEVICE = 25;
+
+pub const SO_ATTACH_FILTER = 26;
+pub const SO_DETACH_FILTER = 27;
+pub const SO_GET_FILTER = SO_ATTACH_FILTER;
+
+pub const SO_PEERNAME = 28;
+pub const SO_TIMESTAMP = 29;
+pub const SCM_TIMESTAMP = SO_TIMESTAMP;
+
+pub const SO_PEERSEC = 31;
+pub const SO_PASSSEC = 34;
+pub const SO_TIMESTAMPNS = 35;
+pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS;
+pub const SO_MARK = 36;
+pub const SO_TIMESTAMPING = 37;
+pub const SCM_TIMESTAMPING = SO_TIMESTAMPING;
+pub const SO_RXQ_OVFL = 40;
+pub const SO_WIFI_STATUS = 41;
+pub const SCM_WIFI_STATUS = SO_WIFI_STATUS;
+pub const SO_PEEK_OFF = 42;
+pub const SO_NOFCS = 43;
+pub const SO_LOCK_FILTER = 44;
+pub const SO_SELECT_ERR_QUEUE = 45;
+pub const SO_BUSY_POLL = 46;
+pub const SO_MAX_PACING_RATE = 47;
+pub const SO_BPF_EXTENSIONS = 48;
+pub const SO_INCOMING_CPU = 49;
+pub const SO_ATTACH_BPF = 50;
+pub const SO_DETACH_BPF = SO_DETACH_FILTER;
+pub const SO_ATTACH_REUSEPORT_CBPF = 51;
+pub const SO_ATTACH_REUSEPORT_EBPF = 52;
+pub const SO_CNX_ADVICE = 53;
+pub const SCM_TIMESTAMPING_OPT_STATS = 54;
+pub const SO_MEMINFO = 55;
+pub const SO_INCOMING_NAPI_ID = 56;
+pub const SO_COOKIE = 57;
+pub const SCM_TIMESTAMPING_PKTINFO = 58;
+pub const SO_PEERGROUPS = 59;
+pub const SO_ZEROCOPY = 60;
+
+pub const SOL_SOCKET = 1;
+
+pub const SOL_IP = 0;
+pub const SOL_IPV6 = 41;
+pub const SOL_ICMPV6 = 58;
+
+pub const SOL_RAW = 255;
+pub const SOL_DECNET = 261;
+pub const SOL_X25 = 262;
+pub const SOL_PACKET = 263;
+pub const SOL_ATM = 264;
+pub const SOL_AAL = 265;
+pub const SOL_IRDA = 266;
+pub const SOL_NETBEUI = 267;
+pub const SOL_LLC = 268;
+pub const SOL_DCCP = 269;
+pub const SOL_NETLINK = 270;
+pub const SOL_TIPC = 271;
+pub const SOL_RXRPC = 272;
+pub const SOL_PPPOL2TP = 273;
+pub const SOL_BLUETOOTH = 274;
+pub const SOL_PNPIPE = 275;
+pub const SOL_RDS = 276;
+pub const SOL_IUCV = 277;
+pub const SOL_CAIF = 278;
+pub const SOL_ALG = 279;
+pub const SOL_NFC = 280;
+pub const SOL_KCM = 281;
+pub const SOL_TLS = 282;
+
+pub const SOMAXCONN = 128;
+
+pub const MSG_OOB = 0x0001;
+pub const MSG_PEEK = 0x0002;
+pub const MSG_DONTROUTE = 0x0004;
+pub const MSG_CTRUNC = 0x0008;
+pub const MSG_PROXY = 0x0010;
+pub const MSG_TRUNC = 0x0020;
+pub const MSG_DONTWAIT = 0x0040;
+pub const MSG_EOR = 0x0080;
+pub const MSG_WAITALL = 0x0100;
+pub const MSG_FIN = 0x0200;
+pub const MSG_SYN = 0x0400;
+pub const MSG_CONFIRM = 0x0800;
+pub const MSG_RST = 0x1000;
+pub const MSG_ERRQUEUE = 0x2000;
+pub const MSG_NOSIGNAL = 0x4000;
+pub const MSG_MORE = 0x8000;
+pub const MSG_WAITFORONE = 0x10000;
+pub const MSG_BATCH = 0x40000;
+pub const MSG_ZEROCOPY = 0x4000000;
+pub const MSG_FASTOPEN = 0x20000000;
+pub const MSG_CMSG_CLOEXEC = 0x40000000;
+
pub const DT_UNKNOWN = 0;
pub const DT_FIFO = 1;
pub const DT_CHR = 2;
@@ -364,6 +478,126 @@ pub const CLOCK_BOOTTIME_ALARM = 9;
pub const CLOCK_SGI_CYCLE = 10;
pub const CLOCK_TAI = 11;
+pub const CSIGNAL = 0x000000ff;
+pub const CLONE_VM = 0x00000100;
+pub const CLONE_FS = 0x00000200;
+pub const CLONE_FILES = 0x00000400;
+pub const CLONE_SIGHAND = 0x00000800;
+pub const CLONE_PTRACE = 0x00002000;
+pub const CLONE_VFORK = 0x00004000;
+pub const CLONE_PARENT = 0x00008000;
+pub const CLONE_THREAD = 0x00010000;
+pub const CLONE_NEWNS = 0x00020000;
+pub const CLONE_SYSVSEM = 0x00040000;
+pub const CLONE_SETTLS = 0x00080000;
+pub const CLONE_PARENT_SETTID = 0x00100000;
+pub const CLONE_CHILD_CLEARTID = 0x00200000;
+pub const CLONE_DETACHED = 0x00400000;
+pub const CLONE_UNTRACED = 0x00800000;
+pub const CLONE_CHILD_SETTID = 0x01000000;
+pub const CLONE_NEWCGROUP = 0x02000000;
+pub const CLONE_NEWUTS = 0x04000000;
+pub const CLONE_NEWIPC = 0x08000000;
+pub const CLONE_NEWUSER = 0x10000000;
+pub const CLONE_NEWPID = 0x20000000;
+pub const CLONE_NEWNET = 0x40000000;
+pub const CLONE_IO = 0x80000000;
+
+pub const MS_RDONLY = 1;
+pub const MS_NOSUID = 2;
+pub const MS_NODEV = 4;
+pub const MS_NOEXEC = 8;
+pub const MS_SYNCHRONOUS = 16;
+pub const MS_REMOUNT = 32;
+pub const MS_MANDLOCK = 64;
+pub const MS_DIRSYNC = 128;
+pub const MS_NOATIME = 1024;
+pub const MS_NODIRATIME = 2048;
+pub const MS_BIND = 4096;
+pub const MS_MOVE = 8192;
+pub const MS_REC = 16384;
+pub const MS_SILENT = 32768;
+pub const MS_POSIXACL = (1<<16);
+pub const MS_UNBINDABLE = (1<<17);
+pub const MS_PRIVATE = (1<<18);
+pub const MS_SLAVE = (1<<19);
+pub const MS_SHARED = (1<<20);
+pub const MS_RELATIME = (1<<21);
+pub const MS_KERNMOUNT = (1<<22);
+pub const MS_I_VERSION = (1<<23);
+pub const MS_STRICTATIME = (1<<24);
+pub const MS_LAZYTIME = (1<<25);
+pub const MS_NOREMOTELOCK = (1<<27);
+pub const MS_NOSEC = (1<<28);
+pub const MS_BORN = (1<<29);
+pub const MS_ACTIVE = (1<<30);
+pub const MS_NOUSER = (1<<31);
+
+pub const MS_RMT_MASK = (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME);
+
+pub const MS_MGC_VAL = 0xc0ed0000;
+pub const MS_MGC_MSK = 0xffff0000;
+
+pub const MNT_FORCE = 1;
+pub const MNT_DETACH = 2;
+pub const MNT_EXPIRE = 4;
+pub const UMOUNT_NOFOLLOW = 8;
+
+
+pub const S_IFMT = 0o170000;
+
+pub const S_IFDIR = 0o040000;
+pub const S_IFCHR = 0o020000;
+pub const S_IFBLK = 0o060000;
+pub const S_IFREG = 0o100000;
+pub const S_IFIFO = 0o010000;
+pub const S_IFLNK = 0o120000;
+pub const S_IFSOCK = 0o140000;
+
+pub const S_ISUID = 0o4000;
+pub const S_ISGID = 0o2000;
+pub const S_ISVTX = 0o1000;
+pub const S_IRUSR = 0o400;
+pub const S_IWUSR = 0o200;
+pub const S_IXUSR = 0o100;
+pub const S_IRWXU = 0o700;
+pub const S_IRGRP = 0o040;
+pub const S_IWGRP = 0o020;
+pub const S_IXGRP = 0o010;
+pub const S_IRWXG = 0o070;
+pub const S_IROTH = 0o004;
+pub const S_IWOTH = 0o002;
+pub const S_IXOTH = 0o001;
+pub const S_IRWXO = 0o007;
+
+pub fn S_ISREG(m: u32) bool {
+ return m & S_IFMT == S_IFREG;
+}
+
+pub fn S_ISDIR(m: u32) bool {
+ return m & S_IFMT == S_IFDIR;
+}
+
+pub fn S_ISCHR(m: u32) bool {
+ return m & S_IFMT == S_IFCHR;
+}
+
+pub fn S_ISBLK(m: u32) bool {
+ return m & S_IFMT == S_IFBLK;
+}
+
+pub fn S_ISFIFO(m: u32) bool {
+ return m & S_IFMT == S_IFIFO;
+}
+
+pub fn S_ISLNK(m: u32) bool {
+ return m & S_IFMT == S_IFLNK;
+}
+
+pub fn S_ISSOCK(m: u32) bool {
+ return m & S_IFMT == S_IFSOCK;
+}
+
pub const TFD_NONBLOCK = O_NONBLOCK;
pub const TFD_CLOEXEC = O_CLOEXEC;
@@ -394,65 +628,81 @@ pub fn getErrno(r: usize) usize {
}
pub fn dup2(old: i32, new: i32) usize {
- return arch.syscall2(arch.SYS_dup2, usize(old), usize(new));
+ return syscall2(SYS_dup2, usize(old), usize(new));
}
pub fn chdir(path: &const u8) usize {
- return arch.syscall1(arch.SYS_chdir, @ptrToInt(path));
+ return syscall1(SYS_chdir, @ptrToInt(path));
+}
+
+pub fn chroot(path: &const u8) usize {
+ return syscall1(SYS_chroot, @ptrToInt(path));
}
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize {
- return arch.syscall3(arch.SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
+ return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
}
pub fn fork() usize {
- return arch.syscall0(arch.SYS_fork);
+ return syscall0(SYS_fork);
}
pub fn getcwd(buf: &u8, size: usize) usize {
- return arch.syscall2(arch.SYS_getcwd, @ptrToInt(buf), size);
+ return syscall2(SYS_getcwd, @ptrToInt(buf), size);
}
pub fn getdents(fd: i32, dirp: &u8, count: usize) usize {
- return arch.syscall3(arch.SYS_getdents, usize(fd), @ptrToInt(dirp), count);
+ return syscall3(SYS_getdents, usize(fd), @ptrToInt(dirp), count);
}
pub fn isatty(fd: i32) bool {
var wsz: winsize = undefined;
- return arch.syscall3(arch.SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
+ return syscall3(SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
}
pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) usize {
- return arch.syscall3(arch.SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
+ return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
}
pub fn mkdir(path: &const u8, mode: u32) usize {
- return arch.syscall2(arch.SYS_mkdir, @ptrToInt(path), mode);
+ return syscall2(SYS_mkdir, @ptrToInt(path), mode);
+}
+
+pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize {
+ return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data);
+}
+
+pub fn umount(special: &const u8) usize {
+ return syscall2(SYS_umount2, @ptrToInt(special), 0);
+}
+
+pub fn umount2(special: &const u8, flags: u32) usize {
+ return syscall2(SYS_umount2, @ptrToInt(special), flags);
}
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {
- return arch.syscall6(arch.SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
+ return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
@bitCast(usize, offset));
}
pub fn munmap(address: &u8, length: usize) usize {
- return arch.syscall2(arch.SYS_munmap, @ptrToInt(address), length);
+ return syscall2(SYS_munmap, @ptrToInt(address), length);
}
pub fn read(fd: i32, buf: &u8, count: usize) usize {
- return arch.syscall3(arch.SYS_read, usize(fd), @ptrToInt(buf), count);
+ return syscall3(SYS_read, usize(fd), @ptrToInt(buf), count);
}
pub fn rmdir(path: &const u8) usize {
- return arch.syscall1(arch.SYS_rmdir, @ptrToInt(path));
+ return syscall1(SYS_rmdir, @ptrToInt(path));
}
pub fn symlink(existing: &const u8, new: &const u8) usize {
- return arch.syscall2(arch.SYS_symlink, @ptrToInt(existing), @ptrToInt(new));
+ return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new));
}
pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
- return arch.syscall4(arch.SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
+ return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
}
pub fn pipe(fd: &[2]i32) usize {
@@ -460,84 +710,136 @@ pub fn pipe(fd: &[2]i32) usize {
}
pub fn pipe2(fd: &[2]i32, flags: usize) usize {
- return arch.syscall2(arch.SYS_pipe2, @ptrToInt(fd), flags);
+ return syscall2(SYS_pipe2, @ptrToInt(fd), flags);
}
pub fn write(fd: i32, buf: &const u8, count: usize) usize {
- return arch.syscall3(arch.SYS_write, usize(fd), @ptrToInt(buf), count);
+ return syscall3(SYS_write, usize(fd), @ptrToInt(buf), count);
}
pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) usize {
- return arch.syscall4(arch.SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset);
+ return syscall4(SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset);
}
pub fn rename(old: &const u8, new: &const u8) usize {
- return arch.syscall2(arch.SYS_rename, @ptrToInt(old), @ptrToInt(new));
+ return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new));
}
pub fn open(path: &const u8, flags: u32, perm: usize) usize {
- return arch.syscall3(arch.SYS_open, @ptrToInt(path), flags, perm);
+ return syscall3(SYS_open, @ptrToInt(path), flags, perm);
}
pub fn create(path: &const u8, perm: usize) usize {
- return arch.syscall2(arch.SYS_creat, @ptrToInt(path), perm);
+ return syscall2(SYS_creat, @ptrToInt(path), perm);
}
pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {
- return arch.syscall4(arch.SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
+ return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
}
pub fn close(fd: i32) usize {
- return arch.syscall1(arch.SYS_close, usize(fd));
+ return syscall1(SYS_close, usize(fd));
}
pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize {
- return arch.syscall3(arch.SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos);
+ return syscall3(SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos);
}
pub fn exit(status: i32) noreturn {
- _ = arch.syscall1(arch.SYS_exit, @bitCast(usize, isize(status)));
+ _ = syscall1(SYS_exit, @bitCast(usize, isize(status)));
unreachable;
}
pub fn getrandom(buf: &u8, count: usize, flags: u32) usize {
- return arch.syscall3(arch.SYS_getrandom, @ptrToInt(buf), count, usize(flags));
+ return syscall3(SYS_getrandom, @ptrToInt(buf), count, usize(flags));
}
pub fn kill(pid: i32, sig: i32) usize {
- return arch.syscall2(arch.SYS_kill, @bitCast(usize, isize(pid)), usize(sig));
+ return syscall2(SYS_kill, @bitCast(usize, isize(pid)), usize(sig));
}
pub fn unlink(path: &const u8) usize {
- return arch.syscall1(arch.SYS_unlink, @ptrToInt(path));
+ return syscall1(SYS_unlink, @ptrToInt(path));
}
pub fn waitpid(pid: i32, status: &i32, options: i32) usize {
- return arch.syscall4(arch.SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
+ return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
}
pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {
- return arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
+ return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
}
pub fn setuid(uid: u32) usize {
- return arch.syscall1(arch.SYS_setuid, uid);
+ return syscall1(SYS_setuid, uid);
}
pub fn setgid(gid: u32) usize {
- return arch.syscall1(arch.SYS_setgid, gid);
+ return syscall1(SYS_setgid, gid);
}
pub fn setreuid(ruid: u32, euid: u32) usize {
- return arch.syscall2(arch.SYS_setreuid, ruid, euid);
+ return syscall2(SYS_setreuid, ruid, euid);
}
pub fn setregid(rgid: u32, egid: u32) usize {
- return arch.syscall2(arch.SYS_setregid, rgid, egid);
+ return syscall2(SYS_setregid, rgid, egid);
+}
+
+pub fn getuid() u32 {
+ return u32(syscall0(SYS_getuid));
+}
+
+pub fn getgid() u32 {
+ return u32(syscall0(SYS_getgid));
+}
+
+pub fn geteuid() u32 {
+ return u32(syscall0(SYS_geteuid));
+}
+
+pub fn getegid() u32 {
+ return u32(syscall0(SYS_getegid));
+}
+
+pub fn seteuid(euid: u32) usize {
+ return syscall1(SYS_seteuid, euid);
+}
+
+pub fn setegid(egid: u32) usize {
+ return syscall1(SYS_setegid, egid);
+}
+
+pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize {
+ return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
+}
+
+pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize {
+ return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
+}
+
+pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
+ return syscall3(SYS_setresuid, ruid, euid, suid);
+}
+
+pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
+ return syscall3(SYS_setresgid, rgid, egid, sgid);
+}
+
+pub fn getgroups(size: usize, list: &u32) usize {
+ return syscall2(SYS_getgroups, size, @ptrToInt(list));
+}
+
+pub fn setgroups(size: usize, list: &const u32) usize {
+ return syscall2(SYS_setgroups, size, @ptrToInt(list));
+}
+
+pub fn getpid() i32 {
+ return @bitCast(i32, u32(syscall0(SYS_getpid)));
}
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize {
- return arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
+ return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
}
pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigaction) usize {
@@ -548,11 +850,11 @@ pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigacti
.handler = act.handler,
.flags = act.flags | SA_RESTORER,
.mask = undefined,
- .restorer = @ptrCast(extern fn()void, arch.restore_rt),
+ .restorer = @ptrCast(extern fn()void, restore_rt),
};
var ksa_old: k_sigaction = undefined;
@memcpy(@ptrCast(&u8, &ksa.mask), @ptrCast(&const u8, &act.mask), 8);
- const result = arch.syscall4(arch.SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask)));
+ const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask)));
const err = getErrno(result);
if (err != 0) {
return result;
@@ -592,22 +894,22 @@ pub const empty_sigset = []usize{0} ** sigset_t.len;
pub fn raise(sig: i32) usize {
var set: sigset_t = undefined;
blockAppSignals(&set);
- const tid = i32(arch.syscall0(arch.SYS_gettid));
- const ret = arch.syscall2(arch.SYS_tkill, usize(tid), usize(sig));
+ const tid = i32(syscall0(SYS_gettid));
+ const ret = syscall2(SYS_tkill, usize(tid), usize(sig));
restoreSignals(&set);
return ret;
}
fn blockAllSignals(set: &sigset_t) void {
- _ = arch.syscall4(arch.SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG/8);
+ _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG/8);
}
fn blockAppSignals(set: &sigset_t) void {
- _ = arch.syscall4(arch.SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG/8);
+ _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG/8);
}
fn restoreSignals(set: &sigset_t) void {
- _ = arch.syscall4(arch.SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG/8);
+ _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG/8);
}
pub fn sigaddset(set: &sigset_t, sig: u6) void {
@@ -620,30 +922,27 @@ pub fn sigismember(set: &const sigset_t, sig: u6) bool {
return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0;
}
-
+pub const in_port_t = u16;
pub const sa_family_t = u16;
pub const socklen_t = u32;
-pub const in_addr = u32;
-pub const in6_addr = [16]u8;
-pub const sockaddr = extern struct {
- family: sa_family_t,
- port: u16,
- data: [12]u8,
+pub const sockaddr = extern union {
+ in: sockaddr_in,
+ in6: sockaddr_in6,
};
pub const sockaddr_in = extern struct {
family: sa_family_t,
- port: u16,
- addr: in_addr,
+ port: in_port_t,
+ addr: u32,
zero: [8]u8,
};
pub const sockaddr_in6 = extern struct {
family: sa_family_t,
- port: u16,
+ port: in_port_t,
flowinfo: u32,
- addr: in6_addr,
+ addr: [16]u8,
scope_id: u32,
};
@@ -653,61 +952,61 @@ pub const iovec = extern struct {
};
pub fn getsockname(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize {
- return arch.syscall3(arch.SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len));
+ return syscall3(SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len));
}
pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize {
- return arch.syscall3(arch.SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
+ return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
}
-pub fn socket(domain: i32, socket_type: i32, protocol: i32) usize {
- return arch.syscall3(arch.SYS_socket, usize(domain), usize(socket_type), usize(protocol));
+pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
+ return syscall3(SYS_socket, domain, socket_type, protocol);
}
-pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) usize {
- return arch.syscall5(arch.SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));
+pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize {
+ return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen));
}
-pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
- return arch.syscall5(arch.SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
+pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
+ return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
}
-pub fn sendmsg(fd: i32, msg: &const arch.msghdr, flags: u32) usize {
- return arch.syscall3(arch.SYS_sendmsg, usize(fd), @ptrToInt(msg), flags);
+pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize {
+ return syscall3(SYS_sendmsg, usize(fd), @ptrToInt(msg), flags);
}
pub fn connect(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
- return arch.syscall3(arch.SYS_connect, usize(fd), @ptrToInt(addr), usize(len));
+ return syscall3(SYS_connect, usize(fd), @ptrToInt(addr), usize(len));
}
-pub fn recvmsg(fd: i32, msg: &arch.msghdr, flags: u32) usize {
- return arch.syscall3(arch.SYS_recvmsg, usize(fd), @ptrToInt(msg), flags);
+pub fn recvmsg(fd: i32, msg: &msghdr, flags: u32) usize {
+ return syscall3(SYS_recvmsg, usize(fd), @ptrToInt(msg), flags);
}
pub fn recvfrom(fd: i32, noalias buf: &u8, len: usize, flags: u32,
noalias addr: ?&sockaddr, noalias alen: ?&socklen_t) usize
{
- return arch.syscall6(arch.SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
+ return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
}
pub fn shutdown(fd: i32, how: i32) usize {
- return arch.syscall2(arch.SYS_shutdown, usize(fd), usize(how));
+ return syscall2(SYS_shutdown, usize(fd), usize(how));
}
pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
- return arch.syscall3(arch.SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
+ return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
}
-pub fn listen(fd: i32, backlog: i32) usize {
- return arch.syscall2(arch.SYS_listen, usize(fd), usize(backlog));
+pub fn listen(fd: i32, backlog: u32) usize {
+ return syscall2(SYS_listen, usize(fd), backlog);
}
pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize {
- return arch.syscall6(arch.SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen));
+ return syscall6(SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen));
}
pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize {
- return arch.syscall4(arch.SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0]));
+ return syscall4(SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0]));
}
pub fn accept(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize {
@@ -715,52 +1014,86 @@ pub fn accept(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize {
}
pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: u32) usize {
- return arch.syscall4(arch.SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
-}
-
-// error NameTooLong;
-// error SystemResources;
-// error Io;
-//
-// pub fn if_nametoindex(name: []u8) !u32 {
-// var ifr: ifreq = undefined;
-//
-// if (name.len >= ifr.ifr_name.len) {
-// return error.NameTooLong;
-// }
-//
-// const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-// const socket_err = getErrno(socket_ret);
-// if (socket_err > 0) {
-// return error.SystemResources;
-// }
-// const socket_fd = i32(socket_ret);
-// @memcpy(&ifr.ifr_name[0], &name[0], name.len);
-// ifr.ifr_name[name.len] = 0;
-// const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
-// close(socket_fd);
-// const ioctl_err = getErrno(ioctl_ret);
-// if (ioctl_err > 0) {
-// return error.Io;
-// }
-// return ifr.ifr_ifindex;
-// }
-
-pub const Stat = arch.Stat;
-pub const timespec = arch.timespec;
+ return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
+}
pub fn fstat(fd: i32, stat_buf: &Stat) usize {
- return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf));
+ return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
+}
+
+pub fn stat(pathname: &const u8, statbuf: &Stat) usize {
+ return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf));
+}
+
+pub fn lstat(pathname: &const u8, statbuf: &Stat) usize {
+ return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf));
+}
+
+pub fn listxattr(path: &const u8, list: &u8, size: usize) usize {
+ return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size);
+}
+
+pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize {
+ return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size);
+}
+
+pub fn flistxattr(fd: usize, list: &u8, size: usize) usize {
+ return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size);
+}
+
+pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize {
+ return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size);
+}
+
+pub fn setxattr(path: &const u8, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
+ size, flags);
}
-pub const epoll_data = extern union {
+pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
+ size, flags);
+}
+
+pub fn fsetxattr(fd: usize, name: &const u8, value: &const void,
+ size: usize, flags: usize) usize {
+
+ return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value),
+ size, flags);
+}
+
+pub fn removexattr(path: &const u8, name: &const u8) usize {
+ return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name));
+}
+
+pub fn lremovexattr(path: &const u8, name: &const u8) usize {
+ return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name));
+}
+
+pub fn fremovexattr(fd: usize, name: &const u8) usize {
+ return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
+}
+
+pub const epoll_data = packed union {
ptr: usize,
fd: i32,
@"u32": u32,
@"u64": u64,
};
-pub const epoll_event = extern struct {
+pub const epoll_event = packed struct {
events: u32,
data: epoll_data,
};
@@ -770,19 +1103,19 @@ pub fn epoll_create() usize {
}
pub fn epoll_create1(flags: usize) usize {
- return arch.syscall1(arch.SYS_epoll_create1, flags);
+ return syscall1(SYS_epoll_create1, flags);
}
-pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
- return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
+pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize {
+ return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
}
pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: u32, timeout: i32) usize {
- return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
+ return syscall4(SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
}
pub fn timerfd_create(clockid: i32, flags: u32) usize {
- return arch.syscall2(arch.SYS_timerfd_create, usize(clockid), usize(flags));
+ return syscall2(SYS_timerfd_create, usize(clockid), usize(flags));
}
pub const itimerspec = extern struct {
@@ -791,11 +1124,130 @@ pub const itimerspec = extern struct {
};
pub fn timerfd_gettime(fd: i32, curr_value: &itimerspec) usize {
- return arch.syscall2(arch.SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value));
+ return syscall2(SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value));
}
pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_value: ?&itimerspec) usize {
- return arch.syscall4(arch.SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
+ return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
+}
+
+pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330;
+pub const _LINUX_CAPABILITY_U32S_1 = 1;
+
+pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026;
+pub const _LINUX_CAPABILITY_U32S_2 = 2;
+
+pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522;
+pub const _LINUX_CAPABILITY_U32S_3 = 2;
+
+pub const VFS_CAP_REVISION_MASK = 0xFF000000;
+pub const VFS_CAP_REVISION_SHIFT = 24;
+pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK;
+pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001;
+
+pub const VFS_CAP_REVISION_1 = 0x01000000;
+pub const VFS_CAP_U32_1 = 1;
+pub const XATTR_CAPS_SZ_1 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_1);
+
+pub const VFS_CAP_REVISION_2 = 0x02000000;
+pub const VFS_CAP_U32_2 = 2;
+pub const XATTR_CAPS_SZ_2 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_2);
+
+pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2;
+pub const VFS_CAP_U32 = VFS_CAP_U32_2;
+pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2;
+
+pub const vfs_cap_data = extern struct {
+ //all of these are mandated as little endian
+ //when on disk.
+ const Data = struct {
+ permitted: u32,
+ inheritable: u32,
+ };
+
+ magic_etc: u32,
+ data: [VFS_CAP_U32]Data,
+};
+
+
+pub const CAP_CHOWN = 0;
+pub const CAP_DAC_OVERRIDE = 1;
+pub const CAP_DAC_READ_SEARCH = 2;
+pub const CAP_FOWNER = 3;
+pub const CAP_FSETID = 4;
+pub const CAP_KILL = 5;
+pub const CAP_SETGID = 6;
+pub const CAP_SETUID = 7;
+pub const CAP_SETPCAP = 8;
+pub const CAP_LINUX_IMMUTABLE = 9;
+pub const CAP_NET_BIND_SERVICE = 10;
+pub const CAP_NET_BROADCAST = 11;
+pub const CAP_NET_ADMIN = 12;
+pub const CAP_NET_RAW = 13;
+pub const CAP_IPC_LOCK = 14;
+pub const CAP_IPC_OWNER = 15;
+pub const CAP_SYS_MODULE = 16;
+pub const CAP_SYS_RAWIO = 17;
+pub const CAP_SYS_CHROOT = 18;
+pub const CAP_SYS_PTRACE = 19;
+pub const CAP_SYS_PACCT = 20;
+pub const CAP_SYS_ADMIN = 21;
+pub const CAP_SYS_BOOT = 22;
+pub const CAP_SYS_NICE = 23;
+pub const CAP_SYS_RESOURCE = 24;
+pub const CAP_SYS_TIME = 25;
+pub const CAP_SYS_TTY_CONFIG = 26;
+pub const CAP_MKNOD = 27;
+pub const CAP_LEASE = 28;
+pub const CAP_AUDIT_WRITE = 29;
+pub const CAP_AUDIT_CONTROL = 30;
+pub const CAP_SETFCAP = 31;
+pub const CAP_MAC_OVERRIDE = 32;
+pub const CAP_MAC_ADMIN = 33;
+pub const CAP_SYSLOG = 34;
+pub const CAP_WAKE_ALARM = 35;
+pub const CAP_BLOCK_SUSPEND = 36;
+pub const CAP_AUDIT_READ = 37;
+pub const CAP_LAST_CAP = CAP_AUDIT_READ;
+
+pub fn cap_valid(u8: x) bool {
+ return x >= 0 and x <= CAP_LAST_CAP;
+}
+
+pub fn CAP_TO_MASK(cap: u8) u32 {
+ return u32(1) << u5(cap & 31);
+}
+
+pub fn CAP_TO_INDEX(cap: u8) u8 {
+ return cap >> 5;
+}
+
+pub const cap_t = extern struct {
+ hdrp: &cap_user_header_t,
+ datap: &cap_user_data_t,
+};
+
+pub const cap_user_header_t = extern struct {
+ version: u32,
+ pid: usize,
+};
+
+pub const cap_user_data_t = extern struct {
+ effective: u32,
+ permitted: u32,
+ inheritable: u32,
+};
+
+pub fn unshare(flags: usize) usize {
+ return syscall1(SYS_unshare, usize(flags));
+}
+
+pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize {
+ return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap));
+}
+
+pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {
+ return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
}
test "import linux test" {
diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig
index 3a76ca4f87..cfb2231df9 100644
--- a/std/os/linux/x86_64.zig
+++ b/std/os/linux/x86_64.zig
@@ -488,3 +488,11 @@ pub const timespec = extern struct {
tv_sec: isize,
tv_nsec: isize,
};
+
+pub const dirent = extern struct {
+ d_ino: usize,
+ d_off: usize,
+ d_reclen: u16,
+ d_name: u8, // field address is the address of first byte of name
+};
+
diff --git a/std/os/test.zig b/std/os/test.zig
new file mode 100644
index 0000000000..9c718d5b6b
--- /dev/null
+++ b/std/os/test.zig
@@ -0,0 +1,25 @@
+const std = @import("../index.zig");
+const os = std.os;
+const assert = std.debug.assert;
+const io = std.io;
+
+const a = std.debug.global_allocator;
+
+const builtin = @import("builtin");
+
+test "makePath, put some files in it, deleteTree" {
+ if (builtin.os == builtin.Os.windows) {
+ // TODO implement os.Dir for windows
+ // https://github.com/zig-lang/zig/issues/709
+ return;
+ }
+ try os.makePath(a, "os_test_tmp/b/c");
+ try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");
+ try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");
+ try os.deleteTree(a, "os_test_tmp");
+ if (os.Dir.open(a, "os_test_tmp")) |dir| {
+ @panic("expected error");
+ } else |err| {
+ assert(err == error.PathNotFound);
+ }
+}
diff --git a/std/rand.zig b/std/rand.zig
deleted file mode 100644
index 96f0a385a4..0000000000
--- a/std/rand.zig
+++ /dev/null
@@ -1,240 +0,0 @@
-const std = @import("index.zig");
-const builtin = @import("builtin");
-const assert = std.debug.assert;
-const rand_test = @import("rand_test.zig");
-const mem = std.mem;
-const math = std.math;
-
-pub const MT19937_32 = MersenneTwister(
- u32, 624, 397, 31,
- 0x9908B0DF,
- 11, 0xFFFFFFFF,
- 7, 0x9D2C5680,
- 15, 0xEFC60000,
- 18, 1812433253);
-
-pub const MT19937_64 = MersenneTwister(
- u64, 312, 156, 31,
- 0xB5026F5AA96619E9,
- 29, 0x5555555555555555,
- 17, 0x71D67FFFEDA60000,
- 37, 0xFFF7EEE000000000,
- 43, 6364136223846793005);
-
-/// Use `init` to initialize this state.
-pub const Rand = struct {
- const Rng = if (@sizeOf(usize) >= 8) MT19937_64 else MT19937_32;
-
- rng: Rng,
-
- /// Initialize random state with the given seed.
- pub fn init(seed: usize) Rand {
- return Rand {
- .rng = Rng.init(seed),
- };
- }
-
- /// Get an integer or boolean with random bits.
- pub fn scalar(r: &Rand, comptime T: type) T {
- if (T == usize) {
- return r.rng.get();
- } else if (T == bool) {
- return (r.rng.get() & 0b1) == 0;
- } else {
- var result: [@sizeOf(T)]u8 = undefined;
- r.fillBytes(result[0..]);
- return mem.readInt(result, T, builtin.Endian.Little);
- }
- }
-
- /// Fill `buf` with randomness.
- pub fn fillBytes(r: &Rand, buf: []u8) void {
- var bytes_left = buf.len;
- while (bytes_left >= @sizeOf(usize)) {
- mem.writeInt(buf[buf.len - bytes_left..], r.rng.get(), builtin.Endian.Little);
- bytes_left -= @sizeOf(usize);
- }
- if (bytes_left > 0) {
- var rand_val_array: [@sizeOf(usize)]u8 = undefined;
- mem.writeInt(rand_val_array[0..], r.rng.get(), builtin.Endian.Little);
- while (bytes_left > 0) {
- buf[buf.len - bytes_left] = rand_val_array[@sizeOf(usize) - bytes_left];
- bytes_left -= 1;
- }
- }
- }
-
- /// Get a random unsigned integer with even distribution between `start`
- /// inclusive and `end` exclusive.
- pub fn range(r: &Rand, comptime T: type, start: T, end: T) T {
- assert(start <= end);
- if (T.is_signed) {
- const uint = @IntType(false, T.bit_count);
- if (start >= 0 and end >= 0) {
- return T(r.range(uint, uint(start), uint(end)));
- } else if (start < 0 and end < 0) {
- // Can't overflow because the range is over signed ints
- return math.negateCast(r.range(uint, math.absCast(end), math.absCast(start)) + 1) catch unreachable;
- } else if (start < 0 and end >= 0) {
- const end_uint = uint(end);
- const total_range = math.absCast(start) + end_uint;
- const value = r.range(uint, 0, total_range);
- const result = if (value < end_uint) x: {
- break :x T(value);
- } else if (value == end_uint) x: {
- break :x start;
- } else x: {
- // Can't overflow because the range is over signed ints
- break :x math.negateCast(value - end_uint) catch unreachable;
- };
- return result;
- } else {
- unreachable;
- }
- } else {
- const total_range = end - start;
- const leftover = @maxValue(T) % total_range;
- const upper_bound = @maxValue(T) - leftover;
- var rand_val_array: [@sizeOf(T)]u8 = undefined;
-
- while (true) {
- r.fillBytes(rand_val_array[0..]);
- const rand_val = mem.readInt(rand_val_array, T, builtin.Endian.Little);
- if (rand_val < upper_bound) {
- return start + (rand_val % total_range);
- }
- }
- }
- }
-
- /// Get a floating point value in the range 0.0..1.0.
- pub fn float(r: &Rand, comptime T: type) T {
- // TODO Implement this way instead:
- // const int = @int_type(false, @sizeOf(T) * 8);
- // const mask = ((1 << @float_mantissa_bit_count(T)) - 1);
- // const rand_bits = r.rng.scalar(int) & mask;
- // return @float_compose(T, false, 0, rand_bits) - 1.0
- const int_type = @IntType(false, @sizeOf(T) * 8);
- const precision = if (T == f32)
- 16777216
- else if (T == f64)
- 9007199254740992
- else
- @compileError("unknown floating point type")
- ;
- return T(r.range(int_type, 0, precision)) / T(precision);
- }
-};
-
-fn MersenneTwister(
- comptime int: type, comptime n: usize, comptime m: usize, comptime r: int,
- comptime a: int,
- comptime u: math.Log2Int(int), comptime d: int,
- comptime s: math.Log2Int(int), comptime b: int,
- comptime t: math.Log2Int(int), comptime c: int,
- comptime l: math.Log2Int(int), comptime f: int) type
-{
- return struct {
- const Self = this;
-
- array: [n]int,
- index: usize,
-
- pub fn init(seed: int) Self {
- var mt = Self {
- .array = undefined,
- .index = n,
- };
-
- var prev_value = seed;
- mt.array[0] = prev_value;
- var i: usize = 1;
- while (i < n) : (i += 1) {
- prev_value = int(i) +% f *% (prev_value ^ (prev_value >> (int.bit_count - 2)));
- mt.array[i] = prev_value;
- }
- return mt;
- }
-
- pub fn get(mt: &Self) int {
- const mag01 = []int{0, a};
- const LM: int = (1 << r) - 1;
- const UM = ~LM;
-
- if (mt.index >= n) {
- var i: usize = 0;
-
- while (i < n - m) : (i += 1) {
- const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
- mt.array[i] = mt.array[i + m] ^ (x >> 1) ^ mag01[usize(x & 0x1)];
- }
-
- while (i < n - 1) : (i += 1) {
- const x = (mt.array[i] & UM) | (mt.array[i + 1] & LM);
- mt.array[i] = mt.array[i + m - n] ^ (x >> 1) ^ mag01[usize(x & 0x1)];
-
- }
- const x = (mt.array[i] & UM) | (mt.array[0] & LM);
- mt.array[i] = mt.array[m - 1] ^ (x >> 1) ^ mag01[usize(x & 0x1)];
-
- mt.index = 0;
- }
-
- var x = mt.array[mt.index];
- mt.index += 1;
-
- x ^= ((x >> u) & d);
- x ^= ((x << s) & b);
- x ^= ((x << t) & c);
- x ^= (x >> l);
-
- return x;
- }
- };
-}
-
-test "rand float 32" {
- var r = Rand.init(42);
- var i: usize = 0;
- while (i < 1000) : (i += 1) {
- const val = r.float(f32);
- assert(val >= 0.0);
- assert(val < 1.0);
- }
-}
-
-test "rand.MT19937_64" {
- var rng = MT19937_64.init(rand_test.mt64_seed);
- for (rand_test.mt64_data) |value| {
- assert(value == rng.get());
- }
-}
-
-test "rand.MT19937_32" {
- var rng = MT19937_32.init(rand_test.mt32_seed);
- for (rand_test.mt32_data) |value| {
- assert(value == rng.get());
- }
-}
-
-test "rand.Rand.range" {
- var r = Rand.init(42);
- testRange(&r, -4, 3);
- testRange(&r, -4, -1);
- testRange(&r, 10, 14);
-}
-
-fn testRange(r: &Rand, start: i32, end: i32) void {
- const count = usize(end - start);
- var values_buffer = []bool{false} ** 20;
- const values = values_buffer[0..count];
- var i: usize = 0;
- while (i < count) {
- const value = r.range(i32, start, end);
- const index = usize(value - start);
- if (!values[index]) {
- i += 1;
- values[index] = true;
- }
- }
-}
diff --git a/std/rand/index.zig b/std/rand/index.zig
new file mode 100644
index 0000000000..6a746fce92
--- /dev/null
+++ b/std/rand/index.zig
@@ -0,0 +1,652 @@
+// The engines provided here should be initialized from an external source. For now, getRandomBytes
+// from the os package is the most suitable. Be sure to use a CSPRNG when required, otherwise using
+// a normal PRNG will be faster and use substantially less stack space.
+//
+// ```
+// var buf: [8]u8 = undefined;
+// try std.os.getRandomBytes(buf[0..]);
+// const seed = mem.readInt(buf[0..8], u64, builtin.Endian.Little);
+//
+// var r = DefaultPrng.init(seed);
+//
+// const s = r.random.scalar(u64);
+// ```
+//
+// TODO(tiehuis): Benchmark these against other reference implementations.
+
+const std = @import("../index.zig");
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+const mem = std.mem;
+const math = std.math;
+
+// When you need fast unbiased random numbers
+pub const DefaultPrng = Xoroshiro128;
+
+// When you need cryptographically secure random numbers
+pub const DefaultCsprng = Isaac64;
+
+pub const Random = struct {
+ fillFn: fn(r: &Random, buf: []u8) void,
+
+ /// Read random bytes into the specified buffer until fill.
+ pub fn bytes(r: &Random, buf: []u8) void {
+ r.fillFn(r, buf);
+ }
+
+ /// Return a random integer/boolean type.
+ pub fn scalar(r: &Random, comptime T: type) T {
+ var rand_bytes: [@sizeOf(T)]u8 = undefined;
+ r.bytes(rand_bytes[0..]);
+
+ if (T == bool) {
+ return rand_bytes[0] & 0b1 == 0;
+ } else {
+ // NOTE: Cannot @bitCast array to integer type.
+ return mem.readInt(rand_bytes, T, builtin.Endian.Little);
+ }
+ }
+
+ /// Get a random unsigned integer with even distribution between `start`
+ /// inclusive and `end` exclusive.
+ pub fn range(r: &Random, comptime T: type, start: T, end: T) T {
+ assert(start <= end);
+ if (T.is_signed) {
+ const uint = @IntType(false, T.bit_count);
+ if (start >= 0 and end >= 0) {
+ return T(r.range(uint, uint(start), uint(end)));
+ } else if (start < 0 and end < 0) {
+ // Can't overflow because the range is over signed ints
+ return math.negateCast(r.range(uint, math.absCast(end), math.absCast(start)) + 1) catch unreachable;
+ } else if (start < 0 and end >= 0) {
+ const end_uint = uint(end);
+ const total_range = math.absCast(start) + end_uint;
+ const value = r.range(uint, 0, total_range);
+ const result = if (value < end_uint) x: {
+ break :x T(value);
+ } else if (value == end_uint) x: {
+ break :x start;
+ } else x: {
+ // Can't overflow because the range is over signed ints
+ break :x math.negateCast(value - end_uint) catch unreachable;
+ };
+ return result;
+ } else {
+ unreachable;
+ }
+ } else {
+ const total_range = end - start;
+ const leftover = @maxValue(T) % total_range;
+ const upper_bound = @maxValue(T) - leftover;
+ var rand_val_array: [@sizeOf(T)]u8 = undefined;
+
+ while (true) {
+ r.bytes(rand_val_array[0..]);
+ const rand_val = mem.readInt(rand_val_array, T, builtin.Endian.Little);
+ if (rand_val < upper_bound) {
+ return start + (rand_val % total_range);
+ }
+ }
+ }
+ }
+
+ /// Return a floating point value evenly distributed in the range [0, 1).
+ pub fn float(r: &Random, comptime T: type) T {
+ // Generate a uniform value between [1, 2) and scale down to [0, 1).
+ // Note: The lowest mantissa bit is always set to 0 so we only use half the available range.
+ switch (T) {
+ f32 => {
+ const s = r.scalar(u32);
+ const repr = (0x7f << 23) | (s >> 9);
+ return @bitCast(f32, repr) - 1.0;
+ },
+ f64 => {
+ const s = r.scalar(u64);
+ const repr = (0x3ff << 52) | (s >> 12);
+ return @bitCast(f64, repr) - 1.0;
+ },
+ else => @compileError("unknown floating point type"),
+ }
+ }
+
+ /// Return a floating point value normally distributed in the range [0, 1].
+ pub fn floatNorm(r: &Random, comptime T: type) T {
+ // TODO(tiehuis): See https://www.doornik.com/research/ziggurat.pdf
+ @compileError("floatNorm is unimplemented");
+ }
+
+ /// Return a exponentially distributed float between (0, @maxValue(f64))
+ pub fn floatExp(r: &Random, comptime T: type) T {
+ @compileError("floatExp is unimplemented");
+ }
+
+ /// Shuffle a slice into a random order.
+ pub fn shuffle(r: &Random, comptime T: type, buf: []T) void {
+ if (buf.len < 2) {
+ return;
+ }
+
+ var i: usize = 0;
+ while (i < buf.len - 1) : (i += 1) {
+ const j = r.range(usize, i, buf.len);
+ mem.swap(T, &buf[i], &buf[j]);
+ }
+ }
+};
+
+// Generator to extend 64-bit seed values into longer sequences.
+//
+// The number of cycles is thus limited to 64-bits regardless of the engine, but this
+// is still plenty for practical purposes.
+const SplitMix64 = struct {
+ s: u64,
+
+ pub fn init(seed: u64) SplitMix64 {
+ return SplitMix64 { .s = seed };
+ }
+
+ pub fn next(self: &SplitMix64) u64 {
+ self.s +%= 0x9e3779b97f4a7c15;
+
+ var z = self.s;
+ z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9;
+ z = (z ^ (z >> 27)) *% 0x94d049bb133111eb;
+ return z ^ (z >> 31);
+ }
+};
+
+test "splitmix64 sequence" {
+ var r = SplitMix64.init(0xaeecf86f7878dd75);
+
+ const seq = []const u64 {
+ 0x5dbd39db0178eb44,
+ 0xa9900fb66b397da3,
+ 0x5c1a28b1aeebcf5c,
+ 0x64a963238f776912,
+ 0xc6d4177b21d1c0ab,
+ 0xb2cbdbdb5ea35394,
+ };
+
+ for (seq) |s| {
+ std.debug.assert(s == r.next());
+ }
+}
+
+// PCG32 - http://www.pcg-random.org/
+//
+// PRNG
+pub const Pcg = struct {
+ const default_multiplier = 6364136223846793005;
+
+ random: Random,
+
+ s: u64,
+ i: u64,
+
+ pub fn init(init_s: u64) Pcg {
+ var pcg = Pcg {
+ .random = Random { .fillFn = fill },
+ .s = undefined,
+ .i = undefined,
+ };
+
+ pcg.seed(init_s);
+ return pcg;
+ }
+
+ fn next(self: &Pcg) u32 {
+ const l = self.s;
+ self.s = l *% default_multiplier +% (self.i | 1);
+
+ const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27);
+ const rot = u32(l >> 59);
+
+ return (xor_s >> u5(rot)) | (xor_s << u5((0 -% rot) & 31));
+ }
+
+ fn seed(self: &Pcg, init_s: u64) void {
+ // Pcg requires 128-bits of seed.
+ var gen = SplitMix64.init(init_s);
+ self.seedTwo(gen.next(), gen.next());
+ }
+
+ fn seedTwo(self: &Pcg, init_s: u64, init_i: u64) void {
+ self.s = 0;
+ self.i = (init_s << 1) | 1;
+ self.s = self.s *% default_multiplier +% self.i;
+ self.s +%= init_i;
+ self.s = self.s *% default_multiplier +% self.i;
+ }
+
+ fn fill(r: &Random, buf: []u8) void {
+ const self = @fieldParentPtr(Pcg, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Complete 4 byte segments.
+ while (i < aligned_len) : (i += 4) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 4) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Remaining. (cuts the stream)
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 4;
+ }
+ }
+ }
+};
+
+test "pcg sequence" {
+ var r = Pcg.init(0);
+ const s0: u64 = 0x9394bf54ce5d79de;
+ const s1: u64 = 0x84e9c579ef59bbf7;
+ r.seedTwo(s0, s1);
+
+ const seq = []const u32 {
+ 2881561918,
+ 3063928540,
+ 1199791034,
+ 2487695858,
+ 1479648952,
+ 3247963454,
+ };
+
+ for (seq) |s| {
+ std.debug.assert(s == r.next());
+ }
+}
+
+// Xoroshiro128+ - http://xoroshiro.di.unimi.it/
+//
+// PRNG
+pub const Xoroshiro128 = struct {
+ random: Random,
+
+ s: [2]u64,
+
+ pub fn init(init_s: u64) Xoroshiro128 {
+ var x = Xoroshiro128 {
+ .random = Random { .fillFn = fill },
+ .s = undefined,
+ };
+
+ x.seed(init_s);
+ return x;
+ }
+
+ fn next(self: &Xoroshiro128) u64 {
+ const s0 = self.s[0];
+ var s1 = self.s[1];
+ const r = s0 +% s1;
+
+ s1 ^= s0;
+ self.s[0] = math.rotl(u64, s0, u8(55)) ^ s1 ^ (s1 << 14);
+ self.s[1] = math.rotl(u64, s1, u8(36));
+
+ return r;
+ }
+
+ // Skip 2^64 places ahead in the sequence
+ fn jump(self: &Xoroshiro128) void {
+ var s0: u64 = 0;
+ var s1: u64 = 0;
+
+ const table = []const u64 {
+ 0xbeac0467eba5facb,
+ 0xd86b048b86aa9922
+ };
+
+ inline for (table) |entry| {
+ var b: usize = 0;
+ while (b < 64) : (b += 1) {
+ if ((entry & (u64(1) << u6(b))) != 0) {
+ s0 ^= self.s[0];
+ s1 ^= self.s[1];
+ }
+ _ = self.next();
+ }
+ }
+
+ self.s[0] = s0;
+ self.s[1] = s1;
+ }
+
+ fn seed(self: &Xoroshiro128, init_s: u64) void {
+ // Xoroshiro requires 128-bits of seed.
+ var gen = SplitMix64.init(init_s);
+
+ self.s[0] = gen.next();
+ self.s[1] = gen.next();
+ }
+
+ fn fill(r: &Random, buf: []u8) void {
+ const self = @fieldParentPtr(Xoroshiro128, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Complete 8 byte segments.
+ while (i < aligned_len) : (i += 8) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 8) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Remaining. (cuts the stream)
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+ }
+};
+
+test "xoroshiro sequence" {
+ var r = Xoroshiro128.init(0);
+ r.s[0] = 0xaeecf86f7878dd75;
+ r.s[1] = 0x01cd153642e72622;
+
+ const seq1 = []const u64 {
+ 0xb0ba0da5bb600397,
+ 0x18a08afde614dccc,
+ 0xa2635b956a31b929,
+ 0xabe633c971efa045,
+ 0x9ac19f9706ca3cac,
+ 0xf62b426578c1e3fb,
+ };
+
+ for (seq1) |s| {
+ std.debug.assert(s == r.next());
+ }
+
+
+ r.jump();
+
+ const seq2 = []const u64 {
+ 0x95344a13556d3e22,
+ 0xb4fb32dafa4d00df,
+ 0xb2011d9ccdcfe2dd,
+ 0x05679a9b2119b908,
+ 0xa860a1da7c9cd8a0,
+ 0x658a96efe3f86550,
+ };
+
+ for (seq2) |s| {
+ std.debug.assert(s == r.next());
+ }
+}
+
+// ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html
+//
+// CSPRNG
+//
+// Follows the general idea of the implementation from here with a few shortcuts.
+// https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html
+pub const Isaac64 = struct {
+ random: Random,
+
+ r: [256]u64,
+ m: [256]u64,
+ a: u64,
+ b: u64,
+ c: u64,
+ i: usize,
+
+ pub fn init(init_s: u64) Isaac64 {
+ var isaac = Isaac64 {
+ .random = Random { .fillFn = fill },
+ .r = undefined,
+ .m = undefined,
+ .a = undefined,
+ .b = undefined,
+ .c = undefined,
+ .i = undefined,
+ };
+
+ // seed == 0 => same result as the unseeded reference implementation
+ isaac.seed(init_s, 1);
+ return isaac;
+ }
+
+ fn step(self: &Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void {
+ const x = self.m[base + m1];
+ self.a = mix +% self.m[base + m2];
+
+ const y = self.a +% self.b +% self.m[(x >> 3) % self.m.len];
+ self.m[base + m1] = y;
+
+ self.b = x +% self.m[(y >> 11) % self.m.len];
+ self.r[self.r.len - 1 - base - m1] = self.b;
+ }
+
+ fn refill(self: &Isaac64) void {
+ const midpoint = self.r.len / 2;
+
+ self.c +%= 1;
+ self.b +%= self.c;
+
+ {
+ var i: usize = 0;
+ while (i < midpoint) : (i += 4) {
+ self.step( ~(self.a ^ (self.a << 21)), i + 0, 0, midpoint);
+ self.step( self.a ^ (self.a >> 5) , i + 1, 0, midpoint);
+ self.step( self.a ^ (self.a << 12) , i + 2, 0, midpoint);
+ self.step( self.a ^ (self.a >> 33) , i + 3, 0, midpoint);
+ }
+ }
+
+ {
+ var i: usize = 0;
+ while (i < midpoint) : (i += 4) {
+ self.step( ~(self.a ^ (self.a << 21)), i + 0, midpoint, 0);
+ self.step( self.a ^ (self.a >> 5) , i + 1, midpoint, 0);
+ self.step( self.a ^ (self.a << 12) , i + 2, midpoint, 0);
+ self.step( self.a ^ (self.a >> 33) , i + 3, midpoint, 0);
+ }
+ }
+
+ self.i = 0;
+ }
+
+ fn next(self: &Isaac64) u64 {
+ if (self.i >= self.r.len) {
+ self.refill();
+ }
+
+ const value = self.r[self.i];
+ self.i += 1;
+ return value;
+ }
+
+ fn seed(self: &Isaac64, init_s: u64, comptime rounds: usize) void {
+ // We ignore the multi-pass requirement since we don't currently expose full access to
+ // seeding the self.m array completely.
+ mem.set(u64, self.m[0..], 0);
+ self.m[0] = init_s;
+
+ // prescrambled golden ratio constants
+ var a = []const u64 {
+ 0x647c4677a2884b7c,
+ 0xb9f8b322c73ac862,
+ 0x8c0ea5053d4712a0,
+ 0xb29b2e824a595524,
+ 0x82f053db8355e0ce,
+ 0x48fe4a0fa5a09315,
+ 0xae985bf2cbfc89ed,
+ 0x98f5704f6c44c0ab,
+ };
+
+ comptime var i: usize = 0;
+ inline while (i < rounds) : (i += 1) {
+ var j: usize = 0;
+ while (j < self.m.len) : (j += 8) {
+ comptime var x1: usize = 0;
+ inline while (x1 < 8) : (x1 += 1) {
+ a[x1] +%= self.m[j + x1];
+ }
+
+ a[0] -%= a[4]; a[5] ^= a[7] >> 9; a[7] +%= a[0];
+ a[1] -%= a[5]; a[6] ^= a[0] << 9; a[0] +%= a[1];
+ a[2] -%= a[6]; a[7] ^= a[1] >> 23; a[1] +%= a[2];
+ a[3] -%= a[7]; a[0] ^= a[2] << 15; a[2] +%= a[3];
+ a[4] -%= a[0]; a[1] ^= a[3] >> 14; a[3] +%= a[4];
+ a[5] -%= a[1]; a[2] ^= a[4] << 20; a[4] +%= a[5];
+ a[6] -%= a[2]; a[3] ^= a[5] >> 17; a[5] +%= a[6];
+ a[7] -%= a[3]; a[4] ^= a[6] << 14; a[6] +%= a[7];
+
+ comptime var x2: usize = 0;
+ inline while (x2 < 8) : (x2 += 1) {
+ self.m[j + x2] = a[x2];
+ }
+ }
+ }
+
+ mem.set(u64, self.r[0..], 0);
+ self.a = 0;
+ self.b = 0;
+ self.c = 0;
+ self.i = self.r.len; // trigger refill on first value
+ }
+
+ fn fill(r: &Random, buf: []u8) void {
+ const self = @fieldParentPtr(Isaac64, "random", r);
+
+ var i: usize = 0;
+ const aligned_len = buf.len - (buf.len & 7);
+
+ // Fill complete 64-byte segments
+ while (i < aligned_len) : (i += 8) {
+ var n = self.next();
+ comptime var j: usize = 0;
+ inline while (j < 8) : (j += 1) {
+ buf[i + j] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+
+ // Fill trailing, ignoring excess (cut the stream).
+ if (i != buf.len) {
+ var n = self.next();
+ while (i < buf.len) : (i += 1) {
+ buf[i] = @truncate(u8, n);
+ n >>= 8;
+ }
+ }
+ }
+};
+
+test "isaac64 sequence" {
+ var r = Isaac64.init(0);
+
+ // from reference implementation
+ const seq = []const u64 {
+ 0xf67dfba498e4937c,
+ 0x84a5066a9204f380,
+ 0xfee34bd5f5514dbb,
+ 0x4d1664739b8f80d6,
+ 0x8607459ab52a14aa,
+ 0x0e78bc5a98529e49,
+ 0xfe5332822ad13777,
+ 0x556c27525e33d01a,
+ 0x08643ca615f3149f,
+ 0xd0771faf3cb04714,
+ 0x30e86f68a37b008d,
+ 0x3074ebc0488a3adf,
+ 0x270645ea7a2790bc,
+ 0x5601a0a8d3763c6a,
+ 0x2f83071f53f325dd,
+ 0xb9090f3d42d2d2ea,
+ };
+
+ for (seq) |s| {
+ std.debug.assert(s == r.next());
+ }
+}
+
+// Actual Random helper function tests, pcg engine is assumed correct.
+test "Random float" {
+ var prng = DefaultPrng.init(0);
+
+ var i: usize = 0;
+ while (i < 1000) : (i += 1) {
+ const val1 = prng.random.float(f32);
+ std.debug.assert(val1 >= 0.0);
+ std.debug.assert(val1 < 1.0);
+
+ const val2 = prng.random.float(f64);
+ std.debug.assert(val2 >= 0.0);
+ std.debug.assert(val2 < 1.0);
+ }
+}
+
+test "Random scalar" {
+ var prng = DefaultPrng.init(0);
+ const s = prng .random.scalar(u64);
+}
+
+test "Random bytes" {
+ var prng = DefaultPrng.init(0);
+ var buf: [2048]u8 = undefined;
+ prng.random.bytes(buf[0..]);
+}
+
+test "Random shuffle" {
+ var prng = DefaultPrng.init(0);
+
+ var seq = []const u8 { 0, 1, 2, 3, 4 };
+ var seen = []bool {false} ** 5;
+
+ var i: usize = 0;
+ while (i < 1000) : (i += 1) {
+ prng.random.shuffle(u8, seq[0..]);
+ seen[seq[0]] = true;
+ std.debug.assert(sumArray(seq[0..]) == 10);
+ }
+
+ // we should see every entry at the head at least once
+ for (seen) |e| {
+ std.debug.assert(e == true);
+ }
+}
+
+fn sumArray(s: []const u8) u32 {
+ var r: u32 = 0;
+ for (s) |e| r += e;
+ return r;
+}
+
+test "Random range" {
+ var prng = DefaultPrng.init(0);
+ testRange(&prng.random, -4, 3);
+ testRange(&prng.random, -4, -1);
+ testRange(&prng.random, 10, 14);
+}
+
+fn testRange(r: &Random, start: i32, end: i32) void {
+ const count = usize(end - start);
+ var values_buffer = []bool{false} ** 20;
+ const values = values_buffer[0..count];
+ var i: usize = 0;
+ while (i < count) {
+ const value = r.range(i32, start, end);
+ const index = usize(value - start);
+ if (!values[index]) {
+ i += 1;
+ values[index] = true;
+ }
+ }
+}
diff --git a/std/rand_test.zig b/std/rand_test.zig
deleted file mode 100644
index 200b50a5b6..0000000000
--- a/std/rand_test.zig
+++ /dev/null
@@ -1,507 +0,0 @@
-pub const mt64_seed = 0xb334e49d0977c37e;
-pub const mt64_data = []u64 {
- 0x2ad1a63fab6d25a9, 0xb7143aba12569814, 0xc1b60d8d49e53e8, 0x5652adfc8da656dc,
- 0x43e3beb6d9e484a9, 0x17b09b71e9418ff7, 0x541646292686cfa4, 0x260457071268ecfc,
- 0x1627af31774e1dc1, 0x362a49b34ed75bb3, 0x7acf72002fe0f733, 0x3aaaf2e7b9409452,
- 0x9cfc2d9908115c2, 0x6e81a7f16ae613e9, 0xfc4da89c04acf3c7, 0x6984b6adb4feb9ae,
- 0x6a128b334e27b03d, 0xcc45a2b02937871a, 0xe585b229e00b2283, 0x7a92c0664a6f678a,
- 0x972735011bdc0744, 0xb494e743d658a084, 0x1eda3c4e7b1b2d0c, 0x4c7adb3831d87332,
- 0x12f8c7355f5ec631, 0xfc2bcb6be7d60eba, 0x74b95b47895f8687, 0x171de6fe92b97f5a,
- 0x86383730f52719ac, 0xe4e43ce0f61274f6, 0x514f7e072d96f19c, 0xabef324fbc6cb7fe,
- 0x7534b945b742f14f, 0x47f9efe33265adbe, 0x7bcab027a0abf16b, 0x3312a2b34225bff7,
- 0xb61455ce8c2e3e0b, 0x2b81008deeee4d94, 0x743b0b2b4974c7b6, 0xfc219101fd665f7d,
- 0x863d78891cbfe5e5, 0x7531bb1839181778, 0xf614359a65356e72, 0xfcdd1f6e3f250bdd,
- 0x528e10bd536eed5b, 0x9f69386ac60cd618, 0xbf5f242706817f01, 0x8e7da8070072cf64,
- 0xaa318b622da0667f, 0xc9580540fb7efd66, 0xb5d996deccd02e0e, 0x81c6a799ea3f5a41,
- 0xe6b896f4e21a8550, 0xde5206f177a24ceb, 0x53343e81639ec0b6, 0x5a6edb63d08be9f6,
- 0x3602c2892f7da1b9, 0xda84f4259841bdea, 0x5880e169a7746e45, 0x57cddb5ffd3c2423,
- 0x28fe1166fe7b8595, 0x92136c9decb42243, 0xa8c4199818ca7d62, 0x5042cd96f62854dd,
- 0x22b2d38c3f21f8d0, 0x73a2bfccf1b5f7bb, 0xaba3718f40b6984e, 0x9c1f5dc3e399f5f0,
- 0x9cf463f95369c149, 0xa7546d69e4232e18, 0x9ea57317d19ab7fc, 0xfb13c83d830731fb,
- 0x635a123eaa099259, 0x9a2fe7d0ba6e3c5c, 0x40b903cd0d0d3b4e, 0xc8210eb2d2e941cb,
- 0xd2582d4b1e016484, 0x1d048030875af39c, 0xb51c31a6c193d76f, 0x5ce9b801b8d61626,
- 0x2bae30455cbb0022, 0xba54df5998b2443f, 0x927abb9342c9a90a, 0xc431eb7df3e06727,
- 0x726f885d3da88d5, 0x7d85ff1ca4260280, 0xaf3fecf8f019817, 0x31d39105d6fc4fe8,
- 0x262d9842dbafd4bd, 0x54c28a2876e62e39, 0x95a986e24e214dde, 0xbf677a1abd2e553,
- 0x48ac890ff787b2b6, 0x2890ec1c67c539f4, 0x7ce88bf3975882c3, 0x88ef340414a29c88,
- 0xe30de9c88a00805b, 0xe772225e3ee6c68a, 0xa3a7d0921c5d5816, 0x8354957227b1663f,
- 0xb5b65ded7c747cbc, 0x93b4a12ff2e8fcea, 0x6359579c3c438b3c, 0x45b2c12e9722f2bd,
- 0x659a604414f19e1, 0x4ec9a149d4219ca5, 0xd830290dd6aebe2b, 0xabc7874a6b4827f8,
- 0xc91be5dd875847e7, 0x5b761d39f3f96aee, 0x5749dffad692b6c8, 0x86c94840cbd249d2,
- 0x411a466e886ad7, 0x27dca1f51aebb9a0, 0x1cceb093fbab7a42, 0x7140c2d6706e927c,
- 0x6881fdb87299a92f, 0xa81a28de171f3c47, 0x8fa9a1b3bb5dfb2a, 0xae076853e3e0abde,
- 0xe76572308ecd6b54, 0x6cd926c2e2760d8a, 0xdf080266cfbe3dc3, 0xb99b961999765d7b,
- 0xadb5d4e2b896ddf1, 0x8d3aaf4c83c83c56, 0x9b66e4f6eb65bef7, 0x7a81c3bf785eb1df,
- 0xc53f02b3e8c38647, 0xcdfeb25ee787759d, 0xead5e734d64ab5f6, 0x7930d87af1072499,
- 0xf30690a71d88ad6e, 0x73c347923c84728a, 0x3f2b588221003fe4, 0xe747052d0b453af2,
- 0xabe6fa70539b5edf, 0x4db6d1530d628c2, 0x4ec929af434eb1b0, 0x15afbe39886181ff,
- 0xa9141b9c89a07b80, 0x7d33f966c6232057, 0x8ddcb412f34a491d, 0xa74472b8ecc1e2f2,
- 0x34d745de1cb7de2e, 0x6cf67091309e5e93, 0xfb25004efa59450a, 0x3355947066522286,
- 0x5a8cffdf079dac21, 0x419445d6e6825887, 0x6e9c064f84381dcf, 0xbbcaf462a3a8ad76,
- 0x75836c68d6c1a13e, 0xf38141565d5c3759, 0x8c65989142ffa802, 0x106067ec26e6463,
- 0xadfac5e3a80de9c2, 0xc48b16e2df25b9f2, 0xa3257889c33669e6, 0x5bc760d4d65a1745,
- 0x303cd31fead81139, 0xfb97f78cade31e1d, 0xb888b8e05820a469, 0x7ebb8e44d47f54d9,
- 0xce76cdbb5ecdc529, 0xb1eb29949a099d52, 0xb6affc1b240a7eb3, 0x22977eac542906f1,
- 0x9b105b391ff729df, 0x83371186b2834968, 0xd5b893f382ea9e90, 0x5d17aa80a1fd4854,
- 0xe8ed8525eb29210c, 0xc789f3cd36c4dae3, 0x556e50a5f46b73fb, 0xef1d129b523dff77,
- 0x851de0f53f6707f9, 0x8deeeadfb1fa8bfc, 0x3d8c89c0e08c4f2e, 0xecfaaea537123333,
- 0x5bc9053d2dfd7669, 0x408c5bb2e880a9a2, 0x495726b3f3248219, 0x2b23cca4a6ea1ccc,
- 0x1df3663045092d61, 0xaf977a46e965e45b, 0x43a2facfff7f97e, 0x9b7714344c7b51e,
- 0x35643b24efb0559a, 0x502820785dc1af13, 0xbf82d2775b46433d, 0x1db626f2e16ca66,
- 0x744b031447c1e27d, 0x99e79898612f4606, 0xda02a728d234821f, 0xcf00c6fbb637a6e9,
- 0x242f2963196fd8b, 0x7aed8efc2dd562bb, 0x6204fb5d3dc6208a, 0x3e84861182fc7f6,
- 0xd14c4ee5aeef5c7a, 0x3749fbef94378dc1, 0x8fe710ec5cfc8566, 0xf43d7e495d5384d7,
- 0x8ff6396f1f1ce7c4, 0xf1252a6b6f86b42c, 0xddbbd098d6dca83f, 0x4e228724a227232a,
- 0x92a5a52ba2b24fa8, 0xdfc172b03fde669c, 0x34ee55adf7f0711c, 0x21d181e79b8000bc,
- 0xc788b1f48b37b693, 0x544fc4cfed0e0f92, 0xafca0c6de41789cc, 0xbb37bb5107ef97f8,
- 0xb9d62bf1dc0f6c95, 0xc78b5a36110dfb1, 0x1d615b658f39657e, 0x2bb2cd04cabcc360,
- 0xe563488ece6362f0, 0x213b56ce006fecc6, 0xc38207089fed0270, 0xa33199ff4a51d095,
- 0x1802ad28fb1896b1, 0xbead8f18c164a332, 0xceb5149101aa450f, 0x39ad89851ee8b62a,
- 0x317229aafabf37c4, 0xee68b8b9bf3520b3, 0xe4db499288350f04, 0xf8ea27feddb0ae7a,
- 0xa17235067b489c42, 0xbf4a570245f95d78, 0x8065c67e1d1537ab, 0xbd9357fb1b30aee5,
- 0xc224166ebeb24c42, 0xf9baf8ccd01b53bf, 0x5c13775c3fea8038, 0x4ea66f6d650ce62d,
- 0x470592ed81c140f2, 0xc2d0eb6f7999321f, 0x85d762f20290dc0c, 0x9f7d0d13936f6e78,
- 0x41f1fd2d20f2d62d, 0x891cb19ce1af2c2f, 0xe7ff34c3b29c3719, 0x246743f43126c69c,
- 0xea4b2da3195ebab9, 0x4831e4de995187dc, 0x7fb8969bbee45ce5, 0xe35b483da73c44ed,
- 0xf89158ca9af36227, 0x9fc7f34a35469a7a, 0xd02483ebca6564e7, 0xca00da156aaeda03,
- 0x303d1514646822f1, 0x226832ae582b8eac, 0xf772d719e413504e, 0x87603b928c068ab1,
- 0x4dc1552230e9b883, 0xef8c5e9db946fc87, 0x935581290bf7a4ee, 0xca632d2c7674bf2,
- 0xd8a3933b80d39efd, 0xf026574d0ffee6fb, 0xe4412d0dcd2fe94f, 0x668916490a2983ec,
- 0x73b1fe84a995718, 0x729bedefe21cc0e7, 0xd3a770f1c683b98f, 0x5d597a96323a10c2,
- 0xfbb7834bbf5fed23, 0xf3546c805a42ccdd, 0x9ef3e2164bb31a0c, 0x388363ce6c6c2253,
- 0x8120f4a949f017cb, 0x925a61942bbd3d10, 0xa03182d8599c0521, 0x2412e23004b40ebb,
- 0x35010a126bf2aecc, 0x21147869a1a84ca7, 0x53cba503b6127b98, 0x10c89dd62ab3591c,
- 0xf4c7f84faaf9f5f1, 0x8b4a37a2e844b97b, 0x23ddeb236a0bd9af, 0x4fd51d7207f49e62,
- 0x6cdab447c27706b2, 0x9e8f54b9a2d1a790, 0x191aed85d4d77087, 0xf74ecf5015265af6,
- 0x45925e25404922a1, 0xcf5467a0f5b42b98, 0x73590809c85c728c, 0xc16beeda74a1a1b8,
- 0xc3bbe7999803dd6a, 0x1a368bb32eec184, 0xafad2d86b7bb574f, 0xdc7c7b8960dc921a,
- 0xd9b68d854f5e0ae1, 0xe9e1a6a16efe0bea, 0x304a15bd6ca1cb14, 0x713ce3144e0af4b9,
- 0xc50eb410981be1d2, 0xb0fba6119bf7a300, 0x7a107296731fd314, 0xdd764898a90042b3,
- 0x8a69973262da3bc8, 0x29c6b9d048596c44, 0x62581bd20da76f1b, 0x4d1a3941d6d3e4bb,
- 0x19c447306245055, 0xb2978afcd04ba357, 0x7c01cdefcfe24432, 0xc4b268314411deae,
- 0x5ba56d49da714765, 0x33299186ac6dfd09, 0xede087aec096ef0d, 0xf758da2c7bcf9ddb,
- 0x5ea6c40d56824cdd, 0x121ff879d6ba905b, 0xb5fed0c42b616f5c, 0x21029cdc347de152,
- 0xb251d93f4cd7bf4a, 0xaedcb2dc6402cf13, 0x840e5e0d96e89407, 0x6a92fd328efcc6ef,
- 0xc63f5d8f6fcadcbd, 0x405bd64d1621128e, 0xe1318888172f58ed, 0x1009c9764d49da2b,
- 0x4bc0592cbfdf9f91, 0x972f0e080dfecd02, 0xa1cb961958eeb6aa, 0x6ee6467ad8c20aca,
- 0xb3f1c738390d6a83, 0x6504eb7ac498650d, 0xdf7dda67f198f59f, 0x72615652e56a82c3,
- 0x85e0fa2dfb51755a, 0x98b1f92a3d2ad940, 0xce81d51d875c0045, 0x437004d0be0a4d69,
- 0x64065895526f896c, 0xe1e1fea920785d49, 0x7d507ffd56fda19a, 0x17309b625cecb42,
- 0x67b6d83f0fd0f572, 0x5178665a5bcd38f4, 0x3c49fda2d35a8606, 0x7f058d2cb0ad351c,
- 0xfb95691559245416, 0xc991b857662b1b9f, 0x9e6d0f4e19774f96, 0x26cc7502212ca578,
- 0x3466110f03225e49, 0x2ae4958375eab9f3, 0x939a8d94c8871191, 0xa27356548ac6b28d,
- 0xacf86d43ec3aa030, 0xe0d16c7fa0b13a8c, 0x408ec2b2f8da531b, 0xde72494115ee4e83,
- 0xd26d7f79a02c5b1e, 0x2b6f835520c97f6a, 0x1f30f008b109ae7d, 0x698dbf9acaa222f1,
- 0xbd55de40838376d5, 0xebe53822cec7eb80, 0x7ce900793008d2bc, 0x494fc7d10e8331bb,
- 0x53509b90bdb7d588, 0x62aa920e9554b2f2, 0xe103098542011b6f, 0xeb722b9523d68af8,
- 0xb71b1a6ea2c6591b, 0x97cd7da55c940270, 0x8ab70184427e45dc, 0x6cb6907427808465,
- 0xf69232a42dbe7475, 0xbc9816d429fa8909, 0xef1e74244539d41b, 0x5569b6d10440d5c,
- 0x7dda6817985c8ef5, 0xb270ed19cc8161b3, 0x87d80b66c8a15db0, 0x96966684c3ba47fb,
- 0x996669bc87aff3bf, 0x17c015383c793f2, 0xa4b5de41fc69f61a, 0x14a0eb4e3055742f,
- 0x5f0da5a7a2b8bc79, 0x5fe0353728aac023, 0x1554daf4abe92eed, 0x545722dac774b6a4,
- 0x733fc54174f2e0d1, 0x3478ef85dd994316, 0x58ba2ac090ef7575, 0xa66b9b0cbc77b6e2,
- 0xdc78cab5c708a3ee, 0x97a30c27be510f61, 0xb95d0b06fc0910b6, 0xdc80bdc42f79a25f,
- 0x5cadc438e65b3070, 0x6263df49ce691b9c, 0xa7ce160daa64416b, 0xa4f8bfedb57288c1,
- 0xa51714e187bbcfe9, 0xe25df4e9fc44644c, 0xeaf1854b3116ce11, 0xde1b8f810991a604,
- 0xd4fc2e365e99be4b, 0x8d1b0d2799527e06, 0x7cac59eaf46baba, 0x4fa63c74d2dabaf1,
- 0x4f6c5d5e676733a6, 0x7ab7ddb9c1b789b7, 0x5d9beb4877a37034, 0x5e96bc9de3985bcb,
- 0x72bab4dcb75b3228, 0xfa40f33c4d799e1f, 0x73e6f61a69984a6b, 0x7499c9af466cf22f,
- 0x42fab9136bfa64dd, 0xd5e8e39513b6fffd, 0x8eda1fd5ad8cd51d, 0x95338744859dff44,
- 0x4f0c5e5ae768c729, 0x5bc92c60495ae348, 0xcbb48c170ac21168, 0x8374aa2440eeb138,
- 0x70b663d6f6d70ca9, 0x11264ca6dd79e5a0, 0xf058a2c156974514, 0x36820eefc435ba63,
- 0xd7b69f3d0c0c27a1, 0xe1a2eddf3b41d205, 0x80508ec93b038bc8, 0xd7e0429bd511ff37,
- 0x7bf55a4e87e183b8, 0x4cb370ce7edb4bea, 0x26fbd0b31dcef45b, 0xf7acbd6781419fa6,
- 0xf7849659f05c90c, 0xb686271ea57a47c6, 0x16f3f839dbfb4e1b, 0x906872b08b2c61a,
- 0xc30c86d0a0203c15, 0xdaf238a6aa4fc9f7, 0x2399aa09ad2c069a, 0xf133c3aca703f545,
- 0x868a10304a1c98ba, 0x60ef0607f46f7e90, 0xe4e69f26931e11a5, 0x487b8f6bd6d92941,
- 0xd10cb2971798c0c7, 0x7126d81aa4bd0106, 0xcb620311dc84ab82, 0x26f8734a7a356bb2,
- 0xcf9b9eeb02ee978c, 0x8a9ff0285d4d6b30, 0xe30e3b957e2cc7f8, 0x7c15a09e4d275809,
- 0xdf723ae1dda5d167, 0xac212e4f6264bba2, 0xe3d60920ed983308, 0x91b403cbc91e290c,
- 0xcdb905b012aff7c7, 0xb5ee73d45f900897, 0xafacc7cd7f5d52e7, 0xa8653272621165d6,
- 0x90e2efc485ddd0d1, 0x56ef1ca9097b1a96, 0xc7a20f85777eb0a, 0xf4cec0271a50eae9,
- 0x21acd76442024973, 0x19a46be82a4abdbe, 0x1bc12e0b8bf41fbb, 0x3766fe3d8e5119f2,
- 0x7fea355c4c18e8ad, 0xd08b496a24fb8017, 0xe1a7cfaa0877aa2, 0xd37ad9e2a2a3fa96,
- 0xaa362ba0e696f679, 0x7e7de89142c3aca5, 0x2dedb0842a3575b8, 0xb74c1e1d9082fe5b,
- 0xb1ae74323699140f, 0x73623f80c727a6ea, 0x132ed204b0f10441, 0xc3e6ebe8ffc252bf,
- 0x5f15cffb8286dce0, 0x66dab32df780fa8f, 0xeb00da7b25ea99e4, 0x113ad2448fafb671,
- 0xee065c10a8f1924f, 0x2fcb7367cc01fe5, 0x484338f5c2d0aacd, 0xecfacd785e42d7a6,
- 0x11513f845de8af3a, 0xd11be6b29054de0d, 0x2536e5d2856af9b7, 0x60ab519760acd4c4,
- 0x6bfe010250a831ac, 0xb28a93e44b53b21b, 0x281cf9b233858583, 0x4ca6139abc79a710,
- 0x5717d33616d77a95, 0x9ba52d2b7dfe71b5, 0x32e1c543476aa17d, 0xae242cc75806b7fa,
- 0xa1415cb8fde770da, 0x3956c67542dc004d, 0x3a6f51518fdd20ce, 0x448c848f6c936d93,
- 0x8fec38ff51bb5fab, 0x7463816cfc0754ac, 0x83b38e531ba39e73, 0xf9fc84dcf4f8c93e,
- 0xdf20dbe8b91c3d6f, 0xec65939ac9516f9a, 0x888346f6c1aaa94a, 0xe42cabb108f60d95,
- 0x39bb2e46b0599fa9, 0x529335ed75acba9e, 0x6a8767c5d00776c4, 0x8243346104fe61a5,
- 0x7a2bd0339b3e9bac, 0xb68ccdf14473f4ba, 0xa06f389531ab553a, 0xc7c6f074fc2882d3,
- 0x50a5fd6e6d0df962, 0xd7c0000d194139b7, 0x5ae27ef4033f873d, 0x4e7abe8a6d3570f8,
- 0x27011ccd3885e709, 0x3dae53f7b7a8924a, 0xa9086c9b2b86fb71, 0xa3f9e534a399e62c,
- 0x9f2f0379f9a33ec6, 0xceb51af95d4472bd, 0x15aa534182f8465, 0x96373b9cd28a627b,
- 0x9fdce0ad99d41907, 0x2755bb0f52b8c239, 0x2f2e241b6aa7d243, 0xf040afbacb2f6001,
- 0x552267c5f8b4c1b0, 0x22bfb3f0b58f9e48, 0x6bff8de368dbee3a, 0x652025c63d4069ce,
- 0x743eb697b25f9c90, 0x8a3742c9dbf67b1c, 0xaf3bcfc260d7e69, 0x491facaa59b7e1d,
- 0xd07d0761e6535fcd, 0x79ef09d9d3859232, 0xe0e0a013317d9207, 0x4b94baecf6fe4b4e,
- 0x574576ed054cfc38, 0x90e90edd1a26f0aa, 0x616d32a371af78c6, 0x392cea9c34ffb0a8,
- 0x692a6e730c33ac6f, 0x9e4b92ef425b78a6, 0x291d4c962d2f3e7c, 0x5f0f8ebb67f308fc,
- 0xc1a0faf70ec747a3, 0x641da9550cd89392, 0x6adbe38115f09648, 0x3a51980fb324da0d,
- 0xee894f2b5c17a380, 0x58788fa693f767c9, 0xc9f490ff5d88c4c0, 0x2ca6e8b204aa3070,
- 0x7693837d7910c40c, 0x9240e04f16051720, 0xab773072d922faa2, 0xacb6892db8362872,
- 0x5ac8b4d130613c0e, 0x65cd5ac259c653cb, 0x9647e0276864f3f8, 0xe207c57ee9237a53,
- 0xe41f2663482a8fdb, 0xf605931fce8b95bd, 0x5b0247a9009255d2, 0xae949df3ab5a4ab5,
- 0xc0e912dacbb72c42, 0x4226971e6351f33a, 0xd98d41f9f0fdb6c3, 0x64bf6a0af66782d5,
- 0xb4ca689b3959bf46, 0xb4cbf621b4970922, 0xe9f27469c2412fe6, 0xb529d406d27c785e,
- 0xf33be21f2672ec78, 0x34b3c4c4603656cd, 0x4a7efe099a0ae9d1, 0xa6ad5b909667945b,
- 0xe20850e47ab440f7, 0x68739e1b4fa069e5, 0x4c191300a9207cf4, 0x9b613a1a38160c98,
- 0x2cc631eb015081c9, 0x52af80aad7b676f4, 0x904b38943300ca8f, 0x50a515c0d302620f,
- 0x52ae95b8a0c1f36c, 0x6a62ab87786774e2, 0x17ea45367ad58985, 0x92959c57166aa21b,
- 0x2a9d91bd1a9716b4, 0x552c2f8528220174, 0xd665856c96b47d17, 0x14c6cba13b6dca3b,
- 0x865f1c93ec9000d0, 0x23dda8b5e161910e, 0xc191ac4342717953, 0x6783fdb95e098f1d,
- 0x1a4c26a5cad1e8ee, 0xde96e2a1e33e3046, 0xa57e65bedcf9047e, 0xa1cdef163fe7f80c,
- 0xeb0abcc13feeb7b2, 0xaa158c61b0e44470, 0x98dc901679caf85b, 0x954046758f8d2e96,
- 0x56db5e99c5d8c68, 0x7bb8d36962a4ac81, 0x2e301b4ecb03821, 0x121c1b2df70f0e1c,
- 0x3fbdad1e8faa0543, 0x6efb222398a2f88b, 0x65760de4d96338e0, 0x15b2c58a67fb43fa,
- 0x22f1532c89367d77, 0x1f726ffdad411d9e, 0x42572b54dcedf3ba, 0x3f8e0f6e9f0bbb7b,
- 0x4b0e705c86571a1c, 0xf8c9b04f8bd75117, 0x67ed2e9e4545557, 0x57e8853f681bbf8c,
- 0x6f99d1fd5fdbf582, 0xd2aa9bd48ad692e2, 0x2efc88bddecfe616, 0x8e9779a1e119abf4,
- 0x52dfee4722a20b75, 0x79465be3aea146d6, 0x588997dbcbd5f005, 0x79bda8bcc5d4c650,
- 0x2384e131ed3b5330, 0x229cb1d89738aa26, 0x1526d1a020a96507, 0xc7b6ac961a740cb9,
- 0xe78cec14478c4f71, 0xaba61cfd8f1e2a71, 0x24123c8a37ae66f3, 0xbad8b07709aa215d,
- 0x7896fa53fd2418e7, 0x72265842e8c4f955, 0x9f6331fd80527661, 0x7c649eeddb382c9d,
- 0xd3b0708dd6b5ae84, 0xeda51f244551e15, 0xb7822c860b93dd44, 0xdf9afcc3c9cac88f,
- 0x9244b9816573be70, 0xf3d103887cc4ce2d, 0xb3295c2e5bcb0218, 0x85243b7a2e0af441,
- 0xbffd3df508d06098, 0x4908b967f6765c12, 0x8886ac94c0dabb, 0x9c7865af133eb6c0,
- 0x5946fee66e64d7b2, 0x7737bc5af713087f, 0xbec815a80782dd5d, 0xe7672cfe0f7945fb,
- 0xe1f80b6df5beece1, 0x5b749d3a22450fd6, 0xaf34a567bf838668, 0xa72a177d20943ceb,
- 0x257c45c1601c4922, 0x3d7c68f6fe36ce59, 0x1b8ef47186e06c96, 0x8040426656154c17,
- 0xc15f74f980647bda, 0x36389393336a78be, 0x15d174ed5536afe2, 0x51e702e8adb61f63,
- 0xc9c95ca3f1c08f30, 0x6653094c8531c93a, 0xf7be5dfc2eb3b5b, 0xb5e21ec5c63850b0,
- 0x9ab5f082e01021a5, 0x75c4ca38d7678fb1, 0xb20bd13e05e5bd67, 0x3378133e631fbaee,
- 0x17cb3686511aeb6e, 0x4c0dea4c80ec7bc, 0x21fe874bf5509300, 0xece4f84e34d52e54,
- 0x3f2649803ca9918b, 0xdb493adeac5c60f5, 0x7c02a1d153afedd, 0x2d08ceda0fef7967,
- 0x6a32e018e432b0ea, 0x86479f3ea38ad57e, 0x84e3e04f1e5877be, 0x41b898cb02772049,
- 0xd6dded3714d71084, 0x9df5b2f3a0ab275d, 0xd5ab652dfa73ee0a, 0x4633e03fa37d6edb,
- 0x226314b8c4b937ff, 0x45d66a00ab031188, 0xb93ce1cfbcd1eaa0, 0xf88e0756f7e7c1c1,
- 0xf3611966fed51e03, 0x81235048c662d9f7, 0xcc8275f866147b4f, 0xd3b3ca0f5033a863,
- 0x970212eb8c2b3429, 0xec848dd58f3449d6, 0xa1d527af824d09f2, 0x60bd5e91448b9cf4,
- 0x1210ddfac603aa88, 0xcbf4270f3407e25a, 0x212955fec55466a0, 0x3afeaef4c9ccc793,
- 0xdd114286ec304817, 0x849e6ae3c2cf794f, 0x71c08228d6a05310, 0x177e77779d155b11,
- 0xdc59148f219a9c04, 0xb0702a7802d5276d, 0x56085d6761aee015, 0x3f79ce06bcfb4f3,
- 0x459a1f917f8f3e0, 0xe4ea5635e8bcd512, 0xa88e99b63f135a39, 0x95bd628d77d39446,
- 0xe6432158ef4c7d98, 0xb349cd3d1c74369e, 0x25b1a32db58efb0a, 0xc2add3a44cecc0b5,
- 0x5d676629f23010b5, 0x9890b3a62599408e, 0xef68ea8144d97805, 0x429e0fda34046a85,
- 0x7723d9043053bf52, 0xbb78842d9b67ae91, 0x9155ef932192e6a3, 0xdb523ea403d39f6b,
- 0xdb8fa1eee23ea58, 0x7c735492524a3448, 0xc580cb82e81505, 0xb0be6a006414841c,
- 0xdec2e763b5cedaa5, 0x8da58bb23638af5f, 0xb0e6b33f6736e7d0, 0x8146bbcd3dd4df61,
- 0x978080148a8989bb, 0x7f8119caa3308095, 0x4e88c318ea0604f3, 0x6ee5262f16b3cf83,
- 0xc44395c7a578ace9, 0x92016ee635de27e1, 0xb8dc5ceb36e67fd2, 0x95c851d65bd35f8c,
- 0xbe393620503c49fa, 0x42af183b92eac923, 0xfef0e660435135ce, 0x262d67d480451ed1,
- 0x590f92e1ee0502b3, 0x18825c97d49e3700, 0x2cd8c30a848c9acf, 0x1025c7747fda0115,
- 0x54109f23e82590aa, 0x93917bf1f8325981, 0xda674b183fa0e3cf, 0x2a0b2467eb8aefc5,
- 0xb0085eb83468793c, 0x607cabb2c9d3a81b, 0xe7a6b6013804d665, 0x67629a769c1efede,
- 0x2830ab6ef6d10166, 0xffd02b0655332bd4, 0x19bd056c3117568f, 0x385a834785662c6f,
- 0x938b5d56bd5f7248, 0x969afe82dd4829c8, 0xf455d4ace41c797d, 0x23d9cd67eff27512,
- 0x2b0c7037ecb322e2, 0x73df193328258bda, 0x5d7ee05cf2054f93, 0xdabfbd5b46b61cea,
- 0xcea02d82546b96de, 0x2245d5e74d5f60ae, 0x842ca45f8ef2a44, 0x7505cc4e1c3060d8,
- 0x869146ac8e68565e, 0x22ea711fb30e73e3, 0x53cd64736898a0c0, 0xfa88458b920684df,
- 0xc3ae23f451e0616f, 0xe2dd69393141ff32, 0x98863ab129bcd866, 0x8c9756a40dd5b834,
- 0x2eeeef78c36fede5, 0xe84d2eb23e22153b, 0xb0ccc2f7ac541d78, 0x151faba0f513acfa,
- 0x4300e3cea0260717, 0xaba308c6d857d2d0, 0x5eb25dd325256c6a, 0x3342627b68038da4,
- 0x70d7d75526da35, 0x80ff3f5ea2ac3cf1, 0xe7434f2f026394b0, 0xa7c5ff17f7d2cb07,
- 0xdfed0bb33a06ff78, 0x485cc64d38ce2596, 0x88db8580147aa8fc, 0xf52a5111d693b973,
- 0xeeaa031c02b370a8, 0xbb3b1678222d0e81, 0x27b569f2a6630939, 0x2b301fa3e50efeb8,
- 0x4dbc1f85bf3f8972, 0xb37e4f2cb75a825b, 0x3c8848e0f1777cc7, 0x8fae2e6938ba00aa,
- 0xfb4674b50884992c, 0x2b765005b85b7388, 0xaaf0007fb9662ce5, 0x684bd59009a4ad65,
- 0x221ffead3d73ab35, 0xccfe6d02d46856b5, 0x54d3323359e1b114, 0xcb412202ed42f097,
- 0x15d63df422771b9b, 0x71852bca1581d14f, 0xf30dcbb6cf891e63, 0x478fb1eed3cc5a10,
- 0x849a3b52bc5bb196, 0x4c1a98dbc546dd81, 0x846dc8c2258ec4f8, 0xbd4da447c7340bd0,
- 0x8f1ee1d6a85b9db0, 0x123ebfa8aaec07f2, 0xae34948e375d4477, 0xd466a4177842d8e4,
- 0xd5108efeb19cac6, 0x3266f7db8f133bdb, 0xe69af4e5d8d767e4, 0xea0efc0331df64a2,
- 0xb879052746ed72ed, 0x2c8233cc84de4144, 0xdcd5dda825186731, 0xb9e3b679d268f34c,
- 0x7ce12e2fa95bda8b, 0xa34afd12e611a4c1, 0x6043d06ee7619f90, 0xca3a3d4813c0addf,
- 0x6e97a61d1e4b3c4f, 0x8cdeae467a4bb292, 0xf08a6c69e70076b5, 0x97aa5c3180d3edbe,
- 0xc39813e1573904d5, 0x42577549d026e8c8, 0xaf5827ffe259b62a, 0x9e1d48596c4f0b24,
- 0xab9dd230ba8efb64, 0x81769493b85868d4, 0x715b45c5e3952245, 0x84735e138d228f35,
- 0xf4f987da7d19c74d, 0x1bdc77979baf29b, 0x785b3640158e0278, 0x77fc9d00681bcdd,
- 0xb5980cc490dcab93, 0x9062c158196b8244, 0xc16af5418cc97c4a, 0xdd4a4e9e8e00e524,
- 0x870b554b629277f1, 0xf90ff72bb54322c0, 0xd4c273bc2199823, 0xeebc75970d438466,
- 0xe4cad67413074a53, 0x6eccde71bd09dc0c, 0x14278b0ca6b910b4, 0x6e8895f17cdb933c,
- 0xa0b3987821416e11, 0x71b7d24b81fa769f, 0x1d3b1a805b885a58, 0x1bc737b1719736a,
- 0xea4d1dbb8823037, 0xe50ce48c8469adbd, 0x34c2d5e6c41a888e, 0x446a756eb06dc3a4,
- 0xbac5ed8a8f90262, 0x7f1b76e0c707ab9d, 0xb31323309b94a12e, 0xf58269b9852f986e,
- 0x3b74c2b338c244fc, 0x879b46f23a4deae4, 0x3f2591e34cdce1c9, 0x73c6f81eb560ed5c,
- 0xd2aa923c7a5c18a9, 0x7170c1f7621cace9, 0xa18b327b11b951a, 0x2b36315510f56370,
- 0x5cfed2f703dfc0bb, 0x1e43b99175054c07, 0x392dfa3210e8013b, 0x65e0c5c0454ee693,
- 0x2a795f6f493349db, 0x17995ebdfe848db0, 0xf23174b823a52cf7, 0xaac6ef104bfd396e,
- 0xfa0f5b29156eeccd, 0x9ccb3590a6e88c1a, 0x2edae0b1b80aafab, 0x1e1e92baae39aa30,
- 0x24475af89cec7f33, 0xacdbeceaadb9936c, 0x7725948da8586e93, 0xcc05595e17947215,
- 0x1f3cbde17a508faa, 0x2795f58ff5c8919b, 0x309658d1748f30d3, 0xe90c11962e5ed4bb,
- 0xc22b876286d32e27, 0x495c8b667c6ea3dd, 0x84263045f7d6eab5, 0xf8d3a9ab1494a315,
- 0xabc42cf769a21d9d, 0x3fef7fb40bdf3a81, 0xfd35336e188925a3, 0xd472bdaf277ab26d,
- 0x4949e8ff51da2307, 0xbec86dce960ff3b, 0x1aa1ce4f70256b10, 0xd52986ff8cd700ea,
- 0x364d8efc0f5afad6, 0xca3d1958f57a9050, 0x17a0a2ec122dc677, 0x7992695be3363fd6,
- 0x5c66a265e0607da8, 0x7250114e050bb917, 0x30cd3a70bc7b723d, 0x7b77433392b3fbf8,
- 0x295bb7bf46318b38, 0xdb15025af2a71c65, 0x26a82b21ef67f50c, 0x14a5573d4fc798c1,
- 0xd8cad4642ec68e5e, 0x9276d7f60142822c, 0xf7b8efc27522e52f, 0xd0a3f36f6d340bed,
- 0x341260fb11765c02, 0xcd1d394d796702cc, 0x7d483ef031eb3346, 0x74eaab49374576c1,
- 0xa073bea32a71a273, 0xd65ce2552993b5cf, 0x8670afe77caaeb16, 0xb607f1455d072a47,
- 0x30ed96be92809559, 0xa58380a503c23c9c, 0x916aa68fb957a30d, 0x30c5a675bf19738c,
- 0xd4cbad34e4e4b886, 0xd6cb83061f2b0ebf, 0xceddb4040f535fa9, 0x778c586927b1e247,
- 0xe4bb5c4b6e0f3c3d, 0x3e857d671db80667, 0x2b909dd8725f1fa2, 0x558ffd0772db7841,
- 0x710f9638d3edb2c4, 0x21a4ccee53d46556, 0xf76e8e4737b9628b, 0x71cd157f23581c71,
- 0x68d8fded9b66efd6, 0x7d9f5e182c0b9457, 0x2140757748a217ff, 0xdd1e5365520b77a4,
- 0x644d8e4b2f30dcfa, 0xa1de42f4e9791564, 0x70e148ababfe9f86, 0xb97f463e0ac7daec,
- 0x82844f729d9fa554, 0x5c8475e84470c924, 0x3a83de748eac32fd, 0x68725fbc9c202c5b,
-};
-
-pub const mt32_seed = 0x7dc0d160;
-pub const mt32_data = []u32 {
- 0x59327332, 0x200858fa, 0xab53c028, 0x5c442427,
- 0xd8be0287, 0x3b69d304, 0x15fdf62f, 0x59b8ecd,
- 0x6d7ab30c, 0x3a3dd6f1, 0xc1b9773e, 0xa12fb017,
- 0xa805b5c1, 0x4a313ba5, 0xd82c790c, 0x8de311f2,
- 0xe7cb23dd, 0x784b2efb, 0x9743487c, 0x73e2f2fb,
- 0x1a7ac286, 0xaef90d, 0x6c0a4514, 0xae1d83aa,
- 0x412fcca1, 0x3acd2d28, 0xde78292f, 0x13237756,
- 0xdd6cdeba, 0x44ae4df9, 0x3e9902eb, 0x39e1cf20,
- 0x62f561b9, 0x6cbdf531, 0x4a000673, 0xb1c82daa,
- 0x896156ca, 0x75e410f2, 0x9c69e72c, 0x396b42bb,
- 0x25c97ec0, 0xe12173f1, 0x8dcd42e5, 0x82aac3e3,
- 0xcdc1c84d, 0x13509c0c, 0x46a696d2, 0xb89ad987,
- 0x92da5e7f, 0xaa87d8a9, 0xe433ff57, 0x80a7ee49,
- 0xd387cfcc, 0x7dc47d92, 0x21516140, 0x989ca465,
- 0xf2a8e002, 0x73b99ddb, 0x2204108f, 0x27e84890,
- 0x371c81c0, 0x9f581854, 0xc0841c35, 0x770f6804,
- 0x87c55f4e, 0xd29516bb, 0x2b6d6bde, 0x5541f6ee,
- 0x1ec9b182, 0x7d599729, 0x4f4b9a14, 0x6f8c8562,
- 0x2d5151aa, 0xb54f5bc, 0xa252452b, 0x2a4266da,
- 0x25a6b75d, 0x2d11106e, 0xc5d77943, 0xb10b6e0b,
- 0xeb5cae4a, 0x43a0dd53, 0xa40bea1f, 0x63e632c2,
- 0xf420b6ce, 0x8b080233, 0x7f70ae87, 0xf460f0d6,
- 0x147c7e74, 0x710692ea, 0xa0a7d8fb, 0xe7f05808,
- 0xa6173aaf, 0xae608de0, 0x8702036, 0xbf1bfc7b,
- 0xf14cd548, 0xbbc7553d, 0x5358dd1d, 0xcc0c1fe5,
- 0xfab6f78d, 0x9365c118, 0xf64216a3, 0xb4bdcf1b,
- 0xc90b8a7a, 0x8b7b78a, 0x4c7b6854, 0xba7b5628,
- 0xdd728c15, 0xcb1f8905, 0xa63e2342, 0xa78822,
- 0xbda61b18, 0x160a59fa, 0xeccf473b, 0xc5a445b5,
- 0x7aa86430, 0x362e0d7c, 0x8006a0cb, 0x8b11586f,
- 0x6677bba9, 0x6208cf27, 0xeec9b5, 0x3dfedfc9,
- 0x886cc0e8, 0x32ed77ca, 0x43525faf, 0x9786354a,
- 0x1a2eb378, 0xf0e6b168, 0x49064b09, 0x8ab39681,
- 0x7b6655fc, 0x35adb168, 0xc417d430, 0x2784288a,
- 0xea17836, 0xc85006e7, 0x673dfdc3, 0x42765688,
- 0xc2b9251, 0x840a45b, 0xcac98e2f, 0x1a6f9777,
- 0x34959b23, 0xf0dcec81, 0xcaa2c6c8, 0x1cf93061,
- 0x787e598d, 0xd5d9e31e, 0x14e08791, 0xd9d9d782,
- 0xef162f23, 0x238f4113, 0x23f42107, 0x6ed5cc3f,
- 0xa55e5a7c, 0x4650595, 0x5217da8b, 0x6eeaacdc,
- 0xb453d7b1, 0xfa1ff004, 0xb9d17f74, 0x2bd6a53e,
- 0xe4c2d9dd, 0xed66375e, 0xf8215568, 0x9bcadbb3,
- 0x9c4f9d51, 0xff68312, 0x82308422, 0x83e990b0,
- 0x38b6135b, 0x70e2aa13, 0xa30065d2, 0x6396a00,
- 0x77d423bc, 0xa93abf0a, 0xc7bb8c31, 0x5d7bd3d3,
- 0x6a374f2e, 0xe4b5bc88, 0x39f6e512, 0xd6aea995,
- 0x878c1bfa, 0x4636014d, 0x9caa2c09, 0x7ac4758b,
- 0xbd3b957e, 0x518c2fd6, 0xea009a2e, 0x542bf419,
- 0x59090006, 0xb1d94703, 0xe0d9eefc, 0xe7fccb17,
- 0x40111951, 0xf2560485, 0xb50ce9e1, 0xd7a1ee51,
- 0x28dffa99, 0x41d12275, 0xdd89a365, 0xf22eda29,
- 0x104f94ee, 0xe669983b, 0x6346a250, 0x86326fc5,
- 0xb7f347df, 0x3849a39f, 0xf433929a, 0xeea5155,
- 0x4cf9b778, 0x6bd7926a, 0xcda9496, 0xf430d7a2,
- 0x41637670, 0xaf3bbad6, 0xeb66e44e, 0x2499605d,
- 0x9988920d, 0xf9d652ef, 0x67aa80c0, 0x505073c9,
- 0x85cd418f, 0x9f83fb65, 0xf50b3eac, 0x812ba6bd,
- 0x74d61788, 0x86d64f3b, 0xb1f8fc1c, 0x3e2af667,
- 0x4d118a2, 0xd028ffa9, 0x32e88a44, 0x4ed9ba35,
- 0xea3c7030, 0xffe44aaf, 0x5e39c467, 0xeabcfebb,
- 0x53e656ec, 0xced701d0, 0x31020b02, 0x4b4c1dc5,
- 0x8744885c, 0xa8e93656, 0x3ef457e5, 0x272bde23,
- 0xe541477c, 0x3ad3ac04, 0x63eaa692, 0x81055cf9,
- 0x3ff5f782, 0xa8efe6bc, 0x15f37656, 0xaaaebf1d,
- 0xf73d461a, 0xe8b2c0b5, 0x5035ff48, 0x3a95e34b,
- 0x6f21d94f, 0x6f6d1f96, 0xdaf79f37, 0x826f69f3,
- 0x209a00b8, 0x2ad1b2f2, 0x2c64fb45, 0xcf8bf26e,
- 0x9befcff2, 0xc08f6951, 0x96d98205, 0xa267dcb5,
- 0xbc43ec5, 0xee6a7e1c, 0x49224eae, 0x14e820e,
- 0xbb340212, 0x68ed572c, 0x45e9e623, 0x1297f3af,
- 0x49a98ed2, 0xddd34ae8, 0x211838ab, 0x47e7652d,
- 0xb40430c6, 0xc8d3bd7, 0x4352356e, 0xf0e5cac9,
- 0x21880df4, 0xc16b343a, 0xd9ed7350, 0x17fe1f65,
- 0x6637192e, 0xd81c93aa, 0x7d6e17d2, 0xd407b13f,
- 0x425da072, 0x380d423d, 0x6ce57b22, 0x7b17ed17,
- 0x95fbf626, 0x768303d6, 0x76ab6b3e, 0x591491e3,
- 0x259f79ab, 0xd4babeaf, 0x9c7de2f8, 0x4fe6cb58,
- 0xf43680a9, 0x651a1266, 0x730ea3c8, 0x9188d4c5,
- 0x12d01e34, 0x47afb2e9, 0xb4b76d35, 0x5e5164bc,
- 0xc864fc46, 0x5d018aa7, 0x17fac975, 0x5a775fbd,
- 0x40e6fa14, 0x7a00b683, 0x99e4e102, 0x2f933b90,
- 0x474e14ba, 0xde1b0754, 0xe84aba2b, 0xb386cd43,
- 0x17ca77c9, 0x7b4f38ef, 0x803ea1a8, 0x93553947,
- 0x806c8224, 0x2608451e, 0x63157fe3, 0xaf53930e,
- 0x5dfe8c16, 0x65592bda, 0x7086eb3f, 0x838e6a50,
- 0xa27836d9, 0xf2f16d92, 0xdc0a981, 0xfbf8f915,
- 0x2caea00d, 0x86bb3e18, 0x6d94c209, 0x3bbbeb6c,
- 0x114d68f4, 0xc271e48f, 0xa3350dc1, 0xb8d55eb4,
- 0x68be5ee1, 0xbf22ef29, 0xd6e0aa54, 0x48f7219,
- 0x21aca253, 0xfbf07910, 0xfcdd61a8, 0x118a09b,
- 0x3f2bbde6, 0x46eea63f, 0xdb51ed16, 0xf8a9fc36,
- 0x31614dc0, 0xdd84f54d, 0xd2b66065, 0xdae0af99,
- 0x6d071a51, 0xbdbac46c, 0x15deee25, 0xf792e64c,
- 0x910194e8, 0xfc989a8f, 0x919727fd, 0x6f93a56c,
- 0x2df36e9a, 0xd395b948, 0xb026b54a, 0xf0938a5,
- 0xe9c64399, 0xb5cda15b, 0xb7b8dd41, 0x7146f944,
- 0x8d41ce2f, 0x47c74099, 0x2e5a8e5f, 0x28f7a19c,
- 0xef7a8ef9, 0x6a763eb9, 0xf13a3ec4, 0x9f352360,
- 0x42317561, 0x6c6a0ca5, 0x5e40b472, 0x3ddaadd4,
- 0x2f5d14eb, 0x5dd49aeb, 0xc89edb24, 0xa2da269b,
- 0x5cf0a38b, 0x8e2f435c, 0x40970e54, 0xa2cb730e,
- 0xf9d8c301, 0x8ef29fb1, 0xf08b1840, 0x7d45e4a2,
- 0xa0fe4ce1, 0x939a21c4, 0xfdeebfea, 0x4c661550,
- 0xdd304d1c, 0x3cdb078d, 0x94ae8db2, 0x4f6b4287,
- 0xffe64fa8, 0x50384bb0, 0x16cf5ed3, 0xa91a8fec,
- 0xdb8ebb1, 0x59c2898b, 0xd587edc9, 0xdec2e75a,
- 0x496ccdd2, 0x897db91d, 0xf8ea5149, 0x6bed4bad,
- 0xce76e472, 0x43c7f976, 0xb055dc01, 0x7ffd5671,
- 0xe193b86a, 0xe288ce11, 0x514d531e, 0xa42fa47e,
- 0xe7c0e194, 0xffc059ba, 0x26548e36, 0xe1f10d92,
- 0x3ef5d95e, 0xa6e69282, 0xffacb09e, 0xf4a16ff5,
- 0x9b7f03bd, 0x588c54b4, 0xc2b6eaa1, 0x2d83acdc,
- 0x7fdbb606, 0x2b160650, 0x9923e57e, 0x32bd23bd,
- 0x50cd6d4c, 0x205d901f, 0x810a9935, 0x27ce6e7a,
- 0xe0c6c66, 0xac06c99c, 0x4326aa9b, 0xe1af1e90,
- 0xe358c8b1, 0x2f601c2a, 0xefca77e7, 0x1a7ed2f8,
- 0x8ad2e191, 0xe5520809, 0x27084438, 0xe4d8e782,
- 0x5e8a4038, 0x87bba694, 0x65f07eba, 0x616f8f07,
- 0xc5565d9, 0x555955e4, 0xf41c2caa, 0xb085fbf5,
- 0xa5f9d9ff, 0x418fa0df, 0xec5a576d, 0x7fc332ab,
- 0x7683ed33, 0x968ef54b, 0x834d598d, 0x6833f356,
- 0x59dc7e7f, 0x779661dc, 0x58942dd4, 0x80387aab,
- 0xf6dac9e5, 0xe043be04, 0x2ae4f872, 0x881f8d01,
- 0x82cfd69d, 0x931f4648, 0x2a76ab31, 0xa3f1dd7c,
- 0xd7f4826a, 0xe74918da, 0xe4c98636, 0x441164f,
- 0x15a0e9aa, 0xce7480ad, 0xba39076b, 0x233aa8d,
- 0x6c32f0e6, 0x169c62bf, 0xa2cd17f6, 0xb5590084,
- 0xb2036f00, 0x18315935, 0x11e9c9c7, 0x25c77861,
- 0x41596cda, 0x635e5e02, 0x8f396cc, 0x4cd00d8d,
- 0xd665597e, 0x90f891ef, 0x547b93ee, 0x376959c1,
- 0xdc5fa80, 0x9b4797a6, 0x53673041, 0x25ab117a,
- 0x7b8b8292, 0xf4e99584, 0x5139da98, 0x30e2afeb,
- 0xff2664b9, 0x591eb6f0, 0x9e87e602, 0xf5e26193,
- 0x61831f07, 0xabc139f9, 0x984eda0a, 0xaea1b8da,
- 0x65c7410d, 0x2b84800d, 0x1d3cfec3, 0xd05cb8a1,
- 0x4529641b, 0x7d6712e6, 0xc38cbde7, 0xacad7787,
- 0xd8482f3a, 0xa5662eaa, 0x24836ee9, 0xf3b5cc97,
- 0x50a581ae, 0xff6004b6, 0x650fc547, 0x161898b1,
- 0xa7593447, 0x325827dd, 0xf1844a1a, 0x7eb56de2,
- 0x89882452, 0xfebb49a, 0xfe86ae9c, 0x7dba98b1,
- 0x1d65adb5, 0xb71acffa, 0x861215af, 0xc0f1496,
- 0x70967c72, 0x3803d127, 0x6c8fdd84, 0xe40991f1,
- 0x1343e3a, 0xf57b4e73, 0x25f34f76, 0xaebcdee8,
- 0x8752d71f, 0xfc710e54, 0x34f3af44, 0xfa7dea4e,
- 0x477d4d83, 0x42640ff1, 0x2c5c31ce, 0xa82de5e4,
- 0xcc813271, 0x4d40bf86, 0x4e416095, 0xb5ac332c,
- 0xd2d44703, 0xe4c5ef57, 0xde193a29, 0xbf3e7974,
- 0xbb313d75, 0x8dc973d5, 0x301b2657, 0x44dc5064,
- 0x8c58c633, 0x83424c74, 0xb7cbf7ac, 0xa04238c2,
- 0x6ceabd59, 0xd25e6fd0, 0x3409167, 0x42d6ef80,
- 0x1f47c437, 0xdb21e45f, 0x2fd48e29, 0x9498cfb7,
- 0xc9e4cb12, 0xc6dcf0df, 0xa1633c39, 0x1b349670,
- 0xf76d4a64, 0x15ecd8dd, 0x777bb76d, 0xc46008e7,
- 0x23d94e44, 0x78aa07de, 0x2eeac782, 0x3757b114,
- 0x2b22de2a, 0x37726519, 0xf107546d, 0xe9847f74,
- 0x449a4ea6, 0x2e31fa5a, 0xd719ea88, 0xb2115c87,
- 0xfa6b7231, 0xf72fc9ff, 0xcd22bc37, 0x9080778a,
- 0x93430a21, 0x97c24360, 0x6e5b1a76, 0x5e8baa7c,
- 0x300c94f8, 0x2843d9da, 0xdceac0ae, 0xeeed885,
- 0x1898ffd0, 0xa3bbee3c, 0xc16f8fd7, 0x82992b68,
- 0x39c153b6, 0x1b3ba4c8, 0x41e7c3ac, 0xcdf8f06a,
- 0xd40b8ae6, 0x4982b6c2, 0xb32f7437, 0x22ed3691,
- 0x16579a2a, 0xff9de457, 0xc421e8e4, 0x17c8f6cb,
- 0xa5c4a8da, 0x49bd8afa, 0xe2be081c, 0x95170f28,
- 0xd679fbdf, 0xcf39d563, 0x4e2d2ee9, 0x39471096,
- 0x3918bef0, 0x279b7679, 0xa5281a0f, 0x49481d6f,
- 0x11f95ee1, 0xd9df649f, 0x2993eb27, 0x48ad815f,
- 0x99cf306d, 0xca9457e4, 0xc27c51d2, 0xc2a838ec,
- 0x537faf4c, 0x55dccddf, 0x8df5aeb8, 0xabb317ca,
- 0xfc1bcf6b, 0x669c2b1b, 0x719b62d5, 0x6b9325cf,
- 0xc123d0d3, 0x2ddc6ace, 0x27fdc30a, 0xd3f93cd8,
- 0x704f5486, 0xd3f448ec, 0xbbd1e32c, 0x3bcd4c0b,
- 0x86f8166, 0x957db888, 0x899b6a5e, 0x270dc8b7,
- 0xff16222e, 0x51e139a8, 0x3d8b4b9f, 0x68d20818,
- 0xa639ad00, 0x4c2e0fd2, 0xb4949cdc, 0x2ab6eb32,
- 0xdd0c67ad, 0xd2208cbe, 0xcd17a0bc, 0xacc541f7,
- 0xfa9e714f, 0x316d31a7, 0xed79fa91, 0xb5c0e980,
- 0x412ecc9b, 0x9815753, 0xd0df1f43, 0x8e37dbb9,
- 0xe640df75, 0x379c2fb6, 0xc7ed26a4, 0xc5190400,
- 0x1cc81b53, 0xcb0b0cd5, 0x360f061b, 0x6d90284e,
- 0x83c05bd0, 0xbd80bae9, 0xd584ef12, 0x228a46ec,
- 0x657c4fbe, 0x5ca1043c, 0x852aca0f, 0x31ce950,
- 0x33ee2cd8, 0x3cdecbf7, 0x787ef08c, 0xea610ee,
- 0x47c1db89, 0x90eeda11, 0x74f8d429, 0x51d3a4c5,
- 0x3135b401, 0x2e14783c, 0xb9af855c, 0xb66348d9,
- 0xa3a47387, 0x6eb72af1, 0x7bb56088, 0xc664542d,
- 0x7ed96b8, 0x995870a8, 0x385b1fd6, 0xa430680d,
- 0x98a883ec, 0x2497a389, 0x7a880627, 0x8350ba9d,
- 0x4cb35c33, 0x30bf6b14, 0x8695a469, 0x9a81e44b,
- 0x8bb27c9a, 0xbfb6a4dd, 0xbae7cf6e, 0x4ebccc87,
- 0xb712ed3d, 0x31e90365, 0xcc1fa63f, 0x32b93df6,
- 0xbad4c7bc, 0xb2570e17, 0x73fa21be, 0x5c02a8d2,
- 0x94446d75, 0x7265f3ad, 0xd58487a2, 0x919b7a07,
- 0xbe2d0e05, 0xd36ccf4f, 0x6d5c66d7, 0x8448522f,
- 0x8409c294, 0x6f1c7af7, 0x173a13bc, 0x1b3e4a0b,
- 0x705b941b, 0x77eb584f, 0x85b68458, 0x8e3ad1ac,
- 0x4aa99702, 0x7ae1b24c, 0x899ba29c, 0x860a3711,
- 0xabe53a4f, 0x37870133, 0x1ed7cb89, 0xea539762,
- 0x4ba64130, 0x48517a2d, 0xce0a869d, 0x937ba48,
- 0xd0f234c4, 0xf9b2cf26, 0xc3c311f0, 0x153d09a9,
- 0x404d3af9, 0x9f7edbc1, 0xbdcecded, 0x97969ba8,
- 0x3379437, 0xadd3c893, 0x7c024639, 0x459390b,
- 0xcb7c7320, 0xa5c63725, 0x65907e3e, 0xbf70583b,
- 0xcebb601b, 0x4edfb286, 0x9350336f, 0xdfb4be76,
- 0x88b56f39, 0x9937d7f9, 0xa12a286d, 0x34f141c,
- 0xa2e75c15, 0xd69a7060, 0x931340c3, 0x22447f25,
- 0xe8aed82c, 0xd76a9ae7, 0xc967288, 0xe572facd,
- 0xbe82b0ee, 0x10f5dce1, 0x4f03ee35, 0x2340b923,
- 0xf4fb6bd0, 0x64adbf01, 0x3d277a0a, 0x39e76f2c,
- 0xe3c024d9, 0x57869c82, 0x743b7826, 0xf66f1574,
- 0xc93965c, 0xf86a552, 0x13557069, 0x9e0845de,
- 0xaee084f9, 0x5eafaedb, 0xc06f5f3, 0x9051f6ea,
- 0x98fceda2, 0x2af2f8cc, 0x6c41b5a8, 0xc1af74de,
- 0x57302276, 0x253923c9, 0xd79996b3, 0x8ecb3141,
- 0x641387cc, 0xf87a4101, 0x96a50c76, 0xbcf24a11,
- 0x87b6bb6, 0x58ee501b, 0xaa859695, 0xb2eed107,
- 0x554173f0, 0xb12ec0e6, 0x57785c1b, 0x53685c9a,
- 0x114c3163, 0x9383cf19, 0x31fd7cdf, 0xaeb8225c,
- 0x58774fe7, 0x54700ad4, 0xad418726, 0xf055b71c,
- 0x7d31237f, 0xdd97cad5, 0xcdd5325e, 0x42f2acf4,
- 0x4bed262b, 0x7a8faaf2, 0x2b1eafdd, 0xa1b806ac,
- 0x26965c6e, 0xb41b7168, 0x15e2e70b, 0x7daa8e13,
- 0x6198aa5a, 0xc9b8b94c, 0x339b5754, 0xcd3b285c,
- 0xffd1486c, 0xf224979a, 0xafb89ec5, 0x222058c,
- 0xcb4814d0, 0x2b0e7c7d, 0x9eb25b84, 0x271564b0,
- 0xbb72e076, 0x48251020, 0x18008023, 0x48d10005,
- 0x4a452eaa, 0xb2365308, 0x19cdb632, 0x1fd56d04,
- 0xffa5ff2a, 0xaba89e42, 0x388fc17d, 0xea61c00f,
- 0x5156273d, 0x776f1a56, 0x8d539d28, 0x289c01cb,
- 0x857aa71f, 0x348e411f, 0xc9eb3c91, 0x67a61079,
- 0xe4276a0f, 0x45bdc15f, 0x8e0a698e, 0xbdefc310,
- 0x82377ba6, 0x3bfbf404, 0xcbf22c79, 0x35f501bc,
- 0xb16044a7, 0xeffdb8, 0xdbac383d, 0x7816663f,
- 0x18f5a318, 0x3d04f1cb, 0x735da9b4, 0x75e339a1,
- 0x5b6c55f, 0x1c18887e, 0xf698e14f, 0x338a6da1,
- 0xdac85699, 0x1aca7768, 0x8eb0fa7a, 0xc98fa71d,
- 0x3b794408, 0x92913041, 0xf8dc8827, 0x1cf706e9,
- 0x3aeee292, 0x321dbaa8, 0xee1eb8d1, 0x23554be9,
- 0x811c7804, 0xf0f4de6b, 0xd457e382, 0xeda56795,
- 0xeffdfc71, 0xf2a52829, 0xa7460732, 0x2c1321c0,
- 0x2f734db0, 0xf04ecb0b, 0xec7d777e, 0x43c54317,
- 0xccaa74cd, 0xfe49dd9d, 0x4c509829, 0x278f9bd7,
- 0x581dc500, 0x4ad38c2e, 0xcbee1047, 0x13302c1c,
- 0xbc0cb734, 0xc1c8f234, 0x1df52b35, 0xd8815548,
- 0x319edefb, 0x437cebe5, 0x3dcb6026, 0xe9d4f93f,
- 0xb2661154, 0xeb8c15a0, 0xb008505, 0x5f869981,
- 0xf5588ca4, 0xd6929c5b, 0xa3dd13d1, 0xdc863314,
- 0x891a454f, 0x91737e49, 0x5064d4d8, 0x2fd32675,
- 0xadefe9b1, 0xdde32b11, 0x741bbd6, 0x3b4363a9,
- 0xb121d9e8, 0x916ca61d, 0x38c0af15, 0x5e3dfd72,
-};
diff --git a/std/sort.zig b/std/sort.zig
index c13e99feda..0f83df7bb4 100644
--- a/std/sort.zig
+++ b/std/sort.zig
@@ -67,7 +67,7 @@ const Iterator = struct {
self.numerator -= self.denominator;
self.decimal += 1;
}
-
+
return Range {.start = start, .end = self.decimal};
}
@@ -82,7 +82,7 @@ const Iterator = struct {
self.numerator_step -= self.denominator;
self.decimal_step += 1;
}
-
+
return (self.decimal_step < self.size);
}
@@ -219,7 +219,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
var B1 = iterator.nextRange();
var A2 = iterator.nextRange();
var B2 = iterator.nextRange();
-
+
if (lessThan(items[B1.end - 1], items[A1.start])) {
// the two ranges are in reverse order, so copy them in reverse order into the cache
mem.copy(T, cache[B1.length()..], items[A1.start..A1.end]);
@@ -230,13 +230,13 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
} else {
// if A1, B1, A2, and B2 are all in order, skip doing anything else
if (!lessThan(items[B2.start], items[A2.end - 1]) and !lessThan(items[A2.start], items[B1.end - 1])) continue;
-
+
// copy A1 and B1 into the cache in the same order
mem.copy(T, cache[0..], items[A1.start..A1.end]);
mem.copy(T, cache[A1.length()..], items[B1.start..B1.end]);
}
A1 = Range.init(A1.start, B1.end);
-
+
// merge A2 and B2 into the cache
if (lessThan(items[B2.end - 1], items[A2.start])) {
// the two ranges are in reverse order, so copy them in reverse order into the cache
@@ -251,11 +251,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
mem.copy(T, cache[A1.length() + A2.length()..], items[B2.start..B2.end]);
}
A2 = Range.init(A2.start, B2.end);
-
+
// merge A1 and A2 from the cache into the items
const A3 = Range.init(0, A1.length());
const B3 = Range.init(A1.length(), A1.length() + A2.length());
-
+
if (lessThan(cache[B3.end - 1], cache[A3.start])) {
// the two ranges are in reverse order, so copy them in reverse order into the items
mem.copy(T, items[A1.start + A2.length()..], cache[A3.start..A3.end]);
@@ -269,17 +269,17 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
mem.copy(T, items[A1.start + A1.length()..], cache[B3.start..B3.end]);
}
}
-
+
// we merged two levels at the same time, so we're done with this level already
// (iterator.nextLevel() is called again at the bottom of this outer merge loop)
_ = iterator.nextLevel();
-
+
} else {
iterator.begin();
while (!iterator.finished()) {
var A = iterator.nextRange();
var B = iterator.nextRange();
-
+
if (lessThan(items[B.end - 1], items[A.start])) {
// the two ranges are in reverse order, so a simple rotation should fix it
mem.rotate(T, items[A.start..B.end], A.length());
@@ -301,10 +301,10 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
// 6. merge each A block with any B values that follow, using the cache or the second internal buffer
// 7. sort the second internal buffer if it exists
// 8. redistribute the two internal buffers back into the items
-
+
var block_size: usize = math.sqrt(iterator.length());
var buffer_size = iterator.length()/block_size + 1;
-
+
// as an optimization, we really only need to pull out the internal buffers once for each level of merges
// after that we can reuse the same buffers over and over, then redistribute it when we're finished with this level
var A: Range = undefined;
@@ -322,11 +322,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
var buffer1 = Range.init(0, 0);
var buffer2 = Range.init(0, 0);
-
+
// find two internal buffers of size 'buffer_size' each
find = buffer_size + buffer_size;
var find_separately = false;
-
+
if (block_size <= cache.len) {
// if every A block fits into the cache then we won't need the second internal buffer,
// so we really only need to find 'buffer_size' unique values
@@ -336,21 +336,21 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
find = buffer_size;
find_separately = true;
}
-
+
// we need to find either a single contiguous space containing 2√A unique values (which will be split up into two buffers of size √A each),
// or we need to find one buffer of < 2√A unique values, and a second buffer of √A unique values,
// OR if we couldn't find that many unique values, we need the largest possible buffer we can get
-
+
// in the case where it couldn't find a single buffer of at least √A unique values,
// all of the Merge steps must be replaced by a different merge algorithm (MergeInPlace)
iterator.begin();
while (!iterator.finished()) {
A = iterator.nextRange();
B = iterator.nextRange();
-
+
// just store information about where the values will be pulled from and to,
// as well as how many values there are, to create the two internal buffers
-
+
// check A for the number of unique values we need to fill an internal buffer
// these values will be pulled out to the start of A
last = A.start;
@@ -360,7 +360,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
if (index == A.end) break;
}
index = last;
-
+
if (count >= buffer_size) {
// keep track of the range within the items where we'll need to "pull out" these values to create the internal buffer
pull[pull_index] = Pull {
@@ -370,7 +370,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
.to = A.start,
};
pull_index = 1;
-
+
if (count == buffer_size + buffer_size) {
// we were able to find a single contiguous section containing 2√A unique values,
// so this section can be used to contain both of the internal buffers we'll need
@@ -405,7 +405,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
.to = A.start,
};
}
-
+
// check B for the number of unique values we need to fill an internal buffer
// these values will be pulled out to the end of B
last = B.end - 1;
@@ -415,7 +415,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
if (index == B.start) break;
}
index = last;
-
+
if (count >= buffer_size) {
// keep track of the range within the items where we'll need to "pull out" these values to create the internal buffe
pull[pull_index] = Pull {
@@ -425,7 +425,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
.to = B.end,
};
pull_index = 1;
-
+
if (count == buffer_size + buffer_size) {
// we were able to find a single contiguous section containing 2√A unique values,
// so this section can be used to contain both of the internal buffers we'll need
@@ -449,7 +449,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
// buffer2 will be pulled out from a 'B' subarray, so if the first buffer was pulled out from the corresponding 'A' subarray,
// we need to adjust the end point for that A subarray so it knows to stop redistributing its values before reaching buffer2
if (pull[0].range.start == A.start) pull[0].range.end -= pull[1].count;
-
+
// we found a second buffer in an 'B' subarray containing √A unique values, so we're done!
buffer2 = Range.init(B.end - count, B.end);
break;
@@ -465,12 +465,12 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
};
}
}
-
+
// pull out the two ranges so we can use them as internal buffers
pull_index = 0;
while (pull_index < 2) : (pull_index += 1) {
const length = pull[pull_index].count;
-
+
if (pull[pull_index].to < pull[pull_index].from) {
// we're pulling the values out to the left, which means the start of an A subarray
index = pull[pull_index].from;
@@ -493,27 +493,27 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
}
}
}
-
+
// adjust block_size and buffer_size based on the values we were able to pull out
buffer_size = buffer1.length();
block_size = iterator.length()/buffer_size + 1;
-
+
// the first buffer NEEDS to be large enough to tag each of the evenly sized A blocks,
// so this was originally here to test the math for adjusting block_size above
// assert((iterator.length() + 1)/block_size <= buffer_size);
-
+
// now that the two internal buffers have been created, it's time to merge each A+B combination at this level of the merge sort!
iterator.begin();
while (!iterator.finished()) {
A = iterator.nextRange();
B = iterator.nextRange();
-
+
// remove any parts of A or B that are being used by the internal buffers
start = A.start;
if (start == pull[0].range.start) {
if (pull[0].from > pull[0].to) {
A.start += pull[0].count;
-
+
// if the internal buffer takes up the entire A or B subarray, then there's nothing to merge
// this only happens for very small subarrays, like √4 = 2, 2 * (2 internal buffers) = 4,
// which also only happens when cache.len is small or 0 since it'd otherwise use MergeExternal
@@ -532,25 +532,25 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
if (B.length() == 0) continue;
}
}
-
+
if (lessThan(items[B.end - 1], items[A.start])) {
// the two ranges are in reverse order, so a simple rotation should fix it
mem.rotate(T, items[A.start..B.end], A.length());
} else if (lessThan(items[A.end], items[A.end - 1])) {
// these two ranges weren't already in order, so we'll need to merge them!
var findA: usize = undefined;
-
+
// break the remainder of A into blocks. firstA is the uneven-sized first A block
var blockA = Range.init(A.start, A.end);
var firstA = Range.init(A.start, A.start + blockA.length() % block_size);
-
+
// swap the first value of each A block with the value in buffer1
var indexA = buffer1.start;
index = firstA.end;
while (index < blockA.end) : ({indexA += 1; index += block_size;}) {
mem.swap(T, &items[indexA], &items[index]);
}
-
+
// start rolling the A blocks through the B blocks!
// whenever we leave an A block behind, we'll need to merge the previous A block with any B blocks that follow it, so track that information as well
var lastA = firstA;
@@ -558,7 +558,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
var blockB = Range.init(B.start, B.start + math.min(block_size, B.length()));
blockA.start += firstA.length();
indexA = buffer1.start;
-
+
// if the first unevenly sized A block fits into the cache, copy it there for when we go to Merge it
// otherwise, if the second buffer is available, block swap the contents into that
if (lastA.length() <= cache.len) {
@@ -566,7 +566,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
} else if (buffer2.length() > 0) {
blockSwap(T, items, lastA.start, buffer2.start, lastA.length());
}
-
+
if (blockA.length() > 0) {
while (true) {
// if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block,
@@ -575,7 +575,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
// figure out where to split the previous B block, and rotate it at the split
const B_split = binaryFirst(T, items, items[indexA], lastB, lessThan);
const B_remaining = lastB.end - B_split;
-
+
// swap the minimum A block to the beginning of the rolling A blocks
var minA = blockA.start;
findA = minA + block_size;
@@ -585,16 +585,16 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
}
}
blockSwap(T, items, blockA.start, minA, block_size);
-
+
// swap the first item of the previous A block back with its original value, which is stored in buffer1
mem.swap(T, &items[blockA.start], &items[indexA]);
indexA += 1;
-
+
// locally merge the previous A block with the B values that follow it
// if lastA fits into the external cache we'll use that (with MergeExternal),
// or if the second internal buffer exists we'll use that (with MergeInternal),
// or failing that we'll use a strictly in-place merge algorithm (MergeInPlace)
-
+
if (lastA.length() <= cache.len) {
mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, cache[0..]);
} else if (buffer2.length() > 0) {
@@ -602,7 +602,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
} else {
mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), lessThan);
}
-
+
if (buffer2.length() > 0 or block_size <= cache.len) {
// copy the previous A block into the cache or buffer2, since that's where we need it to be when we go to merge it anyway
if (block_size <= cache.len) {
@@ -610,7 +610,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
} else {
blockSwap(T, items, blockA.start, buffer2.start, block_size);
}
-
+
// this is equivalent to rotating, but faster
// the area normally taken up by the A block is either the contents of buffer2, or data we don't need anymore since we memcopied it
// either way, we don't need to retain the order of those items, so instead of rotating we can just block swap B to where it belongs
@@ -619,21 +619,21 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
// we are unable to use the 'buffer2' trick to speed up the rotation operation since buffer2 doesn't exist, so perform a normal rotation
mem.rotate(T, items[B_split..blockA.start + block_size], blockA.start - B_split);
}
-
+
// update the range for the remaining A blocks, and the range remaining from the B block after it was split
lastA = Range.init(blockA.start - B_remaining, blockA.start - B_remaining + block_size);
lastB = Range.init(lastA.end, lastA.end + B_remaining);
-
+
// if there are no more A blocks remaining, this step is finished!
blockA.start += block_size;
if (blockA.length() == 0)
break;
-
+
} else if (blockB.length() < block_size) {
// move the last B block, which is unevenly sized, to before the remaining A blocks, by using a rotation
// the cache is disabled here since it might contain the contents of the previous A block
mem.rotate(T, items[blockA.start..blockB.end], blockB.start - blockA.start);
-
+
lastB = Range.init(blockA.start, blockA.start + blockB.length());
blockA.start += blockB.length();
blockA.end += blockB.length();
@@ -642,11 +642,11 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
// roll the leftmost A block to the end by swapping it with the next B block
blockSwap(T, items, blockA.start, blockB.start, block_size);
lastB = Range.init(blockA.start, blockA.start + block_size);
-
+
blockA.start += block_size;
blockA.end += block_size;
blockB.start += block_size;
-
+
if (blockB.end > B.end - block_size) {
blockB.end = B.end;
} else {
@@ -655,7 +655,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
}
}
}
-
+
// merge the last A block with the remaining B values
if (lastA.length() <= cache.len) {
mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, cache[0..]);
@@ -666,14 +666,14 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
}
}
}
-
+
// when we're finished with this merge step we should have the one or two internal buffers left over, where the second buffer is all jumbled up
// insertion sort the second buffer, then redistribute the buffers back into the items using the opposite process used for creating the buffer
-
+
// while an unstable sort like quicksort could be applied here, in benchmarks it was consistently slightly slower than a simple insertion sort,
// even for tens of millions of items. this may be because insertion sort is quite fast when the data is already somewhat sorted, like it is here
insertionSort(T, items[buffer2.start..buffer2.end], lessThan);
-
+
pull_index = 0;
while (pull_index < 2) : (pull_index += 1) {
var unique = pull[pull_index].count * 2;
@@ -702,7 +702,7 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
}
}
}
-
+
// double the size of each A and B subarray that will be merged in the next level
if (!iterator.nextLevel()) break;
}
@@ -711,37 +711,37 @@ pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &cons
// merge operation without a buffer
fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn(&const T,&const T)bool) void {
if (A_arg.length() == 0 or B_arg.length() == 0) return;
-
+
// this just repeatedly binary searches into B and rotates A into position.
// the paper suggests using the 'rotation-based Hwang and Lin algorithm' here,
// but I decided to stick with this because it had better situational performance
- //
+ //
// (Hwang and Lin is designed for merging subarrays of very different sizes,
// but WikiSort almost always uses subarrays that are roughly the same size)
- //
+ //
// normally this is incredibly suboptimal, but this function is only called
// when none of the A or B blocks in any subarray contained 2√A unique values,
// which places a hard limit on the number of times this will ACTUALLY need
// to binary search and rotate.
- //
+ //
// according to my analysis the worst case is √A rotations performed on √A items
// once the constant factors are removed, which ends up being O(n)
- //
+ //
// again, this is NOT a general-purpose solution – it only works well in this case!
// kind of like how the O(n^2) insertion sort is used in some places
var A = *A_arg;
var B = *B_arg;
-
+
while (true) {
// find the first place in B where the first item in A needs to be inserted
const mid = binaryFirst(T, items, items[A.start], B, lessThan);
-
+
// rotate A into place
const amount = mid - A.end;
mem.rotate(T, items[A.start..mid], A.length());
if (B.end == mid) break;
-
+
// calculate the new A and B ranges
B.start = mid;
A = Range.init(A.start + amount, B.start);
@@ -757,7 +757,7 @@ fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range,
var A_count: usize = 0;
var B_count: usize = 0;
var insert: usize = 0;
-
+
if (B.length() > 0 and A.length() > 0) {
while (true) {
if (!lessThan(items[B.start + B_count], items[buffer.start + A_count])) {
@@ -773,7 +773,7 @@ fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range,
}
}
}
-
+
// swap the remainder of A into the final array
blockSwap(T, items, buffer.start + A_count, A.start + insert, A.length() - A_count);
}
@@ -790,56 +790,56 @@ fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_s
fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize {
if (range.length() == 0) return range.start;
const skip = math.max(range.length()/unique, usize(1));
-
+
var index = range.start + skip;
while (lessThan(items[index - 1], value)) : (index += skip) {
if (index >= range.end - skip) {
return binaryFirst(T, items, value, Range.init(index, range.end), lessThan);
}
}
-
+
return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan);
}
fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize {
if (range.length() == 0) return range.start;
const skip = math.max(range.length()/unique, usize(1));
-
+
var index = range.end - skip;
while (index > range.start and !lessThan(items[index - 1], value)) : (index -= skip) {
if (index < range.start + skip) {
return binaryFirst(T, items, value, Range.init(range.start, index), lessThan);
}
}
-
+
return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan);
}
fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize {
if (range.length() == 0) return range.start;
const skip = math.max(range.length()/unique, usize(1));
-
+
var index = range.start + skip;
while (!lessThan(value, items[index - 1])) : (index += skip) {
if (index >= range.end - skip) {
return binaryLast(T, items, value, Range.init(index, range.end), lessThan);
}
}
-
+
return binaryLast(T, items, value, Range.init(index - skip, index), lessThan);
}
fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)bool, unique: usize) usize {
if (range.length() == 0) return range.start;
const skip = math.max(range.length()/unique, usize(1));
-
+
var index = range.end - skip;
while (index > range.start and lessThan(value, items[index - 1])) : (index -= skip) {
if (index < range.start + skip) {
return binaryLast(T, items, value, Range.init(range.start, index), lessThan);
}
}
-
+
return binaryLast(T, items, value, Range.init(index, index + skip), lessThan);
}
@@ -885,7 +885,7 @@ fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, less
const A_last = A.end;
const B_last = B.end;
var insert_index: usize = 0;
-
+
while (true) {
if (!lessThan(from[B_index], from[A_index])) {
into[insert_index] = from[A_index];
@@ -916,7 +916,7 @@ fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range,
var insert_index: usize = A.start;
const A_last = A.length();
const B_last = B.end;
-
+
if (B.length() > 0 and A.length() > 0) {
while (true) {
if (!lessThan(items[B_index], cache[A_index])) {
@@ -932,7 +932,7 @@ fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range,
}
}
}
-
+
// copy the remainder of A into the final array
mem.copy(T, items[insert_index..], cache[A_index..A_last]);
}
@@ -1081,17 +1081,17 @@ test "another sort case" {
}
test "sort fuzz testing" {
- var rng = std.rand.Rand.init(0x12345678);
+ var prng = std.rand.DefaultPrng.init(0x12345678);
const test_case_count = 10;
var i: usize = 0;
while (i < test_case_count) : (i += 1) {
- fuzzTest(&rng);
+ fuzzTest(&prng.random);
}
}
var fixed_buffer_mem: [100 * 1024]u8 = undefined;
-fn fuzzTest(rng: &std.rand.Rand) void {
+fn fuzzTest(rng: &std.rand.Random) void {
const array_size = rng.range(usize, 0, 1000);
var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
var array = fixed_allocator.allocator.alloc(IdAndValue, array_size) catch unreachable;
diff --git a/std/special/build_file_template.zig b/std/special/build_file_template.zig
index 2edfdadf50..1c06c93cdc 100644
--- a/std/special/build_file_template.zig
+++ b/std/special/build_file_template.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) !void {
+pub fn build(b: &Builder) void {
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig");
exe.setBuildMode(mode);
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index 268d0ab545..9de0aa7679 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -14,26 +14,43 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
}
}
-// Note that memset does not return `dest`, like the libc API.
-// The semantics of memset is dictated by the corresponding
-// LLVM intrinsics, not by the libc API.
-export fn memset(dest: ?&u8, c: u8, n: usize) void {
+export fn memset(dest: ?&u8, c: u8, n: usize) ?&u8 {
@setRuntimeSafety(false);
var index: usize = 0;
while (index != n) : (index += 1)
(??dest)[index] = c;
+
+ return dest;
}
-// Note that memcpy does not return `dest`, like the libc API.
-// The semantics of memcpy is dictated by the corresponding
-// LLVM intrinsics, not by the libc API.
-export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) void {
+export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) ?&u8 {
@setRuntimeSafety(false);
var index: usize = 0;
while (index != n) : (index += 1)
(??dest)[index] = (??src)[index];
+
+ return dest;
+}
+
+export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 {
+ @setRuntimeSafety(false);
+
+ if (@ptrToInt(dest) < @ptrToInt(src)) {
+ var index: usize = 0;
+ while (index != n) : (index += 1) {
+ (??dest)[index] = (??src)[index];
+ }
+ } else {
+ var index = n;
+ while (index != 0) {
+ index -= 1;
+ (??dest)[index] = (??src)[index];
+ }
+ }
+
+ return dest;
}
comptime {
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 903dc051e2..045548d624 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -11,34 +11,108 @@ pub const Node = struct {
pub const Id = enum {
Root,
VarDecl,
+ Use,
+ ErrorSetDecl,
+ ContainerDecl,
+ StructField,
+ UnionTag,
+ EnumTag,
Identifier,
+ AsyncAttribute,
FnProto,
ParamDecl,
Block,
+ Defer,
+ Comptime,
+ Payload,
+ PointerPayload,
+ PointerIndexPayload,
+ Else,
+ Switch,
+ SwitchCase,
+ SwitchElse,
+ While,
+ For,
+ If,
InfixOp,
PrefixOp,
+ SuffixOp,
+ GroupedExpression,
+ ControlFlowExpression,
+ Suspend,
+ FieldInitializer,
IntegerLiteral,
FloatLiteral,
StringLiteral,
+ MultilineStringLiteral,
+ CharLiteral,
+ BoolLiteral,
+ NullLiteral,
+ UndefinedLiteral,
+ ThisLiteral,
+ Asm,
+ AsmInput,
+ AsmOutput,
+ Unreachable,
+ ErrorType,
+ VarType,
BuiltinCall,
LineComment,
+ TestDecl,
};
pub fn iterate(base: &Node, index: usize) ?&Node {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
+ Id.Use => @fieldParentPtr(NodeUse, "base", base).iterate(index),
+ Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).iterate(index),
+ Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).iterate(index),
+ Id.StructField => @fieldParentPtr(NodeStructField, "base", base).iterate(index),
+ Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).iterate(index),
+ Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).iterate(index),
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
+ Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).iterate(index),
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
+ Id.Defer => @fieldParentPtr(NodeDefer, "base", base).iterate(index),
+ Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).iterate(index),
+ Id.Payload => @fieldParentPtr(NodePayload, "base", base).iterate(index),
+ Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).iterate(index),
+ Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).iterate(index),
+ Id.Else => @fieldParentPtr(NodeSwitch, "base", base).iterate(index),
+ Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).iterate(index),
+ Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).iterate(index),
+ Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).iterate(index),
+ Id.While => @fieldParentPtr(NodeWhile, "base", base).iterate(index),
+ Id.For => @fieldParentPtr(NodeFor, "base", base).iterate(index),
+ Id.If => @fieldParentPtr(NodeIf, "base", base).iterate(index),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
+ Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).iterate(index),
+ Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).iterate(index),
+ Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).iterate(index),
+ Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).iterate(index),
+ Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).iterate(index),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index),
+ Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).iterate(index),
+ Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).iterate(index),
+ Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).iterate(index),
+ Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).iterate(index),
+ Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).iterate(index),
+ Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).iterate(index),
+ Id.Asm => @fieldParentPtr(NodeAsm, "base", base).iterate(index),
+ Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).iterate(index),
+ Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).iterate(index),
+ Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).iterate(index),
+ Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).iterate(index),
+ Id.VarType => @fieldParentPtr(NodeVarType, "base", base).iterate(index),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
+ Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).iterate(index),
};
}
@@ -46,17 +120,54 @@ pub const Node = struct {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).firstToken(),
+ Id.Use => @fieldParentPtr(NodeUse, "base", base).firstToken(),
+ Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).firstToken(),
+ Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).firstToken(),
+ Id.StructField => @fieldParentPtr(NodeStructField, "base", base).firstToken(),
+ Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).firstToken(),
+ Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).firstToken(),
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).firstToken(),
+ Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).firstToken(),
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),
+ Id.Defer => @fieldParentPtr(NodeDefer, "base", base).firstToken(),
+ Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).firstToken(),
+ Id.Payload => @fieldParentPtr(NodePayload, "base", base).firstToken(),
+ Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).firstToken(),
+ Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).firstToken(),
+ Id.Else => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
+ Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).firstToken(),
+ Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).firstToken(),
+ Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).firstToken(),
+ Id.While => @fieldParentPtr(NodeWhile, "base", base).firstToken(),
+ Id.For => @fieldParentPtr(NodeFor, "base", base).firstToken(),
+ Id.If => @fieldParentPtr(NodeIf, "base", base).firstToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
+ Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).firstToken(),
+ Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).firstToken(),
+ Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).firstToken(),
+ Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).firstToken(),
+ Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).firstToken(),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).firstToken(),
+ Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).firstToken(),
+ Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).firstToken(),
+ Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).firstToken(),
+ Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).firstToken(),
+ Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).firstToken(),
+ Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).firstToken(),
+ Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).firstToken(),
+ Id.Asm => @fieldParentPtr(NodeAsm, "base", base).firstToken(),
+ Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).firstToken(),
+ Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).firstToken(),
+ Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).firstToken(),
+ Id.VarType => @fieldParentPtr(NodeVarType, "base", base).firstToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
+ Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).firstToken(),
};
}
@@ -64,17 +175,54 @@ pub const Node = struct {
return switch (base.id) {
Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),
Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).lastToken(),
+ Id.Use => @fieldParentPtr(NodeUse, "base", base).lastToken(),
+ Id.ErrorSetDecl => @fieldParentPtr(NodeErrorSetDecl, "base", base).lastToken(),
+ Id.ContainerDecl => @fieldParentPtr(NodeContainerDecl, "base", base).lastToken(),
+ Id.StructField => @fieldParentPtr(NodeStructField, "base", base).lastToken(),
+ Id.UnionTag => @fieldParentPtr(NodeUnionTag, "base", base).lastToken(),
+ Id.EnumTag => @fieldParentPtr(NodeEnumTag, "base", base).lastToken(),
Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).lastToken(),
+ Id.AsyncAttribute => @fieldParentPtr(NodeAsyncAttribute, "base", base).lastToken(),
Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),
Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),
Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),
+ Id.Defer => @fieldParentPtr(NodeDefer, "base", base).lastToken(),
+ Id.Comptime => @fieldParentPtr(NodeComptime, "base", base).lastToken(),
+ Id.Payload => @fieldParentPtr(NodePayload, "base", base).lastToken(),
+ Id.PointerPayload => @fieldParentPtr(NodePointerPayload, "base", base).lastToken(),
+ Id.PointerIndexPayload => @fieldParentPtr(NodePointerIndexPayload, "base", base).lastToken(),
+ Id.Else => @fieldParentPtr(NodeElse, "base", base).lastToken(),
+ Id.Switch => @fieldParentPtr(NodeSwitch, "base", base).lastToken(),
+ Id.SwitchCase => @fieldParentPtr(NodeSwitchCase, "base", base).lastToken(),
+ Id.SwitchElse => @fieldParentPtr(NodeSwitchElse, "base", base).lastToken(),
+ Id.While => @fieldParentPtr(NodeWhile, "base", base).lastToken(),
+ Id.For => @fieldParentPtr(NodeFor, "base", base).lastToken(),
+ Id.If => @fieldParentPtr(NodeIf, "base", base).lastToken(),
Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),
Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
+ Id.SuffixOp => @fieldParentPtr(NodeSuffixOp, "base", base).lastToken(),
+ Id.GroupedExpression => @fieldParentPtr(NodeGroupedExpression, "base", base).lastToken(),
+ Id.ControlFlowExpression => @fieldParentPtr(NodeControlFlowExpression, "base", base).lastToken(),
+ Id.Suspend => @fieldParentPtr(NodeSuspend, "base", base).lastToken(),
+ Id.FieldInitializer => @fieldParentPtr(NodeFieldInitializer, "base", base).lastToken(),
Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).lastToken(),
+ Id.MultilineStringLiteral => @fieldParentPtr(NodeMultilineStringLiteral, "base", base).lastToken(),
+ Id.CharLiteral => @fieldParentPtr(NodeCharLiteral, "base", base).lastToken(),
+ Id.BoolLiteral => @fieldParentPtr(NodeBoolLiteral, "base", base).lastToken(),
+ Id.NullLiteral => @fieldParentPtr(NodeNullLiteral, "base", base).lastToken(),
+ Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).lastToken(),
+ Id.ThisLiteral => @fieldParentPtr(NodeThisLiteral, "base", base).lastToken(),
+ Id.Asm => @fieldParentPtr(NodeAsm, "base", base).lastToken(),
+ Id.AsmInput => @fieldParentPtr(NodeAsmInput, "base", base).lastToken(),
+ Id.AsmOutput => @fieldParentPtr(NodeAsmOutput, "base", base).lastToken(),
+ Id.Unreachable => @fieldParentPtr(NodeUnreachable, "base", base).lastToken(),
+ Id.ErrorType => @fieldParentPtr(NodeErrorType, "base", base).lastToken(),
+ Id.VarType => @fieldParentPtr(NodeVarType, "base", base).lastToken(),
Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
+ Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).lastToken(),
};
}
};
@@ -148,6 +296,190 @@ pub const NodeVarDecl = struct {
}
};
+pub const NodeUse = struct {
+ base: Node,
+ visib_token: ?Token,
+ expr: &Node,
+ semicolon_token: Token,
+
+ pub fn iterate(self: &NodeUse, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeUse) Token {
+ if (self.visib_token) |visib_token| return visib_token;
+ return self.expr.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeUse) Token {
+ return self.semicolon_token;
+ }
+};
+
+pub const NodeErrorSetDecl = struct {
+ base: Node,
+ error_token: Token,
+ decls: ArrayList(&NodeIdentifier),
+ rbrace_token: Token,
+
+ pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node {
+ var i = index;
+
+ if (i < self.decls.len) return &self.decls.at(i).base;
+ i -= self.decls.len;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeErrorSetDecl) Token {
+ return self.error_token;
+ }
+
+ pub fn lastToken(self: &NodeErrorSetDecl) Token {
+ return self.rbrace_token;
+ }
+};
+
+pub const NodeContainerDecl = struct {
+ base: Node,
+ ltoken: Token,
+ layout: Layout,
+ kind: Kind,
+ init_arg_expr: InitArg,
+ fields_and_decls: ArrayList(&Node),
+ rbrace_token: Token,
+
+ const Layout = enum {
+ Auto,
+ Extern,
+ Packed,
+ };
+
+ const Kind = enum {
+ Struct,
+ Enum,
+ Union,
+ };
+
+ const InitArg = union(enum) {
+ None,
+ Enum,
+ Type: &Node,
+ };
+
+ pub fn iterate(self: &NodeContainerDecl, index: usize) ?&Node {
+ var i = index;
+
+ switch (self.init_arg_expr) {
+ InitArg.Type => |t| {
+ if (i < 1) return t;
+ i -= 1;
+ },
+ InitArg.None,
+ InitArg.Enum => { }
+ }
+
+ if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i);
+ i -= self.fields_and_decls.len;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeContainerDecl) Token {
+ return self.ltoken;
+ }
+
+ pub fn lastToken(self: &NodeContainerDecl) Token {
+ return self.rbrace_token;
+ }
+};
+
+pub const NodeStructField = struct {
+ base: Node,
+ name_token: Token,
+ type_expr: &Node,
+
+ pub fn iterate(self: &NodeStructField, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.type_expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeStructField) Token {
+ return self.name_token;
+ }
+
+ pub fn lastToken(self: &NodeStructField) Token {
+ return self.type_expr.lastToken();
+ }
+};
+
+pub const NodeUnionTag = struct {
+ base: Node,
+ name_token: Token,
+ type_expr: ?&Node,
+
+ pub fn iterate(self: &NodeUnionTag, index: usize) ?&Node {
+ var i = index;
+
+ if (self.type_expr) |type_expr| {
+ if (i < 1) return type_expr;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeUnionTag) Token {
+ return self.name_token;
+ }
+
+ pub fn lastToken(self: &NodeUnionTag) Token {
+ if (self.type_expr) |type_expr| {
+ return type_expr.lastToken();
+ }
+
+ return self.name_token;
+ }
+};
+
+pub const NodeEnumTag = struct {
+ base: Node,
+ name_token: Token,
+ value: ?&Node,
+
+ pub fn iterate(self: &NodeEnumTag, index: usize) ?&Node {
+ var i = index;
+
+ if (self.value) |value| {
+ if (i < 1) return value;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeEnumTag) Token {
+ return self.name_token;
+ }
+
+ pub fn lastToken(self: &NodeEnumTag) Token {
+ if (self.value) |value| {
+ return value.lastToken();
+ }
+
+ return self.name_token;
+ }
+};
+
pub const NodeIdentifier = struct {
base: Node,
name_token: Token,
@@ -165,6 +497,36 @@ pub const NodeIdentifier = struct {
}
};
+pub const NodeAsyncAttribute = struct {
+ base: Node,
+ async_token: Token,
+ allocator_type: ?&Node,
+ rangle_bracket: ?Token,
+
+ pub fn iterate(self: &NodeAsyncAttribute, index: usize) ?&Node {
+ var i = index;
+
+ if (self.allocator_type) |allocator_type| {
+ if (i < 1) return allocator_type;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeAsyncAttribute) Token {
+ return self.async_token;
+ }
+
+ pub fn lastToken(self: &NodeAsyncAttribute) Token {
+ if (self.rangle_bracket) |rangle_bracket| {
+ return rangle_bracket;
+ }
+
+ return self.async_token;
+ }
+};
+
pub const NodeFnProto = struct {
base: Node,
visib_token: ?Token,
@@ -176,13 +538,13 @@ pub const NodeFnProto = struct {
extern_token: ?Token,
inline_token: ?Token,
cc_token: ?Token,
+ async_attr: ?&NodeAsyncAttribute,
body_node: ?&Node,
lib_name: ?&Node, // populated if this is an extern declaration
align_expr: ?&Node, // populated if align(A) is present
pub const ReturnType = union(enum) {
Explicit: &Node,
- Infer: Token,
InferErrorSet: &Node,
};
@@ -204,7 +566,6 @@ pub const NodeFnProto = struct {
if (i < 1) return node;
i -= 1;
},
- ReturnType.Infer => {},
}
if (self.align_expr) |align_expr| {
@@ -238,7 +599,6 @@ pub const NodeFnProto = struct {
// TODO allow this and next prong to share bodies since the types are the same
ReturnType.Explicit => |node| return node.lastToken(),
ReturnType.InferErrorSet => |node| return node.lastToken(),
- ReturnType.Infer => |token| return token,
}
}
};
@@ -275,9 +635,10 @@ pub const NodeParamDecl = struct {
pub const NodeBlock = struct {
base: Node,
- begin_token: Token,
- end_token: Token,
+ label: ?Token,
+ lbrace: Token,
statements: ArrayList(&Node),
+ rbrace: Token,
pub fn iterate(self: &NodeBlock, index: usize) ?&Node {
var i = index;
@@ -289,11 +650,408 @@ pub const NodeBlock = struct {
}
pub fn firstToken(self: &NodeBlock) Token {
- return self.begin_token;
+ if (self.label) |label| {
+ return label;
+ }
+
+ return self.lbrace;
}
pub fn lastToken(self: &NodeBlock) Token {
- return self.end_token;
+ return self.rbrace;
+ }
+};
+
+pub const NodeDefer = struct {
+ base: Node,
+ defer_token: Token,
+ kind: Kind,
+ expr: &Node,
+
+ const Kind = enum {
+ Error,
+ Unconditional,
+ };
+
+ pub fn iterate(self: &NodeDefer, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeDefer) Token {
+ return self.defer_token;
+ }
+
+ pub fn lastToken(self: &NodeDefer) Token {
+ return self.expr.lastToken();
+ }
+};
+
+pub const NodeComptime = struct {
+ base: Node,
+ comptime_token: Token,
+ expr: &Node,
+
+ pub fn iterate(self: &NodeComptime, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeComptime) Token {
+ return self.comptime_token;
+ }
+
+ pub fn lastToken(self: &NodeComptime) Token {
+ return self.expr.lastToken();
+ }
+};
+
+pub const NodePayload = struct {
+ base: Node,
+ lpipe: Token,
+ error_symbol: &NodeIdentifier,
+ rpipe: Token,
+
+ pub fn iterate(self: &NodePayload, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.error_symbol.base;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodePayload) Token {
+ return self.lpipe;
+ }
+
+ pub fn lastToken(self: &NodePayload) Token {
+ return self.rpipe;
+ }
+};
+
+pub const NodePointerPayload = struct {
+ base: Node,
+ lpipe: Token,
+ is_ptr: bool,
+ value_symbol: &NodeIdentifier,
+ rpipe: Token,
+
+ pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.value_symbol.base;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodePointerPayload) Token {
+ return self.lpipe;
+ }
+
+ pub fn lastToken(self: &NodePointerPayload) Token {
+ return self.rpipe;
+ }
+};
+
+pub const NodePointerIndexPayload = struct {
+ base: Node,
+ lpipe: Token,
+ is_ptr: bool,
+ value_symbol: &NodeIdentifier,
+ index_symbol: ?&NodeIdentifier,
+ rpipe: Token,
+
+ pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.value_symbol.base;
+ i -= 1;
+
+ if (self.index_symbol) |index_symbol| {
+ if (i < 1) return &index_symbol.base;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodePointerIndexPayload) Token {
+ return self.lpipe;
+ }
+
+ pub fn lastToken(self: &NodePointerIndexPayload) Token {
+ return self.rpipe;
+ }
+};
+
+pub const NodeElse = struct {
+ base: Node,
+ else_token: Token,
+ payload: ?&NodePayload,
+ body: &Node,
+
+ pub fn iterate(self: &NodeElse, index: usize) ?&Node {
+ var i = index;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (i < 1) return self.body;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeElse) Token {
+ return self.else_token;
+ }
+
+ pub fn lastToken(self: &NodeElse) Token {
+ return self.body.lastToken();
+ }
+};
+
+pub const NodeSwitch = struct {
+ base: Node,
+ switch_token: Token,
+ expr: &Node,
+ cases: ArrayList(&NodeSwitchCase),
+ rbrace: Token,
+
+ pub fn iterate(self: &NodeSwitch, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ if (i < self.cases.len) return &self.cases.at(i).base;
+ i -= self.cases.len;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSwitch) Token {
+ return self.switch_token;
+ }
+
+ pub fn lastToken(self: &NodeSwitch) Token {
+ return self.rbrace;
+ }
+};
+
+pub const NodeSwitchCase = struct {
+ base: Node,
+ items: ArrayList(&Node),
+ payload: ?&NodePointerPayload,
+ expr: &Node,
+
+ pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {
+ var i = index;
+
+ if (i < self.items.len) return self.items.at(i);
+ i -= self.items.len;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSwitchCase) Token {
+ return self.items.at(0).firstToken();
+ }
+
+ pub fn lastToken(self: &NodeSwitchCase) Token {
+ return self.expr.lastToken();
+ }
+};
+
+pub const NodeSwitchElse = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeSwitchElse, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSwitchElse) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeSwitchElse) Token {
+ return self.token;
+ }
+};
+
+pub const NodeWhile = struct {
+ base: Node,
+ label: ?Token,
+ inline_token: ?Token,
+ while_token: Token,
+ condition: &Node,
+ payload: ?&NodePointerPayload,
+ continue_expr: ?&Node,
+ body: &Node,
+ @"else": ?&NodeElse,
+
+ pub fn iterate(self: &NodeWhile, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.condition;
+ i -= 1;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (self.continue_expr) |continue_expr| {
+ if (i < 1) return continue_expr;
+ i -= 1;
+ }
+
+ if (i < 1) return self.body;
+ i -= 1;
+
+ if (self.@"else") |@"else"| {
+ if (i < 1) return &@"else".base;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeWhile) Token {
+ if (self.label) |label| {
+ return label;
+ }
+
+ if (self.inline_token) |inline_token| {
+ return inline_token;
+ }
+
+ return self.while_token;
+ }
+
+ pub fn lastToken(self: &NodeWhile) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
+
+ return self.body.lastToken();
+ }
+};
+
+pub const NodeFor = struct {
+ base: Node,
+ label: ?Token,
+ inline_token: ?Token,
+ for_token: Token,
+ array_expr: &Node,
+ payload: ?&NodePointerIndexPayload,
+ body: &Node,
+ @"else": ?&NodeElse,
+
+ pub fn iterate(self: &NodeFor, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.array_expr;
+ i -= 1;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (i < 1) return self.body;
+ i -= 1;
+
+ if (self.@"else") |@"else"| {
+ if (i < 1) return &@"else".base;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeFor) Token {
+ if (self.label) |label| {
+ return label;
+ }
+
+ if (self.inline_token) |inline_token| {
+ return inline_token;
+ }
+
+ return self.for_token;
+ }
+
+ pub fn lastToken(self: &NodeFor) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
+
+ return self.body.lastToken();
+ }
+};
+
+pub const NodeIf = struct {
+ base: Node,
+ if_token: Token,
+ condition: &Node,
+ payload: ?&NodePointerPayload,
+ body: &Node,
+ @"else": ?&NodeElse,
+
+ pub fn iterate(self: &NodeIf, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.condition;
+ i -= 1;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (i < 1) return self.body;
+ i -= 1;
+
+ if (self.@"else") |@"else"| {
+ if (i < 1) return &@"else".base;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeIf) Token {
+ return self.if_token;
+ }
+
+ pub fn lastToken(self: &NodeIf) Token {
+ if (self.@"else") |@"else"| {
+ return @"else".body.lastToken();
+ }
+
+ return self.body.lastToken();
}
};
@@ -304,10 +1062,50 @@ pub const NodeInfixOp = struct {
op: InfixOp,
rhs: &Node,
- const InfixOp = enum {
- EqualEqual,
+ const InfixOp = union(enum) {
+ Add,
+ AddWrap,
+ ArrayCat,
+ ArrayMult,
+ Assign,
+ AssignBitAnd,
+ AssignBitOr,
+ AssignBitShiftLeft,
+ AssignBitShiftRight,
+ AssignBitXor,
+ AssignDiv,
+ AssignMinus,
+ AssignMinusWrap,
+ AssignMod,
+ AssignPlus,
+ AssignPlusWrap,
+ AssignTimes,
+ AssignTimesWarp,
BangEqual,
+ BitAnd,
+ BitOr,
+ BitShiftLeft,
+ BitShiftRight,
+ BitXor,
+ BoolAnd,
+ BoolOr,
+ Catch: ?&NodePayload,
+ Div,
+ EqualEqual,
+ ErrorUnion,
+ GreaterOrEqual,
+ GreaterThan,
+ LessOrEqual,
+ LessThan,
+ MergeErrorSets,
+ Mod,
+ Mult,
+ MultWrap,
Period,
+ Range,
+ Sub,
+ SubWrap,
+ UnwrapMaybe,
};
pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
@@ -317,9 +1115,55 @@ pub const NodeInfixOp = struct {
i -= 1;
switch (self.op) {
- InfixOp.EqualEqual,
+ InfixOp.Catch => |maybe_payload| {
+ if (maybe_payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+ },
+
+ InfixOp.Add,
+ InfixOp.AddWrap,
+ InfixOp.ArrayCat,
+ InfixOp.ArrayMult,
+ InfixOp.Assign,
+ InfixOp.AssignBitAnd,
+ InfixOp.AssignBitOr,
+ InfixOp.AssignBitShiftLeft,
+ InfixOp.AssignBitShiftRight,
+ InfixOp.AssignBitXor,
+ InfixOp.AssignDiv,
+ InfixOp.AssignMinus,
+ InfixOp.AssignMinusWrap,
+ InfixOp.AssignMod,
+ InfixOp.AssignPlus,
+ InfixOp.AssignPlusWrap,
+ InfixOp.AssignTimes,
+ InfixOp.AssignTimesWarp,
InfixOp.BangEqual,
- InfixOp.Period => {},
+ InfixOp.BitAnd,
+ InfixOp.BitOr,
+ InfixOp.BitShiftLeft,
+ InfixOp.BitShiftRight,
+ InfixOp.BitXor,
+ InfixOp.BoolAnd,
+ InfixOp.BoolOr,
+ InfixOp.Div,
+ InfixOp.EqualEqual,
+ InfixOp.ErrorUnion,
+ InfixOp.GreaterOrEqual,
+ InfixOp.GreaterThan,
+ InfixOp.LessOrEqual,
+ InfixOp.LessThan,
+ InfixOp.MergeErrorSets,
+ InfixOp.Mod,
+ InfixOp.Mult,
+ InfixOp.MultWrap,
+ InfixOp.Period,
+ InfixOp.Range,
+ InfixOp.Sub,
+ InfixOp.SubWrap,
+ InfixOp.UnwrapMaybe => {},
}
if (i < 1) return self.rhs;
@@ -344,10 +1188,22 @@ pub const NodePrefixOp = struct {
rhs: &Node,
const PrefixOp = union(enum) {
- Return,
- Try,
AddrOf: AddrOfInfo,
+ ArrayType: &Node,
+ Await,
+ BitNot,
+ BoolNot,
+ Cancel,
+ Deref,
+ MaybeType,
+ Negation,
+ NegationWrap,
+ Resume,
+ SliceType: AddrOfInfo,
+ Try,
+ UnwrapMaybe,
};
+
const AddrOfInfo = struct {
align_expr: ?&Node,
bit_offset_start_token: ?Token,
@@ -360,14 +1216,33 @@ pub const NodePrefixOp = struct {
var i = index;
switch (self.op) {
- PrefixOp.Return,
- PrefixOp.Try => {},
+ PrefixOp.SliceType => |addr_of_info| {
+ if (addr_of_info.align_expr) |align_expr| {
+ if (i < 1) return align_expr;
+ i -= 1;
+ }
+ },
PrefixOp.AddrOf => |addr_of_info| {
if (addr_of_info.align_expr) |align_expr| {
if (i < 1) return align_expr;
i -= 1;
}
},
+ PrefixOp.ArrayType => |size_expr| {
+ if (i < 1) return size_expr;
+ i -= 1;
+ },
+ PrefixOp.Await,
+ PrefixOp.BitNot,
+ PrefixOp.BoolNot,
+ PrefixOp.Cancel,
+ PrefixOp.Deref,
+ PrefixOp.MaybeType,
+ PrefixOp.Negation,
+ PrefixOp.NegationWrap,
+ PrefixOp.Try,
+ PrefixOp.Resume,
+ PrefixOp.UnwrapMaybe => {},
}
if (i < 1) return self.rhs;
@@ -385,6 +1260,213 @@ pub const NodePrefixOp = struct {
}
};
+pub const NodeFieldInitializer = struct {
+ base: Node,
+ period_token: Token,
+ name_token: Token,
+ expr: &Node,
+
+ pub fn iterate(self: &NodeFieldInitializer, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeFieldInitializer) Token {
+ return self.period_token;
+ }
+
+ pub fn lastToken(self: &NodeFieldInitializer) Token {
+ return self.expr.lastToken();
+ }
+};
+
+pub const NodeSuffixOp = struct {
+ base: Node,
+ lhs: &Node,
+ op: SuffixOp,
+ rtoken: Token,
+
+ const SuffixOp = union(enum) {
+ Call: CallInfo,
+ ArrayAccess: &Node,
+ Slice: SliceRange,
+ ArrayInitializer: ArrayList(&Node),
+ StructInitializer: ArrayList(&NodeFieldInitializer),
+ };
+
+ const CallInfo = struct {
+ params: ArrayList(&Node),
+ async_attr: ?&NodeAsyncAttribute,
+ };
+
+ const SliceRange = struct {
+ start: &Node,
+ end: ?&Node,
+ };
+
+ pub fn iterate(self: &NodeSuffixOp, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.lhs;
+ i -= 1;
+
+ switch (self.op) {
+ SuffixOp.Call => |call_info| {
+ if (i < call_info.params.len) return call_info.params.at(i);
+ i -= call_info.params.len;
+ },
+ SuffixOp.ArrayAccess => |index_expr| {
+ if (i < 1) return index_expr;
+ i -= 1;
+ },
+ SuffixOp.Slice => |range| {
+ if (i < 1) return range.start;
+ i -= 1;
+
+ if (range.end) |end| {
+ if (i < 1) return end;
+ i -= 1;
+ }
+ },
+ SuffixOp.ArrayInitializer => |exprs| {
+ if (i < exprs.len) return exprs.at(i);
+ i -= exprs.len;
+ },
+ SuffixOp.StructInitializer => |fields| {
+ if (i < fields.len) return &fields.at(i).base;
+ i -= fields.len;
+ },
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSuffixOp) Token {
+ return self.lhs.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeSuffixOp) Token {
+ return self.rtoken;
+ }
+};
+
+pub const NodeGroupedExpression = struct {
+ base: Node,
+ lparen: Token,
+ expr: &Node,
+ rparen: Token,
+
+ pub fn iterate(self: &NodeGroupedExpression, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeGroupedExpression) Token {
+ return self.lparen;
+ }
+
+ pub fn lastToken(self: &NodeGroupedExpression) Token {
+ return self.rparen;
+ }
+};
+
+pub const NodeControlFlowExpression = struct {
+ base: Node,
+ ltoken: Token,
+ kind: Kind,
+ rhs: ?&Node,
+
+ const Kind = union(enum) {
+ Break: ?Token,
+ Continue: ?Token,
+ Return,
+ };
+
+ pub fn iterate(self: &NodeControlFlowExpression, index: usize) ?&Node {
+ var i = index;
+
+ if (self.rhs) |rhs| {
+ if (i < 1) return rhs;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeControlFlowExpression) Token {
+ return self.ltoken;
+ }
+
+ pub fn lastToken(self: &NodeControlFlowExpression) Token {
+ if (self.rhs) |rhs| {
+ return rhs.lastToken();
+ }
+
+ switch (self.kind) {
+ Kind.Break => |maybe_blk_token| {
+ if (maybe_blk_token) |blk_token| {
+ return blk_token;
+ }
+ },
+ Kind.Continue => |maybe_blk_token| {
+ if (maybe_blk_token) |blk_token| {
+ return blk_token;
+ }
+ },
+ Kind.Return => return self.ltoken,
+ }
+
+ return self.ltoken;
+ }
+};
+
+pub const NodeSuspend = struct {
+ base: Node,
+ suspend_token: Token,
+ payload: ?&NodePayload,
+ body: ?&Node,
+
+ pub fn iterate(self: &NodeSuspend, index: usize) ?&Node {
+ var i = index;
+
+ if (self.payload) |payload| {
+ if (i < 1) return &payload.base;
+ i -= 1;
+ }
+
+ if (self.body) |body| {
+ if (i < 1) return body;
+ i -= 1;
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeSuspend) Token {
+ return self.suspend_token;
+ }
+
+ pub fn lastToken(self: &NodeSuspend) Token {
+ if (self.body) |body| {
+ return body.lastToken();
+ }
+
+ if (self.payload) |payload| {
+ return payload.lastToken();
+ }
+
+ return self.suspend_token;
+ }
+};
+
pub const NodeIntegerLiteral = struct {
base: Node,
token: Token,
@@ -460,6 +1542,270 @@ pub const NodeStringLiteral = struct {
}
};
+pub const NodeMultilineStringLiteral = struct {
+ base: Node,
+ tokens: ArrayList(Token),
+
+ pub fn iterate(self: &NodeMultilineStringLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeMultilineStringLiteral) Token {
+ return self.tokens.at(0);
+ }
+
+ pub fn lastToken(self: &NodeMultilineStringLiteral) Token {
+ return self.tokens.at(self.tokens.len - 1);
+ }
+};
+
+pub const NodeCharLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeCharLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeCharLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeCharLiteral) Token {
+ return self.token;
+ }
+};
+
+pub const NodeBoolLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeBoolLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeBoolLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeBoolLiteral) Token {
+ return self.token;
+ }
+};
+
+pub const NodeNullLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeNullLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeNullLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeNullLiteral) Token {
+ return self.token;
+ }
+};
+
+pub const NodeUndefinedLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeUndefinedLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeUndefinedLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeUndefinedLiteral) Token {
+ return self.token;
+ }
+};
+
+pub const NodeThisLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeThisLiteral, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeThisLiteral) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeThisLiteral) Token {
+ return self.token;
+ }
+};
+
+pub const NodeAsmOutput = struct {
+ base: Node,
+ symbolic_name: &NodeIdentifier,
+ constraint: &NodeStringLiteral,
+ kind: Kind,
+
+ const Kind = union(enum) {
+ Variable: &NodeIdentifier,
+ Return: &Node
+ };
+
+ pub fn iterate(self: &NodeAsmOutput, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.symbolic_name.base;
+ i -= 1;
+
+ if (i < 1) return &self.constraint.base;
+ i -= 1;
+
+ switch (self.kind) {
+ Kind.Variable => |variable_name| {
+ if (i < 1) return &variable_name.base;
+ i -= 1;
+ },
+ Kind.Return => |return_type| {
+ if (i < 1) return return_type;
+ i -= 1;
+ }
+ }
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeAsmOutput) Token {
+ return self.symbolic_name.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeAsmOutput) Token {
+ return switch (self.kind) {
+ Kind.Variable => |variable_name| variable_name.lastToken(),
+ Kind.Return => |return_type| return_type.lastToken(),
+ };
+ }
+};
+
+pub const NodeAsmInput = struct {
+ base: Node,
+ symbolic_name: &NodeIdentifier,
+ constraint: &NodeStringLiteral,
+ expr: &Node,
+
+ pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return &self.symbolic_name.base;
+ i -= 1;
+
+ if (i < 1) return &self.constraint.base;
+ i -= 1;
+
+ if (i < 1) return self.expr;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeAsmInput) Token {
+ return self.symbolic_name.firstToken();
+ }
+
+ pub fn lastToken(self: &NodeAsmInput) Token {
+ return self.expr.lastToken();
+ }
+};
+
+pub const NodeAsm = struct {
+ base: Node,
+ asm_token: Token,
+ is_volatile: bool,
+ template: Token,
+ //tokens: ArrayList(AsmToken),
+ outputs: ArrayList(&NodeAsmOutput),
+ inputs: ArrayList(&NodeAsmInput),
+ cloppers: ArrayList(&NodeStringLiteral),
+ rparen: Token,
+
+ pub fn iterate(self: &NodeAsm, index: usize) ?&Node {
+ var i = index;
+
+ if (i < self.outputs.len) return &self.outputs.at(index).base;
+ i -= self.outputs.len;
+
+ if (i < self.inputs.len) return &self.inputs.at(index).base;
+ i -= self.inputs.len;
+
+ if (i < self.cloppers.len) return &self.cloppers.at(index).base;
+ i -= self.cloppers.len;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeAsm) Token {
+ return self.asm_token;
+ }
+
+ pub fn lastToken(self: &NodeAsm) Token {
+ return self.rparen;
+ }
+};
+
+pub const NodeUnreachable = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeUnreachable, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeUnreachable) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeUnreachable) Token {
+ return self.token;
+ }
+};
+
+pub const NodeErrorType = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeErrorType, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeErrorType) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeErrorType) Token {
+ return self.token;
+ }
+};
+
+pub const NodeVarType = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeVarType, index: usize) ?&Node {
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeVarType) Token {
+ return self.token;
+ }
+
+ pub fn lastToken(self: &NodeVarType) Token {
+ return self.token;
+ }
+};
+
pub const NodeLineComment = struct {
base: Node,
lines: ArrayList(Token),
@@ -476,3 +1822,27 @@ pub const NodeLineComment = struct {
return self.lines.at(self.lines.len - 1);
}
};
+
+pub const NodeTestDecl = struct {
+ base: Node,
+ test_token: Token,
+ name: &Node,
+ body_node: &Node,
+
+ pub fn iterate(self: &NodeTestDecl, index: usize) ?&Node {
+ var i = index;
+
+ if (i < 1) return self.body_node;
+ i -= 1;
+
+ return null;
+ }
+
+ pub fn firstToken(self: &NodeTestDecl) Token {
+ return self.test_token;
+ }
+
+ pub fn lastToken(self: &NodeTestDecl) Token {
+ return self.body_node.lastToken();
+ }
+};
diff --git a/std/zig/parser.zig b/std/zig/parser.zig
index 533ad754ac..11b551fec0 100644
--- a/std/zig/parser.zig
+++ b/std/zig/parser.zig
@@ -53,20 +53,33 @@ pub const Parser = struct {
}
const TopLevelDeclCtx = struct {
+ decls: &ArrayList(&ast.Node),
visib_token: ?Token,
extern_token: ?Token,
+ lib_name: ?&ast.Node,
+ };
+
+ const ContainerExternCtx = struct {
+ dest_ptr: DestPtr,
+ ltoken: Token,
+ layout: ast.NodeContainerDecl.Layout,
};
const DestPtr = union(enum) {
Field: &&ast.Node,
NullableField: &?&ast.Node,
- List: &ArrayList(&ast.Node),
- pub fn store(self: &const DestPtr, value: &ast.Node) !void {
+ pub fn store(self: &const DestPtr, value: &ast.Node) void {
switch (*self) {
DestPtr.Field => |ptr| *ptr = value,
DestPtr.NullableField => |ptr| *ptr = value,
- DestPtr.List => |list| try list.append(value),
+ }
+ }
+
+ pub fn get(self: &const DestPtr) &ast.Node {
+ switch (*self) {
+ DestPtr.Field => |ptr| return *ptr,
+ DestPtr.NullableField => |ptr| return ??*ptr,
}
}
};
@@ -76,21 +89,69 @@ pub const Parser = struct {
ptr: &Token,
};
+ const RevertState = struct {
+ parser: Parser,
+ tokenizer: Tokenizer,
+
+ // We expect, that if something is optional, then there is a field,
+ // that needs to be set to null, when we revert.
+ ptr: &?&ast.Node,
+ };
+
+ const ExprListCtx = struct {
+ list: &ArrayList(&ast.Node),
+ end: Token.Id,
+ ptr: &Token,
+ };
+
+ const ElseCtx = struct {
+ payload: ?DestPtr,
+ body: DestPtr,
+ };
+
+ fn ListSave(comptime T: type) type {
+ return struct {
+ list: &ArrayList(T),
+ ptr: &Token,
+ };
+ }
+
+ const LabelCtx = struct {
+ label: ?Token,
+ dest_ptr: DestPtr,
+ };
+
+ const InlineCtx = struct {
+ label: ?Token,
+ inline_token: ?Token,
+ dest_ptr: DestPtr,
+ };
+
+ const LoopCtx = struct {
+ label: ?Token,
+ inline_token: ?Token,
+ loop_token: Token,
+ dest_ptr: DestPtr,
+ };
+
+ const AsyncEndCtx = struct {
+ dest_ptr: DestPtr,
+ attribute: &ast.NodeAsyncAttribute,
+ };
+
const State = union(enum) {
TopLevel,
- TopLevelExtern: ?Token,
+ TopLevelExtern: TopLevelDeclCtx,
TopLevelDecl: TopLevelDeclCtx,
- Expression: DestPtr,
- ExpectOperand,
- Operand: &ast.Node,
- AfterOperand,
- InfixOp: &ast.NodeInfixOp,
- PrefixOp: &ast.NodePrefixOp,
+ ContainerExtern: ContainerExternCtx,
+ ContainerDecl: &ast.NodeContainerDecl,
+ SliceOrArrayAccess: &ast.NodeSuffixOp,
AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
- TypeExpr: DestPtr,
VarDecl: &ast.NodeVarDecl,
VarDeclAlign: &ast.NodeVarDecl,
VarDeclEq: &ast.NodeVarDecl,
+ IfToken: @TagType(Token.Id),
+ IfTokenSave: ExpectTokenSave,
ExpectToken: @TagType(Token.Id),
ExpectTokenSave: ExpectTokenSave,
FnProto: &ast.NodeFnProto,
@@ -99,10 +160,73 @@ pub const Parser = struct {
ParamDecl: &ast.NodeFnProto,
ParamDeclComma,
FnDef: &ast.NodeFnProto,
+ LabeledExpression: LabelCtx,
+ Inline: InlineCtx,
+ While: LoopCtx,
+ For: LoopCtx,
Block: &ast.NodeBlock,
+ Else: &?&ast.NodeElse,
+ WhileContinueExpr: &?&ast.Node,
Statement: &ast.NodeBlock,
- ExprListItemOrEnd: &ArrayList(&ast.Node),
- ExprListCommaOrEnd: &ArrayList(&ast.Node),
+ Semicolon: &const &const ast.Node,
+ AsmOutputItems: &ArrayList(&ast.NodeAsmOutput),
+ AsmInputItems: &ArrayList(&ast.NodeAsmInput),
+ AsmClopperItems: &ArrayList(&ast.NodeStringLiteral),
+ ExprListItemOrEnd: ExprListCtx,
+ ExprListCommaOrEnd: ExprListCtx,
+ FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),
+ FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),
+ FieldListCommaOrEnd: &ast.NodeContainerDecl,
+ SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),
+ SuspendBody: &ast.NodeSuspend,
+ AsyncEnd: AsyncEndCtx,
+ Payload: &?&ast.NodePayload,
+ PointerPayload: &?&ast.NodePointerPayload,
+ PointerIndexPayload: &?&ast.NodePointerIndexPayload,
+ SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
+ SwitchCaseItem: &ArrayList(&ast.Node),
+ SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
+
+ /// A state that can be appended before any other State. If an error occures,
+ /// the parser will first try looking for the closest optional state. If an
+ /// optional state is found, the parser will revert to the state it was in
+ /// when the optional was added. This will polute the arena allocator with
+ /// "leaked" nodes. TODO: Figure out if it's nessesary to handle leaked nodes.
+ Optional: RevertState,
+
+ Expression: DestPtr,
+ RangeExpressionBegin: DestPtr,
+ RangeExpressionEnd: DestPtr,
+ AssignmentExpressionBegin: DestPtr,
+ AssignmentExpressionEnd: DestPtr,
+ UnwrapExpressionBegin: DestPtr,
+ UnwrapExpressionEnd: DestPtr,
+ BoolOrExpressionBegin: DestPtr,
+ BoolOrExpressionEnd: DestPtr,
+ BoolAndExpressionBegin: DestPtr,
+ BoolAndExpressionEnd: DestPtr,
+ ComparisonExpressionBegin: DestPtr,
+ ComparisonExpressionEnd: DestPtr,
+ BinaryOrExpressionBegin: DestPtr,
+ BinaryOrExpressionEnd: DestPtr,
+ BinaryXorExpressionBegin: DestPtr,
+ BinaryXorExpressionEnd: DestPtr,
+ BinaryAndExpressionBegin: DestPtr,
+ BinaryAndExpressionEnd: DestPtr,
+ BitShiftExpressionBegin: DestPtr,
+ BitShiftExpressionEnd: DestPtr,
+ AdditionExpressionBegin: DestPtr,
+ AdditionExpressionEnd: DestPtr,
+ MultiplyExpressionBegin: DestPtr,
+ MultiplyExpressionEnd: DestPtr,
+ CurlySuffixExpressionBegin: DestPtr,
+ CurlySuffixExpressionEnd: DestPtr,
+ TypeExprBegin: DestPtr,
+ TypeExprEnd: DestPtr,
+ PrefixOpExpression: DestPtr,
+ SuffixOpExpressionBegin: DestPtr,
+ SuffixOpExpressionEnd: DestPtr,
+ PrimaryExpression: DestPtr,
};
/// Returns an AST tree, allocated with the parser's allocator.
@@ -167,88 +291,199 @@ pub const Parser = struct {
State.TopLevel => {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_pub, Token.Id.Keyword_export => {
- stack.append(State { .TopLevelExtern = token }) catch unreachable;
+ Token.Id.Keyword_test => {
+ stack.append(State.TopLevel) catch unreachable;
+
+ const name_token = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
+ const lbrace = (try self.eatToken(&stack, Token.Id.LBrace)) ?? continue;
+
+ const name = try self.createStringLiteral(arena, name_token);
+ const block = try self.createBlock(arena, (?Token)(null), token);
+ const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, &name.base, block);
+ stack.append(State { .Block = block }) catch unreachable;
continue;
},
Token.Id.Eof => {
root_node.eof_token = token;
return Tree {.root_node = root_node, .arena_allocator = arena_allocator};
},
+ Token.Id.Keyword_pub, Token.Id.Keyword_export => {
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = token,
+ .extern_token = null,
+ .lib_name = null,
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_comptime => {
+ const node = try arena.create(ast.NodeComptime);
+ *node = ast.NodeComptime {
+ .base = self.initNode(ast.Node.Id.Comptime),
+ .comptime_token = token,
+ .expr = undefined,
+ };
+ try root_node.decls.append(&node.base);
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ continue;
+ },
else => {
self.putBackToken(token);
- stack.append(State { .TopLevelExtern = null }) catch unreachable;
+ stack.append(State.TopLevel) catch unreachable;
+ try stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &root_node.decls,
+ .visib_token = null,
+ .extern_token = null,
+ .lib_name = null,
+ }
+ });
continue;
},
}
},
- State.TopLevelExtern => |visib_token| {
+ State.TopLevelExtern => |ctx| {
const token = self.getNextToken();
- if (token.id == Token.Id.Keyword_extern) {
- stack.append(State {
- .TopLevelDecl = TopLevelDeclCtx {
- .visib_token = visib_token,
- .extern_token = token,
- },
- }) catch unreachable;
- continue;
- }
- self.putBackToken(token);
- stack.append(State {
- .TopLevelDecl = TopLevelDeclCtx {
- .visib_token = visib_token,
- .extern_token = null,
+ switch (token.id) {
+ Token.Id.Keyword_use => {
+ const node = try arena.create(ast.NodeUse);
+ *node = ast.NodeUse {
+ .base = self.initNode(ast.Node.Id.Use),
+ .visib_token = ctx.visib_token,
+ .expr = undefined,
+ .semicolon_token = undefined,
+ };
+ try ctx.decls.append(&node.base);
+
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Semicolon,
+ .ptr = &node.semicolon_token,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ continue;
},
- }) catch unreachable;
- continue;
+ Token.Id.Keyword_extern => {
+ const lib_name_token = self.getNextToken();
+ const lib_name = blk: {
+ if (lib_name_token.id == Token.Id.StringLiteral) {
+ const res = try self.createStringLiteral(arena, lib_name_token);
+ break :blk &res.base;
+ } else {
+ self.putBackToken(lib_name_token);
+ break :blk null;
+ }
+ };
+
+ stack.append(State {
+ .TopLevelDecl = TopLevelDeclCtx {
+ .decls = ctx.decls,
+ .visib_token = ctx.visib_token,
+ .extern_token = token,
+ .lib_name = lib_name,
+ },
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .TopLevelDecl = ctx }) catch unreachable;
+ continue;
+ }
+ }
},
State.TopLevelDecl => |ctx| {
const token = self.getNextToken();
switch (token.id) {
Token.Id.Keyword_var, Token.Id.Keyword_const => {
- stack.append(State.TopLevel) catch unreachable;
// TODO shouldn't need these casts
- const var_decl_node = try self.createAttachVarDecl(arena, &root_node.decls, ctx.visib_token,
- token, (?Token)(null), ctx.extern_token);
- try stack.append(State { .VarDecl = var_decl_node });
+ const var_decl_node = try self.createAttachVarDecl(arena, ctx.decls, ctx.visib_token,
+ token, (?Token)(null), ctx.extern_token, ctx.lib_name);
+ stack.append(State { .VarDecl = var_decl_node }) catch unreachable;
continue;
},
Token.Id.Keyword_fn => {
- stack.append(State.TopLevel) catch unreachable;
// TODO shouldn't need these casts
- const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token,
- ctx.extern_token, (?Token)(null), ctx.visib_token, (?Token)(null));
- try stack.append(State { .FnDef = fn_proto });
+ const fn_proto = try self.createAttachFnProto(arena, ctx.decls, token,
+ ctx.extern_token, ctx.lib_name, (?Token)(null), ctx.visib_token, (?Token)(null));
+ stack.append(State { .FnDef = fn_proto }) catch unreachable;
try stack.append(State { .FnProto = fn_proto });
continue;
},
- Token.Id.StringLiteral => {
- @panic("TODO extern with string literal");
- },
Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
- stack.append(State.TopLevel) catch unreachable;
- const fn_token = try self.eatToken(Token.Id.Keyword_fn);
// TODO shouldn't need this cast
- const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, fn_token,
- ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
- try stack.append(State { .FnDef = fn_proto });
+ const fn_proto = try self.createAttachFnProto(arena, ctx.decls, Token(undefined),
+ ctx.extern_token, ctx.lib_name, (?Token)(token), (?Token)(null), (?Token)(null));
+ stack.append(State { .FnDef = fn_proto }) catch unreachable;
try stack.append(State { .FnProto = fn_proto });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+ continue;
+ },
+ Token.Id.Keyword_async => {
+ // TODO shouldn't need this cast
+ const fn_proto = try self.createAttachFnProto(arena, ctx.decls, Token(undefined),
+ ctx.extern_token, ctx.lib_name, (?Token)(null), (?Token)(null), (?Token)(null));
+
+ const async_node = try arena.create(ast.NodeAsyncAttribute);
+ *async_node = ast.NodeAsyncAttribute {
+ .base = self.initNode(ast.Node.Id.AsyncAttribute),
+ .async_token = token,
+ .allocator_type = null,
+ .rangle_bracket = null,
+ };
+
+ fn_proto.async_attr = async_node;
+ stack.append(State { .FnDef = fn_proto }) catch unreachable;
+ try stack.append(State { .FnProto = fn_proto });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Keyword_fn,
+ .ptr = &fn_proto.fn_token,
+ }
+ });
+
+ const langle_bracket = self.getNextToken();
+ if (langle_bracket.id != Token.Id.AngleBracketLeft) {
+ self.putBackToken(langle_bracket);
+ continue;
+ }
+
+ async_node.rangle_bracket = Token(undefined);
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.AngleBracketRight,
+ .ptr = &??async_node.rangle_bracket,
+ }
+ });
+ try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } });
+ continue;
+ },
+ else => {
+ try self.parseError(&stack, token, "expected variable declaration or function, found {}", @tagName(token.id));
continue;
},
- else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
}
},
State.VarDecl => |var_decl| {
- var_decl.name_token = try self.eatToken(Token.Id.Identifier);
stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
-
- const next_token = self.getNextToken();
- if (next_token.id == Token.Id.Colon) {
- try stack.append(State { .TypeExpr = DestPtr {.NullableField = &var_decl.type_node} });
- continue;
- }
-
- self.putBackToken(next_token);
+ try stack.append(State { .TypeExprBegin = DestPtr {.NullableField = &var_decl.type_node} });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &var_decl.name_token,
+ }
+ });
continue;
},
State.VarDeclAlign => |var_decl| {
@@ -256,9 +491,9 @@ pub const Parser = struct {
const next_token = self.getNextToken();
if (next_token.id == Token.Id.Keyword_align) {
- _ = try self.eatToken(Token.Id.LParen);
try stack.append(State { .ExpectToken = Token.Id.RParen });
try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
continue;
}
@@ -284,194 +519,1617 @@ pub const Parser = struct {
var_decl.semicolon_token = token;
continue;
}
- return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
+ try self.parseError(&stack, token, "expected '=' or ';', found {}", @tagName(token.id));
+ continue;
+ },
+
+ State.ContainerExtern => |ctx| {
+ const token = self.getNextToken();
+
+ const node = try arena.create(ast.NodeContainerDecl);
+ *node = ast.NodeContainerDecl {
+ .base = self.initNode(ast.Node.Id.ContainerDecl),
+ .ltoken = ctx.ltoken,
+ .layout = ctx.layout,
+ .kind = switch (token.id) {
+ Token.Id.Keyword_struct => ast.NodeContainerDecl.Kind.Struct,
+ Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union,
+ Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum,
+ else => {
+ try self.parseError(&stack, token, "expected {}, {} or {}, found {}",
+ @tagName(Token.Id.Keyword_struct),
+ @tagName(Token.Id.Keyword_union),
+ @tagName(Token.Id.Keyword_enum),
+ @tagName(token.id));
+ continue;
+ },
+ },
+ .init_arg_expr = undefined,
+ .fields_and_decls = ArrayList(&ast.Node).init(arena),
+ .rbrace_token = undefined,
+ };
+ ctx.dest_ptr.store(&node.base);
+
+ stack.append(State { .ContainerDecl = node }) catch unreachable;
+ try stack.append(State { .ExpectToken = Token.Id.LBrace });
+
+ const lparen = self.getNextToken();
+ if (lparen.id != Token.Id.LParen) {
+ self.putBackToken(lparen);
+ node.init_arg_expr = ast.NodeContainerDecl.InitArg.None;
+ continue;
+ }
+
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+
+ const init_arg_token = self.getNextToken();
+ switch (init_arg_token.id) {
+ Token.Id.Keyword_enum => {
+ node.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum;
+ },
+ else => {
+ self.putBackToken(init_arg_token);
+ node.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined };
+ try stack.append(State {
+ .Expression = DestPtr {
+ .Field = &node.init_arg_expr.Type
+ }
+ });
+ },
+ }
+ continue;
},
+
+ State.ContainerDecl => |container_decl| {
+ const token = self.getNextToken();
+
+ switch (token.id) {
+ Token.Id.Identifier => {
+ switch (container_decl.kind) {
+ ast.NodeContainerDecl.Kind.Struct => {
+ const node = try arena.create(ast.NodeStructField);
+ *node = ast.NodeStructField {
+ .base = self.initNode(ast.Node.Id.StructField),
+ .name_token = token,
+ .type_expr = undefined,
+ };
+ try container_decl.fields_and_decls.append(&node.base);
+
+ stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } });
+ try stack.append(State { .ExpectToken = Token.Id.Colon });
+ continue;
+ },
+ ast.NodeContainerDecl.Kind.Union => {
+ const node = try arena.create(ast.NodeUnionTag);
+ *node = ast.NodeUnionTag {
+ .base = self.initNode(ast.Node.Id.UnionTag),
+ .name_token = token,
+ .type_expr = null,
+ };
+ try container_decl.fields_and_decls.append(&node.base);
+
+ stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+
+ const next = self.getNextToken();
+ if (next.id != Token.Id.Colon) {
+ self.putBackToken(next);
+ continue;
+ }
+
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.type_expr } });
+ continue;
+ },
+ ast.NodeContainerDecl.Kind.Enum => {
+ const node = try arena.create(ast.NodeEnumTag);
+ *node = ast.NodeEnumTag {
+ .base = self.initNode(ast.Node.Id.EnumTag),
+ .name_token = token,
+ .value = null,
+ };
+ try container_decl.fields_and_decls.append(&node.base);
+
+ stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
+
+ const next = self.getNextToken();
+ if (next.id != Token.Id.Equal) {
+ self.putBackToken(next);
+ continue;
+ }
+
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.value } });
+ continue;
+ },
+ }
+ },
+ Token.Id.Keyword_pub, Token.Id.Keyword_export => {
+ stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = token,
+ .extern_token = null,
+ .lib_name = null,
+ }
+ });
+ continue;
+ },
+ Token.Id.RBrace => {
+ container_decl.rbrace_token = token;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.append(State {
+ .TopLevelExtern = TopLevelDeclCtx {
+ .decls = &container_decl.fields_and_decls,
+ .visib_token = null,
+ .extern_token = null,
+ .lib_name = null,
+ }
+ });
+ continue;
+ }
+ }
+ },
+
State.ExpectToken => |token_id| {
- _ = try self.eatToken(token_id);
+ _ = (try self.eatToken(&stack, token_id)) ?? continue;
continue;
},
State.ExpectTokenSave => |expect_token_save| {
- *expect_token_save.ptr = try self.eatToken(expect_token_save.id);
+ *expect_token_save.ptr = (try self.eatToken(&stack, expect_token_save.id)) ?? continue;
continue;
},
- State.Expression => |dest_ptr| {
- // save the dest_ptr for later
- stack.append(state) catch unreachable;
- try stack.append(State.ExpectOperand);
+ State.IfToken => |token_id| {
+ const token = self.getNextToken();
+ if (@TagType(Token.Id)(token.id) != token_id) {
+ self.putBackToken(token);
+ _ = stack.pop();
+ continue;
+ }
+ continue;
+ },
+
+ State.IfTokenSave => |if_token_save| {
+ const token = self.getNextToken();
+ if (@TagType(Token.Id)(token.id) != if_token_save.id) {
+ self.putBackToken(token);
+ _ = stack.pop();
+ continue;
+ }
+
+ *if_token_save.ptr = token;
continue;
},
- State.ExpectOperand => {
- // we'll either get an operand (like 1 or x),
- // or a prefix operator (like ~ or return).
+
+ State.Optional => { },
+
+ State.Expression => |dest_ptr| {
const token = self.getNextToken();
switch (token.id) {
+ Token.Id.Keyword_try => {
+ const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Try);
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ continue;
+ },
Token.Id.Keyword_return => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.Return) });
- try stack.append(State.ExpectOperand);
+ const node = try self.createControlFlowExpr(arena, token, ast.NodeControlFlowExpression.Kind.Return);
+ dest_ptr.store(&node.base);
+
+ stack.append(State {
+ .Optional = RevertState {
+ .parser = *self,
+ .tokenizer = *self.tokenizer,
+ .ptr = &node.rhs,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
continue;
},
- Token.Id.Keyword_try => {
- try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
- ast.NodePrefixOp.PrefixOp.Try) });
- try stack.append(State.ExpectOperand);
+ Token.Id.Keyword_break => {
+ const label = blk: {
+ const colon = self.getNextToken();
+ if (colon.id != Token.Id.Colon) {
+ self.putBackToken(colon);
+ break :blk null;
+ }
+
+ break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ };
+
+ const node = try self.createControlFlowExpr(arena, token,
+ ast.NodeControlFlowExpression.Kind {
+ .Break = label,
+ }
+ );
+ dest_ptr.store(&node.base);
+
+ stack.append(State {
+ .Optional = RevertState {
+ .parser = *self,
+ .tokenizer = *self.tokenizer,
+ .ptr = &node.rhs,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
continue;
},
- Token.Id.Ampersand => {
- const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
- .AddrOf = ast.NodePrefixOp.AddrOfInfo {
- .align_expr = null,
- .bit_offset_start_token = null,
- .bit_offset_end_token = null,
- .const_token = null,
- .volatile_token = null,
+ Token.Id.Keyword_continue => {
+ const label = blk: {
+ const colon = self.getNextToken();
+ if (colon.id != Token.Id.Colon) {
+ self.putBackToken(colon);
+ break :blk null;
}
- });
- try stack.append(State { .PrefixOp = prefix_op });
- try stack.append(State.ExpectOperand);
- try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
+
+ break :blk (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ };
+
+ const node = try self.createControlFlowExpr(arena, token,
+ ast.NodeControlFlowExpression.Kind {
+ .Continue = label,
+ }
+ );
+ dest_ptr.store(&node.base);
continue;
},
- Token.Id.Identifier => {
- try stack.append(State {
- .Operand = &(try self.createIdentifier(arena, token)).base
- });
- try stack.append(State.AfterOperand);
+ Token.Id.Keyword_cancel => {
+ const cancel_node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Cancel);
+ dest_ptr.store(&cancel_node.base);
+ stack.append(State { .Expression = DestPtr { .Field = &cancel_node.rhs } }) catch unreachable;
+ },
+ Token.Id.Keyword_resume => {
+ const resume_node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp.Resume);
+ dest_ptr.store(&resume_node.base);
+ stack.append(State { .Expression = DestPtr { .Field = &resume_node.rhs } }) catch unreachable;
+ },
+ Token.Id.Keyword_suspend => {
+ const node = try arena.create(ast.NodeSuspend);
+ *node = ast.NodeSuspend {
+ .base = self.initNode(ast.Node.Id.Suspend),
+ .suspend_token = token,
+ .payload = null,
+ .body = null,
+ };
+ dest_ptr.store(&node.base);
+ stack.append(State { .SuspendBody = node }) catch unreachable;
+ try stack.append(State { .Payload = &node.payload });
continue;
},
- Token.Id.IntegerLiteral => {
- try stack.append(State {
- .Operand = &(try self.createIntegerLiteral(arena, token)).base
- });
- try stack.append(State.AfterOperand);
+ Token.Id.Keyword_if => {
+ const node = try arena.create(ast.NodeIf);
+ *node = ast.NodeIf {
+ .base = self.initNode(ast.Node.Id.If),
+ .if_token = token,
+ .condition = undefined,
+ .payload = null,
+ .body = undefined,
+ .@"else" = null,
+ };
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
+ try stack.append(State { .PointerPayload = &node.payload });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.condition } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
continue;
},
- Token.Id.FloatLiteral => {
- try stack.append(State {
- .Operand = &(try self.createFloatLiteral(arena, token)).base
- });
- try stack.append(State.AfterOperand);
+ Token.Id.Keyword_while => {
+ stack.append(State {
+ .While = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token,
+ .dest_ptr = dest_ptr,
+ }
+ }) catch unreachable;
continue;
},
- Token.Id.Builtin => {
- const node = try arena.create(ast.NodeBuiltinCall);
- *node = ast.NodeBuiltinCall {
- .base = self.initNode(ast.Node.Id.BuiltinCall),
- .builtin_token = token,
- .params = ArrayList(&ast.Node).init(arena),
- .rparen_token = undefined,
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .label = null,
+ .inline_token = null,
+ .loop_token = token,
+ .dest_ptr = dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_switch => {
+ const node = try arena.create(ast.NodeSwitch);
+ *node = ast.NodeSwitch {
+ .base = self.initNode(ast.Node.Id.Switch),
+ .switch_token = token,
+ .expr = undefined,
+ .cases = ArrayList(&ast.NodeSwitchCase).init(arena),
+ .rbrace = undefined,
};
- try stack.append(State {
- .Operand = &node.base
+ dest_ptr.store(&node.base);
+
+ stack.append(State {
+ .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) {
+ .list = &node.cases,
+ .ptr = &node.rbrace,
+ },
+ }) catch unreachable;
+ try stack.append(State { .ExpectToken = Token.Id.LBrace });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ },
+ Token.Id.Keyword_comptime => {
+ const node = try arena.create(ast.NodeComptime);
+ *node = ast.NodeComptime {
+ .base = self.initNode(ast.Node.Id.Comptime),
+ .comptime_token = token,
+ .expr = undefined,
+ };
+ dest_ptr.store(&node.base);
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ continue;
+ },
+ Token.Id.LBrace => {
+ const block = try self.createBlock(arena, (?Token)(null), token);
+ dest_ptr.store(&block.base);
+
+ stack.append(State { .Block = block }) catch unreachable;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
+ continue;
+ }
+ }
+ },
+
+ State.RangeExpressionBegin => |dest_ptr| {
+ stack.append(State { .RangeExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .Expression = dest_ptr });
+ continue;
+ },
+
+ State.RangeExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ if (token.id == Token.Id.Ellipsis3) {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Range);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.AssignmentExpressionBegin => |dest_ptr| {
+ stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .Expression = dest_ptr });
+ continue;
+ },
+
+ State.AssignmentExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ if (tokenIdToAssignment(token.id)) |ass_id| {
+ const node = try self.createInfixOp(arena, token, ass_id);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.UnwrapExpressionBegin => |dest_ptr| {
+ stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BoolOrExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.UnwrapExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_catch => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp { .Catch = null });
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
+ try stack.append(State { .Payload = &node.op.Catch });
+ continue;
+ },
+ Token.Id.QuestionMarkQuestionMark => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.UnwrapMaybe);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.BoolOrExpressionBegin => |dest_ptr| {
+ stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BoolOrExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_or => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BoolOr);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BoolAndExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.BoolAndExpressionBegin => |dest_ptr| {
+ stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .ComparisonExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BoolAndExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_and => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BoolAnd);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .ComparisonExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.ComparisonExpressionBegin => |dest_ptr| {
+ stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.ComparisonExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ if (tokenIdToComparison(token.id)) |comp_id| {
+ const node = try self.createInfixOp(arena, token, comp_id);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryOrExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.BinaryOrExpressionBegin => |dest_ptr| {
+ stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BinaryOrExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Pipe => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitOr);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryXorExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.BinaryXorExpressionBegin => |dest_ptr| {
+ stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BinaryXorExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Caret => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitXor);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BinaryAndExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.BinaryAndExpressionBegin => |dest_ptr| {
+ stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BitShiftExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BinaryAndExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Ampersand => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BitAnd);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .BitShiftExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.BitShiftExpressionBegin => |dest_ptr| {
+ stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.BitShiftExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ if (tokenIdToBitShift(token.id)) |bitshift_id| {
+ const node = try self.createInfixOp(arena, token, bitshift_id);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .AdditionExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.AdditionExpressionBegin => |dest_ptr| {
+ stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.AdditionExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ if (tokenIdToAddition(token.id)) |add_id| {
+ const node = try self.createInfixOp(arena, token, add_id);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .MultiplyExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.MultiplyExpressionBegin => |dest_ptr| {
+ stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = dest_ptr });
+ continue;
+ },
+
+ State.MultiplyExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ if (tokenIdToMultiply(token.id)) |mult_id| {
+ const node = try self.createInfixOp(arena, token, mult_id);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .CurlySuffixExpressionBegin = DestPtr { .Field = &node.rhs } });
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.CurlySuffixExpressionBegin => |dest_ptr| {
+ stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .TypeExprBegin = dest_ptr });
+ continue;
+ },
+
+ State.CurlySuffixExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ if (token.id != Token.Id.LBrace) {
+ self.putBackToken(token);
+ continue;
+ }
+
+ const next = self.getNextToken();
+ switch (next.id) {
+ Token.Id.Period => {
+ const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
+ .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
});
- try stack.append(State.AfterOperand);
- try stack.append(State {.ExprListItemOrEnd = &node.params });
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
- .ExpectTokenSave = ExpectTokenSave {
- .id = Token.Id.LParen,
- .ptr = &node.rparen_token,
- },
+ .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
+ .list = &node.op.StructInitializer,
+ .ptr = &node.rtoken,
+ }
});
+ self.putBackToken(next);
continue;
},
- Token.Id.StringLiteral => {
- const node = try arena.create(ast.NodeStringLiteral);
- *node = ast.NodeStringLiteral {
- .base = self.initNode(ast.Node.Id.StringLiteral),
- .token = token,
- };
+ else => {
+ const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
+ .ArrayInitializer = ArrayList(&ast.Node).init(arena),
+ });
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
- .Operand = &node.base
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.op.ArrayInitializer,
+ .end = Token.Id.RBrace,
+ .ptr = &node.rtoken,
+ }
});
- try stack.append(State.AfterOperand);
+ self.putBackToken(next);
+ continue;
+ },
+ }
+ },
+
+ State.TypeExprBegin => |dest_ptr| {
+ stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = dest_ptr });
+ continue;
+ },
+
+ State.TypeExprEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Bang => {
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.ErrorUnion);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .PrefixOpExpression = DestPtr { .Field = &node.rhs } });
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
continue;
},
+ }
+ },
+
+ State.PrefixOpExpression => |dest_ptr| {
+ const token = self.getNextToken();
+ if (tokenIdToPrefixOp(token.id)) |prefix_id| {
+ const node = try self.createPrefixOp(arena, token, prefix_id);
+ dest_ptr.store(&node.base);
- else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
+ stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) {
+ try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
+ }
+ continue;
+ } else {
+ self.putBackToken(token);
+ stack.append(State { .SuffixOpExpressionBegin = dest_ptr }) catch unreachable;
+ continue;
}
},
- State.AfterOperand => {
- // we'll either get an infix operator (like != or ^),
- // or a postfix operator (like () or {}),
- // otherwise this expression is done (like on a ; or else).
- var token = self.getNextToken();
+ State.SuffixOpExpressionBegin => |dest_ptr| {
+ const token = self.getNextToken();
switch (token.id) {
- Token.Id.EqualEqual => {
+ Token.Id.Keyword_async => {
+ const async_node = try arena.create(ast.NodeAsyncAttribute);
+ *async_node = ast.NodeAsyncAttribute {
+ .base = self.initNode(ast.Node.Id.AsyncAttribute),
+ .async_token = token,
+ .allocator_type = null,
+ .rangle_bracket = null,
+ };
+
+ stack.append(State {
+ .AsyncEnd = AsyncEndCtx {
+ .dest_ptr = dest_ptr,
+ .attribute = async_node,
+ }
+ }) catch unreachable;
+ try stack.append(State { .SuffixOpExpressionEnd = dest_ptr });
+ try stack.append(State { .PrimaryExpression = dest_ptr });
+
+ const langle_bracket = self.getNextToken();
+ if (langle_bracket.id != Token.Id.AngleBracketLeft) {
+ self.putBackToken(langle_bracket);
+ continue;
+ }
+
+ async_node.rangle_bracket = Token(undefined);
try stack.append(State {
- .InfixOp = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.EqualEqual)
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.AngleBracketRight,
+ .ptr = &??async_node.rangle_bracket,
+ }
});
- try stack.append(State.ExpectOperand);
+ try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } });
continue;
},
- Token.Id.BangEqual => {
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .PrimaryExpression = dest_ptr });
+ continue;
+ }
+ }
+ },
+
+ State.SuffixOpExpressionEnd => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.LParen => {
+ const node = try self.createSuffixOp(arena, ast.NodeSuffixOp.SuffixOp {
+ .Call = ast.NodeSuffixOp.CallInfo {
+ .params = ArrayList(&ast.Node).init(arena),
+ .async_attr = null,
+ }
+ });
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
try stack.append(State {
- .InfixOp = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.BangEqual)
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.op.Call.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rtoken,
+ }
});
- try stack.append(State.ExpectOperand);
+ continue;
+ },
+ Token.Id.LBracket => {
+ const node = try arena.create(ast.NodeSuffixOp);
+ *node = ast.NodeSuffixOp {
+ .base = self.initNode(ast.Node.Id.SuffixOp),
+ .lhs = undefined,
+ .op = ast.NodeSuffixOp.SuffixOp {
+ .ArrayAccess = undefined,
+ },
+ .rtoken = undefined,
+ };
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .SliceOrArrayAccess = node });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }});
continue;
},
Token.Id.Period => {
- try stack.append(State {
- .InfixOp = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Period)
- });
- try stack.append(State.ExpectOperand);
+ const node = try self.createInfixOp(arena, token, ast.NodeInfixOp.InfixOp.Period);
+ node.lhs = dest_ptr.get();
+ dest_ptr.store(&node.base);
+
+ stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;
+ try stack.append(State { .SuffixOpExpressionBegin = DestPtr { .Field = &node.rhs }});
continue;
},
else => {
- // no postfix/infix operator after this operand.
self.putBackToken(token);
- // reduce the stack
- var expression: &ast.Node = stack.pop().Operand;
+ continue;
+ },
+ }
+ },
+
+ State.PrimaryExpression => |dest_ptr| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.IntegerLiteral => {
+ dest_ptr.store(&(try self.createIntegerLiteral(arena, token)).base);
+ continue;
+ },
+ Token.Id.FloatLiteral => {
+ dest_ptr.store(&(try self.createFloatLiteral(arena, token)).base);
+ continue;
+ },
+ Token.Id.StringLiteral => {
+ dest_ptr.store(&(try self.createStringLiteral(arena, token)).base);
+ continue;
+ },
+ Token.Id.CharLiteral => {
+ const node = try arena.create(ast.NodeCharLiteral);
+ *node = ast.NodeCharLiteral {
+ .base = self.initNode(ast.Node.Id.CharLiteral),
+ .token = token,
+ };
+ dest_ptr.store(&node.base);
+ continue;
+ },
+ Token.Id.Keyword_undefined => {
+ dest_ptr.store(&(try self.createUndefined(arena, token)).base);
+ continue;
+ },
+ Token.Id.Keyword_true, Token.Id.Keyword_false => {
+ const node = try arena.create(ast.NodeBoolLiteral);
+ *node = ast.NodeBoolLiteral {
+ .base = self.initNode(ast.Node.Id.BoolLiteral),
+ .token = token,
+ };
+ dest_ptr.store(&node.base);
+ continue;
+ },
+ Token.Id.Keyword_null => {
+ const node = try arena.create(ast.NodeNullLiteral);
+ *node = ast.NodeNullLiteral {
+ .base = self.initNode(ast.Node.Id.NullLiteral),
+ .token = token,
+ };
+ dest_ptr.store(&node.base);
+ continue;
+ },
+ Token.Id.Keyword_this => {
+ const node = try arena.create(ast.NodeThisLiteral);
+ *node = ast.NodeThisLiteral {
+ .base = self.initNode(ast.Node.Id.ThisLiteral),
+ .token = token,
+ };
+ dest_ptr.store(&node.base);
+ continue;
+ },
+ Token.Id.Keyword_var => {
+ const node = try arena.create(ast.NodeVarType);
+ *node = ast.NodeVarType {
+ .base = self.initNode(ast.Node.Id.VarType),
+ .token = token,
+ };
+ dest_ptr.store(&node.base);
+ },
+ Token.Id.Keyword_unreachable => {
+ const node = try arena.create(ast.NodeUnreachable);
+ *node = ast.NodeUnreachable {
+ .base = self.initNode(ast.Node.Id.Unreachable),
+ .token = token,
+ };
+ dest_ptr.store(&node.base);
+ continue;
+ },
+ Token.Id.MultilineStringLiteralLine => {
+ const node = try arena.create(ast.NodeMultilineStringLiteral);
+ *node = ast.NodeMultilineStringLiteral {
+ .base = self.initNode(ast.Node.Id.MultilineStringLiteral),
+ .tokens = ArrayList(Token).init(arena),
+ };
+ dest_ptr.store(&node.base);
+ try node.tokens.append(token);
+
+ while (true) {
+ const multiline_str = self.getNextToken();
+ if (multiline_str.id != Token.Id.MultilineStringLiteralLine) {
+ self.putBackToken(multiline_str);
+ break;
+ }
+
+ try node.tokens.append(multiline_str);
+ }
+ continue;
+ },
+ Token.Id.LParen => {
+ const node = try arena.create(ast.NodeGroupedExpression);
+ *node = ast.NodeGroupedExpression {
+ .base = self.initNode(ast.Node.Id.GroupedExpression),
+ .lparen = token,
+ .expr = undefined,
+ .rparen = undefined,
+ };
+ dest_ptr.store(&node.base);
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ continue;
+ },
+ Token.Id.Builtin => {
+ const node = try arena.create(ast.NodeBuiltinCall);
+ *node = ast.NodeBuiltinCall {
+ .base = self.initNode(ast.Node.Id.BuiltinCall),
+ .builtin_token = token,
+ .params = ArrayList(&ast.Node).init(arena),
+ .rparen_token = undefined,
+ };
+ dest_ptr.store(&node.base);
+ stack.append(State {
+ .ExprListItemOrEnd = ExprListCtx {
+ .list = &node.params,
+ .end = Token.Id.RParen,
+ .ptr = &node.rparen_token,
+ }
+ }) catch unreachable;
+ try stack.append(State { .ExpectToken = Token.Id.LParen, });
+ continue;
+ },
+ Token.Id.LBracket => {
+ const rbracket_token = self.getNextToken();
+ if (rbracket_token.id == Token.Id.RBracket) {
+ const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
+ .SliceType = ast.NodePrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ }
+ });
+ dest_ptr.store(&node.base);
+ stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
+ continue;
+ }
+
+ self.putBackToken(rbracket_token);
+
+ const node = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
+ .ArrayType = undefined,
+ });
+ dest_ptr.store(&node.base);
+ stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
+ try stack.append(State { .ExpectToken = Token.Id.RBracket });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayType } });
+
+ },
+ Token.Id.Keyword_error => {
+ const next = self.getNextToken();
+
+ if (next.id != Token.Id.LBrace) {
+ self.putBackToken(next);
+ const node = try arena.create(ast.NodeErrorType);
+ *node = ast.NodeErrorType {
+ .base = self.initNode(ast.Node.Id.ErrorType),
+ .token = token,
+ };
+ dest_ptr.store(&node.base);
+ continue;
+ }
+
+ const node = try arena.create(ast.NodeErrorSetDecl);
+ *node = ast.NodeErrorSetDecl {
+ .base = self.initNode(ast.Node.Id.ErrorSetDecl),
+ .error_token = token,
+ .decls = ArrayList(&ast.NodeIdentifier).init(arena),
+ .rbrace_token = undefined,
+ };
+ dest_ptr.store(&node.base);
+
while (true) {
- switch (stack.pop()) {
- State.Expression => |dest_ptr| {
- // we're done
- try dest_ptr.store(expression);
+ const t = self.getNextToken();
+ switch (t.id) {
+ Token.Id.RBrace => {
+ node.rbrace_token = t;
break;
},
- State.InfixOp => |infix_op| {
- infix_op.rhs = expression;
- infix_op.lhs = stack.pop().Operand;
- expression = &infix_op.base;
- continue;
+ Token.Id.Identifier => {
+ try node.decls.append(
+ try self.createIdentifier(arena, t)
+ );
},
- State.PrefixOp => |prefix_op| {
- prefix_op.rhs = expression;
- expression = &prefix_op.base;
+ else => {
+ try self.parseError(&stack, token, "expected {} or {}, found {}",
+ @tagName(Token.Id.RBrace),
+ @tagName(Token.Id.Identifier),
+ @tagName(token.id));
continue;
+ }
+ }
+
+ const t2 = self.getNextToken();
+ switch (t2.id) {
+ Token.Id.RBrace => {
+ node.rbrace_token = t;
+ break;
},
- else => unreachable,
+ Token.Id.Comma => continue,
+ else => {
+ try self.parseError(&stack, token, "expected {} or {}, found {}",
+ @tagName(Token.Id.RBrace),
+ @tagName(Token.Id.Comma),
+ @tagName(token.id));
+ continue;
+ }
}
}
continue;
},
+ Token.Id.Keyword_packed => {
+ stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .dest_ptr = dest_ptr,
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Packed,
+ },
+ }) catch unreachable;
+ },
+ Token.Id.Keyword_extern => {
+ const next = self.getNextToken();
+ if (next.id == Token.Id.Keyword_fn) {
+ // TODO shouldn't need this cast
+ const fn_proto = try self.createFnProto(arena, next,
+ (?Token)(token), (?&ast.Node)(null), (?Token)(null), (?Token)(null), (?Token)(null));
+ dest_ptr.store(&fn_proto.base);
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ continue;
+ }
+
+ self.putBackToken(next);
+ stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .dest_ptr = dest_ptr,
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Extern,
+ },
+ }) catch unreachable;
+ },
+ Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
+ self.putBackToken(token);
+ stack.append(State {
+ .ContainerExtern = ContainerExternCtx {
+ .dest_ptr = dest_ptr,
+ .ltoken = token,
+ .layout = ast.NodeContainerDecl.Layout.Auto,
+ },
+ }) catch unreachable;
+ },
+ Token.Id.Identifier => {
+ const next = self.getNextToken();
+ if (next.id != Token.Id.Colon) {
+ self.putBackToken(next);
+ dest_ptr.store(&(try self.createIdentifier(arena, token)).base);
+ continue;
+ }
+
+ stack.append(State {
+ .LabeledExpression = LabelCtx {
+ .label = token,
+ .dest_ptr = dest_ptr
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_fn => {
+ // TODO shouldn't need these casts
+ const fn_proto = try self.createFnProto(arena, token,
+ (?Token)(null), (?&ast.Node)(null), (?Token)(null), (?Token)(null), (?Token)(null));
+ dest_ptr.store(&fn_proto.base);
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ const fn_token = (try self.eatToken(&stack, Token.Id.Keyword_fn)) ?? continue;
+ // TODO shouldn't need this cast
+ const fn_proto = try self.createFnProto(arena, fn_token,
+ (?Token)(null), (?&ast.Node)(null), (?Token)(token), (?Token)(null), (?Token)(null));
+ dest_ptr.store(&fn_proto.base);
+ stack.append(State { .FnProto = fn_proto }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_asm => {
+ const is_volatile = blk: {
+ const volatile_token = self.getNextToken();
+ if (volatile_token.id != Token.Id.Keyword_volatile) {
+ self.putBackToken(volatile_token);
+ break :blk false;
+ }
+ break :blk true;
+ };
+ _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
+ const template = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
+ // TODO parse template
+
+ const node = try arena.create(ast.NodeAsm);
+ *node = ast.NodeAsm {
+ .base = self.initNode(ast.Node.Id.Asm),
+ .asm_token = token,
+ .is_volatile = is_volatile,
+ .template = template,
+ //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena),
+ .outputs = ArrayList(&ast.NodeAsmOutput).init(arena),
+ .inputs = ArrayList(&ast.NodeAsmInput).init(arena),
+ .cloppers = ArrayList(&ast.NodeStringLiteral).init(arena),
+ .rparen = undefined,
+ };
+ dest_ptr.store(&node.base);
+
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RParen,
+ .ptr = &node.rparen,
+ }
+ }) catch unreachable;
+ try stack.append(State { .AsmClopperItems = &node.cloppers });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State { .AsmInputItems = &node.inputs });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ try stack.append(State { .AsmOutputItems = &node.outputs });
+ try stack.append(State { .IfToken = Token.Id.Colon });
+ },
+ Token.Id.Keyword_inline => {
+ stack.append(State {
+ .Inline = InlineCtx {
+ .label = null,
+ .inline_token = token,
+ .dest_ptr = dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));
+ continue;
+ }
}
},
- State.ExprListItemOrEnd => |params| {
+ State.SliceOrArrayAccess => |node| {
var token = self.getNextToken();
+
switch (token.id) {
- Token.Id.RParen => continue,
+ Token.Id.Ellipsis2 => {
+ const start = node.op.ArrayAccess;
+ node.op = ast.NodeSuffixOp.SuffixOp {
+ .Slice = ast.NodeSuffixOp.SliceRange {
+ .start = start,
+ .end = undefined,
+ }
+ };
+
+ const rbracket_token = self.getNextToken();
+ if (rbracket_token.id != Token.Id.RBracket) {
+ self.putBackToken(rbracket_token);
+ stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.RBracket,
+ .ptr = &node.rtoken,
+ }
+ }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .NullableField = &node.op.Slice.end } });
+ } else {
+ node.rtoken = rbracket_token;
+ }
+ continue;
+ },
+ Token.Id.RBracket => {
+ node.rtoken = token;
+ continue;
+ },
else => {
- self.putBackToken(token);
- stack.append(State { .ExprListCommaOrEnd = params }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.List = params} });
+ try self.parseError(&stack, token, "expected ']' or '..', found {}", @tagName(token.id));
+ continue;
+ }
+ }
+ },
+
+
+ State.AsmOutputItems => |items| {
+ const lbracket = self.getNextToken();
+ if (lbracket.id != Token.Id.LBracket) {
+ self.putBackToken(lbracket);
+ continue;
+ }
+
+ stack.append(State { .AsmOutputItems = items }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.Comma });
+
+ const symbolic_name = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ _ = (try self.eatToken(&stack, Token.Id.RBracket)) ?? continue;
+ const constraint = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
+
+ _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+
+ const node = try arena.create(ast.NodeAsmOutput);
+ *node = ast.NodeAsmOutput {
+ .base = self.initNode(ast.Node.Id.AsmOutput),
+ .symbolic_name = try self.createIdentifier(arena, symbolic_name),
+ .constraint = try self.createStringLiteral(arena, constraint),
+ .kind = undefined,
+ };
+ try items.append(node);
+
+ const symbol_or_arrow = self.getNextToken();
+ switch (symbol_or_arrow.id) {
+ Token.Id.Identifier => {
+ node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createIdentifier(arena, symbol_or_arrow) };
+ },
+ Token.Id.Arrow => {
+ node.kind = ast.NodeAsmOutput.Kind { .Return = undefined };
+ try stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.kind.Return } });
+ },
+ else => {
+ try self.parseError(&stack, symbol_or_arrow, "expected '->' or {}, found {}",
+ @tagName(Token.Id.Identifier),
+ @tagName(symbol_or_arrow.id));
+ continue;
},
}
},
- State.ExprListCommaOrEnd => |params| {
+ State.AsmInputItems => |items| {
+ const lbracket = self.getNextToken();
+ if (lbracket.id != Token.Id.LBracket) {
+ self.putBackToken(lbracket);
+ continue;
+ }
+
+ stack.append(State { .AsmInputItems = items }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.Comma });
+
+ const symbolic_name = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ _ = (try self.eatToken(&stack, Token.Id.RBracket)) ?? continue;
+ const constraint = (try self.eatToken(&stack, Token.Id.StringLiteral)) ?? continue;
+
+ _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+
+ const node = try arena.create(ast.NodeAsmInput);
+ *node = ast.NodeAsmInput {
+ .base = self.initNode(ast.Node.Id.AsmInput),
+ .symbolic_name = try self.createIdentifier(arena, symbolic_name),
+ .constraint = try self.createStringLiteral(arena, constraint),
+ .expr = undefined,
+ };
+ try items.append(node);
+ try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
+ },
+
+ State.AsmClopperItems => |items| {
+ const string = self.getNextToken();
+ if (string.id != Token.Id.StringLiteral) {
+ self.putBackToken(string);
+ continue;
+ }
+
+ try items.append(try self.createStringLiteral(arena, string));
+ stack.append(State { .AsmClopperItems = items }) catch unreachable;
+ try stack.append(State { .IfToken = Token.Id.Comma });
+ },
+
+ State.ExprListItemOrEnd => |list_state| {
var token = self.getNextToken();
- switch (token.id) {
- Token.Id.Comma => {
- stack.append(State { .ExprListItemOrEnd = params }) catch unreachable;
+
+ const IdTag = @TagType(Token.Id);
+ if (IdTag(list_state.end) == token.id) {
+ *list_state.ptr = token;
+ continue;
+ }
+
+ self.putBackToken(token);
+ stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr{ .Field = try list_state.list.addOne() } });
+ },
+
+ State.FieldInitListItemOrEnd => |list_state| {
+ var token = self.getNextToken();
+
+ if (token.id == Token.Id.RBrace){
+ *list_state.ptr = token;
+ continue;
+ }
+
+ self.putBackToken(token);
+
+ const node = try arena.create(ast.NodeFieldInitializer);
+ *node = ast.NodeFieldInitializer {
+ .base = self.initNode(ast.Node.Id.FieldInitializer),
+ .period_token = undefined,
+ .name_token = undefined,
+ .expr = undefined,
+ };
+ try list_state.list.append(node);
+
+ stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr{.Field = &node.expr} });
+ try stack.append(State { .ExpectToken = Token.Id.Equal });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Identifier,
+ .ptr = &node.name_token,
+ }
+ });
+ try stack.append(State {
+ .ExpectTokenSave = ExpectTokenSave {
+ .id = Token.Id.Period,
+ .ptr = &node.period_token,
+ }
+ });
+ },
+
+ State.SwitchCaseOrEnd => |list_state| {
+ var token = self.getNextToken();
+
+ if (token.id == Token.Id.RBrace){
+ *list_state.ptr = token;
+ continue;
+ }
+
+ self.putBackToken(token);
+
+ const node = try arena.create(ast.NodeSwitchCase);
+ *node = ast.NodeSwitchCase {
+ .base = self.initNode(ast.Node.Id.SwitchCase),
+ .items = ArrayList(&ast.Node).init(arena),
+ .payload = null,
+ .expr = undefined,
+ };
+ try list_state.list.append(node);
+ stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr{ .Field = &node.expr } });
+ try stack.append(State { .PointerPayload = &node.payload });
+
+ const maybe_else = self.getNextToken();
+ if (maybe_else.id == Token.Id.Keyword_else) {
+ const else_node = try arena.create(ast.NodeSwitchElse);
+ *else_node = ast.NodeSwitchElse {
+ .base = self.initNode(ast.Node.Id.SwitchElse),
+ .token = maybe_else,
+ };
+ try node.items.append(&else_node.base);
+ try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
+ continue;
+ } else {
+ self.putBackToken(maybe_else);
+ try stack.append(State { .SwitchCaseItem = &node.items });
+ continue;
+ }
+ },
+
+ State.SwitchCaseItem => |case_items| {
+ stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
+ try stack.append(State { .RangeExpressionBegin = DestPtr{ .Field = try case_items.addOne() } });
+ },
+
+ State.ExprListCommaOrEnd => |list_state| {
+ try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state });
+ continue;
+ },
+
+ State.FieldInitListCommaOrEnd => |list_state| {
+ try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });
+ continue;
+ },
+
+ State.FieldListCommaOrEnd => |container_decl| {
+ try self.commaOrEnd(&stack, Token.Id.RBrace, &container_decl.rbrace_token,
+ State { .ContainerDecl = container_decl });
+ continue;
+ },
+
+ State.SwitchCaseCommaOrEnd => |list_state| {
+ try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state });
+ continue;
+ },
+
+ State.SwitchCaseItemCommaOrEnd => |case_items| {
+ try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items });
+ continue;
+ },
+
+ State.Else => |dest| {
+ const else_token = self.getNextToken();
+ if (else_token.id != Token.Id.Keyword_else) {
+ self.putBackToken(else_token);
+ continue;
+ }
+
+ const node = try arena.create(ast.NodeElse);
+ *node = ast.NodeElse {
+ .base = self.initNode(ast.Node.Id.Else),
+ .else_token = else_token,
+ .payload = null,
+ .body = undefined,
+ };
+ *dest = node;
+
+ stack.append(State { .Expression = DestPtr { .Field = &node.body } }) catch unreachable;
+ try stack.append(State { .Payload = &node.payload });
+ },
+
+ State.WhileContinueExpr => |dest| {
+ const colon = self.getNextToken();
+ if (colon.id != Token.Id.Colon) {
+ self.putBackToken(colon);
+ continue;
+ }
+
+ _ = (try self.eatToken(&stack, Token.Id.LParen)) ?? continue;
+ stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = dest } });
+ },
+
+ State.SuspendBody => |suspend_node| {
+ if (suspend_node.payload != null) {
+ try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = &suspend_node.body } });
+ }
+ continue;
+ },
+
+ State.AsyncEnd => |ctx| {
+ const node = ctx.dest_ptr.get();
+
+ switch (node.id) {
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node);
+ fn_proto.async_attr = ctx.attribute;
+ },
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);
+ if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) {
+ suffix_op.op.Call.async_attr = ctx.attribute;
+ continue;
+ }
+
+ try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
+ @tagName(suffix_op.op));
+ continue;
},
- Token.Id.RParen => continue,
- else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
+ else => {
+ try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
+ @tagName(node.id));
+ continue;
+ }
+ }
+ },
+
+ State.Payload => |dest| {
+ const lpipe = self.getNextToken();
+ if (lpipe.id != Token.Id.Pipe) {
+ self.putBackToken(lpipe);
+ continue;
}
+
+ const error_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
+ const node = try arena.create(ast.NodePayload);
+ *node = ast.NodePayload {
+ .base = self.initNode(ast.Node.Id.Payload),
+ .lpipe = lpipe,
+ .error_symbol = try self.createIdentifier(arena, error_symbol),
+ .rpipe = rpipe
+ };
+ *dest = node;
+ },
+
+ State.PointerPayload => |dest| {
+ const lpipe = self.getNextToken();
+ if (lpipe.id != Token.Id.Pipe) {
+ self.putBackToken(lpipe);
+ continue;
+ }
+
+ const is_ptr = blk: {
+ const asterik = self.getNextToken();
+ if (asterik.id == Token.Id.Asterisk) {
+ break :blk true;
+ } else {
+ self.putBackToken(asterik);
+ break :blk false;
+ }
+ };
+
+ const value_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
+ const node = try arena.create(ast.NodePointerPayload);
+ *node = ast.NodePointerPayload {
+ .base = self.initNode(ast.Node.Id.PointerPayload),
+ .lpipe = lpipe,
+ .is_ptr = is_ptr,
+ .value_symbol = try self.createIdentifier(arena, value_symbol),
+ .rpipe = rpipe
+ };
+ *dest = node;
+ },
+
+ State.PointerIndexPayload => |dest| {
+ const lpipe = self.getNextToken();
+ if (lpipe.id != Token.Id.Pipe) {
+ self.putBackToken(lpipe);
+ continue;
+ }
+
+ const is_ptr = blk: {
+ const asterik = self.getNextToken();
+ if (asterik.id == Token.Id.Asterisk) {
+ break :blk true;
+ } else {
+ self.putBackToken(asterik);
+ break :blk false;
+ }
+ };
+
+ const value_symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ const index_symbol = blk: {
+ const comma = self.getNextToken();
+ if (comma.id != Token.Id.Comma) {
+ self.putBackToken(comma);
+ break :blk null;
+ }
+
+ const symbol = (try self.eatToken(&stack, Token.Id.Identifier)) ?? continue;
+ break :blk try self.createIdentifier(arena, symbol);
+ };
+
+ const rpipe = (try self.eatToken(&stack, Token.Id.Pipe)) ?? continue;
+ const node = try arena.create(ast.NodePointerIndexPayload);
+ *node = ast.NodePointerIndexPayload {
+ .base = self.initNode(ast.Node.Id.PointerIndexPayload),
+ .lpipe = lpipe,
+ .is_ptr = is_ptr,
+ .value_symbol = try self.createIdentifier(arena, value_symbol),
+ .index_symbol = index_symbol,
+ .rpipe = rpipe
+ };
+ *dest = node;
},
State.AddrOfModifiers => |addr_of_info| {
@@ -479,21 +2137,30 @@ pub const Parser = struct {
switch (token.id) {
Token.Id.Keyword_align => {
stack.append(state) catch unreachable;
- if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
- _ = try self.eatToken(Token.Id.LParen);
+ if (addr_of_info.align_expr != null) {
+ try self.parseError(&stack, token, "multiple align qualifiers");
+ continue;
+ }
try stack.append(State { .ExpectToken = Token.Id.RParen });
try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
continue;
},
Token.Id.Keyword_const => {
stack.append(state) catch unreachable;
- if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const");
+ if (addr_of_info.const_token != null) {
+ try self.parseError(&stack, token, "duplicate qualifier: const");
+ continue;
+ }
addr_of_info.const_token = token;
continue;
},
Token.Id.Keyword_volatile => {
stack.append(state) catch unreachable;
- if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
+ if (addr_of_info.volatile_token != null) {
+ try self.parseError(&stack, token, "duplicate qualifier: volatile");
+ continue;
+ }
addr_of_info.volatile_token = token;
continue;
},
@@ -504,17 +2171,6 @@ pub const Parser = struct {
}
},
- State.TypeExpr => |dest_ptr| {
- const token = self.getNextToken();
- if (token.id == Token.Id.Keyword_var) {
- @panic("TODO param with type var");
- }
- self.putBackToken(token);
-
- stack.append(State { .Expression = dest_ptr }) catch unreachable;
- continue;
- },
-
State.FnProto => |fn_proto| {
stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
try stack.append(State { .ParamDecl = fn_proto });
@@ -544,20 +2200,17 @@ pub const Parser = struct {
State.FnProtoReturnType => |fn_proto| {
const token = self.getNextToken();
switch (token.id) {
- Token.Id.Keyword_var => {
- fn_proto.return_type = ast.NodeFnProto.ReturnType { .Infer = token };
- },
Token.Id.Bang => {
fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
stack.append(State {
- .TypeExpr = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
+ .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
}) catch unreachable;
},
else => {
self.putBackToken(token);
fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
stack.append(State {
- .TypeExpr = DestPtr {.Field = &fn_proto.return_type.Explicit},
+ .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.Explicit},
}) catch unreachable;
},
}
@@ -600,7 +2253,7 @@ pub const Parser = struct {
stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
try stack.append(State.ParamDeclComma);
try stack.append(State {
- .TypeExpr = DestPtr {.Field = &param_decl.type_node}
+ .TypeExprBegin = DestPtr {.Field = &param_decl.type_node}
});
continue;
},
@@ -613,7 +2266,10 @@ pub const Parser = struct {
continue;
},
Token.Id.Comma => continue,
- else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
+ else => {
+ try self.parseError(&stack, token, "expected ',' or ')', found {}", @tagName(token.id));
+ continue;
+ },
}
},
@@ -621,21 +2277,151 @@ pub const Parser = struct {
const token = self.getNextToken();
switch(token.id) {
Token.Id.LBrace => {
- const block = try self.createBlock(arena, token);
+ const block = try self.createBlock(arena, (?Token)(null), token);
fn_proto.body_node = &block.base;
stack.append(State { .Block = block }) catch unreachable;
continue;
},
Token.Id.Semicolon => continue,
- else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
+ else => {
+ try self.parseError(&stack, token, "expected ';' or '{{', found {}", @tagName(token.id));
+ continue;
+ },
+ }
+ },
+
+ State.LabeledExpression => |ctx| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.LBrace => {
+ const block = try self.createBlock(arena, (?Token)(ctx.label), token);
+ ctx.dest_ptr.store(&block.base);
+
+ stack.append(State { .Block = block }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_while => {
+ stack.append(State {
+ .While = LoopCtx {
+ .label = ctx.label,
+ .inline_token = null,
+ .loop_token = token,
+ .dest_ptr = ctx.dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .label = ctx.label,
+ .inline_token = null,
+ .loop_token = token,
+ .dest_ptr = ctx.dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_inline => {
+ stack.append(State {
+ .Inline = InlineCtx {
+ .label = ctx.label,
+ .inline_token = token,
+ .dest_ptr = ctx.dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ try self.parseError(&stack, token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id));
+ continue;
+ },
+ }
+ },
+
+ State.Inline => |ctx| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_while => {
+ stack.append(State {
+ .While = LoopCtx {
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
+ .loop_token = token,
+ .dest_ptr = ctx.dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_for => {
+ stack.append(State {
+ .For = LoopCtx {
+ .inline_token = ctx.inline_token,
+ .label = ctx.label,
+ .loop_token = token,
+ .dest_ptr = ctx.dest_ptr,
+ }
+ }) catch unreachable;
+ continue;
+ },
+ else => {
+ try self.parseError(&stack, token, "expected 'while' or 'for', found {}", @tagName(token.id));
+ continue;
+ },
}
},
+ State.While => |ctx| {
+ const node = try arena.create(ast.NodeWhile);
+ *node = ast.NodeWhile {
+ .base = self.initNode(ast.Node.Id.While),
+ .label = ctx.label,
+ .inline_token = ctx.inline_token,
+ .while_token = ctx.loop_token,
+ .condition = undefined,
+ .payload = null,
+ .continue_expr = null,
+ .body = undefined,
+ .@"else" = null,
+ };
+ ctx.dest_ptr.store(&node.base);
+
+ stack.append(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
+ try stack.append(State { .WhileContinueExpr = &node.continue_expr });
+ try stack.append(State { .PointerPayload = &node.payload });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.condition } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ },
+
+ State.For => |ctx| {
+ const node = try arena.create(ast.NodeFor);
+ *node = ast.NodeFor {
+ .base = self.initNode(ast.Node.Id.For),
+ .label = ctx.label,
+ .inline_token = ctx.inline_token,
+ .for_token = ctx.loop_token,
+ .array_expr = undefined,
+ .payload = null,
+ .body = undefined,
+ .@"else" = null,
+ };
+ ctx.dest_ptr.store(&node.base);
+
+ stack.append(State { .Else = &node.@"else" }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
+ try stack.append(State { .PointerIndexPayload = &node.payload });
+ try stack.append(State { .ExpectToken = Token.Id.RParen });
+ try stack.append(State { .Expression = DestPtr { .Field = &node.array_expr } });
+ try stack.append(State { .ExpectToken = Token.Id.LParen });
+ },
+
State.Block => |block| {
const token = self.getNextToken();
switch (token.id) {
Token.Id.RBrace => {
- block.end_token = token;
+ block.rbrace = token;
continue;
},
else => {
@@ -648,48 +2434,266 @@ pub const Parser = struct {
},
State.Statement => |block| {
- {
- // Look for comptime var, comptime const
- const comptime_token = self.getNextToken();
- if (comptime_token.id == Token.Id.Keyword_comptime) {
+ const next = self.getNextToken();
+ switch (next.id) {
+ Token.Id.Keyword_comptime => {
const mut_token = self.getNextToken();
if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
// TODO shouldn't need these casts
const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
- mut_token, (?Token)(comptime_token), (?Token)(null));
- try stack.append(State { .VarDecl = var_decl });
+ mut_token, (?Token)(next), (?Token)(null), null);
+ stack.append(State { .VarDecl = var_decl }) catch unreachable;
continue;
+ } else {
+ self.putBackToken(mut_token);
+ self.putBackToken(next);
+ const statememt = try block.statements.addOne();
+ stack.append(State { .Semicolon = statememt }) catch unreachable;
+ try stack.append(State { .Expression = DestPtr{.Field = statememt } });
}
- self.putBackToken(mut_token);
- }
- self.putBackToken(comptime_token);
- }
- {
- // Look for const, var
- const mut_token = self.getNextToken();
- if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
- // TODO shouldn't need these casts
+ },
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
- mut_token, (?Token)(null), (?Token)(null));
- try stack.append(State { .VarDecl = var_decl });
+ next, (?Token)(null), (?Token)(null), null);
+ stack.append(State { .VarDecl = var_decl }) catch unreachable;
+ continue;
+ },
+ Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
+ const node = try arena.create(ast.NodeDefer);
+ *node = ast.NodeDefer {
+ .base = self.initNode(ast.Node.Id.Defer),
+ .defer_token = next,
+ .kind = switch (next.id) {
+ Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional,
+ Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error,
+ else => unreachable,
+ },
+ .expr = undefined,
+ };
+ try block.statements.append(&node.base);
+
+ stack.append(State { .Semicolon = &node.base }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = &node.expr } });
+ continue;
+ },
+ Token.Id.LBrace => {
+ const inner_block = try self.createBlock(arena, (?Token)(null), next);
+ try block.statements.append(&inner_block.base);
+
+ stack.append(State { .Block = inner_block }) catch unreachable;
+ continue;
+ },
+ else => {
+ self.putBackToken(next);
+ const statememt = try block.statements.addOne();
+ stack.append(State { .Semicolon = statememt }) catch unreachable;
+ try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = statememt } });
continue;
}
- self.putBackToken(mut_token);
}
- stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
- try stack.append(State { .Expression = DestPtr{.List = &block.statements} });
- continue;
},
- // These are data, not control flow.
- State.InfixOp => unreachable,
- State.PrefixOp => unreachable,
- State.Operand => unreachable,
+ State.Semicolon => |node_ptr| {
+ const node = *node_ptr;
+ if (requireSemiColon(node)) {
+ _ = (try self.eatToken(&stack, Token.Id.Semicolon)) ?? continue;
+ }
+ }
+ }
+ }
+ }
+
+ fn requireSemiColon(node: &const ast.Node) bool {
+ var n = node;
+ while (true) {
+ switch (n.id) {
+ ast.Node.Id.Root,
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
+ ast.Node.Id.ParamDecl,
+ ast.Node.Id.Block,
+ ast.Node.Id.Payload,
+ ast.Node.Id.PointerPayload,
+ ast.Node.Id.PointerIndexPayload,
+ ast.Node.Id.Switch,
+ ast.Node.Id.SwitchCase,
+ ast.Node.Id.SwitchElse,
+ ast.Node.Id.FieldInitializer,
+ ast.Node.Id.LineComment,
+ ast.Node.Id.TestDecl => return false,
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.NodeWhile, "base", n);
+ if (while_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ n = while_node.body;
+ },
+ ast.Node.Id.For => {
+ const for_node = @fieldParentPtr(ast.NodeFor, "base", n);
+ if (for_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ n = for_node.body;
+ },
+ ast.Node.Id.If => {
+ const if_node = @fieldParentPtr(ast.NodeIf, "base", n);
+ if (if_node.@"else") |@"else"| {
+ n = @"else".base;
+ continue;
+ }
+
+ n = if_node.body;
+ },
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.NodeElse, "base", n);
+ n = else_node.body;
+ },
+ ast.Node.Id.Defer => {
+ const defer_node = @fieldParentPtr(ast.NodeDefer, "base", n);
+ n = defer_node.expr;
+ },
+ ast.Node.Id.Comptime => {
+ const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n);
+ n = comptime_node.expr;
+ },
+ ast.Node.Id.Suspend => {
+ const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", n);
+ if (suspend_node.body) |body| {
+ n = body;
+ continue;
+ }
+
+ return true;
+ },
+ else => return true,
}
}
}
+ fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void {
+ var token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Comma => {
+ stack.append(state_after_comma) catch unreachable;
+ },
+ else => {
+ const IdTag = @TagType(Token.Id);
+ if (IdTag(*end) == token.id) {
+ if (maybe_ptr) |ptr| {
+ *ptr = token;
+ }
+ return;
+ }
+
+ try self.parseError(stack, token, "expected ',' or {}, found {}", @tagName(*end), @tagName(token.id));
+ },
+ }
+ }
+
+ fn tokenIdToAssignment(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
+ // TODO: We have to cast all cases because of this:
+ // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
+ return switch (*id) {
+ Token.Id.AmpersandEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitAnd),
+ Token.Id.AngleBracketAngleBracketLeftEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitShiftLeft),
+ Token.Id.AngleBracketAngleBracketRightEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitShiftRight),
+ Token.Id.AsteriskEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignTimes),
+ Token.Id.AsteriskPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignTimesWarp),
+ Token.Id.CaretEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitXor),
+ Token.Id.Equal => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Assign),
+ Token.Id.MinusEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMinus),
+ Token.Id.MinusPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMinusWrap),
+ Token.Id.PercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignMod),
+ Token.Id.PipeEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignBitOr),
+ Token.Id.PlusEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignPlus),
+ Token.Id.PlusPercentEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignPlusWrap),
+ Token.Id.SlashEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AssignDiv),
+ else => null,
+ };
+ }
+
+ fn tokenIdToComparison(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
+ // TODO: We have to cast all cases because of this:
+ // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
+ return switch (*id) {
+ Token.Id.BangEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BangEqual),
+ Token.Id.EqualEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.EqualEqual),
+ Token.Id.AngleBracketLeft => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.LessThan),
+ Token.Id.AngleBracketLeftEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.LessOrEqual),
+ Token.Id.AngleBracketRight => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.GreaterThan),
+ Token.Id.AngleBracketRightEqual => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.GreaterOrEqual),
+ else => null,
+ };
+ }
+
+ fn tokenIdToBitShift(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
+ // TODO: We have to cast all cases because of this:
+ // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
+ return switch (*id) {
+ Token.Id.AngleBracketAngleBracketLeft => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BitShiftLeft),
+ Token.Id.AngleBracketAngleBracketRight => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.BitShiftRight),
+ else => null,
+ };
+ }
+
+ fn tokenIdToAddition(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
+ // TODO: We have to cast all cases because of this:
+ // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
+ return switch (*id) {
+ Token.Id.Minus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Sub),
+ Token.Id.MinusPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.SubWrap),
+ Token.Id.Plus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Add),
+ Token.Id.PlusPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.AddWrap),
+ Token.Id.PlusPlus => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.ArrayCat),
+ else => null,
+ };
+ }
+
+ fn tokenIdToMultiply(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
+ // TODO: We have to cast all cases because of this:
+ // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
+ return switch (*id) {
+ Token.Id.Slash => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Div),
+ Token.Id.Asterisk => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Mult),
+ Token.Id.AsteriskAsterisk => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.ArrayMult),
+ Token.Id.AsteriskPercent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.MultWrap),
+ Token.Id.Percent => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.Mod),
+ Token.Id.PipePipe => (ast.NodeInfixOp.InfixOp)(ast.NodeInfixOp.InfixOp.MergeErrorSets),
+ else => null,
+ };
+ }
+
+ fn tokenIdToPrefixOp(id: &const Token.Id) ?ast.NodePrefixOp.PrefixOp {
+ // TODO: We have to cast all cases because of this:
+ // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
+ return switch (*id) {
+ Token.Id.Bang => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.BoolNot),
+ Token.Id.Tilde => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.BitNot),
+ Token.Id.Minus => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Negation),
+ Token.Id.MinusPercent => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.NegationWrap),
+ Token.Id.Asterisk => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Deref),
+ Token.Id.Ampersand => ast.NodePrefixOp.PrefixOp {
+ .AddrOf = ast.NodePrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ },
+ },
+ Token.Id.QuestionMark => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.MaybeType),
+ Token.Id.QuestionMarkQuestionMark => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.UnwrapMaybe),
+ Token.Id.Keyword_await => (ast.NodePrefixOp.PrefixOp)(ast.NodePrefixOp.PrefixOp.Await),
+ else => null,
+ };
+ }
+
fn initNode(self: &Parser, id: ast.Node.Id) ast.Node {
if (self.pending_line_comment_node) |comment_node| {
self.pending_line_comment_node = null;
@@ -711,7 +2715,7 @@ pub const Parser = struct {
}
fn createVarDecl(self: &Parser, arena: &mem.Allocator, visib_token: &const ?Token, mut_token: &const Token,
- comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
+ comptime_token: &const ?Token, extern_token: &const ?Token, lib_name: ?&ast.Node) !&ast.NodeVarDecl
{
const node = try arena.create(ast.NodeVarDecl);
@@ -724,7 +2728,7 @@ pub const Parser = struct {
.type_node = null,
.align_node = null,
.init_node = null,
- .lib_name = null,
+ .lib_name = lib_name,
// initialized later
.name_token = undefined,
.eq_token = undefined,
@@ -733,8 +2737,33 @@ pub const Parser = struct {
return node;
}
+ fn createStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeStringLiteral {
+ const node = try arena.create(ast.NodeStringLiteral);
+
+ assert(token.id == Token.Id.StringLiteral);
+ *node = ast.NodeStringLiteral {
+ .base = self.initNode(ast.Node.Id.StringLiteral),
+ .token = *token,
+ };
+ return node;
+ }
+
+ fn createTestDecl(self: &Parser, arena: &mem.Allocator, test_token: &const Token, name: &ast.Node,
+ block: &ast.NodeBlock) !&ast.NodeTestDecl
+ {
+ const node = try arena.create(ast.NodeTestDecl);
+
+ *node = ast.NodeTestDecl {
+ .base = self.initNode(ast.Node.Id.TestDecl),
+ .test_token = *test_token,
+ .name = name,
+ .body_node = &block.base,
+ };
+ return node;
+ }
+
fn createFnProto(self: &Parser, arena: &mem.Allocator, fn_token: &const Token, extern_token: &const ?Token,
- cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
+ lib_name: ?&ast.Node, cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
{
const node = try arena.create(ast.NodeFnProto);
@@ -749,8 +2778,9 @@ pub const Parser = struct {
.extern_token = *extern_token,
.inline_token = *inline_token,
.cc_token = *cc_token,
+ .async_attr = null,
.body_node = null,
- .lib_name = null,
+ .lib_name = lib_name,
.align_expr = null,
};
return node;
@@ -770,14 +2800,28 @@ pub const Parser = struct {
return node;
}
- fn createBlock(self: &Parser, arena: &mem.Allocator, begin_token: &const Token) !&ast.NodeBlock {
+ fn createBlock(self: &Parser, arena: &mem.Allocator, label: &const ?Token, lbrace: &const Token) !&ast.NodeBlock {
const node = try arena.create(ast.NodeBlock);
*node = ast.NodeBlock {
.base = self.initNode(ast.Node.Id.Block),
- .begin_token = *begin_token,
- .end_token = undefined,
+ .label = *label,
+ .lbrace = *lbrace,
.statements = ArrayList(&ast.Node).init(arena),
+ .rbrace = undefined,
+ };
+ return node;
+ }
+
+ fn createControlFlowExpr(self: &Parser, arena: &mem.Allocator, ltoken: &const Token,
+ kind: &const ast.NodeControlFlowExpression.Kind) !&ast.NodeControlFlowExpression
+ {
+ const node = try arena.create(ast.NodeControlFlowExpression);
+ *node = ast.NodeControlFlowExpression {
+ .base = self.initNode(ast.Node.Id.ControlFlowExpression),
+ .ltoken = *ltoken,
+ .kind = *kind,
+ .rhs = null,
};
return node;
}
@@ -807,6 +2851,18 @@ pub const Parser = struct {
return node;
}
+ fn createSuffixOp(self: &Parser, arena: &mem.Allocator, op: &const ast.NodeSuffixOp.SuffixOp) !&ast.NodeSuffixOp {
+ const node = try arena.create(ast.NodeSuffixOp);
+
+ *node = ast.NodeSuffixOp {
+ .base = self.initNode(ast.Node.Id.SuffixOp),
+ .lhs = undefined,
+ .op = *op,
+ .rtoken = undefined,
+ };
+ return node;
+ }
+
fn createIdentifier(self: &Parser, arena: &mem.Allocator, name_token: &const Token) !&ast.NodeIdentifier {
const node = try arena.create(ast.NodeIdentifier);
@@ -837,6 +2893,16 @@ pub const Parser = struct {
return node;
}
+ fn createUndefined(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeUndefinedLiteral {
+ const node = try arena.create(ast.NodeUndefinedLiteral);
+
+ *node = ast.NodeUndefinedLiteral {
+ .base = self.initNode(ast.Node.Id.UndefinedLiteral),
+ .token = *token,
+ };
+ return node;
+ }
+
fn createAttachIdentifier(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
const node = try self.createIdentifier(arena, name_token);
try dest_ptr.store(&node.base);
@@ -850,54 +2916,78 @@ pub const Parser = struct {
}
fn createAttachFnProto(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), fn_token: &const Token,
- extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
+ extern_token: &const ?Token, lib_name: ?&ast.Node, cc_token: &const ?Token, visib_token: &const ?Token,
inline_token: &const ?Token) !&ast.NodeFnProto
{
- const node = try self.createFnProto(arena, fn_token, extern_token, cc_token, visib_token, inline_token);
+ const node = try self.createFnProto(arena, fn_token, extern_token, lib_name, cc_token, visib_token, inline_token);
try list.append(&node.base);
return node;
}
fn createAttachVarDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
- extern_token: &const ?Token) !&ast.NodeVarDecl
+ extern_token: &const ?Token, lib_name: ?&ast.Node) !&ast.NodeVarDecl
{
- const node = try self.createVarDecl(arena, visib_token, mut_token, comptime_token, extern_token);
+ const node = try self.createVarDecl(arena, visib_token, mut_token, comptime_token, extern_token, lib_name);
try list.append(&node.base);
return node;
}
- fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
- const loc = self.tokenizer.getTokenLocation(token);
- warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, token.line + 1, token.column + 1, args);
- warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
- {
- var i: usize = 0;
- while (i < token.column) : (i += 1) {
- warn(" ");
+ fn createAttachTestDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
+ test_token: &const Token, name: &ast.Node, block: &ast.NodeBlock) !&ast.NodeTestDecl
+ {
+ const node = try self.createTestDecl(arena, test_token, name, block);
+ try list.append(&node.base);
+ return node;
+ }
+
+ fn parseError(self: &Parser, stack: &ArrayList(State), token: &const Token, comptime fmt: []const u8, args: ...) !void {
+ // Before reporting an error. We pop the stack to see if our state was optional
+ self.revertIfOptional(stack) catch {
+ const loc = self.tokenizer.getTokenLocation(0, token);
+ warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
+ warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
+ {
+ var i: usize = 0;
+ while (i < loc.column) : (i += 1) {
+ warn(" ");
+ }
}
- }
- {
- const caret_count = token.end - token.start;
- var i: usize = 0;
- while (i < caret_count) : (i += 1) {
- warn("~");
+ {
+ const caret_count = token.end - token.start;
+ var i: usize = 0;
+ while (i < caret_count) : (i += 1) {
+ warn("~");
+ }
}
- }
- warn("\n");
- return error.ParseError;
+ warn("\n");
+ return error.ParseError;
+ };
}
- fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) !void {
+ fn eatToken(self: &Parser, stack: &ArrayList(State), id: @TagType(Token.Id)) !?Token {
+ const token = self.getNextToken();
if (token.id != id) {
- return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
+ try self.parseError(stack, token, "expected {}, found {}", @tagName(id), @tagName(token.id));
+ return null;
}
+ return token;
}
- fn eatToken(self: &Parser, id: @TagType(Token.Id)) !Token {
- const token = self.getNextToken();
- try self.expectToken(token, id);
- return token;
+ fn revertIfOptional(self: &Parser, stack: &ArrayList(State)) !void {
+ while (stack.popOrNull()) |state| {
+ switch (state) {
+ State.Optional => |revert| {
+ *self = revert.parser;
+ *self.tokenizer = revert.tokenizer;
+ *revert.ptr = null;
+ return;
+ },
+ else => { }
+ }
+ }
+
+ return error.NoOptionalStateFound;
}
fn putBackToken(self: &Parser, token: &const Token) void {
@@ -956,6 +3046,7 @@ pub const Parser = struct {
Expression: &ast.Node,
VarDecl: &ast.NodeVarDecl,
Statement: &ast.Node,
+ FieldInitializer: &ast.NodeFieldInitializer,
PrintIndent,
Indent: usize,
};
@@ -976,9 +3067,8 @@ pub const Parser = struct {
try stack.append(RenderState {
.Text = blk: {
const prev_node = root_node.decls.at(i - 1);
- const prev_line_index = prev_node.lastToken().line;
- const this_line_index = decl.firstToken().line;
- if (this_line_index - prev_line_index >= 2) {
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, decl.firstToken());
+ if (loc.line >= 2) {
break :blk "\n\n";
}
break :blk "\n";
@@ -996,64 +3086,76 @@ pub const Parser = struct {
switch (decl.id) {
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
- if (fn_proto.visib_token) |visib_token| {
- switch (visib_token.id) {
- Token.Id.Keyword_pub => try stream.print("pub "),
- Token.Id.Keyword_export => try stream.print("export "),
- else => unreachable,
- }
- }
- if (fn_proto.extern_token) |extern_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
- }
- try stream.print("fn");
- if (fn_proto.name_token) |name_token| {
- try stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
+ if (fn_proto.body_node) |body_node| {
+ stack.append(RenderState { .Expression = body_node}) catch unreachable;
+ try stack.append(RenderState { .Text = " "});
+ } else {
+ stack.append(RenderState { .Text = ";" }) catch unreachable;
}
- try stream.print("(");
-
- if (fn_proto.body_node == null) {
- try stack.append(RenderState { .Text = ";" });
- }
-
- try stack.append(RenderState { .FnProtoRParen = fn_proto});
- var i = fn_proto.params.len;
- while (i != 0) {
- i -= 1;
- const param_decl_node = fn_proto.params.items[i];
- try stack.append(RenderState { .ParamDecl = param_decl_node});
- if (i != 0) {
- try stack.append(RenderState { .Text = ", " });
- }
+ try stack.append(RenderState { .Expression = decl });
+ },
+ ast.Node.Id.Use => {
+ const use_decl = @fieldParentPtr(ast.NodeUse, "base", decl);
+ if (use_decl.visib_token) |visib_token| {
+ try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
}
+ try stream.print("use ");
+ try stack.append(RenderState { .Text = ";" });
+ try stack.append(RenderState { .Expression = use_decl.expr });
},
ast.Node.Id.VarDecl => {
const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
try stack.append(RenderState { .VarDecl = var_decl});
+ },
+ ast.Node.Id.TestDecl => {
+ const test_decl = @fieldParentPtr(ast.NodeTestDecl, "base", decl);
+ try stream.print("test ");
+ try stack.append(RenderState { .Expression = test_decl.body_node });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = test_decl.name });
+ },
+ ast.Node.Id.StructField => {
+ const field = @fieldParentPtr(ast.NodeStructField, "base", decl);
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(field.name_token));
+ try stack.append(RenderState { .Expression = field.type_expr});
+ },
+ ast.Node.Id.UnionTag => {
+ const tag = @fieldParentPtr(ast.NodeUnionTag, "base", decl);
+ try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+
+ if (tag.type_expr) |type_expr| {
+ try stream.print(": ");
+ try stack.append(RenderState { .Expression = type_expr});
+ }
+ },
+ ast.Node.Id.EnumTag => {
+ const tag = @fieldParentPtr(ast.NodeEnumTag, "base", decl);
+ try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
+ if (tag.value) |value| {
+ try stream.print(" = ");
+ try stack.append(RenderState { .Expression = value});
+ }
+ },
+ ast.Node.Id.Comptime => {
+ if (requireSemiColon(decl)) {
+ try stack.append(RenderState { .Text = ";" });
+ }
+ try stack.append(RenderState { .Expression = decl });
},
else => unreachable,
}
},
- RenderState.VarDecl => |var_decl| {
- if (var_decl.visib_token) |visib_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
- }
- if (var_decl.extern_token) |extern_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
- if (var_decl.lib_name != null) {
- @panic("TODO");
- }
- }
- if (var_decl.comptime_token) |comptime_token| {
- try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
- }
- try stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
- try stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
+ RenderState.FieldInitializer => |field_init| {
+ try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));
+ try stream.print(" = ");
+ try stack.append(RenderState { .Expression = field_init.expr });
+ },
+ RenderState.VarDecl => |var_decl| {
try stack.append(RenderState { .Text = ";" });
if (var_decl.init_node) |init_node| {
try stack.append(RenderState { .Expression = init_node });
@@ -1065,8 +3167,30 @@ pub const Parser = struct {
try stack.append(RenderState { .Text = " align(" });
}
if (var_decl.type_node) |type_node| {
- try stream.print(": ");
try stack.append(RenderState { .Expression = type_node });
+ try stack.append(RenderState { .Text = ": " });
+ }
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.name_token) });
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(var_decl.mut_token) });
+
+ if (var_decl.comptime_token) |comptime_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(comptime_token) });
+ }
+
+ if (var_decl.extern_token) |extern_token| {
+ if (var_decl.lib_name != null) {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = ??var_decl.lib_name });
+ }
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_token) });
+ }
+
+ if (var_decl.visib_token) |visib_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
}
},
@@ -1097,6 +3221,10 @@ pub const Parser = struct {
},
ast.Node.Id.Block => {
const block = @fieldParentPtr(ast.NodeBlock, "base", base);
+ if (block.label) |label| {
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ }
+
if (block.statements.len == 0) {
try stream.write("{}");
} else {
@@ -1115,10 +3243,9 @@ pub const Parser = struct {
try stack.append(RenderState {
.Text = blk: {
if (i != 0) {
- const prev_statement_node = block.statements.items[i - 1];
- const prev_line_index = prev_statement_node.lastToken().line;
- const this_line_index = statement_node.firstToken().line;
- if (this_line_index - prev_line_index >= 2) {
+ const prev_node = block.statements.items[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, statement_node.firstToken());
+ if (loc.line >= 2) {
break :blk "\n\n";
}
}
@@ -1128,19 +3255,98 @@ pub const Parser = struct {
}
}
},
+ ast.Node.Id.Defer => {
+ const defer_node = @fieldParentPtr(ast.NodeDefer, "base", base);
+ try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token));
+ try stack.append(RenderState { .Expression = defer_node.expr });
+ },
+ ast.Node.Id.Comptime => {
+ const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", base);
+ try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token));
+ try stack.append(RenderState { .Expression = comptime_node.expr });
+ },
+ ast.Node.Id.AsyncAttribute => {
+ const async_attr = @fieldParentPtr(ast.NodeAsyncAttribute, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token));
+
+ if (async_attr.allocator_type) |allocator_type| {
+ try stack.append(RenderState { .Text = ">" });
+ try stack.append(RenderState { .Expression = allocator_type });
+ try stack.append(RenderState { .Text = "<" });
+ }
+ },
+ ast.Node.Id.Suspend => {
+ const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));
+
+ if (suspend_node.body) |body| {
+ try stack.append(RenderState { .Expression = body });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ if (suspend_node.payload) |payload| {
+ try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Text = " " });
+ }
+ },
ast.Node.Id.InfixOp => {
const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
try stack.append(RenderState { .Expression = prefix_op_node.rhs });
- switch (prefix_op_node.op) {
- ast.NodeInfixOp.InfixOp.EqualEqual => {
- try stack.append(RenderState { .Text = " == "});
- },
- ast.NodeInfixOp.InfixOp.BangEqual => {
- try stack.append(RenderState { .Text = " != "});
- },
- ast.NodeInfixOp.InfixOp.Period => {
- try stack.append(RenderState { .Text = "."});
- },
+
+ if (prefix_op_node.op == ast.NodeInfixOp.InfixOp.Catch) {
+ if (prefix_op_node.op.Catch) |payload| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &payload.base });
+ }
+ try stack.append(RenderState { .Text = " catch " });
+ } else {
+ const text = switch (prefix_op_node.op) {
+ ast.NodeInfixOp.InfixOp.Add => " + ",
+ ast.NodeInfixOp.InfixOp.AddWrap => " +% ",
+ ast.NodeInfixOp.InfixOp.ArrayCat => " ++ ",
+ ast.NodeInfixOp.InfixOp.ArrayMult => " ** ",
+ ast.NodeInfixOp.InfixOp.Assign => " = ",
+ ast.NodeInfixOp.InfixOp.AssignBitAnd => " &= ",
+ ast.NodeInfixOp.InfixOp.AssignBitOr => " |= ",
+ ast.NodeInfixOp.InfixOp.AssignBitShiftLeft => " <<= ",
+ ast.NodeInfixOp.InfixOp.AssignBitShiftRight => " >>= ",
+ ast.NodeInfixOp.InfixOp.AssignBitXor => " ^= ",
+ ast.NodeInfixOp.InfixOp.AssignDiv => " /= ",
+ ast.NodeInfixOp.InfixOp.AssignMinus => " -= ",
+ ast.NodeInfixOp.InfixOp.AssignMinusWrap => " -%= ",
+ ast.NodeInfixOp.InfixOp.AssignMod => " %= ",
+ ast.NodeInfixOp.InfixOp.AssignPlus => " += ",
+ ast.NodeInfixOp.InfixOp.AssignPlusWrap => " +%= ",
+ ast.NodeInfixOp.InfixOp.AssignTimes => " *= ",
+ ast.NodeInfixOp.InfixOp.AssignTimesWarp => " *%= ",
+ ast.NodeInfixOp.InfixOp.BangEqual => " != ",
+ ast.NodeInfixOp.InfixOp.BitAnd => " & ",
+ ast.NodeInfixOp.InfixOp.BitOr => " | ",
+ ast.NodeInfixOp.InfixOp.BitShiftLeft => " << ",
+ ast.NodeInfixOp.InfixOp.BitShiftRight => " >> ",
+ ast.NodeInfixOp.InfixOp.BitXor => " ^ ",
+ ast.NodeInfixOp.InfixOp.BoolAnd => " and ",
+ ast.NodeInfixOp.InfixOp.BoolOr => " or ",
+ ast.NodeInfixOp.InfixOp.Div => " / ",
+ ast.NodeInfixOp.InfixOp.EqualEqual => " == ",
+ ast.NodeInfixOp.InfixOp.ErrorUnion => "!",
+ ast.NodeInfixOp.InfixOp.GreaterOrEqual => " >= ",
+ ast.NodeInfixOp.InfixOp.GreaterThan => " > ",
+ ast.NodeInfixOp.InfixOp.LessOrEqual => " <= ",
+ ast.NodeInfixOp.InfixOp.LessThan => " < ",
+ ast.NodeInfixOp.InfixOp.MergeErrorSets => " || ",
+ ast.NodeInfixOp.InfixOp.Mod => " % ",
+ ast.NodeInfixOp.InfixOp.Mult => " * ",
+ ast.NodeInfixOp.InfixOp.MultWrap => " *% ",
+ ast.NodeInfixOp.InfixOp.Period => ".",
+ ast.NodeInfixOp.InfixOp.Sub => " - ",
+ ast.NodeInfixOp.InfixOp.SubWrap => " -% ",
+ ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ",
+ ast.NodeInfixOp.InfixOp.Range => " ... ",
+ ast.NodeInfixOp.InfixOp.Catch => unreachable,
+ };
+
+ try stack.append(RenderState { .Text = text });
}
try stack.append(RenderState { .Expression = prefix_op_node.lhs });
},
@@ -1148,12 +3354,6 @@ pub const Parser = struct {
const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
try stack.append(RenderState { .Expression = prefix_op_node.rhs });
switch (prefix_op_node.op) {
- ast.NodePrefixOp.PrefixOp.Return => {
- try stream.write("return ");
- },
- ast.NodePrefixOp.PrefixOp.Try => {
- try stream.write("try ");
- },
ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
try stream.write("&");
if (addr_of_info.volatile_token != null) {
@@ -1168,8 +3368,180 @@ pub const Parser = struct {
try stack.append(RenderState { .Expression = align_expr});
}
},
+ ast.NodePrefixOp.PrefixOp.SliceType => |addr_of_info| {
+ try stream.write("[]");
+ if (addr_of_info.volatile_token != null) {
+ try stack.append(RenderState { .Text = "volatile "});
+ }
+ if (addr_of_info.const_token != null) {
+ try stack.append(RenderState { .Text = "const "});
+ }
+ if (addr_of_info.align_expr) |align_expr| {
+ try stream.print("align(");
+ try stack.append(RenderState { .Text = ") "});
+ try stack.append(RenderState { .Expression = align_expr});
+ }
+ },
+ ast.NodePrefixOp.PrefixOp.ArrayType => |array_index| {
+ try stack.append(RenderState { .Text = "]"});
+ try stack.append(RenderState { .Expression = array_index});
+ try stack.append(RenderState { .Text = "["});
+ },
+ ast.NodePrefixOp.PrefixOp.BitNot => try stream.write("~"),
+ ast.NodePrefixOp.PrefixOp.BoolNot => try stream.write("!"),
+ ast.NodePrefixOp.PrefixOp.Deref => try stream.write("*"),
+ ast.NodePrefixOp.PrefixOp.Negation => try stream.write("-"),
+ ast.NodePrefixOp.PrefixOp.NegationWrap => try stream.write("-%"),
+ ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "),
+ ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"),
+ ast.NodePrefixOp.PrefixOp.MaybeType => try stream.write("?"),
+ ast.NodePrefixOp.PrefixOp.Await => try stream.write("await "),
+ ast.NodePrefixOp.PrefixOp.Cancel => try stream.write("cancel "),
+ ast.NodePrefixOp.PrefixOp.Resume => try stream.write("resume "),
+ }
+ },
+ ast.Node.Id.SuffixOp => {
+ const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", base);
+
+ switch (suffix_op.op) {
+ ast.NodeSuffixOp.SuffixOp.Call => |call_info| {
+ try stack.append(RenderState { .Text = ")"});
+ var i = call_info.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_node = call_info.params.at(i);
+ try stack.append(RenderState { .Expression = param_node});
+ if (i != 0) {
+ try stack.append(RenderState { .Text = ", " });
+ }
+ }
+ try stack.append(RenderState { .Text = "("});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+
+ if (call_info.async_attr) |async_attr| {
+ try stack.append(RenderState { .Text = " "});
+ try stack.append(RenderState { .Expression = &async_attr.base });
+ }
+ },
+ ast.NodeSuffixOp.SuffixOp.ArrayAccess => |index_expr| {
+ try stack.append(RenderState { .Text = "]"});
+ try stack.append(RenderState { .Expression = index_expr});
+ try stack.append(RenderState { .Text = "["});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+ },
+ ast.NodeSuffixOp.SuffixOp.Slice => |range| {
+ try stack.append(RenderState { .Text = "]"});
+ if (range.end) |end| {
+ try stack.append(RenderState { .Expression = end});
+ }
+ try stack.append(RenderState { .Text = ".."});
+ try stack.append(RenderState { .Expression = range.start});
+ try stack.append(RenderState { .Text = "["});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+ },
+ ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| {
+ try stack.append(RenderState { .Text = " }"});
+ var i = field_inits.len;
+ while (i != 0) {
+ i -= 1;
+ const field_init = field_inits.at(i);
+ try stack.append(RenderState { .FieldInitializer = field_init });
+ try stack.append(RenderState { .Text = " " });
+ if (i != 0) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ try stack.append(RenderState { .Text = "{"});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+ },
+ ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| {
+ try stack.append(RenderState { .Text = " }"});
+ var i = exprs.len;
+ while (i != 0) {
+ i -= 1;
+ const expr = exprs.at(i);
+ try stack.append(RenderState { .Expression = expr });
+ try stack.append(RenderState { .Text = " " });
+ if (i != 0) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ try stack.append(RenderState { .Text = "{"});
+ try stack.append(RenderState { .Expression = suffix_op.lhs });
+ },
}
},
+ ast.Node.Id.ControlFlowExpression => {
+ const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base);
+ switch (flow_expr.kind) {
+ ast.NodeControlFlowExpression.Kind.Break => |maybe_blk_token| {
+ try stream.print("break");
+ if (maybe_blk_token) |blk_token| {
+ try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token));
+ }
+ },
+ ast.NodeControlFlowExpression.Kind.Continue => |maybe_blk_token| {
+ try stream.print("continue");
+ if (maybe_blk_token) |blk_token| {
+ try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token));
+ }
+ },
+ ast.NodeControlFlowExpression.Kind.Return => {
+ try stream.print("return");
+ },
+
+ }
+
+ if (flow_expr.rhs) |rhs| {
+ try stream.print(" ");
+ try stack.append(RenderState { .Expression = rhs });
+ }
+ },
+ ast.Node.Id.Payload => {
+ const payload = @fieldParentPtr(ast.NodePayload, "base", base);
+ try stack.append(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Expression = &payload.error_symbol.base });
+ try stack.append(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.PointerPayload => {
+ const payload = @fieldParentPtr(ast.NodePointerPayload, "base", base);
+ try stack.append(RenderState { .Text = "|"});
+ try stack.append(RenderState { .Expression = &payload.value_symbol.base });
+
+ if (payload.is_ptr) {
+ try stack.append(RenderState { .Text = "*"});
+ }
+
+ try stack.append(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.PointerIndexPayload => {
+ const payload = @fieldParentPtr(ast.NodePointerIndexPayload, "base", base);
+ try stack.append(RenderState { .Text = "|"});
+
+ if (payload.index_symbol) |index_symbol| {
+ try stack.append(RenderState { .Expression = &index_symbol.base });
+ try stack.append(RenderState { .Text = ", "});
+ }
+
+ try stack.append(RenderState { .Expression = &payload.value_symbol.base });
+
+ if (payload.is_ptr) {
+ try stack.append(RenderState { .Text = "*"});
+ }
+
+ try stack.append(RenderState { .Text = "|"});
+ },
+ ast.Node.Id.GroupedExpression => {
+ const grouped_expr = @fieldParentPtr(ast.NodeGroupedExpression, "base", base);
+ try stack.append(RenderState { .Text = ")"});
+ try stack.append(RenderState { .Expression = grouped_expr.expr });
+ try stack.append(RenderState { .Text = "("});
+ },
+ ast.Node.Id.FieldInitializer => {
+ const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base);
+ try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));
+ try stack.append(RenderState { .Expression = field_init.expr });
+ },
ast.Node.Id.IntegerLiteral => {
const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
@@ -1182,6 +3554,151 @@ pub const Parser = struct {
const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base);
try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
},
+ ast.Node.Id.CharLiteral => {
+ const char_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token));
+ },
+ ast.Node.Id.BoolLiteral => {
+ const bool_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token));
+ },
+ ast.Node.Id.NullLiteral => {
+ const null_literal = @fieldParentPtr(ast.NodeNullLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token));
+ },
+ ast.Node.Id.ThisLiteral => {
+ const this_literal = @fieldParentPtr(ast.NodeThisLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token));
+ },
+ ast.Node.Id.Unreachable => {
+ const unreachable_node = @fieldParentPtr(ast.NodeUnreachable, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token));
+ },
+ ast.Node.Id.ErrorType => {
+ const error_type = @fieldParentPtr(ast.NodeErrorType, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token));
+ },
+ ast.Node.Id.VarType => {
+ const var_type = @fieldParentPtr(ast.NodeVarType, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token));
+ },
+ ast.Node.Id.ContainerDecl => {
+ const container_decl = @fieldParentPtr(ast.NodeContainerDecl, "base", base);
+
+ switch (container_decl.layout) {
+ ast.NodeContainerDecl.Layout.Packed => try stream.print("packed "),
+ ast.NodeContainerDecl.Layout.Extern => try stream.print("extern "),
+ ast.NodeContainerDecl.Layout.Auto => { },
+ }
+
+ switch (container_decl.kind) {
+ ast.NodeContainerDecl.Kind.Struct => try stream.print("struct"),
+ ast.NodeContainerDecl.Kind.Enum => try stream.print("enum"),
+ ast.NodeContainerDecl.Kind.Union => try stream.print("union"),
+ }
+
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
+
+ const fields_and_decls = container_decl.fields_and_decls.toSliceConst();
+ var i = fields_and_decls.len;
+ while (i != 0) {
+ i -= 1;
+ const node = fields_and_decls[i];
+ try stack.append(RenderState { .TopLevelDecl = node});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = fields_and_decls[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+
+ if (i != 0) {
+ const prev_node = fields_and_decls[i - 1];
+ switch (prev_node.id) {
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag => {
+ try stack.append(RenderState { .Text = "," });
+ },
+ else => { }
+ }
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "{"});
+
+ switch (container_decl.init_arg_expr) {
+ ast.NodeContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
+ ast.NodeContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}),
+ ast.NodeContainerDecl.InitArg.Type => |type_expr| {
+ try stack.append(RenderState { .Text = ") "});
+ try stack.append(RenderState { .Expression = type_expr});
+ try stack.append(RenderState { .Text = "("});
+ },
+ }
+ },
+ ast.Node.Id.ErrorSetDecl => {
+ const err_set_decl = @fieldParentPtr(ast.NodeErrorSetDecl, "base", base);
+ try stream.print("error ");
+
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
+
+ const decls = err_set_decl.decls.toSliceConst();
+ var i = decls.len;
+ while (i != 0) {
+ i -= 1;
+ const node = decls[i];
+ try stack.append(RenderState { .Expression = &node.base});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = decls[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+
+ if (i != 0) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "{"});
+ },
+ ast.Node.Id.MultilineStringLiteral => {
+ const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);
+ try stream.print("\n");
+
+ var i : usize = 0;
+ while (i < multiline_str_literal.tokens.len) : (i += 1) {
+ const t = multiline_str_literal.tokens.at(i);
+ try stream.writeByteNTimes(' ', indent + indent_delta);
+ try stream.print("{}", self.tokenizer.getTokenSlice(t));
+ }
+ try stream.writeByteNTimes(' ', indent + indent_delta);
+ },
+ ast.Node.Id.UndefinedLiteral => {
+ const undefined_literal = @fieldParentPtr(ast.NodeUndefinedLiteral, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token));
+ },
ast.Node.Id.BuiltinCall => {
const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base);
try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token));
@@ -1196,11 +3713,420 @@ pub const Parser = struct {
}
}
},
- ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", base);
+
+ switch (fn_proto.return_type) {
+ ast.NodeFnProto.ReturnType.Explicit => |node| {
+ try stack.append(RenderState { .Expression = node});
+ },
+ ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
+ try stack.append(RenderState { .Expression = node});
+ try stack.append(RenderState { .Text = "!"});
+ },
+ }
+
+ if (fn_proto.align_expr != null) {
+ @panic("TODO");
+ }
+
+ try stack.append(RenderState { .Text = ") " });
+ var i = fn_proto.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_decl_node = fn_proto.params.items[i];
+ try stack.append(RenderState { .ParamDecl = param_decl_node});
+ if (i != 0) {
+ try stack.append(RenderState { .Text = ", " });
+ }
+ }
+
+ try stack.append(RenderState { .Text = "(" });
+ if (fn_proto.name_token) |name_token| {
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(name_token) });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ try stack.append(RenderState { .Text = "fn" });
+
+ if (fn_proto.async_attr) |async_attr| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &async_attr.base });
+ }
+
+ if (fn_proto.cc_token) |cc_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(cc_token) });
+ }
+
+ if (fn_proto.lib_name) |lib_name| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = lib_name });
+ }
+ if (fn_proto.extern_token) |extern_token| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(extern_token) });
+ }
+
+ if (fn_proto.visib_token) |visib_token| {
+ assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(visib_token) });
+ }
+ },
ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
+ ast.Node.Id.Switch => {
+ const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base);
+ try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
+
+ try stack.append(RenderState { .Text = "}"});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = "\n"});
+
+ const cases = switch_node.cases.toSliceConst();
+ var i = cases.len;
+ while (i != 0) {
+ i -= 1;
+ const node = cases[i];
+ try stack.append(RenderState { .Expression = &node.base});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ if (i != 0) {
+ const prev_node = cases[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ }
+ break :blk "\n";
+ },
+ });
+
+ if (i != 0) {
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = ") {"});
+ try stack.append(RenderState { .Expression = switch_node.expr });
+ },
+ ast.Node.Id.SwitchCase => {
+ const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base);
+
+ try stack.append(RenderState { .Expression = switch_case.expr });
+ if (switch_case.payload) |payload| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &payload.base });
+ }
+ try stack.append(RenderState { .Text = " => "});
+
+ const items = switch_case.items.toSliceConst();
+ var i = items.len;
+ while (i != 0) {
+ i -= 1;
+ try stack.append(RenderState { .Expression = items[i] });
+
+ if (i != 0) {
+ try stack.append(RenderState { .Text = ", " });
+ }
+ }
+ },
+ ast.Node.Id.SwitchElse => {
+ const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));
+ },
+ ast.Node.Id.Else => {
+ const else_node = @fieldParentPtr(ast.NodeElse, "base", base);
+ try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token));
+
+ switch (else_node.body.id) {
+ ast.Node.Id.Block, ast.Node.Id.If,
+ ast.Node.Id.For, ast.Node.Id.While,
+ ast.Node.Id.Switch => {
+ try stream.print(" ");
+ try stack.append(RenderState { .Expression = else_node.body });
+ },
+ else => {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = else_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ }
+ }
+
+ if (else_node.payload) |payload| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &payload.base });
+ }
+ },
+ ast.Node.Id.While => {
+ const while_node = @fieldParentPtr(ast.NodeWhile, "base", base);
+ if (while_node.label) |label| {
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ }
+
+ if (while_node.inline_token) |inline_token| {
+ try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token));
+ }
+
+ try stream.print("{} ", self.tokenizer.getTokenSlice(while_node.while_token));
+
+ if (while_node.@"else") |@"else"| {
+ try stack.append(RenderState { .Expression = &@"else".base });
+
+ if (while_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = "\n" });
+ }
+ }
+
+ if (while_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Expression = while_node.body });
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = while_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ }
+
+ if (while_node.continue_expr) |continue_expr| {
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = continue_expr });
+ try stack.append(RenderState { .Text = ": (" });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ if (while_node.payload) |payload| {
+ try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = while_node.condition });
+ try stack.append(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.For => {
+ const for_node = @fieldParentPtr(ast.NodeFor, "base", base);
+ if (for_node.label) |label| {
+ try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
+ }
+
+ if (for_node.inline_token) |inline_token| {
+ try stream.print("{} ", self.tokenizer.getTokenSlice(inline_token));
+ }
+
+ try stream.print("{} ", self.tokenizer.getTokenSlice(for_node.for_token));
+
+ if (for_node.@"else") |@"else"| {
+ try stack.append(RenderState { .Expression = &@"else".base });
+
+ if (for_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = "\n" });
+ }
+ }
+
+ if (for_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Expression = for_node.body });
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Expression = for_node.body });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ }
+
+ if (for_node.payload) |payload| {
+ try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = for_node.array_expr });
+ try stack.append(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.If => {
+ const if_node = @fieldParentPtr(ast.NodeIf, "base", base);
+ try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token));
+
+ switch (if_node.body.id) {
+ ast.Node.Id.Block, ast.Node.Id.If,
+ ast.Node.Id.For, ast.Node.Id.While,
+ ast.Node.Id.Switch => {
+ if (if_node.@"else") |@"else"| {
+ try stack.append(RenderState { .Expression = &@"else".base });
+
+ if (if_node.body.id == ast.Node.Id.Block) {
+ try stack.append(RenderState { .Text = " " });
+ } else {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Text = "\n" });
+ }
+ }
+ },
+ else => {
+ if (if_node.@"else") |@"else"| {
+ try stack.append(RenderState { .Expression = @"else".body });
+ if (@"else".payload) |payload| {
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Expression = &payload.base });
+ }
+
+ try stack.append(RenderState { .Text = " " });
+ try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(@"else".else_token) });
+ try stack.append(RenderState { .Text = " " });
+ }
+ }
+ }
+
+ try stack.append(RenderState { .Expression = if_node.body });
+ try stack.append(RenderState { .Text = " " });
+
+ if (if_node.payload) |payload| {
+ try stack.append(RenderState { .Expression = &payload.base });
+ try stack.append(RenderState { .Text = " " });
+ }
+
+ try stack.append(RenderState { .Text = ")" });
+ try stack.append(RenderState { .Expression = if_node.condition });
+ try stack.append(RenderState { .Text = "(" });
+ },
+ ast.Node.Id.Asm => {
+ const asm_node = @fieldParentPtr(ast.NodeAsm, "base", base);
+ try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token));
+
+ if (asm_node.is_volatile) {
+ try stream.write("volatile ");
+ }
+
+ try stream.print("({}", self.tokenizer.getTokenSlice(asm_node.template));
+
+ try stack.append(RenderState { .Indent = indent });
+ try stack.append(RenderState { .Text = ")" });
+ {
+ const cloppers = asm_node.cloppers.toSliceConst();
+ var i = cloppers.len;
+ while (i != 0) {
+ i -= 1;
+ try stack.append(RenderState { .Expression = &cloppers[i].base });
+
+ if (i != 0) {
+ try stack.append(RenderState { .Text = ", " });
+ }
+ }
+ }
+ try stack.append(RenderState { .Text = ": " });
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta });
+ try stack.append(RenderState { .Text = "\n" });
+ {
+ const inputs = asm_node.inputs.toSliceConst();
+ var i = inputs.len;
+ while (i != 0) {
+ i -= 1;
+ const node = inputs[i];
+ try stack.append(RenderState { .Expression = &node.base});
+
+ if (i != 0) {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ const prev_node = inputs[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.append(RenderState { .Text = ": "});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "\n" });
+ {
+ const outputs = asm_node.outputs.toSliceConst();
+ var i = outputs.len;
+ while (i != 0) {
+ i -= 1;
+ const node = outputs[i];
+ try stack.append(RenderState { .Expression = &node.base});
+
+ if (i != 0) {
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState {
+ .Text = blk: {
+ const prev_node = outputs[i - 1];
+ const loc = self.tokenizer.getTokenLocation(prev_node.lastToken().end, node.firstToken());
+ if (loc.line >= 2) {
+ break :blk "\n\n";
+ }
+ break :blk "\n";
+ },
+ });
+ try stack.append(RenderState { .Text = "," });
+ }
+ }
+ }
+ try stack.append(RenderState { .Indent = indent + indent_delta + 2});
+ try stack.append(RenderState { .Text = ": "});
+ try stack.append(RenderState.PrintIndent);
+ try stack.append(RenderState { .Indent = indent + indent_delta});
+ try stack.append(RenderState { .Text = "\n" });
+ },
+ ast.Node.Id.AsmInput => {
+ const asm_input = @fieldParentPtr(ast.NodeAsmInput, "base", base);
+
+ try stack.append(RenderState { .Text = ")"});
+ try stack.append(RenderState { .Expression = asm_input.expr});
+ try stack.append(RenderState { .Text = " ("});
+ try stack.append(RenderState { .Expression = &asm_input.constraint.base});
+ try stack.append(RenderState { .Text = "] "});
+ try stack.append(RenderState { .Expression = &asm_input.symbolic_name.base});
+ try stack.append(RenderState { .Text = "["});
+ },
+ ast.Node.Id.AsmOutput => {
+ const asm_output = @fieldParentPtr(ast.NodeAsmOutput, "base", base);
+
+ try stack.append(RenderState { .Text = ")"});
+ switch (asm_output.kind) {
+ ast.NodeAsmOutput.Kind.Variable => |variable_name| {
+ try stack.append(RenderState { .Expression = &variable_name.base});
+ },
+ ast.NodeAsmOutput.Kind.Return => |return_type| {
+ try stack.append(RenderState { .Expression = return_type});
+ try stack.append(RenderState { .Text = "-> "});
+ },
+ }
+ try stack.append(RenderState { .Text = " ("});
+ try stack.append(RenderState { .Expression = &asm_output.constraint.base});
+ try stack.append(RenderState { .Text = "] "});
+ try stack.append(RenderState { .Expression = &asm_output.symbolic_name.base});
+ try stack.append(RenderState { .Text = "["});
+ },
+
+ ast.Node.Id.StructField,
+ ast.Node.Id.UnionTag,
+ ast.Node.Id.EnumTag,
ast.Node.Id.Root,
ast.Node.Id.VarDecl,
+ ast.Node.Id.Use,
+ ast.Node.Id.TestDecl,
ast.Node.Id.ParamDecl => unreachable,
},
RenderState.FnProtoRParen => |fn_proto| {
@@ -1217,9 +4143,6 @@ pub const Parser = struct {
ast.NodeFnProto.ReturnType.Explicit => |node| {
try stack.append(RenderState { .Expression = node});
},
- ast.NodeFnProto.ReturnType.Infer => {
- try stream.print("var");
- },
ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
try stream.print("!");
try stack.append(RenderState { .Expression = node});
@@ -1239,8 +4162,10 @@ pub const Parser = struct {
try stack.append(RenderState { .VarDecl = var_decl});
},
else => {
- try stack.append(RenderState { .Text = ";"});
- try stack.append(RenderState { .Expression = base});
+ if (requireSemiColon(base)) {
+ try stack.append(RenderState { .Text = ";" });
+ }
+ try stack.append(RenderState { .Expression = base });
},
}
},
@@ -1324,7 +4249,7 @@ fn testCanonical(source: []const u8) !void {
}
}
-test "zig fmt" {
+test "zig fmt: get stdout or fail" {
try testCanonical(
\\const std = @import("std");
\\
@@ -1335,7 +4260,9 @@ test "zig fmt" {
\\}
\\
);
+}
+test "zig fmt: preserve spacing" {
try testCanonical(
\\const std = @import("std");
\\
@@ -1348,25 +4275,26 @@ test "zig fmt" {
\\}
\\
);
+}
+test "zig fmt: return types" {
try testCanonical(
\\pub fn main() !void {}
\\pub fn main() var {}
\\pub fn main() i32 {}
\\
);
+}
+test "zig fmt: imports" {
try testCanonical(
\\const std = @import("std");
\\const std = @import();
\\
);
+}
- try testCanonical(
- \\extern fn puts(s: &const u8) c_int;
- \\
- );
-
+test "zig fmt: global declarations" {
try testCanonical(
\\const a = b;
\\pub const a = b;
@@ -1376,50 +4304,746 @@ test "zig fmt" {
\\pub const a: i32 = b;
\\var a: i32 = b;
\\pub var a: i32 = b;
+ \\extern const a: i32 = b;
+ \\pub extern const a: i32 = b;
+ \\extern var a: i32 = b;
+ \\pub extern var a: i32 = b;
+ \\extern "a" const a: i32 = b;
+ \\pub extern "a" const a: i32 = b;
+ \\extern "a" var a: i32 = b;
+ \\pub extern "a" var a: i32 = b;
\\
);
+}
+test "zig fmt: extern declaration" {
try testCanonical(
\\extern var foo: c_int;
\\
);
+}
- try testCanonical(
+test "zig fmt: alignment" {
+ try testCanonical(
\\var foo: c_int align(1);
\\
);
+}
+test "zig fmt: C main" {
try testCanonical(
\\fn main(argc: c_int, argv: &&u8) c_int {
\\ const a = b;
\\}
\\
);
+}
+test "zig fmt: return" {
try testCanonical(
\\fn foo(argc: c_int, argv: &&u8) c_int {
\\ return 0;
\\}
\\
+ \\fn bar() void {
+ \\ return;
+ \\}
+ \\
);
+}
+test "zig fmt: pointer attributes" {
try testCanonical(
\\extern fn f1(s: &align(&u8) u8) c_int;
+ \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+ \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+ \\extern fn f4(s: &align(1) const volatile u8) c_int;
\\
);
+}
+test "zig fmt: slice attributes" {
try testCanonical(
- \\extern fn f1(s: &&align(1) &const &volatile u8) c_int;
- \\extern fn f2(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
- \\extern fn f3(s: &align(1) const volatile u8) c_int;
+ \\extern fn f1(s: &align(&u8) u8) c_int;
+ \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
+ \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
+ \\extern fn f4(s: &align(1) const volatile u8) c_int;
\\
);
+}
+test "zig fmt: test declaration" {
+ try testCanonical(
+ \\test "test name" {
+ \\ const a = 1;
+ \\ var b = 1;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: infix operators" {
+ try testCanonical(
+ \\test "infix operators" {
+ \\ var i = undefined;
+ \\ i = 2;
+ \\ i *= 2;
+ \\ i |= 2;
+ \\ i ^= 2;
+ \\ i <<= 2;
+ \\ i >>= 2;
+ \\ i &= 2;
+ \\ i *= 2;
+ \\ i *%= 2;
+ \\ i -= 2;
+ \\ i -%= 2;
+ \\ i += 2;
+ \\ i +%= 2;
+ \\ i /= 2;
+ \\ i %= 2;
+ \\ _ = i == i;
+ \\ _ = i != i;
+ \\ _ = i != i;
+ \\ _ = i.i;
+ \\ _ = i || i;
+ \\ _ = i!i;
+ \\ _ = i ** i;
+ \\ _ = i ++ i;
+ \\ _ = i ?? i;
+ \\ _ = i % i;
+ \\ _ = i / i;
+ \\ _ = i *% i;
+ \\ _ = i * i;
+ \\ _ = i -% i;
+ \\ _ = i - i;
+ \\ _ = i +% i;
+ \\ _ = i + i;
+ \\ _ = i << i;
+ \\ _ = i >> i;
+ \\ _ = i & i;
+ \\ _ = i ^ i;
+ \\ _ = i | i;
+ \\ _ = i >= i;
+ \\ _ = i <= i;
+ \\ _ = i > i;
+ \\ _ = i < i;
+ \\ _ = i and i;
+ \\ _ = i or i;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: precedence" {
+ try testCanonical(
+ \\test "precedence" {
+ \\ a!b();
+ \\ (a!b)();
+ \\ !a!b;
+ \\ !(a!b);
+ \\ !a{ };
+ \\ !(a{ });
+ \\ a + b{ };
+ \\ (a + b){ };
+ \\ a << b + c;
+ \\ (a << b) + c;
+ \\ a & b << c;
+ \\ (a & b) << c;
+ \\ a ^ b & c;
+ \\ (a ^ b) & c;
+ \\ a | b ^ c;
+ \\ (a | b) ^ c;
+ \\ a == b | c;
+ \\ (a == b) | c;
+ \\ a and b == c;
+ \\ (a and b) == c;
+ \\ a or b and c;
+ \\ (a or b) and c;
+ \\ (a or b) and c;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: prefix operators" {
+ try testCanonical(
+ \\test "prefix operators" {
+ \\ try return --%~??!*&0;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: call expression" {
try testCanonical(
- \\fn f1(a: bool, b: bool) bool {
- \\ a != b;
- \\ return a == b;
+ \\test "test calls" {
+ \\ a();
+ \\ a(1);
+ \\ a(1, 2);
+ \\ a(1, 2) + a(1, 2);
\\}
\\
);
}
+
+test "zig fmt: var args" {
+ try testCanonical(
+ \\fn print(args: ...) void {}
+ \\
+ );
+}
+
+test "zig fmt: var type" {
+ try testCanonical(
+ \\fn print(args: var) var {}
+ \\const Var = var;
+ \\const i: var = 0;
+ \\
+ );
+}
+
+test "zig fmt: extern function" {
+ try testCanonical(
+ \\extern fn puts(s: &const u8) c_int;
+ \\extern "c" fn puts(s: &const u8) c_int;
+ \\
+ );
+}
+
+test "zig fmt: multiline string" {
+ try testCanonical(
+ \\const s =
+ \\ \\ something
+ \\ \\ something else
+ \\ ;
+ \\
+ );
+}
+
+test "zig fmt: values" {
+ try testCanonical(
+ \\test "values" {
+ \\ 1;
+ \\ 1.0;
+ \\ "string";
+ \\ c"cstring";
+ \\ 'c';
+ \\ true;
+ \\ false;
+ \\ null;
+ \\ undefined;
+ \\ error;
+ \\ this;
+ \\ unreachable;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: indexing" {
+ try testCanonical(
+ \\test "test index" {
+ \\ a[0];
+ \\ a[0 + 5];
+ \\ a[0..];
+ \\ a[0..5];
+ \\ a[a[0]];
+ \\ a[a[0..]];
+ \\ a[a[0..5]];
+ \\ a[a[0]..];
+ \\ a[a[0..5]..];
+ \\ a[a[0]..a[0]];
+ \\ a[a[0..5]..a[0]];
+ \\ a[a[0..5]..a[0..5]];
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: struct declaration" {
+ try testCanonical(
+ \\const S = struct {
+ \\ const Self = this;
+ \\ f1: u8,
+ \\
+ \\ fn method(self: &Self) Self {
+ \\ return *self;
+ \\ }
+ \\
+ \\ f2: u8
+ \\};
+ \\
+ \\const Ps = packed struct {
+ \\ a: u8,
+ \\ b: u8,
+ \\
+ \\ c: u8
+ \\};
+ \\
+ \\const Es = extern struct {
+ \\ a: u8,
+ \\ b: u8,
+ \\
+ \\ c: u8
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: enum declaration" {
+ try testCanonical(
+ \\const E = enum {
+ \\ Ok,
+ \\ SomethingElse = 0
+ \\};
+ \\
+ \\const E2 = enum(u8) {
+ \\ Ok,
+ \\ SomethingElse = 255,
+ \\ SomethingThird
+ \\};
+ \\
+ \\const Ee = extern enum {
+ \\ Ok,
+ \\ SomethingElse,
+ \\ SomethingThird
+ \\};
+ \\
+ \\const Ep = packed enum {
+ \\ Ok,
+ \\ SomethingElse,
+ \\ SomethingThird
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: union declaration" {
+ try testCanonical(
+ \\const U = union {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool
+ \\};
+ \\
+ \\const Ue = union(enum) {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool
+ \\};
+ \\
+ \\const E = enum {
+ \\ Int,
+ \\ Float,
+ \\ None,
+ \\ Bool
+ \\};
+ \\
+ \\const Ue2 = union(E) {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool
+ \\};
+ \\
+ \\const Eu = extern union {
+ \\ Int: u8,
+ \\ Float: f32,
+ \\ None,
+ \\ Bool: bool
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: error set declaration" {
+ try testCanonical(
+ \\const E = error {
+ \\ A,
+ \\ B,
+ \\
+ \\ C
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: arrays" {
+ try testCanonical(
+ \\test "test array" {
+ \\ const a: [2]u8 = [2]u8{ 1, 2 };
+ \\ const a: [2]u8 = []u8{ 1, 2 };
+ \\ const a: [0]u8 = []u8{ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: container initializers" {
+ try testCanonical(
+ \\const a1 = []u8{ };
+ \\const a2 = []u8{ 1, 2, 3, 4 };
+ \\const s1 = S{ };
+ \\const s2 = S{ .a = 1, .b = 2 };
+ \\
+ );
+}
+
+test "zig fmt: catch" {
+ try testCanonical(
+ \\test "catch" {
+ \\ const a: error!u8 = 0;
+ \\ _ = a catch return;
+ \\ _ = a catch |err| return;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: blocks" {
+ try testCanonical(
+ \\test "blocks" {
+ \\ {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ }
+ \\
+ \\ blk: {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ }
+ \\
+ \\ const r = blk: {
+ \\ const a = 0;
+ \\ const b = 0;
+ \\ };
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: switch" {
+ try testCanonical(
+ \\test "switch" {
+ \\ switch (0) {
+ \\ 0 => {},
+ \\ 1 => unreachable,
+ \\ 2, 3 => {},
+ \\ 4 ... 7 => {},
+ \\ 1 + 4 * 3 + 22 => {},
+ \\ else => {
+ \\ const a = 1;
+ \\ const b = a;
+ \\ }
+ \\ }
+ \\
+ \\ const res = switch (0) {
+ \\ 0 => 0,
+ \\ 1 => 2,
+ \\ else => 4
+ \\ };
+ \\
+ \\ const Union = union(enum) {
+ \\ Int: i64,
+ \\ Float: f64
+ \\ };
+ \\
+ \\ const u = Union{ .Int = 0 };
+ \\ switch (u) {
+ \\ Union.Int => |int| {},
+ \\ Union.Float => |*float| unreachable
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: while" {
+ try testCanonical(
+ \\test "while" {
+ \\ while (10 < 1) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ while (10 < 1)
+ \\ unreachable;
+ \\
+ \\ var i: usize = 0;
+ \\ while (i < 10) : (i += 1) {
+ \\ continue;
+ \\ }
+ \\
+ \\ i = 0;
+ \\ while (i < 10) : (i += 1)
+ \\ continue;
+ \\
+ \\ i = 0;
+ \\ var j: usize = 0;
+ \\ while (i < 10) : ({
+ \\ i += 1;
+ \\ j += 1;
+ \\ }) {
+ \\ continue;
+ \\ }
+ \\
+ \\ var a: ?u8 = 2;
+ \\ while (a) |v| : (a = null) {
+ \\ continue;
+ \\ }
+ \\
+ \\ while (a) |v| : (a = null)
+ \\ unreachable;
+ \\
+ \\ label: while (10 < 0) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ const res = while (0 < 10) {
+ \\ break 7;
+ \\ } else {
+ \\ unreachable;
+ \\ };
+ \\
+ \\ const res = while (0 < 10)
+ \\ break 7
+ \\ else
+ \\ unreachable;
+ \\
+ \\ var a: error!u8 = 0;
+ \\ while (a) |v| {
+ \\ a = error.Err;
+ \\ } else |err| {
+ \\ i = 1;
+ \\ }
+ \\
+ \\ comptime var k: usize = 0;
+ \\ inline while (i < 10) : (i += 1)
+ \\ j += 2;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: for" {
+ try testCanonical(
+ \\test "for" {
+ \\ const a = []u8{ 1, 2, 3 };
+ \\ for (a) |v| {
+ \\ continue;
+ \\ }
+ \\
+ \\ for (a) |v|
+ \\ continue;
+ \\
+ \\ for (a) |*v|
+ \\ continue;
+ \\
+ \\ for (a) |v, i| {
+ \\ continue;
+ \\ }
+ \\
+ \\ for (a) |v, i|
+ \\ continue;
+ \\
+ \\ const res = for (a) |v, i| {
+ \\ break v;
+ \\ } else {
+ \\ unreachable;
+ \\ };
+ \\
+ \\ var num: usize = 0;
+ \\ inline for (a) |v, i| {
+ \\ num += v;
+ \\ num += i;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: if" {
+ try testCanonical(
+ \\test "if" {
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ }
+ \\
+ \\ if (10 < 0) unreachable;
+ \\
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ } else {
+ \\ const a = 20;
+ \\ }
+ \\
+ \\ if (10 < 0) {
+ \\ unreachable;
+ \\ } else if (5 < 0) {
+ \\ unreachable;
+ \\ } else {
+ \\ const a = 20;
+ \\ }
+ \\
+ \\ const is_world_broken = if (10 < 0) true else false;
+ \\
+ \\ const a: ?u8 = 10;
+ \\ const b: ?u8 = null;
+ \\ if (a) |v| {
+ \\ const some = v;
+ \\ } else if (b) |*v| {
+ \\ unreachable;
+ \\ } else {
+ \\ const some = 10;
+ \\ }
+ \\
+ \\ const non_null_a = if (a) |v| v else 0;
+ \\
+ \\ const a_err: error!u8 = 0;
+ \\ if (a_err) |v| {
+ \\ const p = v;
+ \\ } else |err| {
+ \\ unreachable;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: defer" {
+ try testCanonical(
+ \\test "defer" {
+ \\ var i: usize = 0;
+ \\ defer i = 1;
+ \\ defer {
+ \\ i += 2;
+ \\ i *= i;
+ \\ }
+ \\
+ \\ errdefer i += 3;
+ \\ errdefer {
+ \\ i += 2;
+ \\ i /= i;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: comptime" {
+ try testCanonical(
+ \\fn a() u8 {
+ \\ return 5;
+ \\}
+ \\
+ \\fn b(comptime i: u8) u8 {
+ \\ return i;
+ \\}
+ \\
+ \\const av = comptime a();
+ \\const av2 = comptime blk: {
+ \\ var res = a();
+ \\ res *= b(2);
+ \\ break :blk res;
+ \\};
+ \\
+ \\comptime {
+ \\ _ = a();
+ \\}
+ \\
+ \\test "comptime" {
+ \\ const av3 = comptime a();
+ \\ const av4 = comptime blk: {
+ \\ var res = a();
+ \\ res *= a();
+ \\ break :blk res;
+ \\ };
+ \\
+ \\ comptime var i = 0;
+ \\ comptime {
+ \\ i = a();
+ \\ i += b(i);
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: fn type" {
+ try testCanonical(
+ \\fn a(i: u8) u8 {
+ \\ return i + 1;
+ \\}
+ \\
+ \\const a: fn(u8) u8 = undefined;
+ \\const b: extern fn(u8) u8 = undefined;
+ \\const c: nakedcc fn(u8) u8 = undefined;
+ \\const ap: fn(u8) u8 = a;
+ \\
+ );
+}
+
+test "zig fmt: inline asm" {
+ try testCanonical(
+ \\pub fn syscall1(number: usize, arg1: usize) usize {
+ \\ return asm volatile ("syscall"
+ \\ : [ret] "={rax}" (-> usize)
+ \\ : [number] "{rax}" (number),
+ \\ [arg1] "{rdi}" (arg1)
+ \\ : "rcx", "r11");
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: coroutines" {
+ try testCanonical(
+ \\async fn simpleAsyncFn() void {
+ \\ x += 1;
+ \\ suspend;
+ \\ x += 1;
+ \\ suspend |p| {}
+ \\ const p = async simpleAsyncFn() catch unreachable;
+ \\ await p;
+ \\}
+ \\
+ \\test "coroutine suspend, resume, cancel" {
+ \\ const p = try async<std.debug.global_allocator> testAsyncSeq();
+ \\ resume p;
+ \\ cancel p;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: Block after if" {
+ try testCanonical(
+ \\test "Block after if" {
+ \\ if (true) {
+ \\ const a = 0;
+ \\ }
+ \\
+ \\ {
+ \\ const a = 0;
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: use" {
+ try testCanonical(
+ \\use @import("std");
+ \\pub use @import("std");
+ \\
+ );
+}
+
+test "zig fmt: string identifier" {
+ try testCanonical(
+ \\const @"a b" = @"c d".@"e f";
+ \\fn @"g h"() void {}
+ \\
+ );
+}
diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig
index 4af6c20cad..a2c4def9e0 100644
--- a/std/zig/tokenizer.zig
+++ b/std/zig/tokenizer.zig
@@ -5,8 +5,6 @@ pub const Token = struct {
id: Id,
start: usize,
end: usize,
- line: usize,
- column: usize,
const KeywordId = struct {
bytes: []const u8,
@@ -17,14 +15,18 @@ pub const Token = struct {
KeywordId{.bytes="align", .id = Id.Keyword_align},
KeywordId{.bytes="and", .id = Id.Keyword_and},
KeywordId{.bytes="asm", .id = Id.Keyword_asm},
+ KeywordId{.bytes="async", .id = Id.Keyword_async},
+ KeywordId{.bytes="await", .id = Id.Keyword_await},
KeywordId{.bytes="break", .id = Id.Keyword_break},
KeywordId{.bytes="catch", .id = Id.Keyword_catch},
+ KeywordId{.bytes="cancel", .id = Id.Keyword_cancel},
KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
KeywordId{.bytes="const", .id = Id.Keyword_const},
KeywordId{.bytes="continue", .id = Id.Keyword_continue},
KeywordId{.bytes="defer", .id = Id.Keyword_defer},
KeywordId{.bytes="else", .id = Id.Keyword_else},
KeywordId{.bytes="enum", .id = Id.Keyword_enum},
+ KeywordId{.bytes="errdefer", .id = Id.Keyword_errdefer},
KeywordId{.bytes="error", .id = Id.Keyword_error},
KeywordId{.bytes="export", .id = Id.Keyword_export},
KeywordId{.bytes="extern", .id = Id.Keyword_extern},
@@ -39,10 +41,12 @@ pub const Token = struct {
KeywordId{.bytes="or", .id = Id.Keyword_or},
KeywordId{.bytes="packed", .id = Id.Keyword_packed},
KeywordId{.bytes="pub", .id = Id.Keyword_pub},
+ KeywordId{.bytes="resume", .id = Id.Keyword_resume},
KeywordId{.bytes="return", .id = Id.Keyword_return},
KeywordId{.bytes="section", .id = Id.Keyword_section},
KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
KeywordId{.bytes="struct", .id = Id.Keyword_struct},
+ KeywordId{.bytes="suspend", .id = Id.Keyword_suspend},
KeywordId{.bytes="switch", .id = Id.Keyword_switch},
KeywordId{.bytes="test", .id = Id.Keyword_test},
KeywordId{.bytes="this", .id = Id.Keyword_this},
@@ -72,38 +76,74 @@ pub const Token = struct {
Invalid,
Identifier,
StringLiteral: StrLitKind,
- StringIdentifier,
+ MultilineStringLiteralLine: StrLitKind,
+ CharLiteral,
Eof,
Builtin,
Bang,
Pipe,
+ PipePipe,
PipeEqual,
Equal,
EqualEqual,
+ EqualAngleBracketRight,
BangEqual,
LParen,
RParen,
Semicolon,
Percent,
+ PercentEqual,
LBrace,
RBrace,
+ LBracket,
+ RBracket,
Period,
Ellipsis2,
Ellipsis3,
+ Caret,
+ CaretEqual,
+ Plus,
+ PlusPlus,
+ PlusEqual,
+ PlusPercent,
+ PlusPercentEqual,
Minus,
+ MinusEqual,
+ MinusPercent,
+ MinusPercentEqual,
+ Asterisk,
+ AsteriskEqual,
+ AsteriskAsterisk,
+ AsteriskPercent,
+ AsteriskPercentEqual,
Arrow,
Colon,
Slash,
+ SlashEqual,
Comma,
Ampersand,
AmpersandEqual,
+ QuestionMark,
+ QuestionMarkQuestionMark,
+ AngleBracketLeft,
+ AngleBracketLeftEqual,
+ AngleBracketAngleBracketLeft,
+ AngleBracketAngleBracketLeftEqual,
+ AngleBracketRight,
+ AngleBracketRightEqual,
+ AngleBracketAngleBracketRight,
+ AngleBracketAngleBracketRightEqual,
+ Tilde,
IntegerLiteral,
FloatLiteral,
LineComment,
Keyword_align,
Keyword_and,
Keyword_asm,
+ Keyword_async,
+ Keyword_await,
Keyword_break,
+ Keyword_cancel,
Keyword_catch,
Keyword_comptime,
Keyword_const,
@@ -111,6 +151,7 @@ pub const Token = struct {
Keyword_defer,
Keyword_else,
Keyword_enum,
+ Keyword_errdefer,
Keyword_error,
Keyword_export,
Keyword_extern,
@@ -125,10 +166,12 @@ pub const Token = struct {
Keyword_or,
Keyword_packed,
Keyword_pub,
+ Keyword_resume,
Keyword_return,
Keyword_section,
Keyword_stdcallcc,
Keyword_struct,
+ Keyword_suspend,
Keyword_switch,
Keyword_test,
Keyword_this,
@@ -147,28 +190,34 @@ pub const Token = struct {
pub const Tokenizer = struct {
buffer: []const u8,
index: usize,
- line: usize,
- column: usize,
pending_invalid_token: ?Token,
- pub const LineLocation = struct {
+ pub const Location = struct {
+ line: usize,
+ column: usize,
line_start: usize,
line_end: usize,
};
- pub fn getTokenLocation(self: &Tokenizer, token: &const Token) LineLocation {
- var loc = LineLocation {
- .line_start = 0,
+ pub fn getTokenLocation(self: &Tokenizer, start_index: usize, token: &const Token) Location {
+ var loc = Location {
+ .line = 0,
+ .column = 0,
+ .line_start = start_index,
.line_end = self.buffer.len,
};
- for (self.buffer) |c, i| {
- if (i == token.start) {
- loc.line_end = i;
+ for (self.buffer[start_index..]) |c, i| {
+ if (i + start_index == token.start) {
+ loc.line_end = i + start_index;
while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
return loc;
}
if (c == '\n') {
+ loc.line += 1;
+ loc.column = 0;
loc.line_start = i + 1;
+ } else {
+ loc.column += 1;
}
}
return loc;
@@ -183,8 +232,6 @@ pub const Tokenizer = struct {
return Tokenizer {
.buffer = buffer,
.index = 0,
- .line = 0,
- .column = 0,
.pending_invalid_token = null,
};
}
@@ -196,10 +243,19 @@ pub const Tokenizer = struct {
C,
StringLiteral,
StringLiteralBackslash,
+ MultilineStringLiteralLine,
+ MultilineStringLiteralLineBackslash,
+ CharLiteral,
+ CharLiteralBackslash,
+ CharLiteralEnd,
+ Backslash,
Equal,
Bang,
Pipe,
Minus,
+ MinusPercent,
+ Asterisk,
+ AsteriskPercent,
Slash,
LineComment,
Zero,
@@ -210,6 +266,15 @@ pub const Tokenizer = struct {
FloatExponentUnsigned,
FloatExponentNumber,
Ampersand,
+ Caret,
+ Percent,
+ QuestionMark,
+ Plus,
+ PlusPercent,
+ AngleBracketLeft,
+ AngleBracketAngleBracketLeft,
+ AngleBracketRight,
+ AngleBracketAngleBracketRight,
Period,
Period2,
SawAtSign,
@@ -220,26 +285,22 @@ pub const Tokenizer = struct {
self.pending_invalid_token = null;
return token;
}
+ const start_index = self.index;
var state = State.Start;
var result = Token {
.id = Token.Id.Eof,
.start = self.index,
.end = undefined,
- .line = self.line,
- .column = self.column,
};
- while (self.index < self.buffer.len) {
+ while (self.index < self.buffer.len) : (self.index += 1) {
const c = self.buffer[self.index];
switch (state) {
State.Start => switch (c) {
' ' => {
result.start = self.index + 1;
- result.column += 1;
},
'\n' => {
result.start = self.index + 1;
- result.line += 1;
- result.column = 0;
},
'c' => {
state = State.C;
@@ -249,6 +310,9 @@ pub const Tokenizer = struct {
state = State.StringLiteral;
result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
},
+ '\'' => {
+ state = State.CharLiteral;
+ },
'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
state = State.Identifier;
result.id = Token.Id.Identifier;
@@ -275,6 +339,16 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
+ '[' => {
+ result.id = Token.Id.LBracket;
+ self.index += 1;
+ break;
+ },
+ ']' => {
+ result.id = Token.Id.RBracket;
+ self.index += 1;
+ break;
+ },
';' => {
result.id = Token.Id.Semicolon;
self.index += 1;
@@ -291,9 +365,29 @@ pub const Tokenizer = struct {
break;
},
'%' => {
- result.id = Token.Id.Percent;
- self.index += 1;
- break;
+ state = State.Percent;
+ },
+ '*' => {
+ state = State.Asterisk;
+ },
+ '+' => {
+ state = State.Plus;
+ },
+ '?' => {
+ state = State.QuestionMark;
+ },
+ '<' => {
+ state = State.AngleBracketLeft;
+ },
+ '>' => {
+ state = State.AngleBracketRight;
+ },
+ '^' => {
+ state = State.Caret;
+ },
+ '\\' => {
+ state = State.Backslash;
+ result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.Normal };
},
'{' => {
result.id = Token.Id.LBrace;
@@ -305,6 +399,11 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
+ '~' => {
+ result.id = Token.Id.Tilde;
+ self.index += 1;
+ break;
+ },
'.' => {
state = State.Period;
},
@@ -334,7 +433,7 @@ pub const Tokenizer = struct {
State.SawAtSign => switch (c) {
'"' => {
- result.id = Token.Id.StringIdentifier;
+ result.id = Token.Id.Identifier;
state = State.StringLiteral;
},
else => {
@@ -356,6 +455,107 @@ pub const Tokenizer = struct {
break;
},
},
+
+ State.Asterisk => switch (c) {
+ '=' => {
+ result.id = Token.Id.AsteriskEqual;
+ self.index += 1;
+ break;
+ },
+ '*' => {
+ result.id = Token.Id.AsteriskAsterisk;
+ self.index += 1;
+ break;
+ },
+ '%' => {
+ state = State.AsteriskPercent;
+ },
+ else => {
+ result.id = Token.Id.Asterisk;
+ break;
+ }
+ },
+
+ State.AsteriskPercent => switch (c) {
+ '=' => {
+ result.id = Token.Id.AsteriskPercentEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.AsteriskPercent;
+ break;
+ }
+ },
+
+ State.QuestionMark => switch (c) {
+ '?' => {
+ result.id = Token.Id.QuestionMarkQuestionMark;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.QuestionMark;
+ break;
+ },
+ },
+
+ State.Percent => switch (c) {
+ '=' => {
+ result.id = Token.Id.PercentEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.Percent;
+ break;
+ },
+ },
+
+ State.Plus => switch (c) {
+ '=' => {
+ result.id = Token.Id.PlusEqual;
+ self.index += 1;
+ break;
+ },
+ '+' => {
+ result.id = Token.Id.PlusPlus;
+ self.index += 1;
+ break;
+ },
+ '%' => {
+ state = State.PlusPercent;
+ },
+ else => {
+ result.id = Token.Id.Plus;
+ break;
+ },
+ },
+
+ State.PlusPercent => switch (c) {
+ '=' => {
+ result.id = Token.Id.PlusPercentEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.PlusPercent;
+ break;
+ },
+ },
+
+ State.Caret => switch (c) {
+ '=' => {
+ result.id = Token.Id.CaretEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.Caret;
+ break;
+ }
+ },
+
State.Identifier => switch (c) {
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
else => {
@@ -369,8 +569,17 @@ pub const Tokenizer = struct {
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
else => break,
},
+ State.Backslash => switch (c) {
+ '\\' => {
+ state = State.MultilineStringLiteralLine;
+ },
+ else => break,
+ },
State.C => switch (c) {
- '\\' => @panic("TODO"),
+ '\\' => {
+ state = State.Backslash;
+ result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.C };
+ },
'"' => {
state = State.StringLiteral;
result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
@@ -399,6 +608,64 @@ pub const Tokenizer = struct {
},
},
+ State.CharLiteral => switch (c) {
+ '\\' => {
+ state = State.CharLiteralBackslash;
+ },
+ '\'' => {
+ result.id = Token.Id.Invalid;
+ break;
+ },
+ else => {
+ if (c < 0x20 or c == 0x7f) {
+ result.id = Token.Id.Invalid;
+ break;
+ }
+
+ state = State.CharLiteralEnd;
+ }
+ },
+
+ State.CharLiteralBackslash => switch (c) {
+ '\n' => {
+ result.id = Token.Id.Invalid;
+ break;
+ },
+ else => {
+ state = State.CharLiteralEnd;
+ },
+ },
+
+ State.CharLiteralEnd => switch (c) {
+ '\'' => {
+ result.id = Token.Id.CharLiteral;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.Invalid;
+ break;
+ },
+ },
+
+ State.MultilineStringLiteralLine => switch (c) {
+ '\\' => {
+ state = State.MultilineStringLiteralLineBackslash;
+ },
+ '\n' => {
+ self.index += 1;
+ break;
+ },
+ else => self.checkLiteralCharacter(),
+ },
+
+ State.MultilineStringLiteralLineBackslash => switch (c) {
+ '\n' => break, // Look for this error later.
+ else => {
+ state = State.MultilineStringLiteralLine;
+ },
+ },
+
State.Bang => switch (c) {
'=' => {
result.id = Token.Id.BangEqual;
@@ -417,6 +684,11 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
+ '|' => {
+ result.id = Token.Id.PipePipe;
+ self.index += 1;
+ break;
+ },
else => {
result.id = Token.Id.Pipe;
break;
@@ -429,6 +701,11 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
+ '>' => {
+ result.id = Token.Id.EqualAngleBracketRight;
+ self.index += 1;
+ break;
+ },
else => {
result.id = Token.Id.Equal;
break;
@@ -441,12 +718,86 @@ pub const Tokenizer = struct {
self.index += 1;
break;
},
+ '=' => {
+ result.id = Token.Id.MinusEqual;
+ self.index += 1;
+ break;
+ },
+ '%' => {
+ state = State.MinusPercent;
+ },
else => {
result.id = Token.Id.Minus;
break;
},
},
+ State.MinusPercent => switch (c) {
+ '=' => {
+ result.id = Token.Id.MinusPercentEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.MinusPercent;
+ break;
+ }
+ },
+
+ State.AngleBracketLeft => switch (c) {
+ '<' => {
+ state = State.AngleBracketAngleBracketLeft;
+ },
+ '=' => {
+ result.id = Token.Id.AngleBracketLeftEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.AngleBracketLeft;
+ break;
+ },
+ },
+
+ State.AngleBracketAngleBracketLeft => switch (c) {
+ '=' => {
+ result.id = Token.Id.AngleBracketAngleBracketLeftEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.AngleBracketAngleBracketLeft;
+ break;
+ },
+ },
+
+ State.AngleBracketRight => switch (c) {
+ '>' => {
+ state = State.AngleBracketAngleBracketRight;
+ },
+ '=' => {
+ result.id = Token.Id.AngleBracketRightEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.AngleBracketRight;
+ break;
+ },
+ },
+
+ State.AngleBracketAngleBracketRight => switch (c) {
+ '=' => {
+ result.id = Token.Id.AngleBracketAngleBracketRightEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.AngleBracketAngleBracketRight;
+ break;
+ },
+ },
+
State.Period => switch (c) {
'.' => {
state = State.Period2;
@@ -474,6 +825,11 @@ pub const Tokenizer = struct {
result.id = Token.Id.LineComment;
state = State.LineComment;
},
+ '=' => {
+ result.id = Token.Id.SlashEqual;
+ self.index += 1;
+ break;
+ },
else => {
result.id = Token.Id.Slash;
break;
@@ -547,14 +903,6 @@ pub const Tokenizer = struct {
else => break,
},
}
-
- self.index += 1;
- if (c == '\n') {
- self.line += 1;
- self.column = 0;
- } else {
- self.column += 1;
- }
} else if (self.index == self.buffer.len) {
switch (state) {
State.Start,
@@ -564,6 +912,7 @@ pub const Tokenizer = struct {
State.FloatFraction,
State.FloatExponentNumber,
State.StringLiteral, // find this error later
+ State.MultilineStringLiteralLine,
State.Builtin => {},
State.Identifier => {
@@ -578,6 +927,11 @@ pub const Tokenizer = struct {
State.NumberDot,
State.FloatExponentUnsigned,
State.SawAtSign,
+ State.Backslash,
+ State.MultilineStringLiteralLineBackslash,
+ State.CharLiteral,
+ State.CharLiteralBackslash,
+ State.CharLiteralEnd,
State.StringLiteralBackslash => {
result.id = Token.Id.Invalid;
},
@@ -609,8 +963,45 @@ pub const Tokenizer = struct {
State.Pipe => {
result.id = Token.Id.Pipe;
},
+ State.AngleBracketAngleBracketRight => {
+ result.id = Token.Id.AngleBracketAngleBracketRight;
+ },
+ State.AngleBracketRight => {
+ result.id = Token.Id.AngleBracketRight;
+ },
+ State.AngleBracketAngleBracketLeft => {
+ result.id = Token.Id.AngleBracketAngleBracketLeft;
+ },
+ State.AngleBracketLeft => {
+ result.id = Token.Id.AngleBracketLeft;
+ },
+ State.PlusPercent => {
+ result.id = Token.Id.PlusPercent;
+ },
+ State.Plus => {
+ result.id = Token.Id.Plus;
+ },
+ State.QuestionMark => {
+ result.id = Token.Id.QuestionMark;
+ },
+ State.Percent => {
+ result.id = Token.Id.Percent;
+ },
+ State.Caret => {
+ result.id = Token.Id.Caret;
+ },
+ State.AsteriskPercent => {
+ result.id = Token.Id.AsteriskPercent;
+ },
+ State.Asterisk => {
+ result.id = Token.Id.Asterisk;
+ },
+ State.MinusPercent => {
+ result.id = Token.Id.MinusPercent;
+ },
}
}
+
if (result.id == Token.Id.Eof) {
if (self.pending_invalid_token) |token| {
self.pending_invalid_token = null;
@@ -634,8 +1025,6 @@ pub const Tokenizer = struct {
.id = Token.Id.Invalid,
.start = self.index,
.end = self.index + invalid_length,
- .line = self.line,
- .column = self.column,
};
}
@@ -685,9 +1074,16 @@ test "tokenizer" {
});
}
+test "tokenizer - chars" {
+ testTokenize("'c'", []Token.Id {Token.Id.CharLiteral});
+}
+
test "tokenizer - invalid token characters" {
testTokenize("#", []Token.Id{Token.Id.Invalid});
testTokenize("`", []Token.Id{Token.Id.Invalid});
+ testTokenize("'c", []Token.Id {Token.Id.Invalid});
+ testTokenize("'", []Token.Id {Token.Id.Invalid});
+ testTokenize("''", []Token.Id {Token.Id.Invalid, Token.Id.Invalid});
}
test "tokenizer - invalid literal/comment characters" {
@@ -739,7 +1135,7 @@ test "tokenizer - string identifier and builtin fns" {
,
[]Token.Id{
Token.Id.Keyword_const,
- Token.Id.StringIdentifier,
+ Token.Id.Identifier,
Token.Id.Equal,
Token.Id.Builtin,
Token.Id.LParen,
@@ -752,8 +1148,8 @@ test "tokenizer - string identifier and builtin fns" {
test "tokenizer - pipe and then invalid" {
testTokenize("||=", []Token.Id{
- Token.Id.Pipe,
- Token.Id.PipeEqual,
+ Token.Id.PipePipe,
+ Token.Id.Equal,
});
}