2017-11-07 00:22:27 -08:00
<!doctype html>
< html >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1, user-scalable=no" / >
< title > The Zig Programming Language< / title >
< link rel = "stylesheet" type = "text/css" href = "highlight/styles/default.css" >
< style type = "text/css" >
img {
max-width: 100%;
}
< / style >
< / head >
< body >
< img src = "zig-logo.svg" >
< p >
Zig is an open-source programming language designed for < strong > robustness< / strong > ,
< strong > optimality< / strong > , and < strong > clarity< / strong > .
< / p >
< p >
< a href = "download/" > Download< / a > |
< a href = "documentation/master/" > Documentation< / a > |
< a href = "https://github.com/zig-lang/zig" > Source Code< / a > |
< a href = "https://github.com/zig-lang/zig/issues" > Bug Tracker< / a > |
< a href = "https://webchat.freenode.net/?channels=%23zig" > IRC< / a > |
< a href = "https://www.patreon.com/andrewrk" > Donate $1/month< / a >
< / p >
< h2 > Feature Highlights< / h2 >
< ul >
< li > Manual memory management. Memory allocation failure is handled correctly. Edge cases matter!< / li >
< li > Zig competes with C instead of depending on it. The Zig Standard Library does not depend on libc.< / li >
< li > Small, simple language. Focus on debugging your application rather than debugging your knowledge of your programming language.< / li >
< li > A fresh take on error handling that resembles what well-written C error handling looks like,
minus the boilerplate and verbosity.< / li >
< li > Debug mode optimizes for fast compilation time and crashing with a stack trace when undefined behavior
< em > would< / em > happen.< / li >
< li > ReleaseFast mode produces heavily optimized code. What other projects call
"Link Time Optimization" Zig does automatically.< / li >
< li > ReleaseSafe mode produces optimized code but keeps safety checks enabled. Disable safety checks in the bottlenecks of your code.< / li >
< li > Generic data structures and functions.< / li >
< li > Compile-time reflection and compile-time code execution.< / li >
< li > Import .h files and directly use C types, variables, and functions.< / li >
< li > Export functions, variables, and types for C code to depend on. Automatically generate .h files.< / li >
< li > Nullable type instead of null pointers.< / li >
< li > Order independent top level declarations.< / li >
< li > Friendly toward package maintainers. Reproducible build, bootstrapping process carefully documented. Issues filed by package maintainers are considered especially important.< / li >
< li > Cross-compiling is a first-class use case.< / li >
< li > No preprocessor. Instead Zig has a few carefully designed features that
provide a way to accomplish things you might do with a preprocessor.< / li >
< / ul >
< h2 id = "reading-material" > Reading Material< / h2 >
< ul >
< li > 2017-10-17 - < a href = "download/0.1.1/release-notes.html" > Zig 0.1.1 Release Notes< / a > < / li >
< li > 2017-07-19 - < a href = "http://tiehuis.github.io/iterative-replacement-of-c-with-zig" > Iterative Replacement of C with Zig< / a > < / li >
< li > 2017-02-16 - < a href = "http://andrewkelley.me/post/a-better-way-to-implement-bit-fields.html" > A Better Way to Implement Bit-Fields< / a > < / li >
< li > 2017-02-13 - < a href = "http://andrewkelley.me/post/zig-already-more-knowable-than-c.html" > Zig: Already More Knowable Than C< / a > < / li >
< li > 2017-01-30 - < a href = "http://andrewkelley.me/post/zig-programming-language-blurs-line-compile-time-run-time.html" > Zig Programming Language Blurs the Line Between Compile-Time and Run-Time< / a > < / li >
< li > 2016-02-08 - < a href = "http://andrewkelley.me/post/intro-to-zig.html" > Introduction to the Zig Programming Language< / a > < / li >
< / ul >
< h2 id = "source-examples" > Source Code Examples< / h2 >
< ul >
< li > < a href = "#hello" > Hello World< / a > < / li >
< li > < a href = "#hello_libc" > Hello World with libc< / a > < / li >
< li > < a href = "#parse" > Parsing Unsigned Integers< / a > < / li >
< li > < a href = "#hashmap" > HashMap with Custom Allocator< / a > < / li >
< li > < a href = "#tetris" > Tetris Clone< / a > < / li >
< li > < a href = "#clashos" > Bare Bones Operating System< / a > < / li >
< li > < a href = "#cat" > Cat Utility< / a > < / li >
< li > < a href = "#multiline-strings" > Multiline String Syntax< / a > < / li >
< li > < a href = "#mersenne" > Mersenne Twister Random Number Generator< / a > < / li >
< / ul >
< h3 id = "hello" > Hello World< / h3 >
2017-11-24 12:06:12 -08:00
< pre > < code class = "zig" > const std = @import("std");
2017-11-07 00:22:27 -08:00
pub fn main() -> %void {
2017-11-24 12:06:12 -08:00
// If this program is run without stdout attached, exit with an error.
var stdout_file = %return std.io.getStdOut();
// If this program encounters pipe failure when printing to stdout, exit
// with an error.
%return stdout_file.write("Hello, world!\n");
2017-11-07 00:22:27 -08:00
}< / code > < / pre >
< p > Build this with:< / p >
< pre > zig build-exe hello.zig< / pre >
< h3 id = "hello_libc" > Hello World with libc< / h3 >
< pre > < code class = "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;
}< / code > < / pre >
< p > Build this with:< / p >
< pre > zig build-exe hello.zig --library c< / pre >
< h3 id = "parse" > Parsing Unsigned Integers< / h3 >
< pre > < code class = "zig" > pub fn parseUnsigned(comptime T: type, buf: []u8, radix: u8) -> %T {
var x: T = 0;
for (buf) |c| {
const digit = %return charToDigit(c, radix);
x = %return mulOverflow(T, x, radix);
x = %return 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) %% 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) %% |err| return err;
const b = parseUnsigned(u32, b_str, 10) %% |err| return err;
return a + b;
}< / code > < / pre >
< h3 id = "hashmap" > HashMap with Custom Allocator< / h3 >
< pre > < code class = "zig" > 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) {
%return 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;
%return 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 = %return 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
}< / code > < / pre >
< h3 id = "tetris" > Tetris Clone< / h3 >
< img src = "tetris-screenshot.png" >
< p >
< a href = "https://github.com/andrewrk/tetris" > Source Code on GitHub< / a >
< / p >
< h3 id = "clashos" > Bare Bones Operating System< / h3 >
< p >
< a href = "https://github.com/andrewrk/clashos" > Source Code on GitHub< / a >
< / p >
< h3 id = "cat" > Cat Utility< / h3 >
< pre > < code class = "zig" > 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;
%return cat_stream(& io.stdin);
} else if (arg[0] == '-') {
return usage(exe);
} else {
var is = io.InStream.open(arg, null) %% |err| {
%%io.stderr.printf("Unable to open file: {}\n", @errorName(err));
return err;
};
defer is.close();
catted_anything = true;
%return cat_stream(& is);
}
}
if (!catted_anything) {
%return cat_stream(& io.stdin);
}
%return 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..]) %% |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]) %% |err| {
%%io.stderr.printf("Unable to write to stdout: {}\n", @errorName(err));
return err;
};
}
}< / code > < / pre >
< h3 id = "multiline-strings" > Multiline String Syntax< / h3 >
< pre > < code class = "zig" > 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;
}< / code > < / pre >
< h3 id = "mersenne" > Mersenne Twister Random Number Generator< / h3 >
< pre > < code class = "zig" > 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());
}
}< / code > < / pre >
< script src = "highlight/highlight.pack.js" > < / script >
< script > hljs . initHighlightingOnLoad ( ) ; < / script >
< / body >
< / html >