diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a090d9f29..fd39dd30da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -368,6 +368,7 @@ set(ZIG_STD_FILES "crypto/md5.zig" "crypto/sha1.zig" "crypto/sha2.zig" + "crypto/sha3.zig" "crypto/blake2.zig" "cstr.zig" "debug/failing_allocator.zig" diff --git a/build.zig b/build.zig index 1fa984dcff..189e0e156f 100644 --- a/build.zig +++ b/build.zig @@ -15,23 +15,17 @@ pub fn build(b: &Builder) -> %void { var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); + const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe); var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8 { docgen_exe.getOutputPath(), + rel_zig_exe, "doc/langref.html.in", os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable, }); docgen_cmd.step.dependOn(&docgen_exe.step); - var docgen_home_cmd = b.addCommand(null, b.env_map, [][]const u8 { - docgen_exe.getOutputPath(), - "doc/home.html.in", - os.path.join(b.allocator, b.cache_root, "home.html") catch unreachable, - }); - docgen_home_cmd.step.dependOn(&docgen_exe.step); - const docs_step = b.step("docs", "Build documentation"); docs_step.dependOn(&docgen_cmd.step); - docs_step.dependOn(&docgen_home_cmd.step); const test_step = b.step("test", "Run all the tests"); diff --git a/doc/docgen.zig b/doc/docgen.zig index f3bb7df6b1..1972a00374 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -1,10 +1,16 @@ const std = @import("std"); const io = std.io; const os = std.os; +const warn = std.debug.warn; +const mem = std.mem; + +const max_doc_file_size = 10 * 1024 * 1024; + +const exe_ext = std.build.Target(std.build.Target.Native).exeFileExt(); pub fn main() -> %void { // TODO use a more general purpose allocator here - var inc_allocator = try std.heap.IncrementingAllocator.init(5 * 1024 * 1024); + var inc_allocator = try std.heap.IncrementingAllocator.init(max_doc_file_size); defer inc_allocator.deinit(); const allocator = &inc_allocator.allocator; @@ -12,6 +18,9 @@ pub fn main() -> %void { if (!args_it.skip()) @panic("expected self arg"); + const zig_exe = try (args_it.next(allocator) ?? @panic("expected zig exe arg")); + defer allocator.free(zig_exe); + const in_file_name = try (args_it.next(allocator) ?? @panic("expected input arg")); defer allocator.free(in_file_name); @@ -25,39 +34,533 @@ pub fn main() -> %void { defer out_file.close(); var file_in_stream = io.FileInStream.init(&in_file); - var buffered_in_stream = io.BufferedInStream.init(&file_in_stream.stream); + + const input_file_bytes = try file_in_stream.stream.readAllAlloc(allocator, max_doc_file_size); var file_out_stream = io.FileOutStream.init(&out_file); var buffered_out_stream = io.BufferedOutStream.init(&file_out_stream.stream); - gen(&buffered_in_stream.stream, &buffered_out_stream.stream); - try buffered_out_stream.flush(); + var tokenizer = Tokenizer.init(in_file_name, input_file_bytes); + var toc = try genToc(allocator, &tokenizer); + try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe); + try buffered_out_stream.flush(); } -const State = enum { - Start, - Derp, +const Token = struct { + id: Id, + start: usize, + end: usize, + + const Id = enum { + Invalid, + Content, + BracketOpen, + TagContent, + Separator, + BracketClose, + Eof, + }; }; -// TODO look for code segments +const Tokenizer = struct { + buffer: []const u8, + index: usize, + state: State, + source_file_name: []const u8, -fn gen(in: &io.InStream, out: &io.OutStream) { - var state = State.Start; - while (true) { - const byte = in.readByte() catch |err| { - if (err == error.EndOfStream) { - return; - } - std.debug.panic("{}", err); + const State = enum { + Start, + LBracket, + Hash, + TagName, + Eof, + }; + + fn init(source_file_name: []const u8, buffer: []const u8) -> Tokenizer { + return Tokenizer { + .buffer = buffer, + .index = 0, + .state = State.Start, + .source_file_name = source_file_name, }; - switch (state) { - State.Start => switch (byte) { - else => { - out.writeByte(byte) catch unreachable; + } + + fn next(self: &Tokenizer) -> Token { + var result = Token { + .id = Token.Id.Eof, + .start = self.index, + .end = undefined, + }; + while (self.index < self.buffer.len) : (self.index += 1) { + const c = self.buffer[self.index]; + switch (self.state) { + State.Start => switch (c) { + '{' => { + self.state = State.LBracket; + }, + else => { + result.id = Token.Id.Content; + }, }, - }, - State.Derp => unreachable, + State.LBracket => switch (c) { + '#' => { + if (result.id != Token.Id.Eof) { + self.index -= 1; + self.state = State.Start; + break; + } else { + result.id = Token.Id.BracketOpen; + self.index += 1; + self.state = State.TagName; + break; + } + }, + else => { + result.id = Token.Id.Content; + self.state = State.Start; + }, + }, + State.TagName => switch (c) { + '|' => { + if (result.id != Token.Id.Eof) { + break; + } else { + result.id = Token.Id.Separator; + self.index += 1; + break; + } + }, + '#' => { + self.state = State.Hash; + }, + else => { + result.id = Token.Id.TagContent; + }, + }, + State.Hash => switch (c) { + '}' => { + if (result.id != Token.Id.Eof) { + self.index -= 1; + self.state = State.TagName; + break; + } else { + result.id = Token.Id.BracketClose; + self.index += 1; + self.state = State.Start; + break; + } + }, + else => { + result.id = Token.Id.TagContent; + self.state = State.TagName; + }, + }, + State.Eof => unreachable, + } + } else { + switch (self.state) { + State.Start, State.LBracket, State.Eof => {}, + else => { + result.id = Token.Id.Invalid; + }, + } + self.state = State.Eof; } + result.end = self.index; + return result; + } + + const Location = struct { + line: usize, + column: usize, + line_start: usize, + line_end: usize, + }; + + fn getTokenLocation(self: &Tokenizer, token: &const Token) -> Location { + var loc = Location { + .line = 0, + .column = 0, + .line_start = 0, + .line_end = 0, + }; + for (self.buffer) |c, i| { + if (i == token.start) { + loc.line_end = i; + 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; + } +}; + +error ParseError; + +fn parseError(tokenizer: &Tokenizer, token: &const Token, comptime fmt: []const u8, args: ...) -> error { + const loc = tokenizer.getTokenLocation(token); + warn("{}:{}:{}: error: " ++ fmt ++ "\n", tokenizer.source_file_name, loc.line + 1, loc.column + 1, args); + if (loc.line_start <= loc.line_end) { + warn("{}\n", 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("~"); + } + } + warn("\n"); + } + return error.ParseError; +} + +fn assertToken(tokenizer: &Tokenizer, token: &const Token, id: Token.Id) -> %void { + if (token.id != id) { + return parseError(tokenizer, token, "expected {}, found {}", @tagName(id), @tagName(token.id)); } } + +fn eatToken(tokenizer: &Tokenizer, id: Token.Id) -> %Token { + const token = tokenizer.next(); + try assertToken(tokenizer, token, id); + return token; +} + +const HeaderOpen = struct { + name: []const u8, + url: []const u8, + n: usize, +}; + +const SeeAlsoItem = struct { + name: []const u8, + token: Token, +}; + +const Code = struct { + id: Id, + name: []const u8, + source_token: Token, + + const Id = enum { + Test, + Exe, + Error, + }; +}; + +const Node = union(enum) { + Content: []const u8, + Nav, + HeaderOpen: HeaderOpen, + SeeAlso: []const SeeAlsoItem, + Code: Code, +}; + +const Toc = struct { + nodes: []Node, + toc: []u8, + urls: std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8), +}; + +const Action = enum { + Open, + Close, +}; + +fn genToc(allocator: &mem.Allocator, tokenizer: &Tokenizer) -> %Toc { + var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator); + %defer urls.deinit(); + + var header_stack_size: usize = 0; + var last_action = Action.Open; + + var toc_buf = try std.Buffer.initSize(allocator, 0); + defer toc_buf.deinit(); + + var toc_buf_adapter = io.BufferOutStream.init(&toc_buf); + var toc = &toc_buf_adapter.stream; + + var nodes = std.ArrayList(Node).init(allocator); + defer nodes.deinit(); + + try toc.writeByte('\n'); + + while (true) { + const token = tokenizer.next(); + switch (token.id) { + Token.Id.Eof => { + if (header_stack_size != 0) { + return parseError(tokenizer, token, "unbalanced headers"); + } + try toc.write(" \n"); + break; + }, + Token.Id.Content => { + try nodes.append(Node {.Content = tokenizer.buffer[token.start..token.end] }); + }, + Token.Id.BracketOpen => { + const tag_token = try eatToken(tokenizer, Token.Id.TagContent); + const tag_name = tokenizer.buffer[tag_token.start..tag_token.end]; + + if (mem.eql(u8, tag_name, "nav")) { + _ = try eatToken(tokenizer, Token.Id.BracketClose); + + try nodes.append(Node.Nav); + } else if (mem.eql(u8, tag_name, "header_open")) { + _ = try eatToken(tokenizer, Token.Id.Separator); + const content_token = try eatToken(tokenizer, Token.Id.TagContent); + const content = tokenizer.buffer[content_token.start..content_token.end]; + _ = try eatToken(tokenizer, Token.Id.BracketClose); + + header_stack_size += 1; + + const urlized = try urlize(allocator, content); + try nodes.append(Node{.HeaderOpen = HeaderOpen { + .name = content, + .url = urlized, + .n = header_stack_size, + }}); + if (try urls.put(urlized, tag_token)) |other_tag_token| { + parseError(tokenizer, tag_token, "duplicate header url: #{}", urlized) catch {}; + parseError(tokenizer, other_tag_token, "other tag here") catch {}; + return error.ParseError; + } + if (last_action == Action.Open) { + try toc.writeByte('\n'); + try toc.writeByteNTimes(' ', header_stack_size * 4); + try toc.write("
See also:
{}", escaped_source);
+ const tmp_dir_name = "docgen_tmp";
+ try os.makePath(allocator, tmp_dir_name);
+ const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
+ const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext);
+ const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
+ const tmp_bin_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_bin_ext);
+ try io.writeFile(tmp_source_file_name, trimmed_raw_source, null);
+
+ switch (code.id) {
+ Code.Id.Exe => {
+ {
+ const args = [][]const u8 {zig_exe, "build-exe", tmp_source_file_name, "--output", tmp_bin_file_name};
+ const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);
+ switch (result.term) {
+ os.ChildProcess.Term.Exited => |exit_code| {
+ if (exit_code != 0) {
+ warn("{}\nThe following command exited with code {}:\n", result.stderr, exit_code);
+ for (args) |arg| warn("{} ", arg) else warn("\n");
+ return parseError(tokenizer, code.source_token, "example failed to compile");
+ }
+ },
+ else => {
+ warn("{}\nThe following command crashed:\n", result.stderr);
+ for (args) |arg| warn("{} ", arg) else warn("\n");
+ return parseError(tokenizer, code.source_token, "example failed to compile");
+ },
+ }
+ }
+ const args = [][]const u8 {tmp_bin_file_name};
+ const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);
+ switch (result.term) {
+ os.ChildProcess.Term.Exited => |exit_code| {
+ if (exit_code != 0) {
+ warn("The following command exited with code {}:\n", exit_code);
+ for (args) |arg| warn("{} ", arg) else warn("\n");
+ return parseError(tokenizer, code.source_token, "example exited with code {}", exit_code);
+ }
+ },
+ else => {
+ warn("The following command crashed:\n");
+ for (args) |arg| warn("{} ", arg) else warn("\n");
+ return parseError(tokenizer, code.source_token, "example crashed");
+ },
+ }
+ try out.print("$ zig build-exe {}.zig\n$ ./{}\n{}{}\n", code.name, code.name, result.stderr, result.stdout);
+ },
+ Code.Id.Test => {
+ @panic("TODO");
+ },
+ Code.Id.Error => {
+ @panic("TODO");
+ },
+ }
+ },
+ }
+ }
+
+}
diff --git a/doc/home.html.in b/doc/home.html.in
deleted file mode 100644
index 086a078a90..0000000000
--- a/doc/home.html.in
+++ /dev/null
@@ -1,724 +0,0 @@
-
-
-
-
-
- - Zig is an open-source programming language designed for robustness, - optimality, and clarity. -
-- Download | - Documentation | - Source Code | - Bug Tracker | - IRC | - Donate $1/month -
-const std = @import("std");
-
-pub fn main() -> %void {
- // If this program is run without stdout attached, exit with an error.
- var stdout_file = try std.io.getStdOut();
- // If this program encounters pipe failure when printing to stdout, exit
- // with an error.
- try stdout_file.write("Hello, world!\n");
-}
- Build this with:
-zig build-exe hello.zig-
const c = @cImport({
- // See https://github.com/zig-lang/zig/issues/515
- @cDefine("_NO_CRT_STDIO_INLINE", "1");
- @cInclude("stdio.h");
- @cInclude("string.h");
-});
-
-const msg = c"Hello, world!\n";
-
-export fn main(argc: c_int, argv: &&u8) -> c_int {
- if (c.printf(msg) != c_int(c.strlen(msg)))
- return -1;
-
- return 0;
-}
- Build this with:
-zig build-exe hello.zig --library c-
pub fn parseUnsigned(comptime T: type, buf: []u8, radix: u8) -> %T {
- var x: T = 0;
-
- for (buf) |c| {
- const digit = try charToDigit(c, radix);
- x = try mulOverflow(T, x, radix);
- x = try addOverflow(T, x, digit);
- }
-
- return x;
-}
-
-error InvalidChar;
-
-fn charToDigit(c: u8, radix: u8) -> %u8 {
- const value = switch (c) {
- '0' ... '9' => c - '0',
- 'A' ... 'Z' => c - 'A' + 10,
- 'a' ... 'z' => c - 'a' + 10,
- else => return error.InvalidChar,
- };
-
- if (value >= radix)
- return error.InvalidChar;
-
- return value;
-}
-
-error Overflow;
-
-pub fn mulOverflow(comptime T: type, a: T, b: T) -> %T {
- var answer: T = undefined;
- if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer
-}
-
-pub fn addOverflow(comptime T: type, a: T, b: T) -> %T {
- var answer: T = undefined;
- if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer
-}
-
-fn getNumberWithDefault(s: []u8) -> u32 {
- parseUnsigned(u32, s, 10) catch 42
-}
-
-fn getNumberOrCrash(s: []u8) -> u32 {
- %%parseUnsigned(u32, s, 10)
-}
-
-fn addTwoTogetherOrReturnErr(a_str: []u8, b_str: []u8) -> %u32 {
- const a = parseUnsigned(u32, a_str, 10) catch |err| return err;
- const b = parseUnsigned(u32, b_str, 10) catch |err| return err;
- return a + b;
-}
- const debug = @import("debug.zig");
-const assert = debug.assert;
-const math = @import("math.zig");
-const mem = @import("mem.zig");
-const Allocator = mem.Allocator;
-
-const want_modification_safety = !@compileVar("is_release");
-const debug_u32 = if (want_modification_safety) u32 else void;
-
-pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn(key: K)->u32,
- comptime eql: fn(a: K, b: K)->bool) -> type
-{
- struct {
- entries: []Entry,
- size: usize,
- max_distance_from_start_index: usize,
- allocator: &Allocator,
- // this is used to detect bugs where a hashtable is edited while an iterator is running.
- modification_count: debug_u32,
-
- const Self = this;
-
- pub const Entry = struct {
- used: bool,
- distance_from_start_index: usize,
- key: K,
- value: V,
- };
-
- pub const Iterator = struct {
- hm: &Self,
- // how many items have we returned
- count: usize,
- // iterator through the entry array
- index: usize,
- // used to detect concurrent modification
- initial_modification_count: debug_u32,
-
- pub fn next(it: &Iterator) -> ?&Entry {
- if (want_modification_safety) {
- assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification
- }
- if (it.count >= it.hm.size) return null;
- while (it.index < it.hm.entries.len) : (it.index += 1) {
- const entry = &it.hm.entries[it.index];
- if (entry.used) {
- it.index += 1;
- it.count += 1;
- return entry;
- }
- }
- unreachable // no next item
- }
- };
-
- pub fn init(hm: &Self, allocator: &Allocator) {
- hm.entries = []Entry{};
- hm.allocator = allocator;
- hm.size = 0;
- hm.max_distance_from_start_index = 0;
- // it doesn't actually matter what we set this to since we use wrapping integer arithmetic
- hm.modification_count = undefined;
- }
-
- pub fn deinit(hm: &Self) {
- hm.allocator.free(Entry, hm.entries);
- }
-
- pub fn clear(hm: &Self) {
- for (hm.entries) |*entry| {
- entry.used = false;
- }
- hm.size = 0;
- hm.max_distance_from_start_index = 0;
- hm.incrementModificationCount();
- }
-
- pub fn put(hm: &Self, key: K, value: V) -> %void {
- if (hm.entries.len == 0) {
- try hm.initCapacity(16);
- }
- hm.incrementModificationCount();
-
- // if we get too full (60%), double the capacity
- if (hm.size * 5 >= hm.entries.len * 3) {
- const old_entries = hm.entries;
- try hm.initCapacity(hm.entries.len * 2);
- // dump all of the old elements into the new table
- for (old_entries) |*old_entry| {
- if (old_entry.used) {
- hm.internalPut(old_entry.key, old_entry.value);
- }
- }
- hm.allocator.free(Entry, old_entries);
- }
-
- hm.internalPut(key, value);
- }
-
- pub fn get(hm: &Self, key: K) -> ?&Entry {
- return hm.internalGet(key);
- }
-
- pub fn remove(hm: &Self, key: K) {
- 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) {
- const index = (start_index + roll_over) % hm.entries.len;
- var entry = &hm.entries[index];
-
- assert(entry.used); // key not found
-
- if (!eql(entry.key, key)) continue;
-
- while (roll_over < hm.entries.len) : (roll_over += 1) {
- const next_index = (start_index + roll_over + 1) % hm.entries.len;
- const next_entry = &hm.entries[next_index];
- if (!next_entry.used or next_entry.distance_from_start_index == 0) {
- entry.used = false;
- hm.size -= 1;
- return;
- }
- *entry = *next_entry;
- entry.distance_from_start_index -= 1;
- entry = next_entry;
- }
- unreachable // shifting everything in the table
- }}
- unreachable // key not found
- }
-
- pub fn entryIterator(hm: &Self) -> Iterator {
- return Iterator {
- .hm = hm,
- .count = 0,
- .index = 0,
- .initial_modification_count = hm.modification_count,
- };
- }
-
- fn initCapacity(hm: &Self, capacity: usize) -> %void {
- hm.entries = try hm.allocator.alloc(Entry, capacity);
- hm.size = 0;
- hm.max_distance_from_start_index = 0;
- for (hm.entries) |*entry| {
- entry.used = false;
- }
- }
-
- fn incrementModificationCount(hm: &Self) {
- if (want_modification_safety) {
- hm.modification_count +%= 1;
- }
- }
-
- fn internalPut(hm: &Self, orig_key: K, orig_value: V) {
- var key = orig_key;
- var value = orig_value;
- const start_index = hm.keyToIndex(key);
- var roll_over: usize = 0;
- var distance_from_start_index: usize = 0;
- while (roll_over < hm.entries.len) : ({roll_over += 1; distance_from_start_index += 1}) {
- const index = (start_index + roll_over) % hm.entries.len;
- const entry = &hm.entries[index];
-
- if (entry.used and !eql(entry.key, key)) {
- if (entry.distance_from_start_index < distance_from_start_index) {
- // robin hood to the rescue
- const tmp = *entry;
- hm.max_distance_from_start_index = math.max(hm.max_distance_from_start_index,
- distance_from_start_index);
- *entry = Entry {
- .used = true,
- .distance_from_start_index = distance_from_start_index,
- .key = key,
- .value = value,
- };
- key = tmp.key;
- value = tmp.value;
- distance_from_start_index = tmp.distance_from_start_index;
- }
- continue;
- }
-
- if (!entry.used) {
- // adding an entry. otherwise overwriting old value with
- // same key
- hm.size += 1;
- }
-
- hm.max_distance_from_start_index = math.max(distance_from_start_index, hm.max_distance_from_start_index);
- *entry = Entry {
- .used = true,
- .distance_from_start_index = distance_from_start_index,
- .key = key,
- .value = value,
- };
- return;
- }
- unreachable // put into a full map
- }
-
- fn internalGet(hm: &Self, key: K) -> ?&Entry {
- const start_index = hm.keyToIndex(key);
- {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
- const index = (start_index + roll_over) % hm.entries.len;
- const entry = &hm.entries[index];
-
- if (!entry.used) return null;
- if (eql(entry.key, key)) return entry;
- }}
- return null;
- }
-
- fn keyToIndex(hm: &Self, key: K) -> usize {
- return usize(hash(key)) % hm.entries.len;
- }
- }
-}
-
-test "basic hash map test" {
- var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined;
- map.init(&debug.global_allocator);
- defer map.deinit();
-
- %%map.put(1, 11);
- %%map.put(2, 22);
- %%map.put(3, 33);
- %%map.put(4, 44);
- %%map.put(5, 55);
-
- assert((??map.get(2)).value == 22);
- map.remove(2);
- assert(if (const entry ?= map.get(2)) false else true);
-}
-
-fn hash_i32(x: i32) -> u32 {
- *(&u32)(&x)
-}
-fn eql_i32(a: i32, b: i32) -> bool {
- a == b
-}
-
-
- const std = @import("std");
-const io = std.io;
-const mem = std.mem;
-const os = std.os;
-
-pub fn main() -> %void {
- const exe = os.args.at(0);
- var catted_anything = false;
- var arg_i: usize = 1;
- while (arg_i < os.args.count()) : (arg_i += 1) {
- const arg = os.args.at(arg_i);
- if (mem.eql(u8, arg, "-")) {
- catted_anything = true;
- try cat_stream(&io.stdin);
- } else if (arg[0] == '-') {
- return usage(exe);
- } else {
- var is = io.InStream.open(arg, null) catch |err| {
- %%io.stderr.printf("Unable to open file: {}\n", @errorName(err));
- return err;
- };
- defer is.close();
-
- catted_anything = true;
- try cat_stream(&is);
- }
- }
- if (!catted_anything) {
- try cat_stream(&io.stdin);
- }
- try io.stdout.flush();
-}
-
-fn usage(exe: []const u8) -> %void {
- %%io.stderr.printf("Usage: {} [FILE]...\n", exe);
- return error.Invalid;
-}
-
-fn cat_stream(is: &io.InStream) -> %void {
- var buf: [1024 * 4]u8 = undefined;
-
- while (true) {
- const bytes_read = is.read(buf[0..]) catch |err| {
- %%io.stderr.printf("Unable to read from stream: {}\n", @errorName(err));
- return err;
- };
-
- if (bytes_read == 0) {
- break;
- }
-
- io.stdout.write(buf[0..bytes_read]) catch |err| {
- %%io.stderr.printf("Unable to write to stdout: {}\n", @errorName(err));
- return err;
- };
- }
-}
- pub fn createAllShaders() -> AllShaders {
- var as : AllShaders = undefined;
-
- as.primitive = createShader(
- \\#version 150 core
- \\
- \\in vec3 VertexPosition;
- \\
- \\uniform mat4 MVP;
- \\
- \\void main(void) {
- \\ gl_Position = vec4(VertexPosition, 1.0) * MVP;
- \\}
- ,
- \\#version 150 core
- \\
- \\out vec4 FragColor;
- \\
- \\uniform vec4 Color;
- \\
- \\void main(void) {
- \\ FragColor = Color;
- \\}
- , null);
-
- as.primitive_attrib_position = as.primitive.attrib_location(c"VertexPosition");
- as.primitive_uniform_mvp = as.primitive.uniform_location(c"MVP");
- as.primitive_uniform_color = as.primitive.uniform_location(c"Color");
-
-
-
- as.texture = createShader(
- \\#version 150 core
- \\
- \\in vec3 VertexPosition;
- \\in vec2 TexCoord;
- \\
- \\out vec2 FragTexCoord;
- \\
- \\uniform mat4 MVP;
- \\
- \\void main(void)
- \\{
- \\ FragTexCoord = TexCoord;
- \\ gl_Position = vec4(VertexPosition, 1.0) * MVP;
- \\}
- ,
- \\#version 150 core
- \\
- \\in vec2 FragTexCoord;
- \\out vec4 FragColor;
- \\
- \\uniform sampler2D Tex;
- \\
- \\void main(void)
- \\{
- \\ FragColor = texture(Tex, FragTexCoord);
- \\}
- , null);
-
- as.texture_attrib_tex_coord = as.texture.attrib_location(c"TexCoord");
- as.texture_attrib_position = as.texture.attrib_location(c"VertexPosition");
- as.texture_uniform_mvp = as.texture.uniform_location(c"MVP");
- as.texture_uniform_tex = as.texture.uniform_location(c"Tex");
-
- debug_gl.assert_no_error();
-
- return as;
-}
- const assert = @import("debug.zig").assert;
-const rand_test = @import("rand_test.zig");
-
-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(r: &Rand, seed: usize) {
- r.rng.init(seed);
- }
-
- /// Get an integer with random bits.
- pub fn scalar(r: &Rand, comptime T: type) -> T {
- if (T == usize) {
- return r.rng.get();
- } else {
- var result: [@sizeOf(T)]u8 = undefined;
- r.fillBytes(result);
- return ([]T)(result)[0];
- }
- }
-
- /// Fill `buf` with randomness.
- pub fn fillBytes(r: &Rand, buf: []u8) {
- var bytes_left = buf.len;
- while (bytes_left >= @sizeOf(usize)) {
- ([]usize)(buf[buf.len - bytes_left...])[0] = r.rng.get();
- bytes_left -= @sizeOf(usize);
- }
- if (bytes_left > 0) {
- var rand_val_array : [@sizeOf(usize)]u8 = undefined;
- ([]usize)(rand_val_array)[0] = r.rng.get();
- 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.
- // TODO support signed integers and then rename to "range"
- pub fn rangeUnsigned(r: &Rand, comptime T: type, start: T, end: T) -> T {
- const range = end - start;
- const leftover = @maxValue(T) % range;
- const upper_bound = @maxValue(T) - leftover;
- var rand_val_array : [@sizeOf(T)]u8 = undefined;
-
- while (true) {
- r.fillBytes(rand_val_array);
- const rand_val = ([]T)(rand_val_array)[0];
- if (rand_val < upper_bound) {
- return start + (rand_val % 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.rangeUnsigned(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: int, comptime d: int,
- comptime s: int, comptime b: int,
- comptime t: int, comptime c: int,
- comptime l: int, comptime f: int) -> type
-{
- struct {
- const Self = this;
-
- array: [n]int,
- index: usize,
-
- pub fn init(mt: &Self, seed: int) {
- mt.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;
- }};
- }
-
- 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[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[x & 0x1];
-
- }
- const x = (mt.array[i] & UM) | (mt.array[0] & LM);
- mt.array[i] = mt.array[m - 1] ^ (x >> 1) ^ mag01[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 "float 32" {
- var r: Rand = undefined;
- r.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 "MT19937_64" {
- var rng: MT19937_64 = undefined;
- rng.init(rand_test.mt64_seed);
- for (rand_test.mt64_data) |value| {
- assert(value == rng.get());
- }
-}
-
-test "MT19937_32" {
- var rng: MT19937_32 = undefined;
- rng.init(rand_test.mt32_seed);
- for (rand_test.mt32_data) |value| {
- assert(value == rng.get());
- }
-}
-
-
-
-
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 94bc780959..11952cd17d 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -31,221 +31,10 @@
Zig is an open-source programming language designed for robustness, optimality, and clarity. @@ -264,37 +53,35 @@ If you search for something specific in this documentation and do not find it, please file an issue or say something on IRC.
-const std = @import("std");
+ {#header_close#}
+ {#header_open|Hello World#}
-pub fn main() -> %void {
+ {#code_begin|exe|hello#}
+const std = @import("std");
+
+pub fn main() -> %void {
// If this program is run without stdout attached, exit with an error.
var stdout_file = try std.io.getStdOut();
// If this program encounters pipe failure when printing to stdout, exit
// with an error.
try stdout_file.write("Hello, world!\n");
-}
- $ zig build-exe hello.zig
-$ ./hello
-Hello, world!
+}
+ {#code_end#}
Usually you don't want to write to stdout. You want to write to stderr. And you don't care if it fails. It's more like a warning message that you want to emit. For that you can use a simpler API:
-const warn = @import("std").debug.warn;
+ {#code_begin|exe|hello#}
+const warn = @import("std").debug.warn;
-pub fn main() -> %void {
+pub fn main() -> %void {
warn("Hello, world!\n");
-}
- See also:
-Zig source code is encoded in UTF-8. An invalid UTF-8 byte sequence results in a compile error.
Throughout all zig source code (including in comments), some codepoints are never allowed:
The codepoint U+000a (LF) (which is encoded as the single-byte value 0x0a) is the line terminator character. This character always terminates a line of zig source code (except possbly the last line of the file).
For some discussion on the rationale behind these design decisions, see issue #663
-const warn = @import("std").debug.warn;
-const os = @import("std").os;
-const assert = @import("std").debug.assert;
+ {#header_close#}
+ {#header_open|Values#}
+ {#code_begin|exe|values#}
+const std = @import("std");
+const warn = std.debug.warn;
+const os = std.os;
+const assert = std.debug.assert;
// error declaration, makes `error.ArgNotFound` available
error ArgNotFound;
-pub fn main() -> %void {
+pub fn main() -> %void {
// integers
const one_plus_one: i32 = 1 + 1;
warn("1 + 1 = {}\n", one_plus_one);
@@ -349,31 +139,9 @@ pub fn main() -> %void {
warn("\nerror union 2\ntype: {}\nvalue: {}\n",
@typeName(@typeOf(number_or_error)), number_or_error);
-}
- $ zig build-exe values.zig
-$ ./values
-1 + 1 = 2
-7.0 / 3.0 = 2.333333
-false
-true
-false
-
-nullable 1
-type: ?[]const u8
-value: null
-
-nullable 2
-type: ?[]const u8
-value: hi
-
-error union 1
-type: %i32
-value: error.ArgNotFound
-
-error union 2
-type: %i32
-value: 1234
- | @@ -599,14 +367,9 @@ value: 1234 | an error code |
|---|
See also:
- -| @@ -633,12 +396,9 @@ value: 1234 | refers to the thing in immediate scope |
|---|
See also:
- -const assert = @import("std").debug.assert;
const mem = @import("std").mem;
@@ -658,12 +418,8 @@ test "string literals" {
}
$ zig test string_literals.zig
Test 1/1 string literals...OK
- See also:
- -| @@ -711,7 +467,8 @@ Test 1/1 string literals...OK |
|---|
Note that the maximum valid Unicode point is 0x10ffff.
Multiline string literals have no escapes and can span across multiple lines.
To start a multiline string literal, use the \\ token. Just like a comment,
@@ -743,11 +500,10 @@ Test 1/1 string literals...OK
In this example the variable c_string_literal has type &const char and
has a terminating null byte.
See also:
-Use const to assign a value to an identifier:
const x = 1234;
@@ -798,14 +554,17 @@ test "init with undefined" {
}
$ zig test test.zig
Test 1/1 init with undefined...OK
- const decimal_int = 98222;
const hex_int = 0xff;
const another_hex_int = 0xFF;
const octal_int = 0o755;
const binary_int = 0b11110000;
-
Integer literals have no size limitation, and if any undefined behavior occurs,
the compiler catches it.
@@ -827,14 +586,12 @@ const binary_int = 0b11110000;
integer overflow. Also available are operations such as +% and
-% which are defined to have wrapping arithmetic on all targets.
See also:
- -const floating_point = 123.0E+77;
const another_float = 123.0;
const yet_another = 123.0e+77;
@@ -842,7 +599,8 @@ const yet_another = 123.0e+77;
const hex_floating_point = 0x103.70p-5;
const another_hex_float = 0x103.70;
const yet_another_hex_float = 0x103.70P-5;
- By default floating point operations use Optimized mode,
but you can switch to Strict mode on a per-block basis:
foo.zig
@@ -876,13 +634,10 @@ $ zig build-exe test.zig --object foo.o $ ./test optimized = 1.0e-2 strict = 9.765625e-3 -See also:
- -| @@ -1470,7 +1225,8 @@ const ptr = &x; |
|---|
x() x[] x.y
!x -x -%x ~x *x &x ?x %x %%x ??x
x{}
@@ -1485,7 +1241,9 @@ and
or
?? catch
= *= /= %= += -= <<= >>= &= ^= |=
- const assert = @import("std").debug.assert;
const mem = @import("std").mem;
@@ -1594,12 +1352,9 @@ Test 1/4 iterate over an array...OK
Test 2/4 modify an array...OK
Test 3/4 compile-time array initalization...OK
Test 4/4 array initialization with function calls...OK
- See also:
- -const assert = @import("std").debug.assert;
test "address of syntax" {
@@ -1737,7 +1492,7 @@ Test 5/8 volatile...OK
Test 6/8 nullable pointers...OK
Test 7/8 pointer casting...OK
Test 8/8 pointer child type...OK
- Each type has an alignment - a number of bytes such that, when a value of the type is loaded from or stored to memory, @@ -1838,7 +1593,8 @@ Test 1/1 pointer alignment safety...incorrect alignment Tests failed. Use the following command to reproduce the failure: ./test -
Zig uses Type Based Alias Analysis (also known as Strict Aliasing) to
perform some optimizations. This means that pointers of different types must
not alias the same memory, with the exception of u8. Pointers to
@@ -1849,12 +1605,10 @@ Tests failed. Use the following command to reproduce the failure:
Instead, use @bitCast:
@bitCast(u32, f32(12.34))
As an added benefit, the @bitcast version works at compile-time.
See also:
- -const assert = @import("std").debug.assert;
test "basic slices" {
@@ -1948,13 +1702,9 @@ test "slice widening" {
Test 1/3 using slices for strings...OK
Test 2/3 slice pointer...OK
Test 3/3 slice widening...OK
- See also:
- -// Declare a struct.
// Zig gives no guarantees about the order of fields and whether or
// not there will be padding.
@@ -2094,12 +1844,9 @@ Test 1/4 dot product...OK
Test 2/4 struct namespaced variable...OK
Test 3/4 field parent pointer...OK
Test 4/4 linked list...OK
- See also:
-const assert = @import("std").debug.assert;
const mem = @import("std").mem;
@@ -2210,13 +1957,9 @@ Test 5/8 @TagType...OK
Test 6/8 @memberCount...OK
Test 7/8 @memberName...OK
Test 8/8 @tagName...OK
- See also:
-const assert = @import("std").debug.assert;
const mem = @import("std").mem;
@@ -2323,7 +2066,8 @@ Test 7/7 @tagName...OK
Unions with an enum tag are generated as a struct with a tag field and union field. Zig
sorts the order of the tag and union field by the largest alignment.
- const assert = @import("std").debug.assert;
const builtin = @import("builtin");
@@ -2419,14 +2163,9 @@ test "switch inside function" {
Test 1/2 switch simple...OK
Test 2/2 switch enum...OK
Test 3/3 switch inside function...OK
- See also:
-const assert = @import("std").debug.assert;
test "while basic" {
@@ -2587,15 +2326,9 @@ Test 5/8 while loop continuation expression, more complicated...OK
Test 6/8 while else...OK
Test 7/8 while null capture...OK
Test 8/8 inline while loop...OK
- See also:
-const assert = @import("std").debug.assert;
test "for basics" {
@@ -2689,14 +2422,9 @@ Test 1/4 for basics...OK
Test 2/4 for reference...OK
Test 3/4 for else...OK
Test 4/4 inline for loop...OK
- See also:
- -// If expressions have three uses, corresponding to the three types:
// * bool
// * ?T
@@ -2809,28 +2537,9 @@ test "if error union" {
Test 1/3 if boolean...OK
Test 2/3 if nullable...OK
Test 3/3 if error union...OK
- See also:
- -const assert = @import("std").debug.assert;
-
-test "goto" {
- var value = false;
- goto label;
- value = true;
-
-label:
- assert(value == false);
-}
-
- $ zig test goto.zig
-Test 1/1 goto...OK
-
-Note that there are plans to remove goto
-const assert = @import("std").debug.assert;
const printf = @import("std").io.stdout.printf;
@@ -2916,11 +2625,9 @@ encountered an error!
end of function
OK
- See also:
-
In Debug and ReleaseSafe mode, and when using zig test,
unreachable emits a call to panic with the message reached unreachable code.
@@ -2930,7 +2637,7 @@ OK
will never be hit to perform optimizations. However, zig test even in ReleaseFast mode
still emits unreachable as calls to panic.
// unreachable is used to assert that control flow will never happen upon a
// particular location:
test "basic math" {
@@ -2974,7 +2681,8 @@ lib/zig/std/special/bootstrap.zig:34:25: 0x0000000000214750 in ??? (test)
Tests failed. Use the following command to reproduce the failure:
./test
- const assert = @import("std").debug.assert;
comptime {
@@ -2989,13 +2697,10 @@ comptime {
test.zig:9:12: error: unreachable code
assert(@typeOf(unreachable) == noreturn);
^
- See also:
-
noreturn is the type of:
const assert = @import("std").debug.assert;
// Functions are declared like this
@@ -3091,7 +2797,7 @@ comptime {
fn foo() { }
$ zig build-obj test.zig
- In Zig, structs, unions, and enums with payloads cannot be passed by value to a function. @@ -3127,7 +2833,9 @@ export fn entry() { the C ABI does allow passing structs and unions by value. So functions which use the C calling convention may pass structs and unions by value.
-One of the distinguishing features of Zig is its exception handling strategy.
@@ -3315,13 +3023,9 @@ pub fn parseU64(buf: []const u8, radix: u8) -> %u64 { in other languages. -See also:
- -One area that Zig provides safety without compromising efficiency or readability is with the nullable type. @@ -3415,7 +3119,8 @@ fn doAThing() -> ?&Foo { The optimizer can sometimes make better decisions knowing that pointer arguments cannot be null.
-TODO: explain implicit vs explicit casting
TODO: resolve peer types builtin
TODO: truncate builtin
@@ -3424,24 +3129,27 @@ fn doAThing() -> ?&Foo {TODO: ptr to int builtin
TODO: ptrcast builtin
TODO: explain number literals vs concrete types
-TODO: assigning void has no codegen
TODO: hashmap with void becomes a set
TODO: difference between c_void and void
TODO: void is the default return value of functions
TODO: functions require assigning the return value
-TODO: example of this referring to Self struct
TODO: example of this referring to recursion function
TODO: example of this referring to basic block for @setDebugSafety
-Zig places importance on the concept of whether an expression is known at compile-time. There are a few different places this concept is used, and these building blocks are used to keep the language small, readable, and powerful.
-Compile-time parameters is how Zig implements generics. It is compile-time duck typing.
@@ -3549,7 +3257,8 @@ fn letsTryToCompareBools(a: bool, b: bool) -> bool { This works the same way forswitch expressions - they are implicitly inlined
when the target expression is compile-time known.
-
In Zig, the programmer can label variables as comptime. This guarantees to the compiler
that every load and store of the variable is performed at compile-time. Any violation of this results in a
@@ -3631,7 +3340,8 @@ fn performFn(start_value: i32) -> i32 {
later in this article, allows expressiveness that in other languages requires using macros,
generated code, or a preprocessor to accomplish.
In Zig, it matters whether a given expression is known at compile-time or run-time. A programmer can
use a comptime expression to guarantee that the expression will be evaluated at compile-time.
@@ -3860,7 +3570,9 @@ fn sum(numbers: []i32) -> i32 {
we could call the sum function as is with a slice of numbers whose length and values were
only known at run-time.
Zig uses these capabilities to implement generic data structures without introducing any
special-case syntax. If you followed along so far, you may already know how to create a
@@ -3895,19 +3607,21 @@ fn sum(numbers: []i32) -> i32 {
Node refers to itself as a pointer, which is not actually an infinite regression, so
it works fine.
- Putting all of this together, let's seee how printf works in Zig.
+ Putting all of this together, let's see how printf works in Zig.
const warn = @import("std").debug.warn;
+ {#code_begin|exe|printf#}
+const warn = @import("std").debug.warn;
const a_number: i32 = 1234;
const a_string = "foobar";
-pub fn main(args: [][]u8) -> %void {
+pub fn main() {
warn("here is a string: '{}' here is a number: {}\n", a_string, a_number);
-}
- here is a string: 'foobar' here is a number: 1234
+}
+ {#code_end#}
Let's crack open the implementation of this and see how it works:
@@ -4027,15 +3741,17 @@ pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) ->
Zig doesn't care whether the format argument is a string literal,
only that it is a compile-time known value that is implicitly castable to a []const u8:
const warn = @import("std").debug.warn;
+ {#code_begin|exe|printf#}
+const warn = @import("std").debug.warn;
const a_number: i32 = 1234;
const a_string = "foobar";
const fmt = "here is a string: '{}' here is a number: {}\n";
-pub fn main(args: [][]u8) -> %void {
+pub fn main() {
warn(fmt, a_string, a_number);
-}
+}
+ {#code_end#}
This works fine.
@@ -4045,35 +3761,42 @@ pub fn main(args: [][]u8) -> %void { a macro language or a preprocessor language. It's Zig all the way down.TODO: suggestion to not use inline unless necessary
-TODO: inline while
TODO: inline for
TODO: suggestion to not use inline unless necessary
-TODO: example of inline assembly
TODO: example of module level assembly
TODO: example of using inline assembly return value
TODO: example of using inline assembly assigning values to variables
-TODO: @fence()
TODO: @atomic rmw
TODO: builtin atomic memory ordering enum
-
Builtin functions are provided by the compiler and are prefixed with @.
The comptime keyword on a parameter means that the parameter must be known
at compile time.
@addWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
Performs *result = a + b. If overflow or underflow occurs,
stores the overflowed bits in result and returns true.
If no overflow or underflow occurs, returns false.
TODO
-@bitCast(comptime DestType: type, value: var) -> DestType
Converts a value of one type to another type. @@ -4094,7 +3817,8 @@ pub fn main(args: [][]u8) -> %void {
Works at compile-time if value is known at compile time. It's a compile error to bitcast a struct to a scalar type of the same size since structs have undefined layout. However if the struct is packed then it works.
@breakpoint()
This function inserts a platform-specific debug trap instruction which causes @@ -4104,7 +3828,8 @@ pub fn main(args: [][]u8) -> %void { This function is only valid within function scope.
-@alignCast(comptime alignment: u29, ptr: var) -> var
ptr can be &T, fn(), ?&T,
@@ -4114,7 +3839,8 @@ pub fn main(args: [][]u8) -> %void {
A pointer alignment safety check is added to the generated code to make sure the pointer is aligned as promised.
-@alignOf(comptime T: type) -> (number literal)
This function returns the number of bytes that this type should be aligned to @@ -4129,12 +3855,9 @@ comptime { The result is a target-specific compile time constant. It is guaranteed to be less than or equal to @sizeOf(T).
-See also:
-@cDefine(comptime name: []u8, value)
This function can only occur inside @cImport.
@@ -4151,15 +3874,9 @@ comptime {
Use the void value, like this:
@cDefine("_GNU_SOURCE", {})
- See also:
-@cImport(expression) -> (namespace)
This function parses C code and imports the functions, types, variables, and
@@ -4170,14 +3887,9 @@ comptime {
@cInclude, @cDefine, and @cUndef work
within this expression, appending to a temporary buffer which is then parsed as C code.
See also:
-@cInclude(comptime path: []u8)
This function can only occur inside @cImport.
@@ -4186,14 +3898,9 @@ comptime {
This appends #include <$path>\n to the c_import
temporary buffer.
See also:
-@cUndef(comptime name: []u8)
This function can only occur inside @cImport.
@@ -4202,19 +3909,15 @@ comptime {
This appends #undef $name to the @cImport
temporary buffer.
See also:
-@canImplicitCast(comptime T: type, value) -> bool
Returns whether a value can be implicitly casted to a given type.
-@clz(x: T) -> U
This function counts the number of leading zeroes in x which is an integer
@@ -4228,7 +3931,8 @@ comptime {
If x is zero, @clz returns T.bit_count.
@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> bool
This function performs an atomic compare exchange operation.
@@ -4237,12 +3941,9 @@ comptime {
AtomicOrder can be found with @import("builtin").AtomicOrder.
@typeOf(ptr).alignment must be >= @sizeOf(T).
See also:
- - -@compileError(comptime msg: []u8)
This function, when semantically analyzed, causes a compile error with the
@@ -4253,7 +3954,8 @@ comptime {
using if or switch with compile time constants,
and comptime functions.
@compileLog(args: ...)
This function prints the arguments passed to it at compile-time. @@ -4303,7 +4005,7 @@ test.zig:6:2: error: found compile log statement program compiles successfully and the generated executable prints:
Runtime in main, num1 = 100.
- @ctz(x: T) -> U
This function counts the number of trailing zeroes in x which is an integer
@@ -4316,7 +4018,8 @@ test.zig:6:2: error: found compile log statement
If x is zero, @ctz returns T.bit_count.
@divExact(numerator: T, denominator: T) -> T
Exact division. Caller guarantees denominator != 0 and
@@ -4326,13 +4029,10 @@ test.zig:6:2: error: found compile log statement
@divExact(6, 3) == 2@divExact(a, b) * b == aSee also:
- -For a function that returns a possible error code, use @import("std").math.divExact.
@divFloor(numerator: T, denominator: T) -> T
Floored division. Rounds toward negative infinity. For unsigned integers it is @@ -4343,13 +4043,10 @@ test.zig:6:2: error: found compile log statement
@divFloor(-5, 3) == -2@divFloor(a, b) + @mod(a, b) == aSee also:
- -For a function that returns a possible error code, use @import("std").math.divFloor.
@divTrunc(numerator: T, denominator: T) -> T
Truncated division. Rounds toward zero. For unsigned integers it is @@ -4360,13 +4057,10 @@ test.zig:6:2: error: found compile log statement
@divTrunc(-5, 3) == -1@divTrunc(a, b) + @rem(a, b) == aSee also:
- -For a function that returns a possible error code, use @import("std").math.divTrunc.
@embedFile(comptime path: []const u8) -> [X]u8
This function returns a compile time constant fixed-size array with length @@ -4376,21 +4070,21 @@ test.zig:6:2: error: found compile log statement
path is absolute or relative to the current file, just like @import.
See also:
-@export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) -> []const u8
Creates a symbol in the output object file.
-@tagName(value: var) -> []const u8
Converts an enum value or union value to a slice of bytes representing the name.
-@TagType(T: type) -> type
For an enum, returns the integer type that is used to store the enumeration value. @@ -4398,7 +4092,8 @@ test.zig:6:2: error: found compile log statement
For a union, returns the enum type that is used to store the tag value.
-@errorName(err: error) -> []u8
This function returns the string representation of an error. If an error
@@ -4413,14 +4108,16 @@ test.zig:6:2: error: found compile log statement
or all calls have a compile-time known value for err, then no
error name table will be generated.
@errorReturnTrace() -> ?&builtin.StackTrace
If the binary is built with error return tracing, and this function is invoked in a function that calls a function with an error or error union return type, returns a stack trace object. Otherwise returns `null`.
-@fence(order: AtomicOrder)
The fence function is used to introduce happens-before edges between operations.
@@ -4428,17 +4125,16 @@ test.zig:6:2: error: found compile log statement
AtomicOrder can be found with @import("builtin").AtomicOrder.
See also:
- -@fieldParentPtr(comptime ParentType: type, comptime field_name: []const u8,
field_ptr: &T) -> &ParentType
Given a pointer to a field, returns the base pointer of a struct.
-@frameAddress()
This function returns the base pointer of the current stack frame. @@ -4451,7 +4147,8 @@ test.zig:6:2: error: found compile log statement
This function is only valid within function scope.
-@import(comptime path: []u8) -> (namespace)
This function finds a zig file corresponding to path and imports all the
@@ -4469,12 +4166,9 @@ test.zig:6:2: error: found compile log statement
@import("std") - Zig Standard Library@import("builtin") - Compiler-provided types and variablesSee also:
-@inlineCall(function: X, args: ...) -> Y
This calls a function, in the same way that invoking an expression with parentheses does:
@@ -4489,21 +4183,21 @@ fn add(a: i32, b: i32) -> i32 { a + b }
Unlike a normal function call, however, @inlineCall guarantees that the call
will be inlined. If the call cannot be inlined, a compile error is emitted.
See also:
-@intToPtr(comptime DestType: type, int: usize) -> DestType
Converts an integer to a pointer. To convert the other way, use @ptrToInt.
-@IntType(comptime is_signed: bool, comptime bit_count: u8) -> type
This function returns an integer type with the given signness and bit count.
-@maxValue(comptime T: type) -> (number literal)
This function returns the maximum value of the integer type T.
@@ -4511,7 +4205,8 @@ fn add(a: i32, b: i32) -> i32 { a + b }
The result is a compile time constant.
-@memberCount(comptime T: type) -> (number literal)
This function returns the number of enum values in an enum type. @@ -4519,11 +4214,14 @@ fn add(a: i32, b: i32) -> i32 { a + b }
The result is a compile time constant.
-TODO
-TODO
-@memcpy(noalias dest: &u8, noalias source: &const u8, byte_count: usize)
This function copies bytes from one region of memory to another. dest and
@@ -4540,7 +4238,8 @@ fn add(a: i32, b: i32) -> i32 { a + b }
There is also a standard library function for this:
const mem = @import("std").mem;
mem.copy(u8, dest[0...byte_count], source[0...byte_count]);
- @memset(dest: &u8, c: u8, byte_count: usize)
This function sets a region of memory to c. dest is a pointer.
@@ -4556,7 +4255,8 @@ mem.copy(u8, dest[0...byte_count], source[0...byte_count]);
There is also a standard library function for this:
const mem = @import("std").mem;
mem.set(u8, dest, c);
- @minValue(comptime T: type) -> (number literal)
This function returns the minimum value of the integer type T. @@ -4564,7 +4264,8 @@ mem.set(u8, dest, c);
The result is a compile time constant.
-@mod(numerator: T, denominator: T) -> T
Modulus division. For unsigned integers this is the same as @@ -4574,19 +4275,18 @@ mem.set(u8, dest, c);
@mod(-5, 3) == 1@divFloor(a, b) + @mod(a, b) == aSee also:
-@import("std").math.modFor a function that returns an error code, see @import("std").math.mod.
@mulWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
Performs *result = a * b. If overflow or underflow occurs,
stores the overflowed bits in result and returns true.
If no overflow or underflow occurs, returns false.
@noInlineCall(function: var, args: ...) -> var
This calls a function, in the same way that invoking an expression with parentheses does:
@@ -4601,16 +4301,15 @@ fn add(a: i32, b: i32) -> i32 { a + b }
Unlike a normal function call, however, @noInlineCall guarantees that the call
will not be inlined. If the call must be inlined, a compile error is emitted.
See also:
-@offsetOf(comptime T: type, comptime field_name: [] const u8) -> (number literal)
This function returns the byte offset of a field relative to its containing struct.
-@OpaqueType() -> type
Creates a new type with an unknown size and alignment. @@ -4630,7 +4329,8 @@ export fn foo(w: &Wat) { test.zig:5:9: error: expected type '&Derp', found '&Wat' bar(w); ^ -
@panic(message: []const u8) -> noreturn
Invokes the panic handler function. By default the panic handler function @@ -4644,17 +4344,15 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
See also:
-@ptrCast(comptime DestType: type, value: var) -> DestType
Converts a pointer of one type to a pointer of another type.
-@ptrToInt(value: var) -> usize
Converts value to a usize which is the address of the pointer. value can be one of these types:
@@ -4667,7 +4365,8 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
To convert the other way, use @intToPtr
-@rem(numerator: T, denominator: T) -> T
Remainder division. For unsigned integers this is the same as @@ -4677,12 +4376,10 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
@rem(-5, 3) == -2@divTrunc(a, b) + @rem(a, b) == aSee also:
-@import("std").math.remFor a function that returns an error code, see @import("std").math.rem.
@returnAddress()
This function returns a pointer to the return address of the current stack @@ -4695,14 +4392,15 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
This function is only valid within function scope.
- -@setDebugSafety(scope, safety_on: bool)
Sets whether debug safety checks are on for a given scope.
-@setEvalBranchQuota(new_quota: usize)
Changes the maximum number of backwards branches that compile-time code @@ -4732,12 +4430,9 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
$ ./zig build-obj test.zig
(no output because it worked fine)
-See also:
-@setFloatMode(scope, mode: @import("builtin").FloatMode)
Sets the floating point mode for a given scope. Possible values are:
@@ -4763,26 +4458,22 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
Strict - Floating point operations follow strict IEEE compliance.
-
See also:
- - -@setGlobalLinkage(global_variable_name, comptime linkage: GlobalLinkage)
GlobalLinkage can be found with @import("builtin").GlobalLinkage.
See also:
- -@setGlobalSection(global_variable_name, comptime section_name: []const u8) -> bool
Puts the global variable in the specified section.
-@shlExact(value: T, shift_amt: Log2T) -> T
Performs the left shift operation (<<). Caller guarantees
@@ -4792,12 +4483,9 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
The type of shift_amt is an unsigned integer with log2(T.bit_count) bits.
This is because shift_amt >= T.bit_count is undefined behavior.
See also:
-@shlWithOverflow(comptime T: type, a: T, shift_amt: Log2T, result: &T) -> bool
Performs *result = a << b. If overflow or underflow occurs,
@@ -4808,12 +4496,9 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
The type of shift_amt is an unsigned integer with log2(T.bit_count) bits.
This is because shift_amt >= T.bit_count is undefined behavior.
See also:
- -@shrExact(value: T, shift_amt: Log2T) -> T
Performs the right shift operation (>>). Caller guarantees
@@ -4823,11 +4508,9 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
The type of shift_amt is an unsigned integer with log2(T.bit_count) bits.
This is because shift_amt >= T.bit_count is undefined behavior.
See also:
-@sizeOf(comptime T: type) -> (number literal)
This function returns the number of bytes it takes to store T in memory.
@@ -4835,14 +4518,16 @@ test.zig:5:9: error: expected type '&Derp', found '&Wat'
The result is a target-specific compile time constant.
-@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -> bool
Performs *result = a - b. If overflow or underflow occurs,
stores the overflowed bits in result and returns true.
If no overflow or underflow occurs, returns false.
@truncate(comptime T: type, integer) -> T
This function truncates bits from an integer type, resulting in a smaller @@ -4865,7 +4550,8 @@ const b: u8 = @truncate(u8, a); of endianness on the target platform.
-@typeId(comptime T: type) -> @import("builtin").TypeId
Returns which kind of type something is. Possible values: @@ -4898,20 +4584,24 @@ const b: u8 = @truncate(u8, a); Opaque, }; -
@typeName(T: type) -> []u8
This function returns the string representation of a type.
-@typeOf(expression) -> type
This function returns a compile-time constant, which is the type of the expression passed as an argument. The expression is evaluated.
-Zig has three build modes:
@@ -4935,34 +4625,33 @@ pub fn build(b: &Builder) { -Drelease-safe=(bool) optimizations on and safety on
-Drelease-fast=(bool) optimizations on and safety off
- $ zig build-exe example.zig
$ zig build-exe example.zig --release-fast
$ zig build-exe example.zig --release-safe
See also:
- -Zig has many instances of undefined behavior. If undefined behavior is detected at compile-time, Zig emits an error. Most undefined behavior that @@ -5000,7 +4689,7 @@ Test 1/1 safety check...reached unreachable code Tests failed. Use the following command to reproduce the failure: ./test -
At compile-time:
comptime {
assert(false);
@@ -5019,7 +4708,8 @@ fn assert(ok: bool) {
comptime {
^
At runtime crashes with the message reached unreachable code and a stack trace.
At compile-time:
comptime {
const array = "hello";
@@ -5030,7 +4720,8 @@ comptime {
const garbage = array[5];
^
At runtime crashes with the message index out of bounds and a stack trace.
At compile-time:
comptime {
const value: i32 = -1;
@@ -5044,7 +4735,8 @@ comptime {
If you are trying to obtain the maximum value of an unsigned integer, use @maxValue(T),
where T is the integer type, such as u32.
- Cast Truncates Data
+ {#header_close#}
+ {#header_open|Cast Truncates Data#}
At compile-time:
comptime {
const spartan_count: u16 = 300;
@@ -5060,8 +4752,9 @@ test.zig:3:20: error: cast from 'u16' to 'u8' truncates bits
where T is the integer type, such as u32, and value
is the value you want to truncate.
- Integer Overflow
- Default Operations
+ {#header_close#}
+ {#header_open|Integer Overflow#}
+ {#header_open|Default Operations#}
The following operators can cause integer overflow:
+ (addition)
@@ -5083,7 +4776,8 @@ test.zig:3:20: error: cast from 'u16' to 'u8' truncates bits
byte += 1;
^
At runtime crashes with the message integer overflow and a stack trace.
- Standard Library Math Functions
+ {#header_close#}
+ {#header_open|Standard Library Math Functions#}
These functions provided by the standard library return possible errors.
@import("std").math.add
@@ -5112,7 +4806,8 @@ pub fn main() -> %void {
$ zig build-exe test.zig
$ ./test
unable to add one: Overflow
- Builtin Overflow Functions
+ {#header_close#}
+ {#header_open|Builtin Overflow Functions#}
These builtins return a bool of whether or not overflow
occurred, as well as returning the overflowed bits:
@@ -5140,7 +4835,8 @@ pub fn main() -> %void {
$ zig build-exe test.zig
$ ./test
overflowed result: 9
- Wrapping Operations
+ {#header_close#}
+ {#header_open|Wrapping Operations#}
These operations have guaranteed wraparound semantics.
@@ -5159,7 +4855,9 @@ test "wraparound addition and subtraction" {
const max_val = min_val -% 1;
assert(max_val == @maxValue(i32));
}
- At compile-time:
comptime {
const x = @shlExact(u8(0b01010101), 2);
@@ -5169,7 +4867,8 @@ test "wraparound addition and subtraction" {
const x = @shlExact(u8(0b01010101), 2);
^
At runtime crashes with the message left shift overflowed bits and a stack trace.
At compile-time:
comptime {
const x = @shrExact(u8(0b10101010), 2);
@@ -5179,7 +4878,8 @@ test "wraparound addition and subtraction" {
const x = @shrExact(u8(0b10101010), 2);
^
At runtime crashes with the message right shift overflowed bits and a stack trace.
At compile-time:
comptime {
const a: i32 = 1;
@@ -5192,7 +4892,8 @@ test "wraparound addition and subtraction" {
^
At runtime crashes with the message division by zero and a stack trace.
At compile-time:
comptime {
const a: i32 = 10;
@@ -5205,11 +4906,14 @@ test "wraparound addition and subtraction" {
^
At runtime crashes with the message remainder division by zero and a stack trace.
TODO
-TODO
-At compile-time:
comptime {
const nullable_number: ?i32 = null;
@@ -5222,8 +4926,9 @@ test "wraparound addition and subtraction" {
At runtime crashes with the message attempt to unwrap null and a stack trace.
One way to avoid this crash is to test for null instead of assuming non-null, with
the if expression:
- const warn = @import("std").debug.warn;
-pub fn main() -> %void {
+ {#code_begin|exe|test#}
+const warn = @import("std").debug.warn;
+pub fn main() {
const nullable_number: ?i32 = null;
if (nullable_number) |number| {
@@ -5231,11 +4936,10 @@ pub fn main() -> %void {
} else {
warn("it's null\n");
}
-}
- % zig build-exe test.zig
-$ ./test
-it's null
- Attempt to Unwrap Error
+}
+ {#code_end#}
+ {#header_close#}
+ {#header_open|Attempt to Unwrap Error#}
At compile-time:
comptime {
const number = %%getNumberOrFail();
@@ -5253,9 +4957,10 @@ fn getNumberOrFail() -> %i32 {
At runtime crashes with the message attempt to unwrap error: ErrorCode and a stack trace.
One way to avoid this crash is to test for an error instead of assuming a successful result, with
the if expression:
- const warn = @import("std").debug.warn;
+ {#code_begin|exe|test#}
+const warn = @import("std").debug.warn;
-pub fn main() -> %void {
+pub fn main() {
const result = getNumberOrFail();
if (result) |number| {
@@ -5267,14 +4972,12 @@ pub fn main() -> %void {
error UnableToReturnNumber;
-fn getNumberOrFail() -> %i32 {
+fn getNumberOrFail() -> %i32 {
return error.UnableToReturnNumber;
-}
- $ zig build-exe test.zig
-$ ./test
-got error: UnableToReturnNumber
-
- Invalid Error Code
+}
+ {#code_end#}
+ {#header_close#}
+ {#header_open|Invalid Error Code#}
At compile-time:
error AnError;
comptime {
@@ -5287,28 +4990,31 @@ comptime {
const invalid_err = error(number);
^
At runtime crashes with the message invalid error code and a stack trace.
- Invalid Enum Cast
+ {#header_close#}
+ {#header_open|Invalid Enum Cast#}
TODO
- Incorrect Pointer Alignment
+ {#header_close#}
+ {#header_open|Incorrect Pointer Alignment#}
TODO
- Wrong Union Field Access
+ {#header_close#}
+ {#header_open|Wrong Union Field Access#}
TODO
- Memory
+ {#header_close#}
+ {#header_close#}
+ {#header_open|Memory#}
TODO: explain no default allocator in zig
TODO: show how to use the allocator interface
TODO: mention debug allocator
TODO: importance of checking for allocation failure
TODO: mention overcommit and the OOM Killer
TODO: mention recursion
- See also:
-
- - Pointers
-
+ {#see_also|Pointers#}
- Compile Variables
+ {#header_close#}
+ {#header_open|Compile Variables#}
Compile variables are accessible by importing the "builtin" package,
which the compiler makes available to every Zig source file. It contains
@@ -5474,11 +5180,9 @@ pub const object_format = ObjectFormat.elf;
pub const mode = Mode.ReleaseFast;
pub const link_libs = [][]const u8 {
};
- See also:
-
- - Build Mode
-
- Root Source File
+ {#see_also|Build Mode#}
+ {#header_close#}
+ {#header_open|Root Source File#}
TODO: explain how root source file finds other files
TODO: pub fn main
TODO: pub fn panic
@@ -5486,17 +5190,20 @@ pub const link_libs = [][]const u8 {
TODO: order independent top level declarations
TODO: lazy analysis
TODO: using comptime { _ = @import() }
- Zig Test
+ {#header_close#}
+ {#header_open|Zig Test#}
TODO: basic usage
TODO: lazy analysis
TODO: --test-filter
TODO: --test-name-prefix
TODO: testing in releasefast and releasesafe mode. assert still works
- Zig Build System
+ {#header_close#}
+ {#header_open|Zig Build System#}
TODO: explain purpose, it's supposed to replace make/cmake
TODO: example of building a zig executable
TODO: example of building a C library
- C
+ {#header_close#}
+ {#header_open|C#}
Although Zig is independent of C, and, unlike most other languages, does not depend on libc,
Zig acknowledges the importance of interacting with existing C code.
@@ -5504,7 +5211,7 @@ pub const link_libs = [][]const u8 {
There are a few ways that Zig facilitates C interop.
- C Type Primitives
+ {#header_open|C Type Primitives#}
These have guaranteed C ABI compatibility and can be used like any other type.
@@ -5520,11 +5227,9 @@ pub const link_libs = [][]const u8 {
c_longdouble
c_void
- See also:
-
- - Primitive Types
-
- C String Literals
+ {#see_also|Primitive Types#}
+ {#header_close#}
+ {#header_open|C String Literals#}
extern fn puts(&const u8);
pub fn main() -> %void {
@@ -5535,11 +5240,9 @@ pub fn main() -> %void {
c\\multiline C string literal
);
}
- See also:
-
- - String Literals
-
- Import from C Header File
+ {#see_also|String Literals#}
+ {#header_close#}
+ {#header_open|Import from C Header File#}
The @cImport builtin function can be used
to directly import symbols from .h files:
@@ -5566,19 +5269,14 @@ const c = @cImport({
}
@cInclude("soundio.h");
});
- See also:
- -You can mix Zig object files with any other object files that respect the C ABI. Example:
-const base64 = @import("std").base64;
export fn decode_base_64(dest_ptr: &u8, dest_len: usize,
@@ -5592,7 +5290,7 @@ export fn decode_base_64(dest_ptr: &u8, dest_len: usize,
return decoded_size;
}
- // This header is generated by zig from base64.zig
#include "base64.h"
@@ -5609,7 +5307,8 @@ int main(int argc, char **argv) {
return 0;
}
- const Builder = @import("std").build.Builder;
pub fn build(b: &Builder) {
@@ -5625,16 +5324,15 @@ pub fn build(b: &Builder) {
b.default_step.dependOn(&exe.step);
}
- $ zig build
$ ./test
all your base are belong to us
- See also:
-
Zig supports generating code for all targets that LLVM supports. Here is
what it looks like to execute zig targets on a Linux x86_64
@@ -5760,14 +5458,15 @@ Environments:
Linux x86_64. Not all standard library code requires operating system abstractions, however,
so things such as generic data structures work an all above platforms.
These coding conventions are not enforced by the compiler, but they are shipped in this documentation along with the compiler in order to provide a point of reference, should anyone wish to point to an authority on agreed upon Zig coding style.
-
Roughly speaking: camelCaseFunctionName, TitleCaseTypeName,
snake_case_variable_name. More precisely:
@@ -5816,7 +5516,8 @@ coding style.
do what makes sense. For example, if there is an established convention such as
ENOENT, follow the established convention.
const namespace_name = @import("dir_name/file_name.zig");
var global_var: i32 = undefined;
const const_name = 42;
@@ -5858,7 +5559,9 @@ fn readU32Be() -> u32 {}
See the Zig Standard Library for more examples.
-Root = many(TopLevelItem) EOF
TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl
@@ -6010,7 +5713,8 @@ KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "u
ContainerDecl = option("extern" | "packed")
("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
"{" many(ContainerMember) "}"
- TODO: document changes from a31b23c46ba2a8c28df01adc1aa0b4d878b9a5cf (compile time reflection additions)
+ {#header_close#}