Compare commits

..

No commits in common. "main" and "v0.12.0" have entirely different histories.

62 changed files with 382 additions and 1303 deletions

View File

@ -1,5 +0,0 @@
Ziglings is a progressive learning series — each exercise builds on previous ones.
Before opening an issue, please ensure you've followed the path and read the instructions carefully.
Respectful and constructive feedback is always welcome.

6
.gitignore vendored
View File

@ -1,9 +1,5 @@
/.zig-cache/ /zig-cache/
/zig-out/ /zig-out/
/answers/ /answers/
/patches/healed/ /patches/healed/
/output/ /output/
.progress.txt
# Leave this in here for older zig versions
/zig-cache/

View File

@ -1,9 +1,8 @@
steps: steps:
- name: eowyn - name: eowyn
image: ziglings/ziglang:latest image: ziglings/ziglang
pull: true
commands: commands:
- sh ./patches/eowyn.sh - sh ./patches/eowyn.sh
when: when:
event: [pull_request, push, cron] event: [push, cron]
cron: daily* cron: daily*

View File

@ -40,7 +40,6 @@ fit for one reason or another.
## Platforms and Zig Versions ## Platforms and Zig Versions
Because it uses the Zig build system, Ziglings should work Because it uses the Zig build system, Ziglings should work
wherever Zig does. wherever Zig does.
@ -76,15 +75,6 @@ interface. Specifically:
eternal Ziglings contributor glory is yours! eternal Ziglings contributor glory is yours!
## Licence
If you submit your contribution to the repository/project,
you agree that your contribution will be licensed under
the license of this repository/this project.
Please note, it does not change your rights to use your own
contribution for any other purpose.
## The Secrets ## The Secrets
If you want to peek at the secrets, take a look at the `patches/` If you want to peek at the secrets, take a look at the `patches/`

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2021 Dave Gauer, Chris Boesch Copyright (c) 2021 Dave Gauer
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -10,10 +10,11 @@ Those broken programs need your help! (You'll also save the
planet from evil aliens and help some friendly elephants stick planet from evil aliens and help some friendly elephants stick
together, which is very sweet of you.) together, which is very sweet of you.)
This project was initiated by [Dave Gauer](https://ratfactor.com/) and is directly inspired This project was directly inspired by the brilliant and fun
by the brilliant and fun [rustlings](https://github.com/rust-lang/rustlings) project. [rustlings](https://github.com/rust-lang/rustlings)
Indirect inspiration comes from [Ruby Koans](http://rubykoans.com/) and the Little LISPer/Little project for the [Rust](https://www.rust-lang.org/) language.
Schemer series of books. Indirect inspiration comes from [Ruby Koans](http://rubykoans.com/)
and the Little LISPer/Little Schemer series of books.
## Intended Audience ## Intended Audience
@ -45,20 +46,20 @@ Verify the installation and build number of `zig` like so:
``` ```
$ zig version $ zig version
0.15.0-dev.xxxx+xxxxxxxxx 0.12.0-dev.xxxx+xxxxxxxxx
``` ```
Clone this repository with Git: Clone this repository with Git:
``` ```
git clone https://codeberg.org/ziglings/exercises.git ziglings $ git clone https://ziglings.org
cd ziglings $ cd ziglings.org
``` ```
Then run `zig build` and follow the instructions to begin! Then run `zig build` and follow the instructions to begin!
``` ```
zig build $ zig build
``` ```
Note: The output of Ziglings is the unaltered output from the Zig Note: The output of Ziglings is the unaltered output from the Zig
@ -73,8 +74,8 @@ the appropriate tag.
The Zig language is under very active development. In order to be The Zig language is under very active development. In order to be
current, Ziglings tracks **development** builds of the Zig current, Ziglings tracks **development** builds of the Zig
compiler rather than versioned **release** builds. The last compiler rather than versioned **release** builds. The last
stable release was `0.14.1`, but Ziglings needs a dev build with stable release was `0.11.0`, but Ziglings needs a dev build with
pre-release version "0.15.0" and a build number at least as high pre-release version "0.12.0" and a build number at least as high
as that shown in the example version check above. as that shown in the example version check above.
It is likely that you'll download a build which is _greater_ than It is likely that you'll download a build which is _greater_ than
@ -87,14 +88,7 @@ that if you update one, you may need to also update the other.
### Version Changes ### Version Changes
Version-0.15.0-dev.1092 Version-0.12.0-dev.3518
* *2025-07-22* zig 0.15.0-dev.1092 - various changes due to new I/O API, see [#24488](https://github.com/ziglang/zig/pull/24488)
* *2024-09-16* zig 0.14.0-dev.1573 - introduction of labeled switch, see [#21257](https://github.com/ziglang/zig/pull/21257)
* *2024-09-02* zig 0.14.0-dev.1409 - several changes in std.builtin, see [#21225](https://github.com/ziglang/zig/pull/21225)
* *2024-08-04* zig 0.14.0-dev.1224 - several changes in build system, see [#21115](https://github.com/ziglang/zig/pull/21115)
* *2024-08-04* zig 0.14.0-dev.839 - several changes in build system, see [#20580](https://github.com/ziglang/zig/pull/20580), [#20600](https://github.com/ziglang/zig/issues/20600)
* *2024-06-17* zig 0.14.0-dev.42 - changes in `std.mem.split and tokenize` - see [#15579](https://github.com/ziglang/zig/pull/15579)
* *2024-05-29* zig 0.13.0-dev.339 - rework std.Progress - see [#20059](https://github.com/ziglang/zig/pull/20059)
* *2024-03-21* zig 0.12.0-dev.3518 - change to @fieldParentPtr - see [#19470](https://github.com/ziglang/zig/pull/19470) * *2024-03-21* zig 0.12.0-dev.3518 - change to @fieldParentPtr - see [#19470](https://github.com/ziglang/zig/pull/19470)
* *2024-03-21* zig 0.12.0-dev.3397 - rename std.os to std.posix - see [#5019](https://github.com/ziglang/zig/issues/5019) * *2024-03-21* zig 0.12.0-dev.3397 - rename std.os to std.posix - see [#5019](https://github.com/ziglang/zig/issues/5019)
* *2024-03-14* zig 0.12.0-dev.3302 - changes in `std.fmt` - floating-point formatting implementation - see [#19229](https://github.com/ziglang/zig/pull/19229) * *2024-03-14* zig 0.12.0-dev.3302 - changes in `std.fmt` - floating-point formatting implementation - see [#19229](https://github.com/ziglang/zig/pull/19229)
@ -131,18 +125,6 @@ It can be handy to check just a single exercise:
zig build -Dn=19 zig build -Dn=19
``` ```
Or run all exercises, starting from a specific one:
```
zig build -Ds=27
```
Or let Ziglings pick an exercise for you:
```
zig build -Drandom
```
You can also run without checking for correctness: You can also run without checking for correctness:
``` ```
@ -174,11 +156,6 @@ zig build -Dn=19 -l
... ...
``` ```
To reset the progress (have it run all the exercises that have already been completed):
```
zig build -Dreset
```
## What's Covered ## What's Covered
The primary goal for Ziglings is to cover the core Zig language. The primary goal for Ziglings is to cover the core Zig language.
@ -234,10 +211,7 @@ Zig Core Language
* [X] Interfaces * [X] Interfaces
* [X] Bit manipulation * [X] Bit manipulation
* [X] Working with C * [X] Working with C
* [ ] Opaque types (anyopaque)
* [X] Threading * [X] Threading
* [x] Labeled switch
* [x] Vector operations (SIMD)
Zig Standard Library Zig Standard Library

207
build.zig
View File

@ -15,7 +15,7 @@ const print = std.debug.print;
// 1) Getting Started // 1) Getting Started
// 2) Version Changes // 2) Version Changes
comptime { comptime {
const required_zig = "0.15.0-dev.1092"; const required_zig = "0.12.0-dev.3518";
const current_zig = builtin.zig_version; const current_zig = builtin.zig_version;
const min_zig = std.SemanticVersion.parse(required_zig) catch unreachable; const min_zig = std.SemanticVersion.parse(required_zig) catch unreachable;
if (current_zig.order(min_zig) == .lt) { if (current_zig.order(min_zig) == .lt) {
@ -103,8 +103,6 @@ const Mode = enum {
normal, normal,
/// Named build mode: `zig build -Dn=n` /// Named build mode: `zig build -Dn=n`
named, named,
/// Random build mode: `zig build -Drandom`
random,
}; };
pub const logo = pub const logo =
@ -120,24 +118,23 @@ pub const logo =
\\ \\
; ;
const progress_filename = ".progress.txt";
pub fn build(b: *Build) !void { pub fn build(b: *Build) !void {
if (!validate_exercises()) std.process.exit(2); if (!validate_exercises()) std.process.exit(2);
use_color_escapes = false; use_color_escapes = false;
if (std.fs.File.stderr().supportsAnsiEscapeCodes()) { if (std.io.getStdErr().supportsAnsiEscapeCodes()) {
use_color_escapes = true; use_color_escapes = true;
} else if (builtin.os.tag == .windows) { } else if (builtin.os.tag == .windows) {
const w32 = struct { const w32 = struct {
const WINAPI = std.os.windows.WINAPI;
const DWORD = std.os.windows.DWORD; const DWORD = std.os.windows.DWORD;
const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
const STD_ERROR_HANDLE: DWORD = @bitCast(@as(i32, -12)); const STD_ERROR_HANDLE: DWORD = @bitCast(@as(i32, -12));
const GetStdHandle = std.os.windows.kernel32.GetStdHandle; extern "kernel32" fn GetStdHandle(id: DWORD) callconv(WINAPI) ?*anyopaque;
const GetConsoleMode = std.os.windows.kernel32.GetConsoleMode; extern "kernel32" fn GetConsoleMode(console: ?*anyopaque, out_mode: *DWORD) callconv(WINAPI) u32;
const SetConsoleMode = std.os.windows.kernel32.SetConsoleMode; extern "kernel32" fn SetConsoleMode(console: ?*anyopaque, mode: DWORD) callconv(WINAPI) u32;
}; };
const handle = w32.GetStdHandle(w32.STD_ERROR_HANDLE).?; const handle = w32.GetStdHandle(w32.STD_ERROR_HANDLE);
var mode: w32.DWORD = 0; var mode: w32.DWORD = 0;
if (w32.GetConsoleMode(handle, &mode) != 0) { if (w32.GetConsoleMode(handle, &mode) != 0) {
mode |= w32.ENABLE_VIRTUAL_TERMINAL_PROCESSING; mode |= w32.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
@ -161,9 +158,6 @@ pub fn build(b: *Build) !void {
false; false;
const override_healed_path = b.option([]const u8, "healed-path", "Override healed path"); const override_healed_path = b.option([]const u8, "healed-path", "Override healed path");
const exno: ?usize = b.option(usize, "n", "Select exercise"); const exno: ?usize = b.option(usize, "n", "Select exercise");
const rand: ?bool = b.option(bool, "random", "Select random exercise");
const start: ?usize = b.option(usize, "s", "Start at exercise");
const reset: ?bool = b.option(bool, "reset", "Reset exercise progress");
const sep = std.fs.path.sep_str; const sep = std.fs.path.sep_str;
const healed_path = if (override_healed_path) |path| const healed_path = if (override_healed_path) |path|
@ -197,109 +191,18 @@ pub fn build(b: *Build) !void {
return; return;
} }
if (rand) |_| {
// Random build mode: verifies one random exercise.
// like for 'exno' but chooses a random exercise number.
print("work in progress: check a random exercise\n", .{});
var prng = std.Random.DefaultPrng.init(blk: {
var seed: u64 = undefined;
try std.posix.getrandom(std.mem.asBytes(&seed));
break :blk seed;
});
const rnd = prng.random();
const ex = exercises[rnd.intRangeLessThan(usize, 0, exercises.len)];
print("random exercise: {s}\n", .{ex.main_file});
const zigling_step = b.step(
"random",
b.fmt("Check the solution of {s}", .{ex.main_file}),
);
b.default_step = zigling_step;
zigling_step.dependOn(&header_step.step);
const verify_step = ZiglingStep.create(b, ex, work_path, .random);
verify_step.step.dependOn(&header_step.step);
zigling_step.dependOn(&verify_step.step);
return;
}
if (start) |s| {
if (s == 0 or s > exercises.len - 1) {
print("unknown exercise number: {}\n", .{s});
std.process.exit(2);
}
const first = exercises[s - 1];
const ziglings_step = b.step("ziglings", b.fmt("Check ziglings starting with {s}", .{first.main_file}));
b.default_step = ziglings_step;
var prev_step = &header_step.step;
for (exercises[(s - 1)..]) |ex| {
const verify_stepn = ZiglingStep.create(b, ex, work_path, .normal);
verify_stepn.step.dependOn(prev_step);
prev_step = &verify_stepn.step;
}
ziglings_step.dependOn(prev_step);
return;
}
if (reset) |_| {
std.fs.cwd().deleteFile(progress_filename) catch |err| {
switch (err) {
std.fs.Dir.DeleteFileError.FileNotFound => {},
else => {
print("Unable to remove progress file, Error: {}\n", .{err});
return err;
},
}
};
print("Progress reset, {s} removed.\n", .{progress_filename});
std.process.exit(0);
}
// Normal build mode: verifies all exercises according to the recommended // Normal build mode: verifies all exercises according to the recommended
// order. // order.
const ziglings_step = b.step("ziglings", "Check all ziglings"); const ziglings_step = b.step("ziglings", "Check all ziglings");
b.default_step = ziglings_step; b.default_step = ziglings_step;
var prev_step = &header_step.step; var prev_step = &header_step.step;
var starting_exercise: u32 = 0;
if (std.fs.cwd().openFile(progress_filename, .{})) |progress_file| {
defer progress_file.close();
const progress_file_size = try progress_file.getEndPos();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const contents = try progress_file.readToEndAlloc(allocator, progress_file_size);
defer allocator.free(contents);
starting_exercise = try std.fmt.parseInt(u32, contents, 10);
} else |err| {
switch (err) {
std.fs.File.OpenError.FileNotFound => {
// This is fine, may be the first time tests are run or progress have been reset
},
else => {
print("Unable to open {s}: {}\n", .{ progress_filename, err });
return err;
},
}
}
for (exercises) |ex| { for (exercises) |ex| {
if (starting_exercise < ex.number()) {
const verify_stepn = ZiglingStep.create(b, ex, work_path, .normal); const verify_stepn = ZiglingStep.create(b, ex, work_path, .normal);
verify_stepn.step.dependOn(prev_step); verify_stepn.step.dependOn(prev_step);
prev_step = &verify_stepn.step; prev_step = &verify_stepn.step;
} }
}
ziglings_step.dependOn(prev_step); ziglings_step.dependOn(prev_step);
const test_step = b.step("test", "Run all the tests"); const test_step = b.step("test", "Run all the tests");
@ -341,7 +244,7 @@ const ZiglingStep = struct {
return self; return self;
} }
fn make(step: *Step, options: Step.MakeOptions) !void { fn make(step: *Step, prog_node: *std.Progress.Node) !void {
// NOTE: Using exit code 2 will prevent the Zig compiler to print the message: // NOTE: Using exit code 2 will prevent the Zig compiler to print the message:
// "error: the following build command failed with exit code 1:..." // "error: the following build command failed with exit code 1:..."
const self: *ZiglingStep = @alignCast(@fieldParentPtr("step", step)); const self: *ZiglingStep = @alignCast(@fieldParentPtr("step", step));
@ -352,7 +255,7 @@ const ZiglingStep = struct {
return; return;
} }
const exe_path = self.compile(options.progress_node) catch { const exe_path = self.compile(prog_node) catch {
self.printErrors(); self.printErrors();
if (self.exercise.hint) |hint| if (self.exercise.hint) |hint|
@ -362,7 +265,7 @@ const ZiglingStep = struct {
std.process.exit(2); std.process.exit(2);
}; };
self.run(exe_path, options.progress_node) catch { self.run(exe_path.?, prog_node) catch {
self.printErrors(); self.printErrors();
if (self.exercise.hint) |hint| if (self.exercise.hint) |hint|
@ -376,7 +279,7 @@ const ZiglingStep = struct {
self.printErrors(); self.printErrors();
} }
fn run(self: *ZiglingStep, exe_path: []const u8, _: std.Progress.Node) !void { fn run(self: *ZiglingStep, exe_path: []const u8, _: *std.Progress.Node) !void {
resetLine(); resetLine();
print("Checking: {s}\n", .{self.exercise.main_file}); print("Checking: {s}\n", .{self.exercise.main_file});
@ -449,18 +352,6 @@ const ZiglingStep = struct {
, .{ red, reset, exercise_output, red, reset, output, red, reset }); , .{ red, reset, exercise_output, red, reset, output, red, reset });
} }
const progress = try std.fmt.allocPrint(b.allocator, "{d}", .{self.exercise.number()});
defer b.allocator.free(progress);
const file = try std.fs.cwd().createFile(
progress_filename,
.{ .read = true, .truncate = true },
);
defer file.close();
try file.writeAll(progress);
try file.sync();
print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text }); print("{s}PASSED:\n{s}{s}\n\n", .{ green_text, output, reset_text });
} }
@ -484,7 +375,7 @@ const ZiglingStep = struct {
print("{s}PASSED{s}\n\n", .{ green_text, reset_text }); print("{s}PASSED{s}\n\n", .{ green_text, reset_text });
} }
fn compile(self: *ZiglingStep, prog_node: std.Progress.Node) ![]const u8 { fn compile(self: *ZiglingStep, prog_node: *std.Progress.Node) !?[]const u8 {
print("Compiling: {s}\n", .{self.exercise.main_file}); print("Compiling: {s}\n", .{self.exercise.main_file});
const b = self.step.owner; const b = self.step.owner;
@ -515,21 +406,7 @@ const ZiglingStep = struct {
zig_args.append("--listen=-") catch @panic("OOM"); zig_args.append("--listen=-") catch @panic("OOM");
// return try self.step.evalZigProcess(zig_args.items, prog_node);
// NOTE: After many changes in zig build system, we need to create the cache path manually.
// See https://github.com/ziglang/zig/pull/21115
// Maybe there is a better way (in the future).
const exe_dir = try self.step.evalZigProcess(zig_args.items, prog_node, false);
const exe_name = switch (self.exercise.kind) {
.exe => self.exercise.name(),
.@"test" => "test",
};
const sep = std.fs.path.sep_str;
const root_path = exe_dir.?.root_dir.path.?;
const sub_path = exe_dir.?.subPathOrDot();
const exe_path = b.fmt("{s}{s}{s}{s}{s}", .{ root_path, sep, sub_path, sep, exe_name });
return exe_path;
} }
fn help(self: *ZiglingStep) void { fn help(self: *ZiglingStep) void {
@ -540,7 +417,6 @@ const ZiglingStep = struct {
const cmd = switch (self.mode) { const cmd = switch (self.mode) {
.normal => "zig build", .normal => "zig build",
.named => b.fmt("zig build -Dn={s}", .{key}), .named => b.fmt("zig build -Dn={s}", .{key}),
.random => "zig build -Drandom",
}; };
print("\n{s}Edit exercises/{s} and run '{s}' again.{s}\n", .{ print("\n{s}Edit exercises/{s} and run '{s}' again.{s}\n", .{
@ -583,7 +459,7 @@ fn resetLine() void {
pub fn trimLines(allocator: std.mem.Allocator, buf: []const u8) ![]const u8 { pub fn trimLines(allocator: std.mem.Allocator, buf: []const u8) ![]const u8 {
var list = try std.ArrayList(u8).initCapacity(allocator, buf.len); var list = try std.ArrayList(u8).initCapacity(allocator, buf.len);
var iter = std.mem.splitSequence(u8, buf, " \n"); var iter = std.mem.split(u8, buf, " \n");
while (iter.next()) |line| { while (iter.next()) |line| {
// TODO: trimming CR characters is probably not necessary. // TODO: trimming CR characters is probably not necessary.
const data = std.mem.trimRight(u8, line, " \r"); const data = std.mem.trimRight(u8, line, " \r");
@ -618,7 +494,7 @@ const PrintStep = struct {
return self; return self;
} }
fn make(step: *Step, _: Step.MakeOptions) !void { fn make(step: *Step, _: *std.Progress.Node) !void {
const self: *PrintStep = @alignCast(@fieldParentPtr("step", step)); const self: *PrintStep = @alignCast(@fieldParentPtr("step", step));
print("{s}", .{self.message}); print("{s}", .{self.message});
} }
@ -645,7 +521,7 @@ fn validate_exercises() bool {
return false; return false;
} }
var iter = std.mem.splitScalar(u8, ex.output, '\n'); var iter = std.mem.split(u8, ex.output, "\n");
while (iter.next()) |line| { while (iter.next()) |line| {
const output = std.mem.trimRight(u8, line, " \r"); const output = std.mem.trimRight(u8, line, " \r");
if (output.len != line.len) { if (output.len != line.len) {
@ -962,7 +838,7 @@ const exercises = [_]Exercise{
}, },
.{ .{
.main_file = "060_floats.zig", .main_file = "060_floats.zig",
.output = "Shuttle liftoff weight: 2032 metric tons", .output = "Shuttle liftoff weight: 2032092kg",
}, },
.{ .{
.main_file = "061_coercions.zig", .main_file = "061_coercions.zig",
@ -1028,7 +904,6 @@ const exercises = [_]Exercise{
.{ .{
.main_file = "074_comptime9.zig", .main_file = "074_comptime9.zig",
.output = "My llama value is 2.", .output = "My llama value is 2.",
.skip = true,
}, },
.{ .{
.main_file = "075_quiz8.zig", .main_file = "075_quiz8.zig",
@ -1063,7 +938,7 @@ const exercises = [_]Exercise{
.{ .{
.main_file = "082_anonymous_structs3.zig", .main_file = "082_anonymous_structs3.zig",
.output = .output =
\\"0"(bool):true "1"(bool):false "2"(i32):42 "3"(f32):3.141592 \\"0"(bool):true "1"(bool):false "2"(i32):42 "3"(f32):3.141592e0
, ,
.hint = "This one is a challenge! But you have everything you need.", .hint = "This one is a challenge! But you have everything you need.",
}, },
@ -1145,7 +1020,7 @@ const exercises = [_]Exercise{
}, },
.{ .{
.main_file = "097_bit_manipulation.zig", .main_file = "097_bit_manipulation.zig",
.output = "x = 1011; y = 1101", .output = "x = 0; y = 1",
}, },
.{ .{
.main_file = "098_bit_manipulation2.zig", .main_file = "098_bit_manipulation2.zig",
@ -1253,51 +1128,9 @@ const exercises = [_]Exercise{
.main_file = "107_files2.zig", .main_file = "107_files2.zig",
.output = .output =
\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
\\Successfully Read 18 bytes: It's zigling time! \\Successfully Read 18 byte: It's zigling time!
, ,
}, },
.{
.main_file = "108_labeled_switch.zig",
.output = "The pull request has been merged.",
},
.{
.main_file = "109_vectors.zig",
.output =
\\Max difference (old fn): 0.014
\\Max difference (new fn): 0.014
,
},
.{ .main_file = "110_quiz9.zig", .output =
\\Toggle pins with XOR on PORTB
\\-----------------------------
\\ 1100 // (initial state of PORTB)
\\^ 0101 // (bitmask)
\\= 1001
\\
\\ 1100 // (initial state of PORTB)
\\^ 0011 // (bitmask)
\\= 1111
\\
\\Set pins with OR on PORTB
\\-------------------------
\\ 1001 // (initial state of PORTB)
\\| 0100 // (bitmask)
\\= 1101
\\
\\ 1001 // (reset state)
\\| 0100 // (bitmask)
\\= 1101
\\
\\Clear pins with AND and NOT on PORTB
\\------------------------------------
\\ 1110 // (initial state of PORTB)
\\& 1011 // (bitmask)
\\= 1010
\\
\\ 0111 // (reset state)
\\& 1110 // (bitmask)
\\= 0110
},
.{ .{
.main_file = "999_the_end.zig", .main_file = "999_the_end.zig",
.output = .output =

View File

@ -16,12 +16,12 @@ const std = @import("std");
// //
pub fn main() !void { pub fn main() !void {
// We get a Writer for Standard Out so we can print() to it. // We get a Writer for Standard Out so we can print() to it.
var stdout = std.fs.File.stdout().writer(&.{}); const stdout = std.io.getStdOut().writer();
// Unlike std.debug.print(), the Standard Out writer can fail // Unlike std.debug.print(), the Standard Out writer can fail
// with an error. We don't care _what_ the error is, we want // with an error. We don't care _what_ the error is, we want
// to be able to pass it up as a return value of main(). // to be able to pass it up as a return value of main().
// //
// We just learned of a single statement which can accomplish this. // We just learned of a single statement which can accomplish this.
stdout.interface.print("Hello world!\n", .{}); stdout.print("Hello world!\n", .{});
} }

View File

@ -17,7 +17,7 @@
// //
// if (foo) |value| { // if (foo) |value| {
// ... // ...
// } else |err| switch (err) { // } else |err| switch(err) {
// ... // ...
// } // }
// //

View File

@ -10,11 +10,11 @@ const std = @import("std");
const NumError = error{IllegalNumber}; const NumError = error{IllegalNumber};
pub fn main() void { pub fn main() void {
var stdout = std.fs.File.stdout().writer(&.{}); const stdout = std.io.getStdOut().writer();
const my_num: u32 = getNumber(); const my_num: u32 = getNumber();
try stdout.interface.print("my_num={}\n", .{my_num}); try stdout.print("my_num={}\n", .{my_num});
} }
// This function is obviously weird and non-functional. But you will not be changing it for this quiz. // This function is obviously weird and non-functional. But you will not be changing it for this quiz.

View File

@ -5,7 +5,7 @@
// linked to the first elephant. This is because we had NO CONCEPT // linked to the first elephant. This is because we had NO CONCEPT
// of a tail that didn't point to another elephant! // of a tail that didn't point to another elephant!
// //
// We also introduce the handy `.?` shortcut: // We also introduce the handy ".?" shortcut:
// //
// const foo = bar.?; // const foo = bar.?;
// //
@ -13,8 +13,7 @@
// //
// const foo = bar orelse unreachable; // const foo = bar orelse unreachable;
// //
// Check out where we use this shortcut below to change control flow // See if you can find where we use this shortcut below.
// based on if an optional value exists.
// //
// Now let's make those elephant tails optional! // Now let's make those elephant tails optional!
// //
@ -32,25 +31,14 @@ pub fn main() void {
var elephantC = Elephant{ .letter = 'C' }; var elephantC = Elephant{ .letter = 'C' };
// Link the elephants so that each tail "points" to the next. // Link the elephants so that each tail "points" to the next.
linkElephants(&elephantA, &elephantB); elephantA.tail = &elephantB;
linkElephants(&elephantB, &elephantC); elephantB.tail = &elephantC;
// `linkElephants` will stop the program if you try and link an
// elephant that doesn't exist! Uncomment and see what happens.
// const missingElephant: ?*Elephant = null;
// linkElephants(&elephantC, missingElephant);
visitElephants(&elephantA); visitElephants(&elephantA);
std.debug.print("\n", .{}); std.debug.print("\n", .{});
} }
// If e1 and e2 are valid pointers to elephants,
// this function links the elephants so that e1's tail "points" to e2.
fn linkElephants(e1: ?*Elephant, e2: ?*Elephant) void {
e1.?.tail = e2.?;
}
// This function visits all elephants once, starting with the // This function visits all elephants once, starting with the
// first elephant and following the tails to the next elephant. // first elephant and following the tails to the next elephant.
fn visitElephants(first_elephant: *Elephant) void { fn visitElephants(first_elephant: *Elephant) void {
@ -63,9 +51,8 @@ fn visitElephants(first_elephant: *Elephant) void {
// We should stop once we encounter a tail that // We should stop once we encounter a tail that
// does NOT point to another element. What can // does NOT point to another element. What can
// we put here to make that happen? // we put here to make that happen?
if (e.tail == null) ???;
// HINT: We want something similar to what `.?` does, e = e.tail.?;
// but instead of ending the program, we want to exit the loop...
e = e.tail ???
} }
} }

View File

@ -40,7 +40,7 @@
// Okay, you're armed. // Okay, you're armed.
// //
// Now, please zap the alien structs until they're all gone or // Now, please zap the alien structs until they're all gone or
// the Earth will be doomed! // Earth will be doomed!
// //
const std = @import("std"); const std = @import("std");

View File

@ -43,7 +43,7 @@
// //
// "void" is a _type_, not a value. It is the most popular of the // "void" is a _type_, not a value. It is the most popular of the
// Zero Bit Types (those types which take up absolutely no space // Zero Bit Types (those types which take up absolutely no space
// and have only a semantic value). When compiled to executable // and have only a semantic value. When compiled to executable
// code, zero bit types generate no code at all. The above example // code, zero bit types generate no code at all. The above example
// shows a variable foo of type void which is assigned the value // shows a variable foo of type void which is assigned the value
// of an empty expression. It's much more common to see void as // of an empty expression. It's much more common to see void as

View File

@ -190,7 +190,7 @@ const TripItem = union(enum) {
fn printMe(self: TripItem) void { fn printMe(self: TripItem) void {
switch (self) { switch (self) {
// Oops! The hermit forgot how to capture the union values // Oops! The hermit forgot how to capture the union values
// in a switch statement. Please capture each value as // in a switch statement. Please capture both values as
// 'p' so the print statements work! // 'p' so the print statements work!
.place => print("{s}", .{p.name}), .place => print("{s}", .{p.name}),
.path => print("--{}->", .{p.dist}), .path => print("--{}->", .{p.dist}),

View File

@ -41,14 +41,14 @@ pub fn main() void {
// The approximate weight of the Space Shuttle upon liftoff // The approximate weight of the Space Shuttle upon liftoff
// (including boosters and fuel tank) was 4,480,000 lb. // (including boosters and fuel tank) was 4,480,000 lb.
// //
// We'll convert this weight from pounds to metric units at a // We'll convert this weight from pound to kilograms at a
// conversion of 0.453592 kg to the pound. // conversion of 0.453592kg to the pound.
const shuttle_weight: f16 = 0.453592 * 4480e3; const shuttle_weight: f16 = 0.453592 * 4480e6;
// By default, float values are formatted in scientific // By default, float values are formatted in scientific
// notation. Try experimenting with '{d}' and '{d:.3}' to see // notation. Try experimenting with '{d}' and '{d:.3}' to see
// how decimal formatting works. // how decimal formatting works.
print("Shuttle liftoff weight: {d:.0} metric tons\n", .{shuttle_weight}); print("Shuttle liftoff weight: {d:.0}kg\n", .{shuttle_weight});
} }
// Floating further: // Floating further:

View File

@ -94,35 +94,31 @@ pub fn main() void {
print("He has room in his heart for:", .{}); print("He has room in his heart for:", .{});
// A StructFields array // A StructFields array
const fields = @typeInfo(Narcissus).@"struct".fields; const fields = @typeInfo(Narcissus).Struct.fields;
// 'fields' is a slice of StructFields. Here's the declaration: // 'fields' is a slice of StructFields. Here's the declaration:
// //
// pub const StructField = struct { // pub const StructField = struct {
// name: [:0]const u8, // name: []const u8,
// type: type, // type: type,
// default_value_ptr: ?*const anyopaque, // default_value: anytype,
// is_comptime: bool, // is_comptime: bool,
// alignment: comptime_int, // alignment: comptime_int,
//
// defaultValue() ?sf.type // Function that loads the
// // field's default value from
// // `default_value_ptr`
// }; // };
// //
// Please complete these 'if' statements so that the field // Please complete these 'if' statements so that the field
// name will not be printed if the field is of type 'void' // name will not be printed if the field is of type 'void'
// (which is a zero-bit type that takes up no space at all!): // (which is a zero-bit type that takes up no space at all!):
if (fields[0].??? != void) { if (fields[0].??? != void) {
print(" {s}", .{fields[0].name}); print(" {s}", .{@typeInfo(Narcissus).Struct.fields[0].name});
} }
if (fields[1].??? != void) { if (fields[1].??? != void) {
print(" {s}", .{fields[1].name}); print(" {s}", .{@typeInfo(Narcissus).Struct.fields[1].name});
} }
if (fields[2].??? != void) { if (fields[2].??? != void) {
print(" {s}", .{fields[2].name}); print(" {s}", .{@typeInfo(Narcissus).Struct.fields[2].name});
} }
// Yuck, look at all that repeated code above! I don't know // Yuck, look at all that repeated code above! I don't know
@ -140,16 +136,14 @@ pub fn main() void {
// But a change after Zig 0.10.0 added the source file name to the // But a change after Zig 0.10.0 added the source file name to the
// type. "Narcissus" became "065_builtins2.Narcissus". // type. "Narcissus" became "065_builtins2.Narcissus".
// //
// To fix this, we've added this function to strip the filename from // To fix this, I've added this function to strip the filename from
// the front of the type name. (It returns a slice of the type name // the front of the type name in the dumbest way possible. (It returns
// starting at the index + 1 of character ".") // a slice of the type name starting at character 14 (assuming
// single-byte characters).
// //
// We'll be seeing @typeName again in Exercise 070. For now, you can // We'll be seeing @typeName again in Exercise 070. For now, you can
// see that it takes a Type and returns a u8 "string". // see that it takes a Type and returns a u8 "string".
fn maximumNarcissism(myType: anytype) []const u8 { fn maximumNarcissism(myType: anytype) []const u8 {
const indexOf = @import("std").mem.indexOf; // Turn '065_builtins2.Narcissus' into 'Narcissus'
return @typeName(myType)[14..];
// Turn "065_builtins2.Narcissus" into "Narcissus"
const name = @typeName(myType);
return name[indexOf(u8, name, ".").? + 1 ..];
} }

View File

@ -7,7 +7,7 @@
// doing this work. // doing this work.
// //
// An 'inline for' is performed at compile time, allowing you to // An 'inline for' is performed at compile time, allowing you to
// programmatically loop through a series of items in situations // programatically loop through a series of items in situations
// like those mentioned above where a regular runtime 'for' loop // like those mentioned above where a regular runtime 'for' loop
// wouldn't be allowed: // wouldn't be allowed:
// //
@ -38,7 +38,7 @@ pub fn main() void {
// Please use an 'inline for' to implement the block below // Please use an 'inline for' to implement the block below
// for each field in the slice 'fields'! // for each field in the slice 'fields'!
const fields = @typeInfo(Narcissus).@"struct".fields; const fields = @typeInfo(Narcissus).Struct.fields;
??? { ??? {
if (field.type != void) { if (field.type != void) {

View File

@ -78,7 +78,7 @@ fn printSequence(my_seq: anytype) void {
// a switch to handle printing the Array or Pointer fields, // a switch to handle printing the Array or Pointer fields,
// depending on which type of my_seq was passed in: // depending on which type of my_seq was passed in:
switch (my_typeinfo) { switch (my_typeinfo) {
.array => { .Array => {
print("Array:", .{}); print("Array:", .{});
// Loop through the items in my_seq. // Loop through the items in my_seq.
@ -86,7 +86,7 @@ fn printSequence(my_seq: anytype) void {
print("{}", .{s}); print("{}", .{s});
} }
}, },
.pointer => { .Pointer => {
// Check this out - it's pretty cool: // Check this out - it's pretty cool:
const my_sentinel = sentinel(@TypeOf(my_seq)); const my_sentinel = sentinel(@TypeOf(my_seq));
print("Many-item pointer:", .{}); print("Many-item pointer:", .{});

View File

@ -19,12 +19,12 @@
// const MyBar = Bar(); // store the struct type // const MyBar = Bar(); // store the struct type
// const bar = Bar() {}; // create instance of the struct // const bar = Bar() {}; // create instance of the struct
// //
// * The value of @typeName(Bar()) is "<filename>.Bar()". // * The value of @typeName(Bar()) is "Bar()".
// * The value of @typeName(MyBar) is "<filename>.Bar()". // * The value of @typeName(MyBar) is "Bar()".
// * The value of @typeName(@TypeOf(bar)) is "<filename>.Bar()". // * The value of @typeName(@TypeOf(bar)) is "Bar()".
// //
// You can also have completely anonymous structs. The value // You can also have completely anonymous structs. The value
// of @typeName(struct {}) is "<filename>.<function>__struct_<nnn>". // of @typeName(struct {}) is "struct:<position in source>".
// //
const print = @import("std").debug.print; const print = @import("std").debug.print;

View File

@ -75,11 +75,11 @@ fn printTuple(tuple: anytype) void {
// with fields specific to that type. // with fields specific to that type.
// //
// The list of a struct type's fields can be found in // The list of a struct type's fields can be found in
// TypeInfo's @"struct".fields. // TypeInfo's Struct.fields.
// //
// Example: // Example:
// //
// @typeInfo(Circle).@"struct".fields // @typeInfo(Circle).Struct.fields
// //
// This will be an array of StructFields. // This will be an array of StructFields.
const fields = ???; const fields = ???;
@ -95,15 +95,13 @@ fn printTuple(tuple: anytype) void {
// Each 'field' in this loop is one of these: // Each 'field' in this loop is one of these:
// //
// pub const StructField = struct { // pub const StructField = struct {
// name: [:0]const u8, // name: []const u8,
// type: type, // type: type,
// default_value_ptr: ?*const anyopaque, // default_value: anytype,
// is_comptime: bool, // is_comptime: bool,
// alignment: comptime_int, // alignment: comptime_int,
// }; // };
// //
// Note we will learn about 'anyopaque' type later
//
// You'll need this builtin: // You'll need this builtin:
// //
// @field(lhs: anytype, comptime field_name: []const u8) // @field(lhs: anytype, comptime field_name: []const u8)

View File

@ -13,7 +13,7 @@
// How could we do that? A good method is to use the modulo function. // How could we do that? A good method is to use the modulo function.
// But if we write "765.2 % 360", it only works with float values // But if we write "765.2 % 360", it only works with float values
// that are known at compile time. // that are known at compile time.
// In Zig, we would use @mod(a, b) instead. // In Zig, we would use %mod(a, b) instead.
// //
// Let us now assume that we cannot do this in Zig, but only with // Let us now assume that we cannot do this in Zig, but only with
// a C function from the standard library. In the library "math", // a C function from the standard library. In the library "math",

View File

@ -28,7 +28,7 @@
// 0..10 is a range from 0 to 9 // 0..10 is a range from 0 to 9
// 1..4 is a range from 1 to 3 // 1..4 is a range from 1 to 3
// //
// At the moment, ranges in loops are only supported in 'for' loops. // At the moment, ranges are only supported in 'for' loops.
// //
// Perhaps you recall Exercise 13? We were printing a numeric // Perhaps you recall Exercise 13? We were printing a numeric
// sequence like so: // sequence like so:

View File

@ -30,9 +30,9 @@
// std.debug.print("slice_ptr={*}\n", .{slice_ptr}); // std.debug.print("slice_ptr={*}\n", .{slice_ptr});
// } // }
// Instead of a simple integer or a slice with a constant size, // Instead of a simple integer or a constant sized slice, this
// this program requires allocating a slice that is the same size // program requires a slice to be allocated that is the same size as
// as an input array. // an input array.
// Given a series of numbers, take the running average. In other // Given a series of numbers, take the running average. In other
// words, each item N should contain the average of the last N // words, each item N should contain the average of the last N

View File

@ -1,5 +1,5 @@
// //
// Bit manipulation is a very powerful tool, also from Zig. // Bit manipulations is a very powerful tool just also from Zig.
// Since the dawn of the computer age, numerous algorithms have been // Since the dawn of the computer age, numerous algorithms have been
// developed that solve tasks solely by moving, setting, or logically // developed that solve tasks solely by moving, setting, or logically
// combining bits. // combining bits.
@ -8,10 +8,10 @@
// functions where possible. And it is often possible with calculations // functions where possible. And it is often possible with calculations
// based on integers. // based on integers.
// //
// At first glance, it is often not easy to understand what exactly these // Often it is not easy to understand at first glance what exactly these
// algorithms do when only "numbers" in memory areas change outwardly. // algorithms do when only "numbers" in memory areas change outwardly.
// However, it should never be forgotten that the numbers only represent // But it must never be forgotten that the numbers only represent the
// the interpretation of the bit sequences. // interpretation of the bit sequences.
// //
// Quasi the reversed case we have otherwise, namely that we represent // Quasi the reversed case we have otherwise, namely that we represent
// numbers in bit sequences. // numbers in bit sequences.
@ -21,7 +21,7 @@
// Zig provides all the necessary functions to change the bits inside // Zig provides all the necessary functions to change the bits inside
// a variable. It is distinguished whether the bit change leads to an // a variable. It is distinguished whether the bit change leads to an
// overflow or not. The details are in the Zig documentation in section // overflow or not. The details are in the Zig documentation in section
// "Table of Operators". // 10.1 "Table of Operators".
// //
// Here are some examples of how the bits of variables can be changed: // Here are some examples of how the bits of variables can be changed:
// //
@ -71,9 +71,9 @@ const print = std.debug.print;
pub fn main() !void { pub fn main() !void {
// Let us use 1101 and 1011 as values for x and y // As in the example above, we use 1 and 0 as values for x and y
var x: u8 = 0b1101; var x: u8 = 1;
var y: u8 = 0b1011; var y: u8 = 0;
// Now we swap the values of the two variables by doing xor on them // Now we swap the values of the two variables by doing xor on them
x ^= y; x ^= y;
@ -82,7 +82,7 @@ pub fn main() !void {
// What must be written here? // What must be written here?
???; ???;
print("x = {b}; y = {b}\n", .{ x, y }); print("x = {d}; y = {d}\n", .{ x, y });
} }
// This variable swap takes advantage of the fact that the value resulting // This variable swap takes advantage of the fact that the value resulting

View File

@ -1,5 +1,5 @@
// //
// Another useful application for bit manipulation is setting bits as flags. // Another useful practice for bit manipulation is setting bits as flags.
// This is especially useful when processing lists of something and storing // This is especially useful when processing lists of something and storing
// the states of the entries, e.g. a list of numbers and for each prime // the states of the entries, e.g. a list of numbers and for each prime
// number a flag is set. // number a flag is set.
@ -19,9 +19,9 @@
// For example, you could take an array of bool and set the value to 'true' // For example, you could take an array of bool and set the value to 'true'
// for each letter in the order of the alphabet (a=0; b=1; etc.) found in // for each letter in the order of the alphabet (a=0; b=1; etc.) found in
// the sentence. However, this is neither memory efficient nor particularly // the sentence. However, this is neither memory efficient nor particularly
// fast. Instead we choose a simpler approach that is very similar in principle: // fast. Instead we take a simpler way, very similar in principle, we define
// We define a variable with at least 26 bits (e.g. u32) and set the bit for // a variable with at least 26 bits (e.g. u32) and also set the bit for each
// each letter that is found in the corresponding position. // letter found at the corresponding position.
// //
// Zig provides functions for this in the standard library, but we prefer to // Zig provides functions for this in the standard library, but we prefer to
// solve it without these extras, after all we want to learn something. // solve it without these extras, after all we want to learn something.
@ -32,20 +32,20 @@ const print = std.debug.print;
pub fn main() !void { pub fn main() !void {
// let's check the pangram // let's check the pangram
print("Is this a pangram? {}!\n", .{isPangram("The quick brown fox jumps over the lazy dog.")}); print("Is this a pangram? {?}!\n", .{isPangram("The quick brown fox jumps over the lazy dog.")});
} }
fn isPangram(str: []const u8) bool { fn isPangram(str: []const u8) bool {
// first we check if the string has at least 26 characters // first we check if the string has at least 26 characters
if (str.len < 26) return false; if (str.len < 26) return false;
// we use a 32 bit variable of which we need 26 bits // we uses a 32 bit variable of which we need 26 bits
var bits: u32 = 0; var bits: u32 = 0;
// loop about all characters in the string // loop about all characters in the string
for (str) |c| { for (str) |c| {
// if the character is an alphabetical character // if the character is an alphabetical character
if (ascii.isAscii(c) and ascii.isAlphabetic(c)) { if (ascii.isASCII(c) and ascii.isAlphabetic(c)) {
// then we set the bit at the position // then we set the bit at the position
// //
// to do this, we use a little trick: // to do this, we use a little trick:

View File

@ -16,13 +16,13 @@
// Therefore, the comments for the format() function are the only // Therefore, the comments for the format() function are the only
// way to definitively learn how to format strings in Zig: // way to definitively learn how to format strings in Zig:
// //
// https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L33 // https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L29
// //
// Zig already has a very nice selection of formatting options. // Zig already has a very nice selection of formatting options.
// These can be used in different ways, but generally to convert // These can be used in different ways, but typically to convert
// numerical values into various text representations. The results // numerical values into various text representations. The
// can be used for direct output to a terminal or stored for // results can be used for direct output to a terminal or stored
// later use or written to a file. The latter is useful when // for later use or written to a file. The latter is useful when
// large amounts of data are to be processed by other programs. // large amounts of data are to be processed by other programs.
// //
// In Ziglings, we are concerned with the output to the console. // In Ziglings, we are concerned with the output to the console.
@ -45,7 +45,7 @@
// output. Escape sequences can also be written one after the // output. Escape sequences can also be written one after the
// other, e.g. "\n\n" will cause two line feeds. // other, e.g. "\n\n" will cause two line feeds.
// //
// By the way, the result of these escape sequences is passed // By the way, the result of these escape sequences are passed
// directly to the terminal program. Other than translating them // directly to the terminal program. Other than translating them
// into control codes, escape sequences have nothing to do with // into control codes, escape sequences have nothing to do with
// Zig. Zig knows nothing about "line feeds" or "tabs" or // Zig. Zig knows nothing about "line feeds" or "tabs" or
@ -60,7 +60,7 @@
// variety of formatting instructions. It's basically a tiny // variety of formatting instructions. It's basically a tiny
// language of its own. Here's a numeric example: // language of its own. Here's a numeric example:
// //
// print("Catch-0x{x:0>4}.", .{twenty_two}); // print("Catch-{x:0>4}.", .{twenty_two});
// //
// This formatting instruction outputs a hexadecimal number with // This formatting instruction outputs a hexadecimal number with
// leading zeros: // leading zeros:
@ -95,7 +95,7 @@
// ... // ...
// //
// Without string formatting, this would be a more challenging // Without string formatting, this would be a more challenging
// assignment because the number of digits in the numbers varies // assignment because the number of digits in the numbers vary
// from 1 to 3. But formatting can help us with that. // from 1 to 3. But formatting can help us with that.
// //
const std = @import("std"); const std = @import("std");

View File

@ -41,12 +41,12 @@ pub fn main() void {
for (hex_nums, ???) |hn, ???| { for (hex_nums, ???) |hn, ???| {
if (hn != dn) { if (hn != dn) {
print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn }); std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
return; return;
} }
} }
print("Arrays match!\n", .{}); std.debug.print("Arrays match!\n", .{});
} }
// //
// You are perhaps wondering what happens if one of the two lists // You are perhaps wondering what happens if one of the two lists

View File

@ -4,8 +4,8 @@
// one possibility, namely asynchronous processes, in Exercises 84-91. // one possibility, namely asynchronous processes, in Exercises 84-91.
// //
// However, the computing power of the processor is only distributed to // However, the computing power of the processor is only distributed to
// the started and running tasks, which always reaches its limits when // the started tasks, which always reaches its limits when pure computing
// pure computing power is called up. // power is called up.
// //
// For example, in blockchains based on proof of work, the miners have // For example, in blockchains based on proof of work, the miners have
// to find a nonce for a certain character string so that the first m bits // to find a nonce for a certain character string so that the first m bits
@ -106,7 +106,7 @@ pub fn main() !void {
// After the threads have been started, // After the threads have been started,
// they run in parallel and we can still do some work in between. // they run in parallel and we can still do some work in between.
std.Thread.sleep(1500 * std.time.ns_per_ms); std.time.sleep(1500 * std.time.ns_per_ms);
std.debug.print("Some weird stuff, after starting the threads.\n", .{}); std.debug.print("Some weird stuff, after starting the threads.\n", .{});
} }
// After we have left the closed area, we wait until // After we have left the closed area, we wait until
@ -117,12 +117,12 @@ pub fn main() !void {
// This function is started with every thread that we set up. // This function is started with every thread that we set up.
// In our example, we pass the number of the thread as a parameter. // In our example, we pass the number of the thread as a parameter.
fn thread_function(num: usize) !void { fn thread_function(num: usize) !void {
std.Thread.sleep(200 * num * std.time.ns_per_ms); std.time.sleep(200 * num * std.time.ns_per_ms);
std.debug.print("thread {d}: {s}\n", .{ num, "started." }); std.debug.print("thread {d}: {s}\n", .{ num, "started." });
// This timer simulates the work of the thread. // This timer simulates the work of the thread.
const work_time = 3 * ((5 - num % 3) - 2); const work_time = 3 * ((5 - num % 3) - 2);
std.Thread.sleep(work_time * std.time.ns_per_s); std.time.sleep(work_time * std.time.ns_per_s);
std.debug.print("thread {d}: {s}\n", .{ num, "finished." }); std.debug.print("thread {d}: {s}\n", .{ num, "finished." });
} }

View File

@ -1,6 +1,6 @@
// //
// Now that we are familiar with the principles of multi-threading, // Now that we are familiar with the principles of multi threading, we
// let's boldly venture into a practical example from mathematics. // boldly venture into a practical example from mathematics.
// We will determine the circle number PI with sufficient accuracy. // We will determine the circle number PI with sufficient accuracy.
// //
// There are different methods for this, and some of them are several // There are different methods for this, and some of them are several
@ -21,9 +21,9 @@
// There were the Scottish mathematician Gregory and the German // There were the Scottish mathematician Gregory and the German
// mathematician Leibniz, and even a few hundred years earlier the Indian // mathematician Leibniz, and even a few hundred years earlier the Indian
// mathematician Madhava. All of them independently developed the same // mathematician Madhava. All of them independently developed the same
// formula, which was published by Leibniz in 1682 in the journal // formula, which was published by Leibnitz in 1682 in the journal
// "Acta Eruditorum". // "Acta Eruditorum".
// This is why this method has become known as the "Leibniz series", // This is why this method has become known as the "Leibnitz series",
// although the other names are also often used today. // although the other names are also often used today.
// We will not go into the formula and its derivation in detail, but // We will not go into the formula and its derivation in detail, but
// will deal with the series straight away: // will deal with the series straight away:
@ -39,7 +39,7 @@
// in practice. Because either you don't need the precision, or you use a // in practice. Because either you don't need the precision, or you use a
// calculator in which the number is stored as a very precise constant. // calculator in which the number is stored as a very precise constant.
// But at some point this constant was calculated and we are doing the same // But at some point this constant was calculated and we are doing the same
// now. The question at this point is, how many partial values do we have // now.The question at this point is, how many partial values do we have
// to calculate for which accuracy? // to calculate for which accuracy?
// //
// The answer is chewing, to get 8 digits after the decimal point we need // The answer is chewing, to get 8 digits after the decimal point we need
@ -50,7 +50,7 @@
// enough for us for now, because we want to understand the principle and // enough for us for now, because we want to understand the principle and
// nothing more, right? // nothing more, right?
// //
// As we have already discovered, the Leibniz series is a series with a // As we have already discovered, the Leibnitz series is a series with a
// fixed distance of 2 between the individual partial values. This makes // fixed distance of 2 between the individual partial values. This makes
// it easy to apply a simple loop to it, because if we start with n = 1 // it easy to apply a simple loop to it, because if we start with n = 1
// (which is not necessarily useful now) we always have to add 2 in each // (which is not necessarily useful now) we always have to add 2 in each
@ -104,4 +104,4 @@ fn thread_pi(pi: *f64, begin: u64, end: u64) !void {
// to such an extent that seconds become minutes during execution. // to such an extent that seconds become minutes during execution.
// //
// And you should remove the formatting restriction in "print", // And you should remove the formatting restriction in "print",
// otherwise you will not be able to see the additional digits. // otherwise you will not be able to see the additional diggits.

View File

@ -1,22 +1,22 @@
// //
// Until now, we've only been printing our output in the console, // Until now, we've only been printing our output in the console,
// which is good enough for fighting aliens and hermit bookkeeping. // which is good enough for fighting alien and hermit bookkeeping.
// //
// However, many other tasks require some interaction with the file system, // However, many other task require some interaction with the file system,
// which is the underlying structure for organizing files on your computer. // which is the underlying structure for organizing files on your computer.
// //
// The file system provides a hierarchical structure for storing files // The File System provide a hierarchical structure for storing files
// by organizing them into directories, which hold files and other directories, // by organizing files into directories, which hold files and other directories,
// thus creating a tree structure that can be navigated. // thus creating a tree structure for navigating.
// //
// Fortunately, the Zig Standard Library provides a simple API for interacting // Fortunately, zig standard library provide a simple api for interacting
// with the file system, see the detail documentation here: // with the file system, see the detail documentation here
// //
// https://ziglang.org/documentation/master/std/#std.fs // https://ziglang.org/documentation/master/std/#std.fs
// //
// In this exercise, we'll try to: // In this exercise, we'll try to
// - create a new directory, // - create a new directory
// - open a file in the directory, // - open a file in the directory
// - write to the file. // - write to the file.
// //
// import std as always // import std as always
@ -27,43 +27,43 @@ pub fn main() !void {
const cwd: std.fs.Dir = std.fs.cwd(); const cwd: std.fs.Dir = std.fs.cwd();
// then we'll try to make a new directory /output/ // then we'll try to make a new directory /output/
// to store our output files. // to put our output files.
cwd.makeDir("output") catch |e| switch (e) { cwd.makeDir("output") catch |e| switch (e) {
// there is a chance you might want to run this // there are chance you might want to run this
// program more than once and the path might already // program more than once and the path might already
// have been created, so we'll have to handle this error // been created, so we'll have to handle this error
// by doing nothing // by doing nothing
// //
// we want to catch error.PathAlreadyExists and do nothing // we want to catch error.PathAlreadyExists and do nothing
??? => {}, ??? => {},
// if there's any other unexpected error we just propagate it through // if is any other unexpected error we just propagate it through
else => return e, else => return e,
}; };
// then we'll try to open our freshly created directory // then we'll try to open our freshly created directory
// wait a minute... // wait a minute
// opening a directory might fail! // opening a directory might fail!
// what should we do here? // what should we do here?
var output_dir: std.fs.Dir = cwd.openDir("output", .{}); var output_dir: std.fs.Dir = cwd.openDir("output", .{});
defer output_dir.close(); defer output_dir.close();
// we try to open the file `zigling.txt`, // we try to open the file `zigling.txt`,
// and propagate any error up // and propagate the error up if there are any errors
const file: std.fs.File = try output_dir.createFile("zigling.txt", .{}); const file: std.fs.File = try output_dir.createFile("zigling.txt", .{});
// it is a good habit to close a file after you are done with it // it is a good habit to close a file after you are done with
// so that other programs can read it and prevent data corruption // so that other program can read it and prevent data corruption
// but here we are not yet done writing to the file // but here we are not yet done writing to the file
// if only there were a keyword in Zig that // if only there are a keyword in zig that
// allowed you to "defer" code execution to the end of the scope... // allow you "defer" code execute to the end of scope...
file.close(); file.close();
// you are not allowed to move these two lines above the file closing line! // !you are not allow to switch this two lines to before file closing line!
const byte_written = try file.write("It's zigling time!"); const byte_written = try file.write("It's zigling time!");
std.debug.print("Successfully wrote {d} bytes.\n", .{byte_written}); std.debug.print("Successfully wrote {d} bytes.\n", .{byte_written});
} }
// to check if you actually write to the file, you can either, // to check if you actually write to the file, you can either,
// 1. open the file in your text editor, or // 1. open the file on your text editor, or
// 2. print the content of the file in the console with the following command // 2. print the content of the file in the console with command
// >> cat ./output/zigling.txt // >> cat ./output/zigling.txt
// //
// //
@ -86,7 +86,7 @@ pub fn main() !void {
// //
// Question: // Question:
// - what should you do if you want to also read the file after opening it? // - what should you do if you want to also read the file after opening it?
// - go to the documentation of the struct `std.fs.Dir` here: // - go to documentation of the struct `std.fs.Dir` here
// https://ziglang.org/documentation/master/std/#std.fs.Dir // https://ziglang.org/documentation/master/std/#std.fs.Dir
// - can you find a function for opening a file? how about deleting a file? // - can you find a function for opening a file? how about deleting a file?
// - what kind of options can you use with those functions? // - what kind of option can you uses with those function?

View File

@ -4,17 +4,17 @@
// - create a file {project_root}/output/zigling.txt // - create a file {project_root}/output/zigling.txt
// with content `It's zigling time!`(18 byte total) // with content `It's zigling time!`(18 byte total)
// //
// Now there's no point in writing to a file if we don't read from it, am I right? // Now there no point in writing to a file if we don't read from it am I right?
// Let's write a program to read the content of the file that we just created. // let's wrote a program to read the content of the file that we just created.
// //
// I am assuming that you've created the appropriate files for this to work. // I am assuming you've created the appropriate files for this to work.
// //
// Alright, bud, lean in close. Here's the game plan. // Alright, bud, lean in close here's the game plan.
// - First, we open the {project_root}/output/ directory // - First, we open the {project_root}/output/ directory
// - Secondly, we open file `zigling.txt` in that directory // - Secondly, we open file `zigling.txt` in that directory
// - Then, we initialize an array of characters with all letter 'A', and print it // - then, we initalize an array of character with all letter 'A', and print it
// - After that, we read the content of the file into the array // - After that, we read the content of the file to the array
// - Finally, we print out the content we just read // - Finally, we print out the read content
const std = @import("std"); const std = @import("std");
@ -30,23 +30,23 @@ pub fn main() !void {
const file = try output_dir.openFile("zigling.txt", .{}); const file = try output_dir.openFile("zigling.txt", .{});
defer file.close(); defer file.close();
// initialize an array of u8 with all letter 'A' // initalize an array of u8 with all letter 'A'.
// we need to pick the size of the array, 64 seems like a good number // we need to pick a size of the array, 64 seems like a good number.
// fix the initialization below // fix the initalization below
var content = ['A']*64; var content = ['A']*64;
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA` // this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content}); std.debug.print("{s}\n", .{content});
// okay, seems like a threat of violence is not the answer in this case // okay, seem like threat of violence is not the answer in this case
// can you go here to find a way to read the content? // can you go here to find a way to read the content ?
// https://ziglang.org/documentation/master/std/#std.fs.File // https://ziglang.org/documentation/master/std/#std.fs.File
// hint: you might find two answers that are both valid in this case // hint: you might find two answer that are both vaild in this case
const bytes_read = zig_read_the_file_or_i_will_fight_you(&content); const byte_read = zig_read_the_file_or_i_will_fight_you(&content);
// Woah, too screamy. I know you're excited for zigling time but tone it down a bit. // Woah, too screamy, I know you're excited for zigling time but tone it down a bit
// Can you print only what we read from the file? // Can you print only what we read from the file ?
std.debug.print("Successfully Read {d} bytes: {s}\n", .{ std.debug.print("Successfully Read {d} byte: {s}\n", .{
bytes_read, byte_read,
content, // change this line only content, // change this line only
}); });
} }

View File

@ -1,79 +0,0 @@
//
// You've heard of while loops in exercises 011,012,013 and 014
// You've also heard of switch expressions in exercises 030 and 31.
// You've also seen how labels can be used in exercise 063.
//
// By combining while loops and switch statements with continue and break statements
// one can create very concise State Machines.
//
// One such example would be:
//
// pub fn main() void {
// var op: u8 = 1;
// while (true) {
// switch (op) {
// 1 => { op = 2; continue; },
// 2 => { op = 3; continue; },
// 3 => return,
// else => {},
// }
// break;
// }
// std.debug.print("This statement cannot be reached\n", .{});
// }
//
// By combining all we've learned so far, we can now proceed with a labeled switch.
//
// A labeled switch is some extra syntactic sugar, which comes with all sorts of
// candy (performance benefits). Don't believe me? Directly to source https://github.com/ziglang/zig/pull/21367
//
// Here is the previous excerpt implemented as a labeled switch instead:
//
// pub fn main() void {
// foo: switch (@as(u8, 1)) {
// 1 => continue :foo 2,
// 2 => continue :foo 3,
// 3 => return,
// else => {},
// }
// std.debug.print("This statement cannot be reached\n", .{});
// }
//
// The flow of execution on this second case is:
// 1. The switch starts with value '1';
// 2. The switch evaluates to case '1' which in turn uses the continue statement
// to re-evaluate the labeled switch again, now providing the value '2';
// 3. In the case '2' we repeat the same pattern as case '1'
// but instead the value to be evaluated is now '3';
// 4. Finally we get to case '3', where we return from the function as a whole,
// so the debug statement is never executed.
// 5. In this example, since the input does not have clear, exhaustive patterns and
// can essentially be any 'u8' integer, we need to handle all cases not explicitly
// covered by using the 'else => {}' branch as the default case.
//
//
const std = @import("std");
const PullRequestState = enum(u8) {
Draft,
InReview,
Approved,
Rejected,
Merged,
};
pub fn main() void {
// Oh no, your pull request keeps being rejected,
// how would you fix it?
pr: switch (PullRequestState.Draft) {
PullRequestState.Draft => continue :pr PullRequestState.InReview,
PullRequestState.InReview => continue :pr PullRequestState.Rejected,
PullRequestState.Approved => continue :pr PullRequestState.Merged,
PullRequestState.Rejected => {
std.debug.print("The pull request has been rejected.\n", .{});
return;
},
PullRequestState.Merged => break, // Would you know where to break to?
}
std.debug.print("The pull request has been merged.\n", .{});
}

View File

@ -1,147 +0,0 @@
// So far in Ziglings, we've seen how for loops can be used to
// repeat calculations across an array in several ways.
//
// For loops are generally great for this kind of task, but
// sometimes they don't fully utilize the capabilities of the
// CPU.
//
// Most modern CPUs can execute instructions in which SEVERAL
// calculations are performed WITHIN registers at the SAME TIME.
// These are known as "single instruction, multiple data" (SIMD)
// instructions. SIMD instructions can make code significantly
// more performant.
//
// To see why, imagine we have a program in which we take the
// square root of four (changing) f32 floats.
//
// A simple compiler would take the program and produce machine code
// which calculates each square root sequentially. Most registers on
// modern CPUs have 64 bits, so we could imagine that each float moves
// into a 64-bit register, and the following happens four times:
//
// 32 bits 32 bits
// +-------------------+
// register | 0 | x |
// +-------------------+
//
// |
// [SQRT instruction]
// V
//
// +-------------------+
// | 0 | sqrt(x) |
// +-------------------+
//
// Notice that half of the register contains blank data to which
// nothing happened. What a waste! What if we were able to use
// that space instead? This is the idea at the core of SIMD.
//
// Most modern CPUs contain specialized registers with at least 128 bits
// for performing SIMD instructions. On a machine with 128-bit SIMD
// registers, a smart compiler would probably NOT issue four sqrt
// instructions as above, but instead pack the floats into a single
// 128-bit register, then execute a single "packed" sqrt
// instruction to do ALL the square root calculations at once.
//
// For example:
//
//
// 32 bits 32 bits 32 bits 32 bits
// +---------------------------------------+
// register | 4.0 | 9.0 | 25.0 | 49.0 |
// +---------------------------------------+
//
// |
// [SIMD SQRT instruction]
// V
//
// +---------------------------------------+
// register | 2.0 | 3.0 | 5.0 | 7.0 |
// +---------------------------------------+
//
// Pretty cool, right?
//
// Code with SIMD instructions is usually more performant than code
// without SIMD instructions. Zig cares a lot about performance,
// so it has built-in support for SIMD! It has a data structure that
// directly supports SIMD instructions:
//
// +-----------+
// | Vectors |
// +-----------+
//
// Operations performed on vectors in Zig will be done in parallel using
// SIMD instructions, whenever possible.
//
// Defining vectors in Zig is straightforwards. No library import is needed.
const v1 = @Vector(3, i32){ 1, 10, 100 };
const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };
// Vectors support the same builtin operators as their underlying base types.
const v3 = v1 + v1; // { 2, 20, 200};
const v4 = v2 * v2; // { 4.0, 9.0, 25.0};
// Intrinsics that apply to base types usually extend to vectors.
const v5: @Vector(3, f32) = @floatFromInt(v3); // { 2.0, 20.0, 200.0}
const v6 = v4 - v5; // { 2.0, -11.0, -175.0}
const v7 = @abs(v6); // { 2.0, 11.0, 175.0}
// We can make constant vectors, and reduce vectors.
const v8: @Vector(4, u8) = @splat(2); // { 2, 2, 2, 2}
const v8_sum = @reduce(.Add, v8); // 8
const v8_min = @reduce(.Min, v8); // 2
// Fixed-length arrays can be automatically assigned to vectors (and vice-versa).
const single_digit_primes = [4]i8{ 2, 3, 5, 7 };
const prime_vector: @Vector(4, i8) = single_digit_primes;
// Now let's use vectors to simplify and optimize some code!
//
// Ewa is writing a program in which they frequently want to compare
// two lists of four f32s. Ewa expects the lists to be similar, and
// wants to determine the largest pairwise difference between the lists.
//
// Ewa wrote the following function to figure this out.
fn calcMaxPairwiseDiffOld(list1: [4]f32, list2: [4]f32) f32 {
var max_diff: f32 = 0;
for (list1, list2) |n1, n2| {
const abs_diff = @abs(n1 - n2);
if (abs_diff > max_diff) {
max_diff = abs_diff;
}
}
return max_diff;
}
// Ewa heard about vectors in Zig, and started writing a new vector
// version of the function, but has got stuck!
//
// Help Ewa finish the vector version! The examples above should help.
const Vec4 = @Vector(4, f32);
fn calcMaxPairwiseDiffNew(a: Vec4, b: Vec4) f32 {
const abs_diff_vec = ???;
const max_diff = @reduce(???, abs_diff_vec);
return max_diff;
}
// Quite the simplification! We could even write the function in one line
// and it would still be readable.
//
// Since the entire function is now expressed in terms of vector operations,
// the Zig compiler will easily be able to compile it down to machine code
// which utilizes the all-powerful SIMD instructions and does a lot of the
// computation in parallel.
const std = @import("std");
const print = std.debug.print;
pub fn main() void {
const l1 = [4]f32{ 3.141, 2.718, 0.577, 1.000 };
const l2 = [4]f32{ 3.154, 2.707, 0.591, 0.993 };
const mpd_old = calcMaxPairwiseDiffOld(l1, l2);
const mpd_new = calcMaxPairwiseDiffNew(l1, l2);
print("Max difference (old fn): {d: >5.3}\n", .{mpd_old});
print("Max difference (new fn): {d: >5.3}\n", .{mpd_new});
}

View File

@ -1,484 +0,0 @@
// ----------------------------------------------------------------------------
// Quiz Time: Toggling, Setting, and Clearing Bits
// ----------------------------------------------------------------------------
//
// Another exciting thing about Zig is its suitability for embedded
// programming. Your Zig code doesn't have to remain on your laptop; you can
// also deploy your code to microcontrollers! This means you can write Zig to
// drive your next robot or greenhouse climate control system! Ready to enter
// the exciting world of embedded programming? Let's get started!
//
// ----------------------------------------------------------------------------
// Some Background
// ----------------------------------------------------------------------------
//
// A common activity in microcontroller programming is setting and clearing
// bits on input and output pins. This lets you control LEDs, sensors, motors
// and more! In a previous exercise (097_bit_manipulation.zig) you learned how
// to swap two bytes using the ^ (XOR - exclusive or) operator. This quiz will
// test your knowledge of bit manipulation in Zig while giving you a taste of
// what it's like to control registers in a real microcontroller. Included at
// the end are some helper functions that demonstrate how we might make our
// code a little more readable.
//
// Below is a pinout diagram for the famous ATmega328 AVR microcontroller used
// as the primary microchip on popular microcontroller platforms like the
// Arduino UNO.
//
// ============ PINOUT DIAGRAM FOR ATMEGA328 MICROCONTROLLER ============
// _____ _____
// | U |
// (RESET) PC6 --| 1 28 |-- PC5
// PD0 --| 2 27 |-- PC4
// PD1 --| 3 26 |-- PC3
// PD2 --| 4 25 |-- PC2
// PD3 --| 5 24 |-- PC1
// PD4 --| 6 23 |-- PC0
// VCC --| 7 22 |-- GND
// GND --| 8 21 |-- AREF
// |-- PB6 --| 9 20 |-- AVCC
// |-- PB7 --| 10 19 |-- PB5 --|
// | PD5 --| 11 18 |-- PB4 --|
// | PD6 --| 12 17 |-- PB3 --|
// | PD7 --| 13 16 |-- PB2 --|
// |-- PB0 --| 14 15 |-- PB1 --|
// | |___________| |
// \_______________________________/
// |
// PORTB
//
// Drawing inspiration from this diagram, we'll use the pins for PORTB as our
// mental model for this quiz on bit manipulation. It should be noted that
// in the following problems we are using ordinary variables, one of which we
// have named PORTB, to simulate modifying the bits of real hardware registers.
// But in actual microcontroller code, PORTB would be defined something like
// this:
// pub const PORTB = @as(*volatile u8, @ptrFromInt(0x25));
//
// This lets the compiler know not to make any optimizations to PORTB so that
// the IO pins are properly mapped to our code.
//
// NOTE : To keep things simple, the following problems are given using type
// u4, so applying the output to PORTB would only affect the lower four pins
// PB0..PB3. Of course, there is nothing to prevent you from swapping the u4
// with a u8 so you can control all 8 of PORTB's IO pins.
const std = @import("std");
const print = std.debug.print;
const testing = std.testing;
pub fn main() !void {
var PORTB: u4 = 0b0000; // only 4 bits wide for simplicity
// ------------------------------------------------------------------------
// Quiz
// ------------------------------------------------------------------------
// See if you can solve the following problems. The last two problems throw
// you a bit of a curve ball. Try solving them on your own. If you need
// help, scroll to the bottom of main to see some in depth explanations on
// toggling, setting, and clearing bits in Zig.
print("Toggle pins with XOR on PORTB\n", .{});
print("-----------------------------\n", .{});
PORTB = 0b1100;
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("^ {b:0>4} // (bitmask)\n", .{0b0101});
PORTB ^= (1 << 1) | (1 << 0); // What's wrong here?
checkAnswer(0b1001, PORTB);
newline();
PORTB = 0b1100;
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("^ {b:0>4} // (bitmask)\n", .{0b0011});
PORTB ^= (1 << 1) & (1 << 0); // What's wrong here?
checkAnswer(0b1111, PORTB);
newline();
print("Set pins with OR on PORTB\n", .{});
print("-------------------------\n", .{});
PORTB = 0b1001; // reset PORTB
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("| {b:0>4} // (bitmask)\n", .{0b0100});
PORTB = PORTB ??? (1 << 2); // What's missing here?
checkAnswer(0b1101, PORTB);
newline();
PORTB = 0b1001; // reset PORTB
print(" {b:0>4} // (reset state)\n", .{PORTB});
print("| {b:0>4} // (bitmask)\n", .{0b0100});
PORTB ??? (1 << 2); // What's missing here?
checkAnswer(0b1101, PORTB);
newline();
print("Clear pins with AND and NOT on PORTB\n", .{});
print("------------------------------------\n", .{});
PORTB = 0b1110; // reset PORTB
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("& {b:0>4} // (bitmask)\n", .{0b1011});
PORTB = PORTB & ???@as(u4, 1 << 2); // What character is missing here?
checkAnswer(0b1010, PORTB);
newline();
PORTB = 0b0111; // reset PORTB
print(" {b:0>4} // (reset state)\n", .{PORTB});
print("& {b:0>4} // (bitmask)\n", .{0b1110});
PORTB &= ~(1 << 0); // What's missing here?
checkAnswer(0b0110, PORTB);
newline();
newline();
}
// ************************************************************************
// IN-DEPTH EXPLANATIONS BELOW
// ************************************************************************
//
//
//
//
//
//
//
//
//
//
//
// ------------------------------------------------------------------------
// Toggling bits with XOR:
// ------------------------------------------------------------------------
// XOR stands for "exclusive or". We can toggle bits with the ^ (XOR)
// bitwise operator, like so:
//
//
// In order to output a 1, the logic of an XOR operation requires that the
// two input bits are of different values. Therefore, 0 ^ 1 and 1 ^ 0 will
// both yield a 1 but 0 ^ 0 and 1 ^ 1 will output 0. XOR's unique behavior
// of outputting a 0 when both inputs are 1s is what makes it different from
// the OR operator; it also gives us the ability to toggle bits by putting
// 1s into our bitmask.
//
// - 1s in our bitmask operand, can be thought of as causing the
// corresponding bits in the other operand to flip to the opposite value.
// - 0s cause no change.
//
// The 0s in our bitmask preserve these values
// -XOR op- ---expanded--- in the output.
// _______________/
// / /
// 1100 1 1 0 0
// ^ 0101 0 1 0 1 (bitmask)
// ------ - - - -
// = 1001 1 0 0 1 <- This bit was already cleared.
// \_______\
// \
// We can think of these bits having flipped
// because of the presence of 1s in those columns
// of our bitmask.
//
// Now let's take a look at setting bits with the | operator.
//
//
//
//
//
// ------------------------------------------------------------------------
// Setting bits with OR:
// ------------------------------------------------------------------------
// We can set bits on PORTB with the | (OR) operator, like so:
//
// var PORTB: u4 = 0b1001;
// PORTB = PORTB | 0b0010;
// print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011
//
// -OR op- ---expanded---
// _ Set only this bit.
// /
// 1001 1 0 0 1
// | 0010 0 0 1 0 (bitmask)
// ------ - - - -
// = 1011 1 0 1 1
// \___\_______\
// \
// These bits remain untouched because OR-ing with
// a 0 effects no change.
//
// ------------------------------------------------------------------------
// To create a bitmask like 0b0010 used above:
//
// 1. First, shift the value 1 over one place with the bitwise << (shift
// left) operator as indicated below:
// 1 << 0 -> 0001
// 1 << 1 -> 0010 <-- Shift 1 one place to the left
// 1 << 2 -> 0100
// 1 << 3 -> 1000
//
// This allows us to rewrite the above code like this:
//
// var PORTB: u4 = 0b1001;
// PORTB = PORTB | (1 << 1);
// print("PORTB: {b:0>4}\n", .{PORTB}); // output: 1011
//
// Finally, as in the C language, Zig allows us to use the |= operator, so
// we can rewrite our code again in an even more compact and idiomatic
// form: PORTB |= (1 << 1)
// So now we've covered how to toggle and set bits. What about clearing
// them? Well, this is where Zig throws us a curve ball. Don't worry we'll
// go through it step by step.
//
//
//
//
//
// ------------------------------------------------------------------------
// Clearing bits with AND and NOT:
// ------------------------------------------------------------------------
// We can clear bits with the & (AND) bitwise operator, like so:
// PORTB = 0b1110; // reset PORTB
// PORTB = PORTB & 0b1011;
// print("PORTB: {b:0>4}\n", .{PORTB}); // output -> 1010
//
// - 0s clear bits when used in conjunction with a bitwise AND.
// - 1s do nothing, thus preserving the original bits.
//
// -AND op- ---expanded---
// __________ Clear only this bit.
// /
// 1110 1 1 1 0
// & 1011 1 0 1 1 (bitmask)
// ------ - - - -
// = 1010 1 0 1 0 <- This bit was already cleared.
// \_______\
// \
// These bits remain untouched because AND-ing with a
// 1 preserves the original bit value whether 0 or 1.
//
// ------------------------------------------------------------------------
// We can use the ~ (NOT) operator to easily create a bitmask like 1011:
//
// 1. First, shift the value 1 over two places with the bit-wise << (shift
// left) operator as indicated below:
// 1 << 0 -> 0001
// 1 << 1 -> 0010
// 1 << 2 -> 0100 <- The 1 has been shifted two places to the left
// 1 << 3 -> 1000
//
// 2. The second step in creating our bitmask is to invert the bits
// ~0100 -> 1011
// in C we would write this as:
// ~(1 << 2) -> 1011
//
// But if we try to compile ~(1 << 2) in Zig, we'll get an error:
// unable to perform binary not operation on type 'comptime_int'
//
// Before Zig can invert our bits, it needs to know the number of
// bits it's being asked to invert.
//
// We do this with the @as (cast as) built-in like this:
// @as(u4, 1 << 2) -> 0100
//
// Finally, we can invert our new mask by placing the NOT ~ operator
// before our expression, like this:
// ~@as(u4, 1 << 2) -> 1011
//
// If you are offput by the fact that you can't simply invert bits like
// you can in languages such as C without casting to a particular size
// of integer, you're not alone. However, this is actually another
// instance where Zig is really helpful because it protects you from
// difficult to debug integer overflow bugs that can have you tearing
// your hair out. In the interest of keeping things sane, Zig requires
// you simply to tell it the size of number you are inverting. In the
// words of Andrew Kelley, "If you want to invert the bits of an
// integer, zig has to know how many bits there are."
//
// For more insight into the Zig team's position on why the language
// takes the approach it does with the ~ operator, take a look at
// Andrew's comments on the following github issue:
// https://github.com/ziglang/zig/issues/1382#issuecomment-414459529
//
// Whew, so after all that what we end up with is:
// PORTB = PORTB & ~@as(u4, 1 << 2);
//
// We can shorten this with the &= combined AND and assignment operator,
// which applies the AND operator on PORTB and then reassigns PORTB. Here's
// what that looks like:
// PORTB &= ~@as(u4, 1 << 2);
//
// ------------------------------------------------------------------------
// Conclusion
// ------------------------------------------------------------------------
//
// While the examples in this quiz have used only 4-bit wide variables,
// working with 8 bits is no different. Here's an example where we set
// every other bit beginning with the two's place:
// var PORTD: u8 = 0b0000_0000;
// print("PORTD: {b:0>8}\n", .{PORTD});
// PORTD |= (1 << 1);
// PORTD = setBit(u8, PORTD, 3);
// PORTD |= (1 << 5) | (1 << 7);
// print("PORTD: {b:0>8} // set every other bit\n", .{PORTD});
// PORTD = ~PORTD;
// print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD});
// newline();
//
// // Here we clear every other bit beginning with the two's place.
//
// PORTD = 0b1111_1111;
// print("PORTD: {b:0>8}\n", .{PORTD});
// PORTD &= ~@as(u8, 1 << 1);
// PORTD = clearBit(u8, PORTD, 3);
// PORTD &= ~@as(u8, (1 << 5) | (1 << 7));
// print("PORTD: {b:0>8} // clear every other bit\n", .{PORTD});
// PORTD = ~PORTD;
// print("PORTD: {b:0>8} // bits flipped with NOT (~)\n", .{PORTD});
// newline();
// ----------------------------------------------------------------------------
// Here are some helper functions for manipulating bits
// ----------------------------------------------------------------------------
// Functions for setting, clearing, and toggling a single bit
fn setBit(comptime T: type, byte: T, comptime bit_pos: T) !T {
return byte | (1 << bit_pos);
}
test "setBit" {
try testing.expectEqual(setBit(u8, 0b0000_0000, 3), 0b0000_1000);
}
fn clearBit(comptime T: type, byte: T, comptime bit_pos: T) T {
return byte & ~@as(T, (1 << bit_pos));
}
test "clearBit" {
try testing.expectEqual(clearBit(u8, 0b1111_1111, 0), 0b1111_1110);
}
fn toggleBit(comptime T: type, byte: T, comptime bit_pos: T) T {
return byte ^ (1 << bit_pos);
}
test "toggleBit" {
var byte = toggleBit(u8, 0b0000_0000, 0);
try testing.expectEqual(byte, 0b0000_0001);
byte = toggleBit(u8, byte, 0);
try testing.expectEqual(byte, 0b0000_0000);
}
// ----------------------------------------------------------------------------
// Some additional functions for setting, clearing, and toggling multiple bits
// at once with a tuple because, hey, why not?
// ----------------------------------------------------------------------------
//
fn createBitmask(comptime T: type, comptime bits: anytype) !T {
comptime var bitmask: T = 0;
inline for (bits) |bit| {
if (bit >= @bitSizeOf(T)) return error.BitPosTooLarge;
if (bit < 0) return error.BitPosTooSmall;
bitmask |= (1 << bit);
}
return bitmask;
}
test "creating bitmasks from a tuple" {
try testing.expectEqual(createBitmask(u8, .{0}), 0b0000_0001);
try testing.expectEqual(createBitmask(u8, .{1}), 0b0000_0010);
try testing.expectEqual(createBitmask(u8, .{2}), 0b0000_0100);
try testing.expectEqual(createBitmask(u8, .{3}), 0b0000_1000);
//
try testing.expectEqual(createBitmask(u8, .{ 0, 4 }), 0b0001_0001);
try testing.expectEqual(createBitmask(u8, .{ 1, 5 }), 0b0010_0010);
try testing.expectEqual(createBitmask(u8, .{ 2, 6 }), 0b0100_0100);
try testing.expectEqual(createBitmask(u8, .{ 3, 7 }), 0b1000_1000);
try testing.expectError(error.BitPosTooLarge, createBitmask(u4, .{4}));
}
fn setBits(byte: u8, bits: anytype) !u8 {
const bitmask = try createBitmask(u8, bits);
return byte | bitmask;
}
test "setBits" {
try testing.expectEqual(setBits(0b0000_0000, .{0}), 0b0000_0001);
try testing.expectEqual(setBits(0b0000_0000, .{7}), 0b1000_0000);
try testing.expectEqual(setBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
try testing.expectEqual(setBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
try testing.expectEqual(setBits(0b0000_0000, .{ 2, 3, 4, 5 }), 0b0011_1100);
try testing.expectError(error.BitPosTooLarge, setBits(0b1111_1111, .{8}));
try testing.expectError(error.BitPosTooSmall, setBits(0b1111_1111, .{-1}));
}
fn clearBits(comptime byte: u8, comptime bits: anytype) !u8 {
const bitmask: u8 = try createBitmask(u8, bits);
return byte & ~@as(u8, bitmask);
}
test "clearBits" {
try testing.expectEqual(clearBits(0b1111_1111, .{0}), 0b1111_1110);
try testing.expectEqual(clearBits(0b1111_1111, .{7}), 0b0111_1111);
try testing.expectEqual(clearBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
try testing.expectEqual(clearBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
try testing.expectEqual(clearBits(0b1111_1111, .{ 0, 1, 6, 7 }), 0b0011_1100);
try testing.expectError(error.BitPosTooLarge, clearBits(0b1111_1111, .{8}));
try testing.expectError(error.BitPosTooSmall, clearBits(0b1111_1111, .{-1}));
}
fn toggleBits(comptime byte: u8, comptime bits: anytype) !u8 {
const bitmask = try createBitmask(u8, bits);
return byte ^ bitmask;
}
test "toggleBits" {
try testing.expectEqual(toggleBits(0b0000_0000, .{0}), 0b0000_0001);
try testing.expectEqual(toggleBits(0b0000_0000, .{7}), 0b1000_0000);
try testing.expectEqual(toggleBits(0b1111_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b000_0000);
try testing.expectEqual(toggleBits(0b0000_0000, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_1111);
try testing.expectEqual(toggleBits(0b0000_1111, .{ 0, 1, 2, 3, 4, 5, 6, 7 }), 0b1111_0000);
try testing.expectEqual(toggleBits(0b0000_1111, .{ 0, 1, 2, 3 }), 0b0000_0000);
try testing.expectEqual(toggleBits(0b0000_0000, .{ 0, 2, 4, 6 }), 0b0101_0101);
try testing.expectError(error.BitPosTooLarge, toggleBits(0b1111_1111, .{8}));
try testing.expectError(error.BitPosTooSmall, toggleBits(0b1111_1111, .{-1}));
}
// ----------------------------------------------------------------------------
// Utility functions
// ----------------------------------------------------------------------------
fn newline() void {
print("\n", .{});
}
fn checkAnswer(expected: u4, answer: u4) void {
if (expected != answer) {
print("*************************************************************\n", .{});
print("= {b:0>4} <- INCORRECT! THE EXPECTED OUTPUT IS {b:0>4}\n", .{ answer, expected });
print("*************************************************************\n", .{});
} else {
print("= {b:0>4}", .{answer});
}
newline();
}

View File

@ -12,12 +12,6 @@
# using the patches in this directory and convey them # using the patches in this directory and convey them
# to convalesce in the healed directory. # to convalesce in the healed directory.
# #
delete_progress() {
progress_file=".progress.txt"
if [ -f $progress_file ]; then
rm $progress_file
fi
}
set -e set -e
# We check ourselves before we wreck ourselves. # We check ourselves before we wreck ourselves.
@ -29,12 +23,9 @@ fi
# Which version we have? # Which version we have?
echo "Zig version" $(zig version) echo "Zig version" $(zig version)
echo "Eowyn version 25.1.9, let's try our magic power." echo "Eowyn version 23.10.5.1, let's try our magic power."
echo "" echo ""
# Remove progress file
delete_progress
# Create directory of healing if it doesn't already exist. # Create directory of healing if it doesn't already exist.
mkdir -p patches/healed mkdir -p patches/healed
@ -63,6 +54,3 @@ zig fmt --check patches/healed
# Test the healed exercises. May the compiler have mercy upon us. # Test the healed exercises. May the compiler have mercy upon us.
zig build -Dhealed zig build -Dhealed
# Remove progress file again
delete_progress

View File

@ -1,9 +1,9 @@
--- exercises/026_hello2.zig 2025-07-22 09:55:51.337832401 +0200 --- exercises/026_hello2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/026_hello2.zig 2025-07-22 10:00:11.233348058 +0200 +++ answers/026_hello2.zig 2023-10-05 20:04:06.959431737 +0200
@@ -23,5 +23,5 @@ @@ -23,5 +23,5 @@
// to be able to pass it up as a return value of main(). // to be able to pass it up as a return value of main().
// //
// We just learned of a single statement which can accomplish this. // We just learned of a single statement which can accomplish this.
- stdout.interface.print("Hello world!\n", .{}); - stdout.print("Hello world!\n", .{});
+ try stdout.interface.print("Hello world!\n", .{}); + try stdout.print("Hello world!\n", .{});
} }

View File

@ -1,15 +1,15 @@
--- exercises/034_quiz4.zig 2025-07-22 09:55:51.337832401 +0200 --- exercises/034_quiz4.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/034_quiz4.zig 2025-07-22 10:05:08.320323184 +0200 +++ answers/034_quiz4.zig 2023-10-05 20:04:06.996099091 +0200
@@ -9,10 +9,10 @@ @@ -9,10 +9,10 @@
const NumError = error{IllegalNumber}; const NumError = error{IllegalNumber};
-pub fn main() void { -pub fn main() void {
+pub fn main() !void { +pub fn main() !void {
var stdout = std.fs.File.stdout().writer(&.{}); const stdout = std.io.getStdOut().writer();
- const my_num: u32 = getNumber(); - const my_num: u32 = getNumber();
+ const my_num: u32 = try getNumber(); + const my_num: u32 = try getNumber();
try stdout.interface.print("my_num={}\n", .{my_num}); try stdout.print("my_num={}\n", .{my_num});
} }

View File

@ -1,19 +1,20 @@
--- exercises/046_optionals2.zig 2024-11-08 22:46:25.592875338 +0100 --- exercises/046_optionals2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/046_optionals2.zig 2024-11-08 22:46:20.699447951 +0100 +++ answers/046_optionals2.zig 2023-10-05 20:04:07.049433424 +0200
@@ -22,7 +22,7 @@ @@ -21,7 +21,7 @@
const Elephant = struct { const Elephant = struct {
letter: u8, letter: u8,
- tail: *Elephant = null, // Hmm... tail needs something... - tail: *Elephant = null, // Hmm... tail needs something...
+ tail: ?*Elephant = null, // Hmm... tail needs something... + tail: ?*Elephant = null, // <---- make this optional!
visited: bool = false, visited: bool = false,
}; };
@@ -66,6 +66,6 @@ @@ -51,7 +51,7 @@
// We should stop once we encounter a tail that
// does NOT point to another element. What can
// we put here to make that happen?
- if (e.tail == null) ???;
+ if (e.tail == null) break;
// HINT: We want something similar to what `.?` does, e = e.tail.?;
// but instead of ending the program, we want to exit the loop...
- e = e.tail ???
+ e = e.tail orelse break;
}
} }

View File

@ -1,8 +1,8 @@
--- exercises/058_quiz7.zig 2024-10-28 09:06:49.448505460 +0100 --- exercises/058_quiz7.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/058_quiz7.zig 2024-10-28 09:35:14.631932322 +0100 +++ answers/058_quiz7.zig 2023-10-05 20:04:07.106101152 +0200
@@ -192,8 +192,8 @@ @@ -192,8 +192,8 @@
// Oops! The hermit forgot how to capture the union values // Oops! The hermit forgot how to capture the union values
// in a switch statement. Please capture each value as // in a switch statement. Please capture both values as
// 'p' so the print statements work! // 'p' so the print statements work!
- .place => print("{s}", .{p.name}), - .place => print("{s}", .{p.name}),
- .path => print("--{}->", .{p.dist}), - .path => print("--{}->", .{p.dist}),

View File

@ -1,11 +1,11 @@
--- exercises/060_floats.zig 2025-03-03 20:23:40.255443963 +0400 --- exercises/060_floats.zig 2023-11-06 19:45:03.609687304 +0100
+++ answers/060_floats.zig 2025-03-03 20:29:58.554854977 +0400 +++ answers/060_floats.zig 2023-11-06 19:44:49.249419994 +0100
@@ -43,7 +43,7 @@ @@ -43,7 +43,7 @@
// //
// We'll convert this weight from pounds to metric units at a // We'll convert this weight from pound to kilograms at a
// conversion of 0.453592 kg to the pound. // conversion of 0.453592kg to the pound.
- const shuttle_weight: f16 = 0.453592 * 4480e3; - const shuttle_weight: f16 = 0.453592 * 4480e6;
+ const shuttle_weight: f32 = 0.453592 * 4.480e3; + const shuttle_weight: f32 = 0.453592 * 4.480e6;
// By default, float values are formatted in scientific // By default, float values are formatted in scientific
// notation. Try experimenting with '{d}' and '{d:.3}' to see // notation. Try experimenting with '{d}' and '{d:.3}' to see

View File

@ -1,5 +1,5 @@
--- exercises/065_builtins2.zig 2025-06-17 13:58:07.857258167 +0200 --- exercises/065_builtins2.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/065_builtins2.zig 2025-06-17 13:56:36.630415938 +0200 +++ answers/065_builtins2.zig 2023-10-05 20:04:07.136101712 +0200
@@ -58,7 +58,7 @@ @@ -58,7 +58,7 @@
// Oops! We cannot leave the 'me' and 'myself' fields // Oops! We cannot leave the 'me' and 'myself' fields
// undefined. Please set them here: // undefined. Please set them here:
@ -18,22 +18,22 @@
// Now we print a pithy statement about Narcissus. // Now we print a pithy statement about Narcissus.
print("A {s} loves all {s}es. ", .{ print("A {s} loves all {s}es. ", .{
@@ -113,15 +113,15 @@ @@ -109,15 +109,15 @@
// Please complete these 'if' statements so that the field // Please complete these 'if' statements so that the field
// name will not be printed if the field is of type 'void' // name will not be printed if the field is of type 'void'
// (which is a zero-bit type that takes up no space at all!): // (which is a zero-bit type that takes up no space at all!):
- if (fields[0].??? != void) { - if (fields[0].??? != void) {
+ if (fields[0].type != void) { + if (fields[0].type != void) {
print(" {s}", .{fields[0].name}); print(" {s}", .{@typeInfo(Narcissus).Struct.fields[0].name});
} }
- if (fields[1].??? != void) { - if (fields[1].??? != void) {
+ if (fields[1].type != void) { + if (fields[1].type != void) {
print(" {s}", .{fields[1].name}); print(" {s}", .{@typeInfo(Narcissus).Struct.fields[1].name});
} }
- if (fields[2].??? != void) { - if (fields[2].??? != void) {
+ if (fields[2].type != void) { + if (fields[2].type != void) {
print(" {s}", .{fields[2].name}); print(" {s}", .{@typeInfo(Narcissus).Struct.fields[2].name});
} }

View File

@ -1,8 +1,8 @@
--- exercises/071_comptime6.zig 2024-09-02 19:21:50.250454978 +0200 --- exercises/071_comptime6.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/071_comptime6.zig 2024-09-02 19:21:23.553250563 +0200 +++ answers/071_comptime6.zig 2023-10-05 20:04:07.162768879 +0200
@@ -40,7 +40,7 @@ @@ -40,7 +40,7 @@
const fields = @typeInfo(Narcissus).@"struct".fields; const fields = @typeInfo(Narcissus).Struct.fields;
- ??? { - ??? {
+ inline for (fields) |field| { + inline for (fields) |field| {

View File

@ -1,5 +1,5 @@
--- exercises/076_sentinels.zig 2024-09-02 19:27:04.336781039 +0200 --- exercises/076_sentinels.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/076_sentinels.zig 2024-09-02 19:26:15.709134934 +0200 +++ answers/076_sentinels.zig 2023-10-05 20:04:07.186102649 +0200
@@ -82,7 +82,7 @@ @@ -82,7 +82,7 @@
print("Array:", .{}); print("Array:", .{});

View File

@ -1,11 +1,11 @@
--- exercises/082_anonymous_structs3.zig 2025-03-14 16:41:17.892873287 +0200 --- exercises/082_anonymous_structs3.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/082_anonymous_structs3.zig 2025-03-14 16:40:56.043829543 +0200 +++ answers/082_anonymous_structs3.zig 2023-10-05 20:04:07.212769813 +0200
@@ -82,14 +82,14 @@ @@ -82,14 +82,14 @@
// @typeInfo(Circle).@"struct".fields // @typeInfo(Circle).Struct.fields
// //
// This will be an array of StructFields. // This will be an array of StructFields.
- const fields = ???; - const fields = ???;
+ const fields = @typeInfo(@TypeOf(tuple)).@"struct".fields; + const fields = @typeInfo(@TypeOf(tuple)).Struct.fields;
// 2. Loop through each field. This must be done at compile // 2. Loop through each field. This must be done at compile
// time. // time.
@ -17,7 +17,7 @@
// 3. Print the field's name, type, and value. // 3. Print the field's name, type, and value.
// //
// Each 'field' in this loop is one of these: // Each 'field' in this loop is one of these:
@@ -119,9 +119,9 @@ @@ -117,9 +117,9 @@
// //
// The first field should print as: "0"(bool):true // The first field should print as: "0"(bool):true
print("\"{s}\"({any}):{any} ", .{ print("\"{s}\"({any}):{any} ", .{

View File

@ -0,0 +1,11 @@
--- exercises/084_async.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/084_async.zig 2023-10-05 20:04:07.219436606 +0200
@@ -48,7 +48,7 @@
pub fn main() void {
// Additional Hint: you can assign things to '_' when you
// don't intend to do anything with them.
- foo();
+ _ = async foo();
}
fn foo() void {

View File

@ -0,0 +1,10 @@
--- exercises/085_async2.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/085_async2.zig 2023-10-05 20:04:07.226103397 +0200
@@ -19,6 +19,7 @@
pub fn main() void {
var foo_frame = async foo();
+ resume foo_frame;
}
fn foo() void {

View File

@ -0,0 +1,16 @@
--- exercises/086_async3.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/086_async3.zig 2023-10-05 20:04:07.229436793 +0200
@@ -13,7 +13,12 @@
const n = 5;
var foo_frame = async foo(n);
- ???
+ // Silly solution. You can also use a loop.
+ resume foo_frame;
+ resume foo_frame;
+ resume foo_frame;
+ resume foo_frame;
+ resume foo_frame;
print("\n", .{});
}

View File

@ -0,0 +1,21 @@
--- exercises/087_async4.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/087_async4.zig 2023-10-05 20:04:07.236103584 +0200
@@ -16,7 +16,7 @@
while (global_counter <= 5) {
print("{} ", .{global_counter});
- ???
+ resume foo_frame;
}
print("\n", .{});
@@ -24,7 +24,7 @@
fn foo() void {
while (true) {
- ???
- ???
+ global_counter += 1;
+ suspend {}
}
}

View File

@ -0,0 +1,11 @@
--- exercises/088_async5.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/088_async5.zig 2023-10-05 20:04:07.239436980 +0200
@@ -36,7 +36,7 @@
pub fn main() void {
var myframe = async getPageTitle("http://example.com");
- var value = ???
+ var value = await myframe;
print("{s}\n", .{value});
}

View File

@ -0,0 +1,13 @@
--- exercises/089_async6.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/089_async6.zig 2023-10-05 20:04:07.242770376 +0200
@@ -41,8 +41,8 @@
var com_frame = async getPageTitle("http://example.com");
var org_frame = async getPageTitle("http://example.org");
- var com_title = com_frame;
- var org_title = org_frame;
+ var com_title = await com_frame;
+ var org_title = await org_frame;
print(".com: {s}, .org: {s}.\n", .{ com_title, org_title });
}

View File

@ -0,0 +1,11 @@
--- exercises/090_async7.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/090_async7.zig 2023-10-05 20:04:07.249437167 +0200
@@ -29,7 +29,7 @@
// The main() function can not be async. But we know
// getBeef() will not suspend with this particular
// invocation. Please make this okay:
- var my_beef = getBeef(0);
+ var my_beef = nosuspend getBeef(0);
print("beef? {X}!\n", .{my_beef});
}

View File

@ -0,0 +1,26 @@
--- exercises/091_async8.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/091_async8.zig 2023-10-05 20:04:07.252770563 +0200
@@ -17,7 +17,7 @@
var frame = async suspendable();
- print("X", .{});
+ print("D", .{});
resume frame;
@@ -25,11 +25,11 @@
}
fn suspendable() void {
- print("X", .{});
+ print("B", .{});
suspend {
- print("X", .{});
+ print("C", .{});
}
- print("X", .{});
+ print("E", .{});
}

View File

@ -1,5 +1,5 @@
--- exercises/097_bit_manipulation.zig 2025-05-12 21:25:03.395385743 +0200 --- exercises/097_bit_manipulation.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/097_bit_manipulation.zig 2025-05-12 21:22:57.472986976 +0200 +++ answers/097_bit_manipulation.zig 2023-10-05 20:04:07.282771124 +0200
@@ -80,7 +80,7 @@ @@ -80,7 +80,7 @@
y ^= x; y ^= x;
@ -7,5 +7,5 @@
- ???; - ???;
+ x ^= y; + x ^= y;
print("x = {b}; y = {b}\n", .{ x, y }); print("x = {d}; y = {d}\n", .{ x, y });
} }

View File

@ -1,5 +1,5 @@
--- exercises/099_formatting.zig 2024-11-07 21:45:10.459123650 +0100 --- exercises/099_formatting.zig 2023-10-03 22:15:22.125574535 +0200
+++ answers/099_formatting.zig 2024-11-07 21:43:55.154345991 +0100 +++ answers/099_formatting.zig 2023-10-05 20:04:07.292771311 +0200
@@ -131,7 +131,7 @@ @@ -131,7 +131,7 @@
for (0..size) |b| { for (0..size) |b| {
// What formatting is needed here to make our columns // What formatting is needed here to make our columns

View File

@ -7,5 +7,5 @@
- for (hex_nums, ???) |hn, ???| { - for (hex_nums, ???) |hn, ???| {
+ for (hex_nums, dec_nums) |hn, dn| { + for (hex_nums, dec_nums) |hn, dn| {
if (hn != dn) { if (hn != dn) {
print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn }); std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
return; return;

View File

@ -1,16 +1,16 @@
--- exercises/106_files.zig 2025-03-13 15:26:59.532367792 +0200 --- exercises/106_files.zig 2024-03-27 16:52:05.660910200 +0800
+++ answers/106_files.zig 2025-03-14 22:04:52.243435159 +0200 +++ answers/106_files.zig 2024-03-27 16:52:09.649422200 +0800
@@ -35,7 +35,7 @@ @@ -35,7 +35,7 @@
// by doing nothing // by doing nothing
// //
// we want to catch error.PathAlreadyExists and do nothing // we want to catch error.PathAlreadyExists and do nothing
- ??? => {}, - ??? => {},
+ error.PathAlreadyExists => {}, + error.PathAlreadyExists => {},
// if there's any other unexpected error we just propagate it through // if is any other unexpected error we just propagate it through
else => return e, else => return e,
}; };
@@ -44,7 +44,7 @@ @@ -44,7 +44,7 @@
// wait a minute... // wait a minute
// opening a directory might fail! // opening a directory might fail!
// what should we do here? // what should we do here?
- var output_dir: std.fs.Dir = cwd.openDir("output", .{}); - var output_dir: std.fs.Dir = cwd.openDir("output", .{});
@ -20,10 +20,10 @@
// we try to open the file `zigling.txt`, // we try to open the file `zigling.txt`,
@@ -55,7 +55,7 @@ @@ -55,7 +55,7 @@
// but here we are not yet done writing to the file // but here we are not yet done writing to the file
// if only there were a keyword in Zig that // if only there are a keyword in zig that
// allowed you to "defer" code execution to the end of the scope... // allow you "defer" code execute to the end of scope...
- file.close(); - file.close();
+ defer file.close(); + defer file.close();
// you are not allowed to move these two lines above the file closing line! // !you are not allow to switch this two lines to before file closing line!
const byte_written = try file.write("It's zigling time!"); const byte_written = try file.write("It's zigling time!");

View File

@ -1,26 +1,26 @@
--- exercises/107_files2.zig 2025-03-13 15:26:59.532367792 +0200 --- exercises/107_files2.zig 2024-03-27 16:51:56.199719600 +0800
+++ answers/107_files2.zig 2025-03-14 22:08:35.167953736 +0200 +++ answers/107_files2.zig 2024-03-27 16:52:01.650935300 +0800
@@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
// initialize an array of u8 with all letter 'A' // initalize an array of u8 with all letter 'A'.
// we need to pick the size of the array, 64 seems like a good number // we need to pick a size of the array, 64 seems like a good number.
// fix the initialization below // fix the initalization below
- var content = ['A']*64; - var content = ['A']*64;
+ var content = [_]u8{'A'} ** 64; + var content = [_]u8{'A'} ** 64;
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA` // this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content}); std.debug.print("{s}\n", .{content});
@@ -41,12 +41,12 @@ @@ -41,12 +41,12 @@
// can you go here to find a way to read the content? // can you go here to find a way to read the content ?
// https://ziglang.org/documentation/master/std/#std.fs.File // https://ziglang.org/documentation/master/std/#std.fs.File
// hint: you might find two answers that are both valid in this case // hint: you might find two answer that are both vaild in this case
- const bytes_read = zig_read_the_file_or_i_will_fight_you(&content); - const byte_read = zig_read_the_file_or_i_will_fight_you(&content);
+ const bytes_read = try file.read(&content); + const byte_read = try file.read(&content);
// Woah, too screamy. I know you're excited for zigling time but tone it down a bit. // Woah, too screamy, I know you're excited for zigling time but tone it down a bit
// Can you print only what we read from the file? // Can you print only what we read from the file ?
std.debug.print("Successfully Read {d} bytes: {s}\n", .{ std.debug.print("Successfully Read {d} byte: {s}\n", .{
bytes_read, byte_read,
- content, // change this line only - content, // change this line only
+ content[0..bytes_read], // change this line only + content[0..byte_read], // change this line only
}); });
} }

View File

@ -1,18 +0,0 @@
--- exercises/108_labeled_switch.zig 2024-09-20 12:09:24.370066539 +0200
+++ answers/108_labeled_switch.zig 2024-09-20 12:09:06.499711739 +0200
@@ -65,13 +65,13 @@
// how would you fix it?
pr: switch (PullRequestState.Draft) {
PullRequestState.Draft => continue :pr PullRequestState.InReview,
- PullRequestState.InReview => continue :pr PullRequestState.Rejected,
+ PullRequestState.InReview => continue :pr PullRequestState.Approved,
PullRequestState.Approved => continue :pr PullRequestState.Merged,
PullRequestState.Rejected => {
std.debug.print("The pull request has been rejected.\n", .{});
return;
},
- PullRequestState.Merged => break, // Would you know where to break to?
+ PullRequestState.Merged => break :pr, // Would you know where to break to?
}
std.debug.print("The pull request has been merged.\n", .{});
}

View File

@ -1,13 +0,0 @@
--- exercises/109_vectors.zig 2024-11-07 14:57:09.673383618 +0100
+++ answers/109_vectors.zig 2024-11-07 14:22:59.069150138 +0100
@@ -121,8 +121,8 @@
const Vec4 = @Vector(4, f32);
fn calcMaxPairwiseDiffNew(a: Vec4, b: Vec4) f32 {
- const abs_diff_vec = ???;
- const max_diff = @reduce(???, abs_diff_vec);
+ const abs_diff_vec = @abs(a - b);
+ const max_diff = @reduce(.Max, abs_diff_vec);
return max_diff;
}

View File

@ -1,56 +0,0 @@
--- exercises/110_quiz9.zig 2025-02-08 13:19:48.522641785 -0800
+++ answers/110_quiz9.zig 2025-02-10 17:42:04.525004335 -0800
@@ -108,7 +108,7 @@
PORTB = 0b1100;
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("^ {b:0>4} // (bitmask)\n", .{0b0101});
- PORTB ^= (1 << 1) | (1 << 0); // What's wrong here?
+ PORTB ^= (1 << 2) | (1 << 0);
checkAnswer(0b1001, PORTB);
newline();
@@ -116,7 +116,7 @@
PORTB = 0b1100;
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("^ {b:0>4} // (bitmask)\n", .{0b0011});
- PORTB ^= (1 << 1) & (1 << 0); // What's wrong here?
+ PORTB ^= (1 << 1) | (1 << 0);
checkAnswer(0b1111, PORTB);
newline();
@@ -170,7 +170,7 @@
PORTB = 0b1001; // reset PORTB
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("| {b:0>4} // (bitmask)\n", .{0b0100});
- PORTB = PORTB ??? (1 << 2); // What's missing here?
+ PORTB = PORTB | (1 << 2);
checkAnswer(0b1101, PORTB);
newline();
@@ -178,7 +178,7 @@
PORTB = 0b1001; // reset PORTB
print(" {b:0>4} // (reset state)\n", .{PORTB});
print("| {b:0>4} // (bitmask)\n", .{0b0100});
- PORTB ??? (1 << 2); // What's missing here?
+ PORTB |= (1 << 2);
checkAnswer(0b1101, PORTB);
newline();
@@ -269,7 +269,7 @@
PORTB = 0b1110; // reset PORTB
print(" {b:0>4} // (initial state of PORTB)\n", .{PORTB});
print("& {b:0>4} // (bitmask)\n", .{0b1011});
- PORTB = PORTB & ???@as(u4, 1 << 2); // What character is missing here?
+ PORTB = PORTB & ~@as(u4, 1 << 2);
checkAnswer(0b1010, PORTB);
newline();
@@ -277,7 +277,7 @@
PORTB = 0b0111; // reset PORTB
print(" {b:0>4} // (reset state)\n", .{PORTB});
print("& {b:0>4} // (bitmask)\n", .{0b1110});
- PORTB &= ~(1 << 0); // What's missing here?
+ PORTB &= ~@as(u4, 1 << 0);
checkAnswer(0b0110, PORTB);
newline();

View File

@ -50,7 +50,7 @@ pub fn addCliTests(b: *std.Build, exercises: []const Exercise) *Step {
case_step.dependOn(&verify.step); case_step.dependOn(&verify.step);
} }
const cleanup = b.addRemoveDirTree(.{ .src_path = .{ .owner = b, .sub_path = tmp_path } }); const cleanup = b.addRemoveDirTree(tmp_path);
cleanup.step.dependOn(case_step); cleanup.step.dependOn(case_step);
step.dependOn(&cleanup.step); step.dependOn(&cleanup.step);
@ -82,7 +82,7 @@ pub fn addCliTests(b: *std.Build, exercises: []const Exercise) *Step {
const verify = CheckStep.create(b, exercises, stderr); const verify = CheckStep.create(b, exercises, stderr);
verify.step.dependOn(&cmd.step); verify.step.dependOn(&cmd.step);
const cleanup = b.addRemoveDirTree(.{ .src_path = .{ .owner = b, .sub_path = tmp_path } }); const cleanup = b.addRemoveDirTree(tmp_path);
cleanup.step.dependOn(&verify.step); cleanup.step.dependOn(&verify.step);
step.dependOn(&cleanup.step); step.dependOn(&cleanup.step);
@ -150,7 +150,7 @@ const CheckNamedStep = struct {
return self; return self;
} }
fn make(step: *Step, _: Step.MakeOptions) !void { fn make(step: *Step, _: *std.Progress.Node) !void {
const b = step.owner; const b = step.owner;
const self: *CheckNamedStep = @alignCast(@fieldParentPtr("step", step)); const self: *CheckNamedStep = @alignCast(@fieldParentPtr("step", step));
const ex = self.exercise; const ex = self.exercise;
@ -161,7 +161,7 @@ const CheckNamedStep = struct {
); );
defer stderr_file.close(); defer stderr_file.close();
var stderr = stderr_file.readerStreaming(&.{}); const stderr = stderr_file.reader();
{ {
// Skip the logo. // Skip the logo.
const nlines = mem.count(u8, root.logo, "\n"); const nlines = mem.count(u8, root.logo, "\n");
@ -169,10 +169,10 @@ const CheckNamedStep = struct {
var lineno: usize = 0; var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) { while (lineno < nlines) : (lineno += 1) {
_ = try readLine(&stderr, &buf); _ = try readLine(stderr, &buf);
} }
} }
try check_output(step, ex, &stderr); try check_output(step, ex, stderr);
} }
}; };
@ -202,7 +202,7 @@ const CheckStep = struct {
return self; return self;
} }
fn make(step: *Step, _: Step.MakeOptions) !void { fn make(step: *Step, _: *std.Progress.Node) !void {
const b = step.owner; const b = step.owner;
const self: *CheckStep = @alignCast(@fieldParentPtr("step", step)); const self: *CheckStep = @alignCast(@fieldParentPtr("step", step));
const exercises = self.exercises; const exercises = self.exercises;
@ -213,7 +213,7 @@ const CheckStep = struct {
); );
defer stderr_file.close(); defer stderr_file.close();
var stderr = stderr_file.readerStreaming(&.{}); const stderr = stderr_file.reader();
for (exercises) |ex| { for (exercises) |ex| {
if (ex.number() == 1) { if (ex.number() == 1) {
// Skip the logo. // Skip the logo.
@ -222,15 +222,15 @@ const CheckStep = struct {
var lineno: usize = 0; var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) { while (lineno < nlines) : (lineno += 1) {
_ = try readLine(&stderr, &buf); _ = try readLine(stderr, &buf);
} }
} }
try check_output(step, ex, &stderr); try check_output(step, ex, stderr);
} }
} }
}; };
fn check_output(step: *Step, exercise: Exercise, reader: *Reader) !void { fn check_output(step: *Step, exercise: Exercise, reader: Reader) !void {
const b = step.owner; const b = step.owner;
var buf: [1024]u8 = undefined; var buf: [1024]u8 = undefined;
@ -297,9 +297,12 @@ fn check(
} }
} }
fn readLine(reader: *fs.File.Reader, buf: []u8) !?[]const u8 { fn readLine(reader: fs.File.Reader, buf: []u8) !?[]const u8 {
try reader.interface.readSliceAll(buf); if (try reader.readUntilDelimiterOrEof(buf, '\n')) |line| {
return mem.trimRight(u8, buf, " \r\n"); return mem.trimRight(u8, line, " \r\n");
}
return null;
} }
/// Fails with a custom error message. /// Fails with a custom error message.
@ -322,7 +325,7 @@ const FailStep = struct {
return self; return self;
} }
fn make(step: *Step, _: Step.MakeOptions) !void { fn make(step: *Step, _: *std.Progress.Node) !void {
const b = step.owner; const b = step.owner;
const self: *FailStep = @alignCast(@fieldParentPtr("step", step)); const self: *FailStep = @alignCast(@fieldParentPtr("step", step));
@ -365,7 +368,7 @@ const HealStep = struct {
return self; return self;
} }
fn make(step: *Step, _: Step.MakeOptions) !void { fn make(step: *Step, _: *std.Progress.Node) !void {
const b = step.owner; const b = step.owner;
const self: *HealStep = @alignCast(@fieldParentPtr("step", step)); const self: *HealStep = @alignCast(@fieldParentPtr("step", step));
@ -402,8 +405,7 @@ fn heal(allocator: Allocator, exercises: []const Exercise, work_path: []const u8
/// difference that returns an error when the temp path cannot be created. /// difference that returns an error when the temp path cannot be created.
pub fn makeTempPath(b: *Build) ![]const u8 { pub fn makeTempPath(b: *Build) ![]const u8 {
const rand_int = std.crypto.random.int(u64); const rand_int = std.crypto.random.int(u64);
const rand_hex64 = std.fmt.hex(rand_int); const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ Build.hex64(rand_int);
const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ rand_hex64;
const path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch const path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch
@panic("OOM"); @panic("OOM");
try b.cache_root.handle.makePath(tmp_dir_sub_path); try b.cache_root.handle.makePath(tmp_dir_sub_path);