mirror of
https://codeberg.org/ziglings/exercises.git
synced 2025-08-03 15:15:37 +00:00
Compare commits
227 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1f6ce9a268 | ||
![]() |
4044b93dd2 | ||
![]() |
f461986896 | ||
![]() |
0d06220ec5 | ||
![]() |
54f48c75c4 | ||
![]() |
ed2f76e960 | ||
![]() |
f21f8f7863 | ||
![]() |
b499788606 | ||
![]() |
49e73db5f5 | ||
![]() |
4bad15a95b | ||
![]() |
b9a372bde8 | ||
![]() |
cb0a1a027f | ||
![]() |
9ae739c4c9 | ||
![]() |
34a7c6c861 | ||
![]() |
bc96d06da4 | ||
![]() |
451e1a3739 | ||
![]() |
afaab2efca | ||
![]() |
e431cbb0cf | ||
![]() |
d1ffdd16e8 | ||
![]() |
37698736ce | ||
![]() |
cabbef97e8 | ||
![]() |
b1223f92ed | ||
![]() |
91f1c045bc | ||
![]() |
69ad718446 | ||
![]() |
14c81a6cef | ||
![]() |
fd764e90ab | ||
![]() |
76b8fcdb28 | ||
![]() |
54b2b58b6a | ||
![]() |
761fb1a501 | ||
![]() |
377d184cdc | ||
![]() |
d7052e4137 | ||
![]() |
522b4673a4 | ||
![]() |
7ce659f7fa | ||
![]() |
02edb1f5c6 | ||
![]() |
5734e68789 | ||
![]() |
1a9b7ec342 | ||
![]() |
6f3cdcf018 | ||
![]() |
eeb1246c2c | ||
![]() |
e0ecff7045 | ||
![]() |
ce64fe5a1a | ||
![]() |
a22f1df0a1 | ||
![]() |
2a9ef2d2c8 | ||
![]() |
f285bd59a4 | ||
![]() |
7382949018 | ||
![]() |
0e0cafb71e | ||
![]() |
064184c8a7 | ||
![]() |
cd94f6023c | ||
![]() |
90e90c997e | ||
![]() |
8384d4d9bf | ||
![]() |
2437edd51f | ||
![]() |
f7e8d4c444 | ||
![]() |
b7b3297d06 | ||
![]() |
6fbf81d929 | ||
![]() |
465536baf0 | ||
![]() |
a7cd808bb8 | ||
![]() |
20596bc290 | ||
![]() |
0af4f75e07 | ||
![]() |
d75f49b25b | ||
![]() |
838fb4ab9d | ||
![]() |
c13a00fc71 | ||
![]() |
c1777f47d5 | ||
![]() |
c02e6ead16 | ||
![]() |
b3dd04885d | ||
![]() |
bfa0e624f1 | ||
![]() |
b38d70002a | ||
![]() |
1478d41801 | ||
![]() |
596d5e50ca | ||
![]() |
a9487c246f | ||
![]() |
e09c11a16a | ||
![]() |
92c7235362 | ||
![]() |
0903c5927b | ||
![]() |
8e6a52d5a6 | ||
![]() |
e54b30f3d3 | ||
![]() |
4ac1d70f93 | ||
![]() |
502ac8711e | ||
![]() |
0fa86eb8c8 | ||
![]() |
657fd6aff0 | ||
![]() |
3faad6e17b | ||
![]() |
fabedb4a24 | ||
![]() |
b1fcca9d82 | ||
![]() |
a47dfe1f4d | ||
![]() |
90d05995b5 | ||
![]() |
54f743ba43 | ||
![]() |
87b258d0a9 | ||
![]() |
6b29634279 | ||
![]() |
c2dcaf3099 | ||
![]() |
5cdaee7420 | ||
![]() |
468e3d7eed | ||
![]() |
26978cb2d3 | ||
![]() |
de353bcb3b | ||
![]() |
5ed3af57ee | ||
![]() |
105c98782c | ||
![]() |
f446402105 | ||
![]() |
8da60edb82 | ||
![]() |
83e00872b8 | ||
![]() |
02bd0b2022 | ||
![]() |
63951df523 | ||
![]() |
0d9458807e | ||
![]() |
ae541a0d9c | ||
![]() |
fb46871dab | ||
![]() |
2606e53733 | ||
![]() |
33d61cb3c9 | ||
![]() |
e55f8e8699 | ||
![]() |
4d4eefa523 | ||
![]() |
37a0f42621 | ||
![]() |
97c2ea2f49 | ||
![]() |
731a3eb0a6 | ||
![]() |
bfed660020 | ||
![]() |
46e8fc0b61 | ||
![]() |
8cce587d3b | ||
![]() |
75e5e53497 | ||
![]() |
c90d6015e3 | ||
![]() |
150b3de299 | ||
![]() |
fb018d212c | ||
![]() |
d0d31ae73a | ||
![]() |
530dcde3d4 | ||
![]() |
286439cddc | ||
![]() |
f629d78268 | ||
![]() |
71b4f5ea81 | ||
![]() |
8f49400aa1 | ||
![]() |
e2b20b3406 | ||
![]() |
02478759b4 | ||
![]() |
173af62d34 | ||
![]() |
9d1da38d38 | ||
![]() |
775f5ebd29 | ||
![]() |
96df8ba304 | ||
![]() |
67c824118e | ||
![]() |
d1b49f353e | ||
![]() |
94b5b4bf4b | ||
![]() |
f0c2c56087 | ||
![]() |
ed1a20d51b | ||
![]() |
d9ce18631d | ||
![]() |
db569a1478 | ||
![]() |
798bb40396 | ||
![]() |
580e126266 | ||
![]() |
335a78f8f5 | ||
![]() |
102ba8c894 | ||
![]() |
827146f680 | ||
![]() |
995a7c8f36 | ||
![]() |
f28346d009 | ||
![]() |
3dc79ca9c3 | ||
![]() |
26f4785a99 | ||
![]() |
b9cde40610 | ||
![]() |
c8b60f2c30 | ||
![]() |
c9a98ad555 | ||
![]() |
49e0c32ec5 | ||
![]() |
7f1df86938 | ||
![]() |
b0f859c62f | ||
![]() |
a6759458b0 | ||
![]() |
2e5b570aef | ||
![]() |
b2f56c7cea | ||
![]() |
296a2b5c6a | ||
![]() |
8e35cf7990 | ||
![]() |
dab5197027 | ||
![]() |
ed88236fa7 | ||
![]() |
031e11aa61 | ||
![]() |
118c1252be | ||
![]() |
3baf0ae657 | ||
![]() |
97c8bc39bc | ||
![]() |
17f9312034 | ||
![]() |
e2f356d95c | ||
![]() |
70b0522cb2 | ||
![]() |
94d6a4da5f | ||
![]() |
1ba74488e4 | ||
![]() |
d6887f21c8 | ||
![]() |
a671def2cf | ||
![]() |
28320b169f | ||
![]() |
b26e3b2ac4 | ||
![]() |
4cc1158fbc | ||
![]() |
e7dcf0b6ef | ||
![]() |
e8f09190d6 | ||
![]() |
1fd6aa1c65 | ||
![]() |
4401cf8f49 | ||
![]() |
140c22e9f4 | ||
![]() |
c33898bc65 | ||
![]() |
7705473447 | ||
![]() |
5bf56f03b0 | ||
![]() |
94fe012c77 | ||
![]() |
2259a18ece | ||
![]() |
f496d79ab9 | ||
![]() |
f656e950d5 | ||
![]() |
3763f976eb | ||
![]() |
da46008761 | ||
![]() |
5c000ce2d7 | ||
![]() |
c0c315b25a | ||
![]() |
83f16709ba | ||
![]() |
6d3d3651d0 | ||
![]() |
582a2558c7 | ||
![]() |
c9e5c7d561 | ||
![]() |
65387dda45 | ||
![]() |
1f5ec78df8 | ||
![]() |
bb032fad40 | ||
![]() |
984f4af3a1 | ||
![]() |
99c9c42ff2 | ||
![]() |
7f78701d19 | ||
![]() |
5c9b9bdc34 | ||
![]() |
0279826f5b | ||
![]() |
914141ec5a | ||
![]() |
c2caf1a32e | ||
![]() |
227bcb18db | ||
![]() |
b224ed510e | ||
![]() |
6223dc0f83 | ||
![]() |
a6d13af0c2 | ||
![]() |
776316e60b | ||
![]() |
efe2d19c69 | ||
![]() |
49c54fb075 | ||
![]() |
67f87a76c2 | ||
![]() |
dfdaf03d99 | ||
![]() |
5da194ba9c | ||
![]() |
8345e839b0 | ||
![]() |
6c23f2682e | ||
![]() |
19bd8745e4 | ||
![]() |
1ac46d7a42 | ||
![]() |
165cc199ca | ||
![]() |
e182d1f19d | ||
![]() |
09e2f37a50 | ||
![]() |
27db3112f9 | ||
![]() |
8cb2a5aa3c | ||
![]() |
c936c5e123 | ||
![]() |
5c2354a1bf | ||
![]() |
4dbd056100 | ||
![]() |
9ce4a7d6f0 | ||
![]() |
b484be9ac1 | ||
![]() |
a2a0a6e891 | ||
![]() |
e7eaa080b6 | ||
![]() |
ce0a6cc91c | ||
![]() |
c94eb33e44 |
5
.gitea/issue_template.md
Normal file
5
.gitea/issue_template.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
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
6
.gitignore
vendored
|
@ -1,5 +1,9 @@
|
||||||
/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/
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
steps:
|
steps:
|
||||||
- name: eowyn
|
- name: eowyn
|
||||||
image: ziglings/ziglang
|
image: ziglings/ziglang:latest
|
||||||
|
pull: true
|
||||||
commands:
|
commands:
|
||||||
- sh ./patches/eowyn.sh
|
- sh ./patches/eowyn.sh
|
||||||
when:
|
when:
|
||||||
event: [push, cron]
|
event: [pull_request, push, cron]
|
||||||
cron: daily*
|
cron: daily*
|
||||||
|
|
|
@ -40,6 +40,7 @@ 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.
|
||||||
|
|
||||||
|
@ -75,6 +76,15 @@ 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/`
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Dave Gauer
|
Copyright (c) 2021 Dave Gauer, Chris Boesch
|
||||||
|
|
||||||
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
|
||||||
|
|
50
README.md
50
README.md
|
@ -10,11 +10,10 @@ 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 directly inspired by the brilliant and fun
|
This project was initiated by [Dave Gauer](https://ratfactor.com/) and is directly inspired
|
||||||
[rustlings](https://github.com/rust-lang/rustlings)
|
by the brilliant and fun [rustlings](https://github.com/rust-lang/rustlings) project.
|
||||||
project for the [Rust](https://www.rust-lang.org/) language.
|
Indirect inspiration comes from [Ruby Koans](http://rubykoans.com/) and the Little LISPer/Little
|
||||||
Indirect inspiration comes from [Ruby Koans](http://rubykoans.com/)
|
Schemer series of books.
|
||||||
and the Little LISPer/Little Schemer series of books.
|
|
||||||
|
|
||||||
## Intended Audience
|
## Intended Audience
|
||||||
|
|
||||||
|
@ -46,20 +45,20 @@ Verify the installation and build number of `zig` like so:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ zig version
|
$ zig version
|
||||||
0.12.0-dev.xxxx+xxxxxxxxx
|
0.15.0-dev.xxxx+xxxxxxxxx
|
||||||
```
|
```
|
||||||
|
|
||||||
Clone this repository with Git:
|
Clone this repository with Git:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ git clone https://ziglings.org
|
git clone https://codeberg.org/ziglings/exercises.git ziglings
|
||||||
$ cd ziglings.org
|
cd ziglings
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
||||||
|
@ -74,8 +73,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.11.0`, but Ziglings needs a dev build with
|
stable release was `0.14.1`, but Ziglings needs a dev build with
|
||||||
pre-release version "0.12.0" and a build number at least as high
|
pre-release version "0.15.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
|
||||||
|
@ -88,7 +87,14 @@ that if you update one, you may need to also update the other.
|
||||||
|
|
||||||
### Version Changes
|
### Version Changes
|
||||||
|
|
||||||
Version-0.12.0-dev.3518
|
Version-0.15.0-dev.1092
|
||||||
|
* *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)
|
||||||
|
@ -125,6 +131,18 @@ 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:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -156,6 +174,11 @@ 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.
|
||||||
|
@ -211,7 +234,10 @@ 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
207
build.zig
|
@ -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.12.0-dev.3518";
|
const required_zig = "0.15.0-dev.1092";
|
||||||
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,6 +103,8 @@ 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 =
|
||||||
|
@ -118,23 +120,24 @@ 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.io.getStdErr().supportsAnsiEscapeCodes()) {
|
if (std.fs.File.stderr().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));
|
||||||
extern "kernel32" fn GetStdHandle(id: DWORD) callconv(WINAPI) ?*anyopaque;
|
const GetStdHandle = std.os.windows.kernel32.GetStdHandle;
|
||||||
extern "kernel32" fn GetConsoleMode(console: ?*anyopaque, out_mode: *DWORD) callconv(WINAPI) u32;
|
const GetConsoleMode = std.os.windows.kernel32.GetConsoleMode;
|
||||||
extern "kernel32" fn SetConsoleMode(console: ?*anyopaque, mode: DWORD) callconv(WINAPI) u32;
|
const SetConsoleMode = std.os.windows.kernel32.SetConsoleMode;
|
||||||
};
|
};
|
||||||
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;
|
||||||
|
@ -158,6 +161,9 @@ 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|
|
||||||
|
@ -191,18 +197,109 @@ 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");
|
||||||
|
@ -244,7 +341,7 @@ const ZiglingStep = struct {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
|
fn make(step: *Step, options: Step.MakeOptions) !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));
|
||||||
|
@ -255,7 +352,7 @@ const ZiglingStep = struct {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const exe_path = self.compile(prog_node) catch {
|
const exe_path = self.compile(options.progress_node) catch {
|
||||||
self.printErrors();
|
self.printErrors();
|
||||||
|
|
||||||
if (self.exercise.hint) |hint|
|
if (self.exercise.hint) |hint|
|
||||||
|
@ -265,7 +362,7 @@ const ZiglingStep = struct {
|
||||||
std.process.exit(2);
|
std.process.exit(2);
|
||||||
};
|
};
|
||||||
|
|
||||||
self.run(exe_path.?, prog_node) catch {
|
self.run(exe_path, options.progress_node) catch {
|
||||||
self.printErrors();
|
self.printErrors();
|
||||||
|
|
||||||
if (self.exercise.hint) |hint|
|
if (self.exercise.hint) |hint|
|
||||||
|
@ -279,7 +376,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});
|
||||||
|
|
||||||
|
@ -352,6 +449,18 @@ 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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +484,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;
|
||||||
|
@ -406,7 +515,21 @@ 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 {
|
||||||
|
@ -417,6 +540,7 @@ 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", .{
|
||||||
|
@ -459,7 +583,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.split(u8, buf, " \n");
|
var iter = std.mem.splitSequence(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");
|
||||||
|
@ -494,7 +618,7 @@ const PrintStep = struct {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make(step: *Step, _: *std.Progress.Node) !void {
|
fn make(step: *Step, _: Step.MakeOptions) !void {
|
||||||
const self: *PrintStep = @alignCast(@fieldParentPtr("step", step));
|
const self: *PrintStep = @alignCast(@fieldParentPtr("step", step));
|
||||||
print("{s}", .{self.message});
|
print("{s}", .{self.message});
|
||||||
}
|
}
|
||||||
|
@ -521,7 +645,7 @@ fn validate_exercises() bool {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var iter = std.mem.split(u8, ex.output, "\n");
|
var iter = std.mem.splitScalar(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) {
|
||||||
|
@ -838,7 +962,7 @@ const exercises = [_]Exercise{
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.main_file = "060_floats.zig",
|
.main_file = "060_floats.zig",
|
||||||
.output = "Shuttle liftoff weight: 2032092kg",
|
.output = "Shuttle liftoff weight: 2032 metric tons",
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.main_file = "061_coercions.zig",
|
.main_file = "061_coercions.zig",
|
||||||
|
@ -904,6 +1028,7 @@ 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",
|
||||||
|
@ -938,7 +1063,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.141592e0
|
\\"0"(bool):true "1"(bool):false "2"(i32):42 "3"(f32):3.141592
|
||||||
,
|
,
|
||||||
.hint = "This one is a challenge! But you have everything you need.",
|
.hint = "This one is a challenge! But you have everything you need.",
|
||||||
},
|
},
|
||||||
|
@ -1020,7 +1145,7 @@ const exercises = [_]Exercise{
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.main_file = "097_bit_manipulation.zig",
|
.main_file = "097_bit_manipulation.zig",
|
||||||
.output = "x = 0; y = 1",
|
.output = "x = 1011; y = 1101",
|
||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.main_file = "098_bit_manipulation2.zig",
|
.main_file = "098_bit_manipulation2.zig",
|
||||||
|
@ -1128,9 +1253,51 @@ const exercises = [_]Exercise{
|
||||||
.main_file = "107_files2.zig",
|
.main_file = "107_files2.zig",
|
||||||
.output =
|
.output =
|
||||||
\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
\\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
\\Successfully Read 18 byte: It's zigling time!
|
\\Successfully Read 18 bytes: 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 =
|
||||||
|
|
|
@ -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.
|
||||||
const stdout = std.io.getStdOut().writer();
|
var stdout = std.fs.File.stdout().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.print("Hello world!\n", .{});
|
stdout.interface.print("Hello world!\n", .{});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,11 +10,11 @@ const std = @import("std");
|
||||||
const NumError = error{IllegalNumber};
|
const NumError = error{IllegalNumber};
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
const stdout = std.io.getStdOut().writer();
|
var stdout = std.fs.File.stdout().writer(&.{});
|
||||||
|
|
||||||
const my_num: u32 = getNumber();
|
const my_num: u32 = getNumber();
|
||||||
|
|
||||||
try stdout.print("my_num={}\n", .{my_num});
|
try stdout.interface.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.
|
||||||
|
|
|
@ -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,7 +13,8 @@
|
||||||
//
|
//
|
||||||
// const foo = bar orelse unreachable;
|
// const foo = bar orelse unreachable;
|
||||||
//
|
//
|
||||||
// See if you can find where we use this shortcut below.
|
// Check out where we use this shortcut below to change control flow
|
||||||
|
// based on if an optional value exists.
|
||||||
//
|
//
|
||||||
// Now let's make those elephant tails optional!
|
// Now let's make those elephant tails optional!
|
||||||
//
|
//
|
||||||
|
@ -31,14 +32,25 @@ 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.
|
||||||
elephantA.tail = &elephantB;
|
linkElephants(&elephantA, &elephantB);
|
||||||
elephantB.tail = &elephantC;
|
linkElephants(&elephantB, &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 {
|
||||||
|
@ -51,8 +63,9 @@ 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) ???;
|
|
||||||
|
|
||||||
e = e.tail.?;
|
// HINT: We want something similar to what `.?` does,
|
||||||
|
// but instead of ending the program, we want to exit the loop...
|
||||||
|
e = e.tail ???
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
// Earth will be doomed!
|
// the Earth will be doomed!
|
||||||
//
|
//
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 both values as
|
// in a switch statement. Please capture each value 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}),
|
||||||
|
|
|
@ -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 pound to kilograms at a
|
// We'll convert this weight from pounds to metric units at a
|
||||||
// conversion of 0.453592 kg to the pound.
|
// conversion of 0.453592 kg to the pound.
|
||||||
const shuttle_weight: f16 = 0.453592 * 4480e6;
|
const shuttle_weight: f16 = 0.453592 * 4480e3;
|
||||||
|
|
||||||
// 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}kg\n", .{shuttle_weight});
|
print("Shuttle liftoff weight: {d:.0} metric tons\n", .{shuttle_weight});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Floating further:
|
// Floating further:
|
||||||
|
|
|
@ -94,31 +94,35 @@ 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: []const u8,
|
// name: [:0]const u8,
|
||||||
// type: type,
|
// type: type,
|
||||||
// default_value: anytype,
|
// default_value_ptr: ?*const anyopaque,
|
||||||
// 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}", .{@typeInfo(Narcissus).Struct.fields[0].name});
|
print(" {s}", .{fields[0].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields[1].??? != void) {
|
if (fields[1].??? != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[1].name});
|
print(" {s}", .{fields[1].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields[2].??? != void) {
|
if (fields[2].??? != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[2].name});
|
print(" {s}", .{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
|
||||||
|
@ -136,14 +140,16 @@ 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, I've added this function to strip the filename from
|
// To fix this, we've added this function to strip the filename from
|
||||||
// the front of the type name in the dumbest way possible. (It returns
|
// the front of the type name. (It returns a slice of the type name
|
||||||
// a slice of the type name starting at character 14 (assuming
|
// starting at the index + 1 of character ".")
|
||||||
// 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 {
|
||||||
// Turn '065_builtins2.Narcissus' into 'Narcissus'
|
const indexOf = @import("std").mem.indexOf;
|
||||||
return @typeName(myType)[14..];
|
|
||||||
|
// Turn "065_builtins2.Narcissus" into "Narcissus"
|
||||||
|
const name = @typeName(myType);
|
||||||
|
return name[indexOf(u8, name, ".").? + 1 ..];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
// programatically loop through a series of items in situations
|
// programmatically 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) {
|
||||||
|
|
|
@ -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:", .{});
|
||||||
|
|
|
@ -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 "Bar()".
|
// * The value of @typeName(Bar()) is "<filename>.Bar()".
|
||||||
// * The value of @typeName(MyBar) is "Bar()".
|
// * The value of @typeName(MyBar) is "<filename>.Bar()".
|
||||||
// * The value of @typeName(@TypeOf(bar)) is "Bar()".
|
// * The value of @typeName(@TypeOf(bar)) is "<filename>.Bar()".
|
||||||
//
|
//
|
||||||
// You can also have completely anonymous structs. The value
|
// You can also have completely anonymous structs. The value
|
||||||
// of @typeName(struct {}) is "struct:<position in source>".
|
// of @typeName(struct {}) is "<filename>.<function>__struct_<nnn>".
|
||||||
//
|
//
|
||||||
const print = @import("std").debug.print;
|
const print = @import("std").debug.print;
|
||||||
|
|
||||||
|
|
|
@ -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,13 +95,15 @@ 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: []const u8,
|
// name: [:0]const u8,
|
||||||
// type: type,
|
// type: type,
|
||||||
// default_value: anytype,
|
// default_value_ptr: ?*const anyopaque,
|
||||||
// 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)
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 are only supported in 'for' loops.
|
// At the moment, ranges in loops 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:
|
||||||
|
|
|
@ -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 constant sized slice, this
|
// Instead of a simple integer or a slice with a constant size,
|
||||||
// program requires a slice to be allocated that is the same size as
|
// this program requires allocating a slice that is the same size
|
||||||
// an input array.
|
// as 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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Bit manipulations is a very powerful tool just also from Zig.
|
// Bit manipulation is a very powerful tool, 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.
|
||||||
//
|
//
|
||||||
// Often it is not easy to understand at first glance what exactly these
|
// At first glance, it is often not easy to understand what exactly these
|
||||||
// algorithms do when only "numbers" in memory areas change outwardly.
|
// algorithms do when only "numbers" in memory areas change outwardly.
|
||||||
// But it must never be forgotten that the numbers only represent the
|
// However, it should never be forgotten that the numbers only represent
|
||||||
// interpretation of the bit sequences.
|
// the 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
|
||||||
// 10.1 "Table of Operators".
|
// "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 {
|
||||||
|
|
||||||
// As in the example above, we use 1 and 0 as values for x and y
|
// Let us use 1101 and 1011 as values for x and y
|
||||||
var x: u8 = 1;
|
var x: u8 = 0b1101;
|
||||||
var y: u8 = 0;
|
var y: u8 = 0b1011;
|
||||||
|
|
||||||
// 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 = {d}; y = {d}\n", .{ x, y });
|
print("x = {b}; y = {b}\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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// Another useful practice for bit manipulation is setting bits as flags.
|
// Another useful application 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 take a simpler way, very similar in principle, we define
|
// fast. Instead we choose a simpler approach that is very similar in principle:
|
||||||
// a variable with at least 26 bits (e.g. u32) and also set the bit for each
|
// We define a variable with at least 26 bits (e.g. u32) and set the bit for
|
||||||
// letter found at the corresponding position.
|
// each letter that is found in 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 uses a 32 bit variable of which we need 26 bits
|
// we use 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:
|
||||||
|
|
|
@ -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#L29
|
// https://github.com/ziglang/zig/blob/master/lib/std/fmt.zig#L33
|
||||||
//
|
//
|
||||||
// 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 typically to convert
|
// These can be used in different ways, but generally to convert
|
||||||
// numerical values into various text representations. The
|
// numerical values into various text representations. The results
|
||||||
// results can be used for direct output to a terminal or stored
|
// can be used for direct output to a terminal or stored for
|
||||||
// for later use or written to a file. The latter is useful when
|
// 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 are passed
|
// By the way, the result of these escape sequences is 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-{x:0>4}.", .{twenty_two});
|
// print("Catch-0x{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 vary
|
// assignment because the number of digits in the numbers varies
|
||||||
// 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");
|
||||||
|
|
|
@ -41,12 +41,12 @@ pub fn main() void {
|
||||||
|
|
||||||
for (hex_nums, ???) |hn, ???| {
|
for (hex_nums, ???) |hn, ???| {
|
||||||
if (hn != dn) {
|
if (hn != dn) {
|
||||||
std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
|
print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.print("Arrays match!\n", .{});
|
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
|
||||||
|
|
|
@ -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 tasks, which always reaches its limits when pure computing
|
// the started and running tasks, which always reaches its limits when
|
||||||
// power is called up.
|
// pure computing 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.time.sleep(1500 * std.time.ns_per_ms);
|
std.Thread.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.time.sleep(200 * num * std.time.ns_per_ms);
|
std.Thread.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.time.sleep(work_time * std.time.ns_per_s);
|
std.Thread.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." });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Now that we are familiar with the principles of multi threading, we
|
// Now that we are familiar with the principles of multi-threading,
|
||||||
// boldly venture into a practical example from mathematics.
|
// let's 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 Leibnitz in 1682 in the journal
|
// formula, which was published by Leibniz in 1682 in the journal
|
||||||
// "Acta Eruditorum".
|
// "Acta Eruditorum".
|
||||||
// This is why this method has become known as the "Leibnitz series",
|
// This is why this method has become known as the "Leibniz 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:
|
||||||
|
@ -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 Leibnitz series is a series with a
|
// As we have already discovered, the Leibniz 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 diggits.
|
// otherwise you will not be able to see the additional digits.
|
||||||
|
|
|
@ -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 alien and hermit bookkeeping.
|
// which is good enough for fighting aliens and hermit bookkeeping.
|
||||||
//
|
//
|
||||||
// However, many other task require some interaction with the file system,
|
// However, many other tasks 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 provide a hierarchical structure for storing files
|
// The file system provides a hierarchical structure for storing files
|
||||||
// by organizing files into directories, which hold files and other directories,
|
// by organizing them into directories, which hold files and other directories,
|
||||||
// thus creating a tree structure for navigating.
|
// thus creating a tree structure that can be navigated.
|
||||||
//
|
//
|
||||||
// Fortunately, zig standard library provide a simple api for interacting
|
// Fortunately, the Zig Standard Library provides 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 put our output files.
|
// to store our output files.
|
||||||
cwd.makeDir("output") catch |e| switch (e) {
|
cwd.makeDir("output") catch |e| switch (e) {
|
||||||
// there are chance you might want to run this
|
// there is a 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
|
||||||
// been created, so we'll have to handle this error
|
// have 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 is any other unexpected error we just propagate it through
|
// if there's 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 the error up if there are any errors
|
// and propagate any error up
|
||||||
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 is a good habit to close a file after you are done with it
|
||||||
// so that other program can read it and prevent data corruption
|
// so that other programs 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 are a keyword in zig that
|
// if only there were a keyword in Zig that
|
||||||
// allow you "defer" code execute to the end of scope...
|
// allowed you to "defer" code execution to the end of the scope...
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
// !you are not allow to switch this two lines to before file closing line!
|
// you are not allowed to move these two lines above the 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 on your text editor, or
|
// 1. open the file in your text editor, or
|
||||||
// 2. print the content of the file in the console with command
|
// 2. print the content of the file in the console with the following 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 documentation of the struct `std.fs.Dir` here
|
// - go to the 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 option can you uses with those function?
|
// - what kind of options can you use with those functions?
|
||||||
|
|
|
@ -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 no point in writing to a file if we don't read from it am I right?
|
// Now there's no point in writing to a file if we don't read from it, am I right?
|
||||||
// let's wrote a program to read the content of the file that we just created.
|
// Let's write a program to read the content of the file that we just created.
|
||||||
//
|
//
|
||||||
// I am assuming you've created the appropriate files for this to work.
|
// I am assuming that 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 initalize an array of character with all letter 'A', and print it
|
// - Then, we initialize an array of characters with all letter 'A', and print it
|
||||||
// - After that, we read the content of the file to the array
|
// - After that, we read the content of the file into the array
|
||||||
// - Finally, we print out the read content
|
// - Finally, we print out the content we just read
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
// initalize an array of u8 with all letter 'A'.
|
// initialize an array of u8 with all letter 'A'
|
||||||
// we need to pick a size of the array, 64 seems like a good number.
|
// we need to pick the size of the array, 64 seems like a good number
|
||||||
// fix the initalization below
|
// fix the initialization 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, seem like threat of violence is not the answer in this case
|
// okay, seems like a 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 answer that are both vaild in this case
|
// hint: you might find two answers that are both valid in this case
|
||||||
const byte_read = zig_read_the_file_or_i_will_fight_you(&content);
|
const bytes_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} byte: {s}\n", .{
|
std.debug.print("Successfully Read {d} bytes: {s}\n", .{
|
||||||
byte_read,
|
bytes_read,
|
||||||
content, // change this line only
|
content, // change this line only
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
79
exercises/108_labeled_switch.zig
Normal file
79
exercises/108_labeled_switch.zig
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
//
|
||||||
|
// 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", .{});
|
||||||
|
}
|
147
exercises/109_vectors.zig
Normal file
147
exercises/109_vectors.zig
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
// 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});
|
||||||
|
}
|
484
exercises/110_quiz9.zig
Normal file
484
exercises/110_quiz9.zig
Normal file
|
@ -0,0 +1,484 @@
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// 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();
|
||||||
|
}
|
|
@ -12,6 +12,12 @@
|
||||||
# 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.
|
||||||
|
@ -23,9 +29,12 @@ fi
|
||||||
|
|
||||||
# Which version we have?
|
# Which version we have?
|
||||||
echo "Zig version" $(zig version)
|
echo "Zig version" $(zig version)
|
||||||
echo "Eowyn version 23.10.5.1, let's try our magic power."
|
echo "Eowyn version 25.1.9, 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
|
||||||
|
|
||||||
|
@ -54,3 +63,6 @@ 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
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
--- exercises/026_hello2.zig 2023-10-03 22:15:22.122241138 +0200
|
--- exercises/026_hello2.zig 2025-07-22 09:55:51.337832401 +0200
|
||||||
+++ answers/026_hello2.zig 2023-10-05 20:04:06.959431737 +0200
|
+++ answers/026_hello2.zig 2025-07-22 10:00:11.233348058 +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.print("Hello world!\n", .{});
|
- stdout.interface.print("Hello world!\n", .{});
|
||||||
+ try stdout.print("Hello world!\n", .{});
|
+ try stdout.interface.print("Hello world!\n", .{});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
--- exercises/034_quiz4.zig 2023-10-03 22:15:22.122241138 +0200
|
--- exercises/034_quiz4.zig 2025-07-22 09:55:51.337832401 +0200
|
||||||
+++ answers/034_quiz4.zig 2023-10-05 20:04:06.996099091 +0200
|
+++ answers/034_quiz4.zig 2025-07-22 10:05:08.320323184 +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 {
|
||||||
const stdout = std.io.getStdOut().writer();
|
var stdout = std.fs.File.stdout().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.print("my_num={}\n", .{my_num});
|
try stdout.interface.print("my_num={}\n", .{my_num});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
--- exercises/046_optionals2.zig 2023-10-03 22:15:22.122241138 +0200
|
--- exercises/046_optionals2.zig 2024-11-08 22:46:25.592875338 +0100
|
||||||
+++ answers/046_optionals2.zig 2023-10-05 20:04:07.049433424 +0200
|
+++ answers/046_optionals2.zig 2024-11-08 22:46:20.699447951 +0100
|
||||||
@@ -21,7 +21,7 @@
|
@@ -22,7 +22,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, // <---- make this optional!
|
+ tail: ?*Elephant = null, // Hmm... tail needs something...
|
||||||
visited: bool = false,
|
visited: bool = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@
|
@@ -66,6 +66,6 @@
|
||||||
// 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;
|
|
||||||
|
|
||||||
e = e.tail.?;
|
// HINT: We want something similar to what `.?` does,
|
||||||
|
// but instead of ending the program, we want to exit the loop...
|
||||||
|
- e = e.tail ???
|
||||||
|
+ e = e.tail orelse break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
--- exercises/058_quiz7.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/058_quiz7.zig 2024-10-28 09:06:49.448505460 +0100
|
||||||
+++ answers/058_quiz7.zig 2023-10-05 20:04:07.106101152 +0200
|
+++ answers/058_quiz7.zig 2024-10-28 09:35:14.631932322 +0100
|
||||||
@@ -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 both values as
|
// in a switch statement. Please capture each value 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}),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
--- exercises/060_floats.zig 2023-11-06 19:45:03.609687304 +0100
|
--- exercises/060_floats.zig 2025-03-03 20:23:40.255443963 +0400
|
||||||
+++ answers/060_floats.zig 2023-11-06 19:44:49.249419994 +0100
|
+++ answers/060_floats.zig 2025-03-03 20:29:58.554854977 +0400
|
||||||
@@ -43,7 +43,7 @@
|
@@ -43,7 +43,7 @@
|
||||||
//
|
//
|
||||||
// We'll convert this weight from pound to kilograms at a
|
// We'll convert this weight from pounds to metric units at a
|
||||||
// conversion of 0.453592 kg to the pound.
|
// conversion of 0.453592 kg to the pound.
|
||||||
- const shuttle_weight: f16 = 0.453592 * 4480e6;
|
- const shuttle_weight: f16 = 0.453592 * 4480e3;
|
||||||
+ const shuttle_weight: f32 = 0.453592 * 4.480e6;
|
+ const shuttle_weight: f32 = 0.453592 * 4.480e3;
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--- exercises/065_builtins2.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/065_builtins2.zig 2025-06-17 13:58:07.857258167 +0200
|
||||||
+++ answers/065_builtins2.zig 2023-10-05 20:04:07.136101712 +0200
|
+++ answers/065_builtins2.zig 2025-06-17 13:56:36.630415938 +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. ", .{
|
||||||
@@ -109,15 +109,15 @@
|
@@ -113,15 +113,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}", .{@typeInfo(Narcissus).Struct.fields[0].name});
|
print(" {s}", .{fields[0].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
- if (fields[1].??? != void) {
|
- if (fields[1].??? != void) {
|
||||||
+ if (fields[1].type != void) {
|
+ if (fields[1].type != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[1].name});
|
print(" {s}", .{fields[1].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
- if (fields[2].??? != void) {
|
- if (fields[2].??? != void) {
|
||||||
+ if (fields[2].type != void) {
|
+ if (fields[2].type != void) {
|
||||||
print(" {s}", .{@typeInfo(Narcissus).Struct.fields[2].name});
|
print(" {s}", .{fields[2].name});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
--- exercises/071_comptime6.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/071_comptime6.zig 2024-09-02 19:21:50.250454978 +0200
|
||||||
+++ answers/071_comptime6.zig 2023-10-05 20:04:07.162768879 +0200
|
+++ answers/071_comptime6.zig 2024-09-02 19:21:23.553250563 +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| {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--- exercises/076_sentinels.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/076_sentinels.zig 2024-09-02 19:27:04.336781039 +0200
|
||||||
+++ answers/076_sentinels.zig 2023-10-05 20:04:07.186102649 +0200
|
+++ answers/076_sentinels.zig 2024-09-02 19:26:15.709134934 +0200
|
||||||
@@ -82,7 +82,7 @@
|
@@ -82,7 +82,7 @@
|
||||||
print("Array:", .{});
|
print("Array:", .{});
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
--- exercises/082_anonymous_structs3.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/082_anonymous_structs3.zig 2025-03-14 16:41:17.892873287 +0200
|
||||||
+++ answers/082_anonymous_structs3.zig 2023-10-05 20:04:07.212769813 +0200
|
+++ answers/082_anonymous_structs3.zig 2025-03-14 16:40:56.043829543 +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:
|
||||||
@@ -117,9 +117,9 @@
|
@@ -119,9 +119,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} ", .{
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
--- 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 {
|
|
|
@ -1,10 +0,0 @@
|
||||||
--- 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 {
|
|
|
@ -1,16 +0,0 @@
|
||||||
--- 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", .{});
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
--- 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 {}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
--- 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});
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
--- 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 });
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
--- 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});
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
--- 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", .{});
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
--- exercises/097_bit_manipulation.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/097_bit_manipulation.zig 2025-05-12 21:25:03.395385743 +0200
|
||||||
+++ answers/097_bit_manipulation.zig 2023-10-05 20:04:07.282771124 +0200
|
+++ answers/097_bit_manipulation.zig 2025-05-12 21:22:57.472986976 +0200
|
||||||
@@ -80,7 +80,7 @@
|
@@ -80,7 +80,7 @@
|
||||||
y ^= x;
|
y ^= x;
|
||||||
|
|
||||||
|
@ -7,5 +7,5 @@
|
||||||
- ???;
|
- ???;
|
||||||
+ x ^= y;
|
+ x ^= y;
|
||||||
|
|
||||||
print("x = {d}; y = {d}\n", .{ x, y });
|
print("x = {b}; y = {b}\n", .{ x, y });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
--- exercises/099_formatting.zig 2023-10-03 22:15:22.125574535 +0200
|
--- exercises/099_formatting.zig 2024-11-07 21:45:10.459123650 +0100
|
||||||
+++ answers/099_formatting.zig 2023-10-05 20:04:07.292771311 +0200
|
+++ answers/099_formatting.zig 2024-11-07 21:43:55.154345991 +0100
|
||||||
@@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
std.debug.print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
|
print("Uh oh! Found a mismatch: {d} vs {d}\n", .{ hn, dn });
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
--- exercises/106_files.zig 2024-03-27 16:52:05.660910200 +0800
|
--- exercises/106_files.zig 2025-03-13 15:26:59.532367792 +0200
|
||||||
+++ answers/106_files.zig 2024-03-27 16:52:09.649422200 +0800
|
+++ answers/106_files.zig 2025-03-14 22:04:52.243435159 +0200
|
||||||
@@ -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 is any other unexpected error we just propagate it through
|
// if there's 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 are a keyword in zig that
|
// if only there were a keyword in Zig that
|
||||||
// allow you "defer" code execute to the end of scope...
|
// allowed you to "defer" code execution to the end of the scope...
|
||||||
- file.close();
|
- file.close();
|
||||||
+ defer file.close();
|
+ defer file.close();
|
||||||
|
|
||||||
// !you are not allow to switch this two lines to before file closing line!
|
// you are not allowed to move these two lines above the file closing line!
|
||||||
const byte_written = try file.write("It's zigling time!");
|
const byte_written = try file.write("It's zigling time!");
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
--- exercises/107_files2.zig 2024-03-27 16:51:56.199719600 +0800
|
--- exercises/107_files2.zig 2025-03-13 15:26:59.532367792 +0200
|
||||||
+++ answers/107_files2.zig 2024-03-27 16:52:01.650935300 +0800
|
+++ answers/107_files2.zig 2025-03-14 22:08:35.167953736 +0200
|
||||||
@@ -33,7 +33,7 @@
|
@@ -33,7 +33,7 @@
|
||||||
// initalize an array of u8 with all letter 'A'.
|
// initialize an array of u8 with all letter 'A'
|
||||||
// we need to pick a size of the array, 64 seems like a good number.
|
// we need to pick the size of the array, 64 seems like a good number
|
||||||
// fix the initalization below
|
// fix the initialization 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`
|
||||||
|
@ -12,15 +12,15 @@
|
||||||
@@ -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 answer that are both vaild in this case
|
// hint: you might find two answers that are both valid in this case
|
||||||
- const byte_read = zig_read_the_file_or_i_will_fight_you(&content);
|
- const bytes_read = zig_read_the_file_or_i_will_fight_you(&content);
|
||||||
+ const byte_read = try file.read(&content);
|
+ const bytes_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} byte: {s}\n", .{
|
std.debug.print("Successfully Read {d} bytes: {s}\n", .{
|
||||||
byte_read,
|
bytes_read,
|
||||||
- content, // change this line only
|
- content, // change this line only
|
||||||
+ content[0..byte_read], // change this line only
|
+ content[0..bytes_read], // change this line only
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
18
patches/patches/108_labeled_switch.patch
Normal file
18
patches/patches/108_labeled_switch.patch
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
--- 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", .{});
|
||||||
|
}
|
13
patches/patches/109_vectors.patch
Normal file
13
patches/patches/109_vectors.patch
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
--- 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;
|
||||||
|
}
|
||||||
|
|
56
patches/patches/110_quiz9.patch
Normal file
56
patches/patches/110_quiz9.patch
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
--- 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();
|
|
@ -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(tmp_path);
|
const cleanup = b.addRemoveDirTree(.{ .src_path = .{ .owner = b, .sub_path = 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(tmp_path);
|
const cleanup = b.addRemoveDirTree(.{ .src_path = .{ .owner = b, .sub_path = 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, _: *std.Progress.Node) !void {
|
fn make(step: *Step, _: Step.MakeOptions) !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();
|
||||||
|
|
||||||
const stderr = stderr_file.reader();
|
var stderr = stderr_file.readerStreaming(&.{});
|
||||||
{
|
{
|
||||||
// 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, _: *std.Progress.Node) !void {
|
fn make(step: *Step, _: Step.MakeOptions) !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();
|
||||||
|
|
||||||
const stderr = stderr_file.reader();
|
var stderr = stderr_file.readerStreaming(&.{});
|
||||||
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,12 +297,9 @@ fn check(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn readLine(reader: fs.File.Reader, buf: []u8) !?[]const u8 {
|
fn readLine(reader: *fs.File.Reader, buf: []u8) !?[]const u8 {
|
||||||
if (try reader.readUntilDelimiterOrEof(buf, '\n')) |line| {
|
try reader.interface.readSliceAll(buf);
|
||||||
return mem.trimRight(u8, line, " \r\n");
|
return mem.trimRight(u8, buf, " \r\n");
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fails with a custom error message.
|
/// Fails with a custom error message.
|
||||||
|
@ -325,7 +322,7 @@ const FailStep = struct {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make(step: *Step, _: *std.Progress.Node) !void {
|
fn make(step: *Step, _: Step.MakeOptions) !void {
|
||||||
const b = step.owner;
|
const b = step.owner;
|
||||||
const self: *FailStep = @alignCast(@fieldParentPtr("step", step));
|
const self: *FailStep = @alignCast(@fieldParentPtr("step", step));
|
||||||
|
|
||||||
|
@ -368,7 +365,7 @@ const HealStep = struct {
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make(step: *Step, _: *std.Progress.Node) !void {
|
fn make(step: *Step, _: Step.MakeOptions) !void {
|
||||||
const b = step.owner;
|
const b = step.owner;
|
||||||
const self: *HealStep = @alignCast(@fieldParentPtr("step", step));
|
const self: *HealStep = @alignCast(@fieldParentPtr("step", step));
|
||||||
|
|
||||||
|
@ -405,7 +402,8 @@ 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 tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ Build.hex64(rand_int);
|
const rand_hex64 = std.fmt.hex(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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user