114 Commits

Author SHA1 Message Date
Chris Boesch c85daab1a1 Merge pull request 'rivendell: Remove the resetLine function' (#488) from manliop/exercises:remove-resetLine into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/488
2026-07-02 23:56:15 +02:00
Manlio Perillo f6c3e4edc4 rivendell: Remove the resetLine function
This was necessary in the past (commit 30db9d, 2023-04-17).

The new Progress.zig and, probably, the fact that exercises run on a
separate executable solved the problem.
2026-07-02 15:07:51 +02:00
Chris Boesch a5ed7b636b Merge pull request 'added explanation about stdout' (#487) from i481-stdout-explanation into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/487
2026-07-01 16:32:38 +02:00
Chris Boesch ba99eea43b created also new patch file 2026-07-01 16:28:04 +02:00
Chris Boesch 708436c63d added explanation about stdout 2026-07-01 16:20:41 +02:00
Chris Boesch e65b359055 Merge pull request 'Added comment to help run exercize 108_threading2.zig with the ReleaseFast flag.' (#482) from atici/ziglings-exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/482
2026-07-01 16:11:19 +02:00
atici 719f7f0740 removed weird characters 2026-07-01 16:29:20 +03:00
atici 9a1ff49206 Added comment to help run exercize 108 with the ReleaseFast flag. 2026-06-30 11:33:03 +02:00
Chris Boesch 89fe65c158 Merge pull request 'Fix count to actually _be_ a count' (#458) from strega-nil/ziglings-exercises:make-threading-mathier into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/458
2026-06-30 00:25:04 +02:00
Chris Boesch 6e34e835e0 Merge branch 'main' into make-threading-mathier 2026-06-30 00:09:19 +02:00
Nicole Patricia Mazzuca a03b676bed Fix count to actually _be_ a count
Also, start from 1 in the positive direction, instead of 5, since that
is more how the thing works (sorry, mathematician brain).

Additionally, adds some extra information in the comments:
information about how long the code would take if one actually sets
count to 2,5 * 10^12, and adding a plus in front of the 4/1 to show that
that is in fact the first element of the "plus" side.

Finally, add an (floating point) error to the debug print.
2026-06-29 23:30:22 +02:00
Chris Boesch 151db7bcd4 Merge pull request 'Remove prefilled solution' (#485) from it3x/remove-solution into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/485
2026-06-29 20:30:13 +02:00
it3x b39f223b8c Fix patch for 109_files 2026-06-29 17:14:05 +00:00
it3x e932615712 Remove prefilled solution
See #478
2026-06-29 17:09:23 +00:00
it3x 98b831aec4 Refresh 005_arrays2 exercise
Updated the exercise to use SIMD. This allows us to
cross-reference the concept from 112_vectors back to 005_arrays2,
introducing a handy feature of vectors early on.

See #430
2026-06-29 18:48:05 +02:00
Chris Boesch f69c79bcb6 Merge pull request 'added absolute path to exercises, also for tests' (#484) from root_path into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/484
2026-06-29 18:38:40 +02:00
Chris Boesch 951351aa2e added absolute path to exercises, also for tests 2026-06-29 18:30:49 +02:00
Chris Boesch e1bf750b03 Merge pull request 'fix typo in CONTRIBUTING.md (License)' (#477) from morerunes/ziglings-exercises:feature/fix-contributing-typo into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/477
2026-06-29 15:15:02 +02:00
Chris Boesch 7e2cf64ae8 Merge pull request 'Update color setup using new IO/Terminal API' (#483) from manliop/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/483
2026-06-29 11:47:09 +02:00
Manlio Perillo 4f5a26345f Update color setup using new IO/Terminal API
The new API add support for NO_COLOR and CLICOLOR_FORCE.

Move color variables to a namespace.

Add a message in case colors are not available un unix systems.

Add a message in case on Windows colors are available (on old versions).
2026-06-28 15:29:48 +02:00
Aster Echols 4f240aaefa fix typo in CONTRIBUTING.md (License) 2026-06-26 23:27:20 -04:00
Chris Boesch d093f37e1e Merge pull request 'Fix misleading comments' (#476) from it3x/fix-threads-comment into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/476
2026-06-26 16:41:49 +02:00
it3x fdf47e3b8c Fix misleading comments
See #450
2026-06-26 12:14:02 +00:00
Chris Boesch 12c8511412 Merge pull request 'fix typo: field_types is a slice of strings -> field_types is a slice of types' (#473) from omihirofumi/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/473
2026-06-26 12:45:34 +02:00
Chris Boesch e55204d342 Merge pull request 'added absolute path to exercises' (#475) from root_path into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/475
2026-06-26 12:40:52 +02:00
Chris Boesch fe465a466e added absolute path to exercises 2026-06-26 12:29:25 +02:00
omihirofumi f6514ebb13 fix comment field_types is a slice of (strings -> types) 2026-06-25 12:26:17 +09:00
Chris Boesch 529b294cc3 Merge pull request 'skipped #5 until a better solution is found' (#472) from minor-fixes into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/472
2026-06-22 17:54:36 +02:00
Chris Boesch e928d2c7b1 skipped #5 until a better solution is found 2026-06-22 17:45:35 +02:00
Chris Boesch a463aafb91 Merge pull request '107: change comment "brackets" -> "braces"' (#471) from xiaolizhi/exercises:text-fix1 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/471
2026-06-21 22:45:37 +02:00
xiaolizhi 11868112c5 107: change comment "brackets" -> "braces"
Signed-off-by: xiaolizhi <vogelwanderung@pm.me>
2026-06-21 21:22:12 +02:00
Chris Boesch d52661fd39 Merge pull request 'fix style mismatch on 065_builtins2.zig' (#469) from garipew/exercises:fix_typo into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/469
2026-06-21 17:51:03 +02:00
Chris Boesch fe1c9fb9bd Merge branch 'main' into fix_typo 2026-06-21 17:29:19 +02:00
Gabriel Sa 935d268280 fix style mismatch on patches/065_builtins2.zig
Rename `fetchTheMostBeautifulType` to `FetchTheMostBeautifulType` to
follow Zig's standard naming practice.
2026-06-21 11:58:29 -03:00
Chris Boesch 151450cc7d Merge pull request 'minor fixes' (#470) from minor-fixes into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/470
2026-06-21 16:48:15 +02:00
Chris Boesch cbcb570248 expanded explanation about self vs *self 2026-06-21 16:16:31 +02:00
Chris Boesch f3df08d8e0 fixed description 2026-06-21 15:43:28 +02:00
Chris Boesch 1ad3534e4d fixed grammar 2026-06-21 15:31:18 +02:00
Chris Boesch 894ebf1a37 Merge pull request 'Use significand terminology' (#465) from FilipKubis/ziglings:significand-consistent-naming into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/465
2026-06-21 14:24:14 +02:00
Chris Boesch 50d699df57 Merge pull request 'fix typos in CONTRIBUTING.md' (#461) from tariq/exercises:fix-typo-contributing-md into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/461
2026-06-21 13:42:56 +02:00
Chris Boesch 272ab59937 Merge pull request 'Fixed a couple of exercise references as they are no longer accurate after the recent changes' (#457) from killyosaur/zigling-exercises:fixing-exercise-references into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/457
2026-06-21 13:12:01 +02:00
Chris Boesch bfd3d013e0 Merge pull request 'Fixed typo: "you will wish" -> "you wish"' (#455) from xaviii/exercises:xaviii-typo into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/455
2026-06-21 12:49:02 +02:00
Chris Boesch 66de1124bc Merge pull request 'fix typo' (#452) from mmsaki/exercises:114-typo into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/452
2026-06-21 12:37:21 +02:00
Gabriel Sa dce9c803bf fix style mismatch on 065_builtins2.zig
Rename `fetchTheMostBeautifulType` to `FetchTheMostBeautifulType` to
follow Zig's standard naming practice.
2026-06-20 19:09:14 -03:00
Filip Kubiš 2cf2a885fd Use significand terminology
As exercise 60 says: Donald Knuth would not be happy with us calling
this mantissa. Proposing to use the less amiguous term significand
2026-06-20 11:18:05 +02:00
Tariq Ibrahim 90f847e8b9 fix typos in CONTRIBUTING.md
Signed-off-by: Tariq Ibrahim <tariq181290@gmail.com>
2026-06-13 17:28:48 -07:00
Michael Walker c24a170ca8 missed a change to add the phrase 'and quiz 95' to the statement regarding Zig's Io interface. 2026-06-11 09:59:17 -04:00
Michael Walker bc443a1113 Fixed a couple of exercise references as they are no longer accurate after the recent changes
- Exercise 107 claimed the async exercises are 84-91, they are actually 85-94 and quiz 95
- Exercise 113 claimed the bit manip exercise is 097, it is now 100
- Exercise 114 has the bit manipulation exercises as 097 and 098, they are now 100 and 101
- Exercise 114 has the last switch exercise as being 108, it is now 111.
2026-06-11 09:58:17 -04:00
xaviii d39241a20e Fixed typo: "you will wish" -> "you wish" 2026-06-09 08:54:40 +02:00
mmsaki 1891f99502 fix typo 2026-06-08 05:55:17 +02:00
Chris Boesch 83a89702b2 Merge pull request 'Improve command line style' (#451) from manliop/exercises:improve-command-line-style into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/451
2026-06-06 22:11:53 +02:00
Manlio Perillo 72a441a6bd Improve command line style
Parse the command line following the code from the Zig compiler cli.

Use the args iterator instead of slice, and use while instead of for
loop.

Use the new std.mem.cutPrefix instead of the custom function prefix.

Move Context struct to the top.
2026-06-06 17:39:42 +02:00
Chris Boesch 6aaf048935 Merge pull request 'removed unnecessary shell prompt' (#449) from readme-fixes into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/449
2026-06-04 20:17:48 +02:00
Chris Boesch febc5e8bd2 removed unnecessary shell prompt 2026-06-04 20:16:35 +02:00
Chris Boesch b7ff71cb9e Merge pull request 'Fix exercises 65, 71, 82 due to the build system update' (#443) from mark2185/exercises:fix-build-update into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/443
2026-06-03 18:37:25 +02:00
Chris Boesch a403436fe8 Merge branch 'main' into fix-build-update 2026-06-03 17:39:58 +02:00
Chris Boesch beeca8d510 Merge pull request 'Elrond has moved to Rivendell (where he lives)' (#447) from rivendell into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/447
2026-06-02 20:26:16 +02:00
Chris Boesch 01ad296114 Elrond has moved to Rivendell (where he lives) 2026-06-02 20:17:48 +02:00
Chris Boesch 9b87962595 Merge pull request 'fixed patch file path' (#446) from contribute-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/446
2026-06-02 20:05:42 +02:00
Chris Boesch 9d6ef1f5ba fixed patch file path 2026-06-02 20:03:18 +02:00
Chris Boesch fadd2ea4c7 Merge pull request 'fixed minor issues' (#444) from elrond-fixes into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/444
2026-06-02 12:10:11 +02:00
Chris Boesch e69a865ce3 fixed minor issues 2026-06-02 11:56:34 +02:00
Chris Boesch f6dda8181a Merge pull request 'Add another defer exercise' (#438) from mark2185/exercises:defer3 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/438
2026-06-02 08:56:50 +02:00
Luka Markušić bd55c4ac5a fixup! Add another defer exercise 2026-06-02 06:12:06 +02:00
Luka Markušić e61bedaa25 Add another defer exercise 2026-06-01 22:32:11 +02:00
Chris Boesch ac9f96459c Merge pull request 'fix removed array multiplication' (#436) from mark2185/exercises:array_mult into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/436
2026-06-01 20:50:08 +02:00
Luka Markušić 014560c3f5 fix removed array multiplication 2026-06-01 16:05:36 +02:00
Luka Markušić 3b865a0c17 Fix 082_anonymous_structs3.zig because of new build system 2026-06-01 16:00:27 +02:00
Luka Markušić 63c798637c Fix 071_comptime6.zig because of new build system 2026-06-01 15:37:05 +02:00
Luka Markušić 4480762e83 Fix 065_builtins2.zig because of new build system 2026-06-01 15:34:01 +02:00
Chris Boesch 4ce3ed23f3 Merge pull request 'updated version in history' (#439) from build-system-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/439
2026-06-01 00:23:49 +02:00
Chris Boesch 8db82ef84e updated version in history 2026-06-01 00:22:32 +02:00
Chris Boesch 6d23c876d6 Merge pull request 'started fix for new build system' (#437) from build-system-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/437
2026-06-01 00:20:31 +02:00
Chris Boesch fa36a4520f removed ziglings logic from build file 2026-05-31 23:54:20 +02:00
Chris Boesch 7889d15b84 moved ziglings logic to elrond 2026-05-31 23:48:17 +02:00
Chris Boesch 8a0077e83b Merge pull request 'fix typos' (#435) from mark2185/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/435
2026-05-31 21:12:39 +02:00
Chris Boesch 762d8b8fa5 started fix for new build system 2026-05-31 20:00:50 +02:00
Luka Markušić 7300e87028 fix contributing instructions for patches 2026-05-31 19:10:30 +02:00
Luka Markušić 1c3238d619 fix typos 2026-05-31 19:10:30 +02:00
Chris Boesch 000e9448d5 Merge pull request '051: add a comma' (#429) from xiaolizhi/exercises:comma-fix into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/429
2026-05-25 12:49:37 +02:00
xiaolizhi f434dbfc01 051: add a comma 2026-05-25 10:37:05 +02:00
Chris Boesch 8468040a8e Merge pull request 'added expalantion for const pointer' (#428) from pointer into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/428
2026-05-22 22:27:15 +02:00
Chris Boesch 1d965491c1 added expalantion for const pointer 2026-05-22 21:58:19 +02:00
Chris Boesch 926fc04d81 Merge pull request 'Instruct user not to worry about sentinel syntax in 065_builtins2' (#425) from nickgrim/exercises:sentinel_warning into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/425
2026-05-18 19:50:18 +02:00
Chris Boesch 6b8bbfd980 Merge pull request 'Minor grammar fix to comment in 005_arrays2' (#426) from nickgrim/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/426
2026-05-18 19:49:52 +02:00
Nick Grimshaw e96ce4da8e Minor grammar fix to comment in 005_arrays2 2026-05-18 17:38:04 +01:00
Nick Grimshaw 42a417f9a5 Instruct user not to worry about sentinel syntax in 065_builtins2 2026-05-18 17:29:17 +01:00
Chris Boesch dde51b3126 Merge pull request 'fixed removed array multiplication' (#423) from array_mult into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/423
2026-05-04 17:30:31 +02:00
Chris Boesch 8af3372cf2 fixed removed array multiplication 2026-05-04 17:15:30 +02:00
Chris Boesch 1ba1e301a8 Merge pull request 'adjusted comment to zig 0.16' (#422) from i421 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/422
2026-05-02 23:08:18 +02:00
Chris Boesch 1c897e1951 adjusted comment to zig 0.16 2026-05-02 19:25:52 +02:00
Chris Boesch 6048e6ef21 Merge pull request 'fixed compileError printing' (#419) from i418 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/419
2026-04-30 19:53:56 +02:00
Chris Boesch 3a782a96d5 fixed compileError printing 2026-04-30 19:30:27 +02:00
Chris Boesch 4bdcf195b9 Merge pull request 'docs: update wrong comment reference to prior exercise in 110_files2' (#416) from kwyse/exercises:fix-110_files2 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/416
2026-04-23 10:20:48 +02:00
kwyse 656d6824d9 docs: update wrong reference to prior exercise
The comment should reference exercise 109, not 106.
2026-04-22 22:49:10 +02:00
Chris Boesch 0d9652d867 Merge pull request 'fix: move exercise 100 celebration comment to the right file' (#413) from Frost-Phoenix/exercises:fix_comment into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/413
2026-04-21 22:25:41 +02:00
Chris Boesch 945d9b84b0 Merge branch 'main' into fix_comment 2026-04-21 22:13:57 +02:00
Chris Boesch c7afea6ef5 Merge pull request 'added skip to the C exercises' (#414) from fixing_cimport into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/414
2026-04-20 20:20:14 +02:00
Chris Boesch 6c2531b824 added skip to the C exercises 2026-04-20 19:38:31 +02:00
Chris Boesch 42db1f05a7 Merge pull request 'docs: fix wrong prerequisite in 110_files2.zig' (#412) from Kercy/exercises:main into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/412
2026-04-20 19:28:22 +02:00
Frost-Phoenix 333ca33e78 fix: move exercise 100 celebration comment to the right file 2026-04-20 17:40:49 +02:00
KercyDing 5eb9a30abe docs: fix wrong prerequisite in 110_files2.zig 2026-04-20 04:22:21 +08:00
Chris Boesch 0603b66cc7 Merge pull request 'fixed readme' (#411) from readme into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/411
2026-04-19 21:29:03 +02:00
Chris Boesch a79e923768 fixed readme 2026-04-19 21:26:05 +02:00
Chris Boesch 234e829b15 Merge pull request 'fixed readme' (#409) from readme into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/409
2026-04-19 15:01:03 +02:00
Chris Boesch 823b4987f7 fixed readme 2026-04-19 14:49:29 +02:00
Chris Boesch ca8aee84ad Merge pull request 'Demonstrate the benefit of a mutex' (#408) from MatthijsBlom/ziglings:matthijsblom-patch-1 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/408
2026-04-19 14:21:24 +02:00
MatthijsBlom c4cb76ea8e Update patches/patches/091_async7.patch 2026-04-18 23:50:13 +02:00
MatthijsBlom e0470c4f45 demonstrate the benefit of the mutex 2026-04-18 23:46:59 +02:00
Chris Boesch f35c9419d2 Merge pull request 'fix: mutex unlock call in async example' (#407) from gabelluardo/ziglings:fix-async91 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/407
2026-04-18 21:50:49 +02:00
Gabriele Belluardo 656e1ba1e7 fix: mutex unlock call in async example 2026-04-18 11:55:49 +02:00
Chris Boesch 8e2cbf7b5a Merge pull request 'Some deprecated functions removed and a progress bar added.' (#403) from fixes_v016 into main
Reviewed-on: https://codeberg.org/ziglings/exercises/pulls/403
2026-04-15 18:39:30 +02:00
Chris Boesch d918dbae59 Some deprecated functions removed and a progress bar added. 2026-04-15 18:21:21 +02:00
Chris Boesch 4746ccc1c8 v0.16 2026-04-15 14:33:47 +02:00
52 changed files with 1638 additions and 2127 deletions
+7 -7
View File
@@ -8,11 +8,11 @@ of Ziglings Maintenance!
Ziglings is intended for programmers of all experience levels. No
specific language knowledge is expected. Anyone who can install
the current Zig snapshot, setup a copy of Ziglings, and knows
the current Zig snapshot, set up a copy of Ziglings, and knows
common language building blocks (if/then/else, loops, and
functions) is ready for Ziglings.
Zigling's excercises are self-contained. If you can't solve
Zigling's exercises are self-contained. If you can't solve
an exercise from the information you've gleaned so far from
Ziglings, then the exercise probably needs some additional work.
Please file an issue!
@@ -95,7 +95,7 @@ interface. Specifically:
eternal Ziglings contributor glory is yours!
## Licence
## License
If you submit your contribution to the repository/project,
you agree that your contribution will be licensed under
@@ -111,7 +111,7 @@ directory.
Every Ziglings exercise contains mistakes on purpose.
To keep our automated tests happy, each exercise also
has a patch in `patches/healed` that “heals” it.
has a patch in `patches/patches` that “heals” it.
When you change an exercise, you will usually need to update
its patch too. Thats where our little helper Gollum comes in:
@@ -119,12 +119,12 @@ its patch too. Thats where our little helper Gollum comes in:
1. In the project root, create a folder called `answers/`
2. Put your solved version of the exercise file in there
3. Back in the root, run:
`./patches/gollum <exercise-number>`<br>
For example: `./patches/gollum 106`
`./patches/gollum.sh <exercise-number>`<br>
For example: `./patches/gollum.sh 106`
This will generate a shiny new patch.
Double-check everything by asking the magical Eowyn:
`./patches/eowyn`<br>
`./patches/eowyn.sh`<br>
If all tests pass: You are done!
Dont forget to commit the patch file.
+7 -24
View File
@@ -44,8 +44,8 @@ in that case, you can download it directly from the [build directory](https://zi
Verify the installation and build number of `zig` like so:
```
$ zig version
0.16.0-dev.xxxx+xxxxxxxxx
zig version
0.17.0-dev.xxxx+xxxxxxxxx
```
Clone this repository with Git:
@@ -73,11 +73,11 @@ the appropriate tag.
The Zig language is under very active development. In order to be
current, Ziglings tracks **development** builds of the Zig
compiler rather than versioned **release** builds. The last
stable release was `0.15.2`, but Ziglings needs a dev build with
pre-release version "0.16.0" and a build number at least as high
stable release was `0.16`, but Ziglings needs a dev build with
pre-release version "0.17.0-dev" and a build number at least as high
as that shown in the example version check above.
**Hint**: You can find a version change summary at the end of this README.
**Hint**: You can find a summary of breaking changes at the end of this README.
It is likely that you'll download a build which is _greater_ than
the minimum.
@@ -106,12 +106,6 @@ Or let Ziglings pick an exercise for you:
zig build -Drandom
```
You can also run without checking for correctness:
```
zig build -Dn=19 test
```
Or skip the build system entirely and interact directly with the
compiler if you're into that sort of thing:
@@ -119,22 +113,10 @@ compiler if you're into that sort of thing:
zig run exercises/001_hello.zig
```
Calling all wizards: To prepare an executable for debugging,
install it to zig-cache/bin with:
```
zig build -Dn=19 install
```
To get a list of all possible options, run:
```
zig build -Dn=19 -l
install Install 019_functions2.zig to prefix path
uninstall Uninstall 019_functions2.zig from prefix path
test Run 019_functions2.zig without checking output
...
zig build -h
```
To reset the progress (have it run all the exercises that have already been completed):
@@ -211,6 +193,7 @@ Zig Standard Library
### Version Changes
* 2026-05-31 zig 0.17.0-dev.607 - zig build: separate the maker process from the configurer process, see[#35428](https://codeberg.org/ziglang/zig/pulls/35428)
* 2026-03-20 zig 0.16.0-dev.2915 - `GeneralPurposeAllocator` was changed to `DebugAllocator`
* 2026-02-04 zig 0.16.0-dev.2471 - added process.Child.Cwd, see [#31090](https://codeberg.org/ziglang/zig/pulls/31090)
* 2026-01-09 zig 0.16.0-dev.2075 - move randomness API to `std.Io`, see [#30709](https://codeberg.org/ziglang/zig/pulls/30709)
+47 -1340
View File
File diff suppressed because it is too large Load Diff
+19 -8
View File
@@ -1,5 +1,5 @@
//
// Zig has some fun array operators.
// Zig has one array operator.
//
// You can use '++' to concatenate two arrays:
//
@@ -7,12 +7,8 @@
// const b = [_]u8{ 3,4 };
// const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5
//
// You can use '**' to repeat an array:
//
// const d = [_]u8{ 1,2,3 } ** 2; // equals 1 2 3 1 2 3
//
// Note that both '++' and '**' only operate on arrays while your
// program is _being compiled_. This special time is known in Zig
// Note that '++' only operates on arrays while your program is
// _being compiled_. This special time is known in Zig
// parlance as "comptime" and we'll learn plenty more about that
// later.
//
@@ -30,7 +26,13 @@ pub fn main() void {
// (Problem 2)
// Please set this array using repetition.
// It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
const bit_pattern = [_]u8{ ??? } ** 3;
const bit_pattern_unit = ???;
// How long should the bit pattern be?
const len = ???;
// For now, don't worry about the use of SIMD.
const bit_pattern: [len]u8 = std.simd.repeat(len, bit_pattern_unit);
// Okay, that's all of the problems. Let's see the results.
//
@@ -56,3 +58,12 @@ pub fn main() void {
std.debug.print("\n", .{});
}
// For the curious:
//
// The `std.simd.repeat` function takes a target length and a pattern,
// and returns a vector filled with that pattern repeated to the
// desired length.
//
// For example, `repeat(5, [_]u8{1, 2})` will return a vector
// equivalent to `.{1, 2, 1, 2, 1}`.
+1 -5
View File
@@ -27,10 +27,6 @@ pub fn main() void {
const d: u8 = ziggy[???];
// (Problem 2)
// Use the array repeat '**' operator to make "ha ha ha ".
const laugh = "ha " ???;
// (Problem 3)
// Use the array concatenation '++' operator to make "Major Tom".
// (You'll need to add a space as well!)
const major = "Major";
@@ -38,7 +34,7 @@ pub fn main() void {
const major_tom = major ??? tom;
// That's all the problems. Let's see our results:
std.debug.print("d={u} {s}{s}\n", .{ d, laugh, major_tom });
std.debug.print("d={u} {s}\n", .{ d, major_tom });
// Keen eyes will notice that we've put 'u' and 's' inside the '{}'
// placeholders in the format string above. This tells the
// print() function to format the values as a UTF-8 character and
+8
View File
@@ -30,3 +30,11 @@ pub fn main(init: std.process.Init) !void {
// We just learned of a single statement which can accomplish this.
stdout.print("Hello world!\n", .{});
}
// Now you must be thinking "Why would I need to do this instead of good
// old 'std.debug.print()'?", but here's what you need to understand:
// 'std.debug.print()' prints its output to stderr, while the stdout_writer
// approach prints to stdout.
// A common practice is to pipe output of a command to other commands,
// and in order for the piping to work, those commands expect thier input
// to come from stdout, not stderr.
+23
View File
@@ -10,6 +10,8 @@ pub fn main() void {
for (animals) |a| printAnimal(a);
std.debug.print("done.\n", .{});
std.debug.print("Answer to everything? {d}\n", .{calculateTheUltimateQuestionOfLife()});
}
// This function is _supposed_ to print an animal name in parentheses
@@ -35,3 +37,24 @@ fn printAnimal(animal: u8) void {
std.debug.print("Unknown", .{});
}
// This function is supposed to calculate the answer to the
// ultimate question of life, the universe, and everything,
// but it needs to be deferred as far in the future as possible,
// in order to gather more data.
//
// When there are multiple defers in a single block, they are executed in reverse order.
// This example might seem silly, but it's important to know when e.g.
// deinitializing containers whose elements need to be deinitialized first.
fn calculateTheUltimateQuestionOfLife() u32 {
var x: u32 = 100;
// Try reordering the statements to get the answer 42
{
defer x = x / 10;
defer x = x + 11;
defer x = x * 2;
}
return x;
}
+16
View File
@@ -27,3 +27,19 @@ pub fn main() void {
std.debug.print("a: {}, b: {}\n", .{ a, b.* });
}
//
// A look into the future:
// When you allocate memory, you store the returned address in
// a const var. The pointer itself never changes — it always
// refers to the same allocation — but you can still read and
// write the data it points to.
//
// Example:
//
// const buf = try allocator.alloc(u8, 1024);
// buf[0] = 42; // fine: the *contents* are mutable
//
// Note:
// Passing this pointer to a function is cheap: it's just an address
// copied on the stack. The caller can work with the data without
// needing to know where it came from or how it was allocated.
+1 -1
View File
@@ -1,6 +1,6 @@
//
// As with integers, you can pass a pointer to a struct when you
// will wish to modify that struct. Pointers are also useful when
// wish to modify that struct. Pointers are also useful when
// you need to store a reference to a struct (a "link" to it).
//
// const Vertex = struct{ x: u32, y: u32, z: u32 };
+18
View File
@@ -37,6 +37,24 @@
// self, others use a lowercase version of the type name, but feel
// free to use whatever is most appropriate.
//
// "But hold on," you say, eyeing a() and b() suspiciously, "why
// does one take 'self' and another take '*self'?" Sharp eye!
//
// It all comes down to a single question: does the function need
// to CHANGE the struct?
//
// * Needs to change it? Take a pointer (*Bar). Without it you'd
// be scribbling on a COPY, and your changes would evaporate the
// instant the function returns. Poof.
// * Only reads it? Plain Bar is just fine.
// (For a big, bulky struct you might still write *const Bar to
// avoid copying it around, but for small ones a copy is cheap.)
//
// You'll see this below: zap() takes 'self: HeatRay' by value
// because it only reads the ray's damage, but it takes the alien
// as '*Alien' because zapping is supposed to HURT - and that means
// changing the alien's health for real, not on a throwaway copy.
//
// Okay, you're armed.
//
// Now, please zap the alien structs until they're all gone or
+1 -1
View File
@@ -54,7 +54,7 @@ var global_wizard = Character{};
// an extremely efficient place for memory storage.
//
// Also, when a function executes, the input arguments are often
// loaded into the beating heart of the CPU itself in registers.
// loaded into the beating heart of the CPU itself, in registers.
//
// Our main() function here has no input parameters, but it will have
// a stack entry (called a "frame").
+5 -6
View File
@@ -224,11 +224,10 @@ const NotebookEntry = struct {
// +---+----------------+----------------+----------+
//
const HermitsNotebook = struct {
// Remember the array repetition operator `**`? It is no mere
// novelty, it's also a great way to assign multiple items in an
// array without having to list them one by one. Here we use it to
// initialize an array with null values.
entries: [place_count]?NotebookEntry = .{null} ** place_count,
// Remember the array repetition function @splat()? It is a great way
// to assign multiple items in an array without having to list them
// one by one. Here we use it to initialize an array with null values.
entries: [place_count]?NotebookEntry = @splat(null),
// The next entry keeps track of where we are in our "todo" list.
next_entry: u8 = 0,
@@ -409,7 +408,7 @@ pub fn main() void {
// aside memory for the trip and have the hermit's notebook fill
// in the trip from the destination back to the path. Note that
// this is the first time we've actually used the destination!
var trip = [_]?TripItem{null} ** (place_count * 2);
var trip: [place_count * 2]?TripItem = @splat(null);
notebook.getTripTo(trip[0..], destination) catch |err| {
print("Oh no! {}\n", .{err});
+9 -2
View File
@@ -29,8 +29,15 @@
//
// var foo: f16 = 5; // NO ERROR
//
// var foo: u16 = 5; // A literal of a different type
// var bar: f16 = foo; // ERROR
// A runtime value can coerce to a different type,
// as long as the value is losslessly representable:
//
// var foo: u16 = 5;
// var bar: f16 = foo; // NO ERROR (5 fits in f16)
//
// var foo: u16 = 49876;
// var bar: f16 = foo; // ERROR (49876 not representable in f16)
//
//
// Please fix the two float problems with this program and
// display the result as a whole number.
+13 -24
View File
@@ -47,7 +47,7 @@ const Narcissus = struct {
myself: *Narcissus = undefined,
echo: void = undefined, // Alas, poor Echo!
fn fetchTheMostBeautifulType() type {
fn FetchTheMostBeautifulType() type {
return @This();
}
};
@@ -70,7 +70,7 @@ pub fn main() void {
//
// The fix for this is very subtle, but it makes a big
// difference!
const Type2 = narcissus.fetchTheMostBeautifulType();
const Type2 = narcissus.FetchTheMostBeautifulType();
// Now we print a pithy statement about Narcissus.
print("A {s} loves all {s}es. ", .{
@@ -93,36 +93,25 @@ pub fn main() void {
print("He has room in his heart for:", .{});
// A StructFields array
const fields = @typeInfo(Narcissus).@"struct".fields;
// `field_names` is a slice of strings and it holds the names of the struct's fields
// `field_types` is a slice of types and it holds the types of the struct's fields,
// it is guaranteed to be the same length as `field_names`
const field_names = @typeInfo(Narcissus).@"struct".field_names;
const field_types = @typeInfo(Narcissus).@"struct".field_types;
// 'fields' is a slice of StructFields. Here's the declaration:
//
// pub const StructField = struct {
// name: [:0]const u8,
// type: type,
// default_value_ptr: ?*const anyopaque,
// is_comptime: bool,
// 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
// 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!):
if (fields[0].??? != void) {
print(" {s}", .{fields[0].name});
if (field_???[???] != void) {
print(" {s}", .{field_???[???]});
}
if (fields[1].??? != void) {
print(" {s}", .{fields[1].name});
if (field_???[???] != void) {
print(" {s}", .{field_???[???]});
}
if (fields[2].??? != void) {
print(" {s}", .{fields[2].name});
if (field_???[???] != void) {
print(" {s}", .{field_???[???]});
}
// Yuck, look at all that repeated code above! I don't know
+4 -4
View File
@@ -39,16 +39,16 @@ pub fn main() void {
var count = 0;
count += 1;
const a1: [count]u8 = .{'A'} ** count;
const a1: [count]u8 = @splat('A');
count += 1;
const a2: [count]u8 = .{'B'} ** count;
const a2: [count]u8 = @splat('B');
count += 1;
const a3: [count]u8 = .{'C'} ** count;
const a3: [count]u8 = @splat('C');
count += 1;
const a4: [count]u8 = .{'D'} ** count;
const a4: [count]u8 = @splat('D');
print("{s} {s} {s} {s}\n", .{ a1, a2, a3, a4 });
+5 -4
View File
@@ -36,13 +36,14 @@ pub fn main() void {
// statement was repeated three times almost verbatim. Yuck!
//
// Please use an 'inline for' to implement the block below
// for each field in the slice 'fields'!
// for each field in the corresponding slices (they're of the same length)!
const fields = @typeInfo(Narcissus).@"struct".fields;
const field_names = @typeInfo(Narcissus).@"struct".field_names;
const field_types = @typeInfo(Narcissus).@"struct".field_types;
??? {
if (field.type != void) {
print(" {s}", .{field.name});
if (field_type != void) {
print(" {s}", .{field_name});
}
}
+4 -3
View File
@@ -1,4 +1,5 @@
const print = @import("std").debug.print;
const std = @import("std");
const print = std.debug.print;
// We're going to (ab)use the power of Zig to make animal hybrid creatures!
// What do you think a GatorMouse would look like? Eek.
@@ -31,7 +32,7 @@ fn makeCreature(comptime count: usize, comptime fmt: []const u8) [count]Animal {
// We return an array of animals representing the creature. (This is why we
// really needed the 'count' parameter. Arrays need a size.)
var animals: [count]Animal = .{undefined} ** count;
var animals: [count]Animal = undefined;
var next_animal: usize = 0;
inline for (fmt) |char| {
@@ -58,7 +59,7 @@ fn makeCreature(comptime count: usize, comptime fmt: []const u8) [count]Animal {
// other animals or is this an error?
'g' => ???,
else => @compileError("No animal starts with '" ++ char ++ "'!"),
else => @compileError(std.fmt.comptimePrint("No animal starts with '{c}'!", .{char})),
},
.l => switch (char) {
+3 -5
View File
@@ -48,9 +48,7 @@ const Path = struct {
// instead.
//
// Please fill in the body of this function!
fn makePath(from: *Place, to: *Place, dist: u8) Path {
}
fn makePath(from: *Place, to: *Place, dist: u8) Path {}
// Using our new function, these path definitions take up considerably less
// space in our program now!
@@ -97,7 +95,7 @@ const NotebookEntry = struct {
};
const HermitsNotebook = struct {
entries: [place_count]?NotebookEntry = .{null} ** place_count,
entries: [place_count]?NotebookEntry = @splat(null),
next_entry: u8 = 0,
end_of_entries: u8 = 0,
@@ -193,7 +191,7 @@ pub fn main() void {
}
}
var trip = [_]?TripItem{null} ** (place_count * 2);
var trip: [place_count * 2]?TripItem = @splat(null);
notebook.getTripTo(trip[0..], destination) catch |err| {
print("Oh no! {}\n", .{err});
+11 -20
View File
@@ -74,36 +74,27 @@ fn printTuple(tuple: anytype) void {
// @typeInfo() - takes a type, returns a TypeInfo union
// with fields specific to that type.
//
// The list of a struct type's fields can be found in
// TypeInfo's @"struct".fields.
// The list of a struct type's field types can be found in
// TypeInfo's @"struct".field_types.
//
// Example:
//
// @typeInfo(Circle).@"struct".fields
// @typeInfo(Circle).@"struct".field_types
//
// This will be an array of StructFields.
const fields = ???;
// This will be an array of field types.
const field_types = ???;
// This will be an array of field names.
const field_names = ???;
// 2. Loop through each field. This must be done at compile
// time.
//
// Hint: remember 'inline' loops?
//
for (fields) |field| {
for (???, ???) |???, ???| {
// 3. Print the field's name, type, and value.
//
// Each 'field' in this loop is one of these:
//
// pub const StructField = struct {
// name: [:0]const u8,
// type: type,
// default_value_ptr: ?*const anyopaque,
// is_comptime: bool,
// alignment: comptime_int,
// };
//
// Note we will learn about 'anyopaque' type later
//
// You'll need this builtin:
//
// @field(lhs: anytype, comptime field_name: []const u8)
@@ -123,8 +114,8 @@ fn printTuple(tuple: anytype) void {
// for declarations. If it's a value, it looks for data.
//
print("\"{s}\"({any}):{any} ", .{
field.???,
field.???,
field_name,
field_type,
???,
});
}
+7 -1
View File
@@ -6,7 +6,7 @@
//
// // In a task:
// try mutex.lock(io); // blocks until lock is acquired
// defer mutex.unlock();
// defer mutex.unlock(io);
// // ... critical section: safe to modify shared data ...
//
// Without the mutex, concurrent tasks could read and write the
@@ -52,6 +52,12 @@ fn increment(io: std.Io, state: *SharedState, times: u32) void {
state.mutex.??? catch return;
defer state.mutex.unlock(); // <-- what's missing here?
// Sleep to give the other tasks a chance to run in the meantime.
// We do this here only to make nondeterminism more visible.
io.sleep(std.Io.Duration.fromMilliseconds(1), .awake) catch {};
// What happens if you neglect to lock the mutex?
state.counter += 1;
}
}
+9
View File
@@ -93,3 +93,12 @@ pub fn main() !void {
//
// For Crypto it is better not to use this, but in sorting algorithms like
// Bubble Sort it works very well.
//
// By the way, congratulations for making it to Exercise 100!
//
// +-------------+
// | Celebration |
// | Area * * * |
// +-------------+
//
// Please keep your celebrating within the area provided.
-9
View File
@@ -51,12 +51,3 @@ pub fn main() void {
//
// You are perhaps wondering what happens if one of the two lists
// is longer than the other? Try it!
//
// By the way, congratulations for making it to Exercise 100!
//
// +-------------+
// | Celebration |
// | Area * * * |
// +-------------+
//
// Please keep your celebrating within the area provided.
+1 -1
View File
@@ -14,7 +14,7 @@
// exercises.
//
// A nice example of this has been published on the Zig homepage,
// replacing the somewhat dusty 'Hello world!
// replacing the somewhat dusty 'Hello world!'
//
// Nothing against 'Hello world!', but it just doesn't do justice
// to the elegance of Zig and that's a pity, if someone takes a short,
+7 -8
View File
@@ -1,5 +1,5 @@
//
// In Exercises 84-91, we learned about Zig's Io interface for
// In Exercises 85-94 and quiz 95, we learned about Zig's Io interface for
// concurrent execution: io.async(), Group, Select, and Futures.
// Under the hood, the Threaded backend manages a pool of real
// OS threads for you - including scheduling, cancellation, and
@@ -74,11 +74,11 @@ pub fn main() !void {
// before the parallel processing begins.
std.debug.print("Starting work...\n", .{});
// These curly brackets are very important, they are necessary
// to enclose the area where the threads are called.
// Without these brackets, the program would not wait for the
// end of the threads and they would continue to run beyond the
// end of the program.
// These curly braces are very important, they are necessary
// to enclose the area where the threads are called and joined.
// With these braces, the program will block and wait for all threads
// to finish right at the closing brace of this block, ensuring
// "Zig is cool!" is always printed last.
{
// Now we start the first thread, with the number as parameter
const handle = try std.Thread.spawn(.{}, thread_function, .{1});
@@ -102,8 +102,7 @@ pub fn main() !void {
try io.sleep(std.Io.Duration.fromMilliseconds(400), .awake);
std.debug.print("Some weird stuff, after starting the threads.\n", .{});
}
// After we have left the closed area, we wait until
// the threads have run through, if this has not yet been the case.
// The threads are guaranteed to be finished by the time we reach here.
std.debug.print("Zig is cool!\n", .{});
}
+18 -11
View File
@@ -29,7 +29,7 @@
// will deal with the series straight away:
//
// 4 4 4 4 4
// PI = --- - --- + --- - --- + --- ...
// PI = + --- - --- + --- - --- + --- ...
// 1 3 5 7 9
//
// As you can clearly see, the series starts with the whole number 4 and
@@ -46,7 +46,7 @@
// 1,000,000,000 partial values. And for each additional digit we have to
// add a zero.
// Even fast computers - and I mean really fast computers - get a bit warmer
// on the CPU when it comes to really many digits. But the 8 digits are
// on the CPU when it comes to a large number of digits. But 8 digits are
// enough for us for now, because we want to understand the principle and
// nothing more, right?
//
@@ -71,27 +71,28 @@
const std = @import("std");
pub fn main() !void {
const count = 1_000_000_000;
const count = 500_000_000;
var pi_plus: f64 = 0;
var pi_minus: f64 = 0;
{
// First thread to calculate the plus numbers.
const handle1 = try std.Thread.spawn(.{}, thread_pi, .{ &pi_plus, 5, count });
const handle1 = try std.Thread.spawn(.{}, thread_pi, .{ &pi_plus, 1, count });
defer handle1.join();
// Second thread to calculate the minus numbers.
???
}
// Here we add up the results.
std.debug.print("PI ≈ {d:.8}\n", .{4 + pi_plus - pi_minus});
std.debug.print("PI ≈ {d:.8} (error = {e:.1})\n", .{ pi_plus - pi_minus, std.math.pi - (pi_plus - pi_minus) });
}
fn thread_pi(pi: *f64, begin: u64, end: u64) !void {
var n: u64 = begin;
while (n < end) : (n += 4) {
pi.* += 4 / @as(f64, @floatFromInt(n));
fn thread_pi(pi: *f64, begin: u64, count: u64) !void {
for (0..count) |i| {
const term = 4 / @as(f64, @floatFromInt(begin + 4 * i));
pi.* += term;
}
}
// If you wish, you can increase the number of loop passes, which
@@ -103,8 +104,14 @@ fn thread_pi(pi: *f64, begin: u64, end: u64) !void {
// is created. Otherwise the debug functions slow down the speed
// to such an extent that seconds become minutes during execution.
//
// You can use the following command to build and run this
// exercise with the "ReleaseFast" flag:
//
// zig run exercises/108_threading2.zig -O ReleaseFast
//
// And you should remove the formatting restriction in "print",
// otherwise you will not be able to see the additional digits.
//
// If count = 10_000_000_000_000 you should see the following:
// If count = 2_500_000_000_000, you should see the following:
// 3.141592653589
// (after waiting on the order of 1000 seconds, or 17 minutes)
+1 -1
View File
@@ -50,7 +50,7 @@ pub fn main(init: std.process.Init) !void {
// wait a minute...
// opening a directory might fail!
// what should we do here?
var output_dir: std.Io.Dir = try cwd.openDir(io, "output", .{});
var output_dir: std.Io.Dir = cwd.openDir(io, "output", .{});
defer output_dir.close(io);
// we try to open the file `zigling.txt`,
+5 -5
View File
@@ -1,6 +1,6 @@
//
// Prerequisite :
// - exercise/106_files.zig, or
// - exercise/109_files.zig, or
// - create a file {project_root}/output/zigling.txt
// with content `It's zigling time!`(18 bytes total)
//
@@ -28,7 +28,7 @@ pub fn main(init: std.process.Init) !void {
// Get the current working directory
const cwd = std.Io.Dir.cwd();
// try to open ./output assuming you did your 106_files exercise
// try to open ./output assuming you did your 109_files exercise
var output_dir = try cwd.openDir(io, "output", .{});
defer output_dir.close(io);
@@ -36,10 +36,10 @@ pub fn main(init: std.process.Init) !void {
const file = try output_dir.openFile(io, "zigling.txt", .{});
defer file.close(io);
// initialize an array of u8 with all letter 'A'
// initialize an array of u8 entirely with the letter 'A'
// we need to pick the size of the array, 64 seems like a good number
// fix the initialization below
var content = ['A']*64;
// do you remember the array repetition function?
var content: ??? = ???('A');
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content});
+5 -1
View File
@@ -73,7 +73,7 @@
// 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.
// Defining vectors in Zig is straightforward. No library import is needed.
const v1 = @Vector(3, i32){ 1, 10, 100 };
const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };
@@ -145,3 +145,7 @@ pub fn main() void {
print("Max difference (old fn): {d: >5.3}\n", .{mpd_old});
print("Max difference (new fn): {d: >5.3}\n", .{mpd_new});
}
// Another cool feature of Vectors is repeating patterns.
// Remember the arrays exercise from earlier where we created an array
// by repeating a pattern? See `005_arrays2.zig`.
+1 -1
View File
@@ -14,7 +14,7 @@
//
// 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
// and more! In a previous exercise (100_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
+3 -3
View File
@@ -1,9 +1,9 @@
//
// We've already learned plenty about bit manipulation using bitwise operations
// in exercices 097 and 098 and in quiz 110. The techniques we already know work
// in exercises 100 and 101 and in quiz 113. The techniques we already know work
// just fine, but creating masks and shifting individual bits around can become
// quite tedious and unwieldy pretty quickly.
// What if there was a better, a more convenient way to control invidivual bits?
// What if there was a better, a more convenient way to control individual bits?
//
// Luckily, Zig has a keyword for exactly this purpose:
//
@@ -78,7 +78,7 @@ const FLG = packed struct(u8) {
content_checksum: bool,
content_size: bool,
block_checksum: bool,
block_indepencence: bool,
block_independence: bool,
version: u2,
};
+3 -3
View File
@@ -1,5 +1,5 @@
//
// We've already learned about switch statements in exercises 030, 031 and 108.
// We've already learned about switch statements in exercises 030, 031 and 111.
// They also work with packed containers:
const S = packed struct(u2) {
@@ -51,11 +51,11 @@ comptime {
//
// Try to make the float below negative:
/// IEEE 754 half precision float
// IEEE 754 binary16 floating-point format
const Float = packed union(u16) {
value: f16,
bits: packed struct(u16) {
mantissa: u10,
significand: u10,
exponent: u5,
sign: u1,
},
+11 -7
View File
@@ -1,6 +1,6 @@
--- exercises/005_arrays2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/005_arrays2.zig 2023-10-05 20:04:06.862763262 +0200
@@ -25,12 +25,12 @@
--- exercises/005_arrays2.zig 2026-06-27 09:03:29.918124920 +0000
+++ answers/005_arrays2.zig 2026-06-27 09:01:13.162138431 +0000
@@ -21,15 +21,15 @@
// (Problem 1)
// Please set this array concatenating the two arrays above.
// It should result in: 1 3 3 7
@@ -10,8 +10,12 @@
// (Problem 2)
// Please set this array using repetition.
// It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
- const bit_pattern = [_]u8{ ??? } ** 3;
+ const bit_pattern = [_]u8{ 1, 0, 0, 1 } ** 3;
- const bit_pattern_unit = ???;
+ const bit_pattern_unit = [_]u8{ 1, 0, 0, 1 };
// Okay, that's all of the problems. Let's see the results.
//
// How long should the bit pattern be?
- const len = ???;
+ const len = 3 * bit_pattern_unit.len;
// For now, don't worry about the use of SIMD.
const bit_pattern: [len]u8 = std.simd.repeat(len, bit_pattern_unit);
+4 -9
View File
@@ -1,6 +1,6 @@
--- exercises/006_strings.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/006_strings.zig 2023-10-05 20:04:06.869430053 +0200
@@ -24,18 +24,18 @@
--- exercises/006_strings.zig 2026-05-04 17:04:31.763821070 +0200
+++ answers/006_strings.zig 2026-05-04 17:02:11.672866263 +0200
@@ -24,14 +24,14 @@
// (Problem 1)
// Use array square bracket syntax to get the letter 'd' from
// the string "stardust" above.
@@ -8,11 +8,6 @@
+ const d: u8 = ziggy[4];
// (Problem 2)
// Use the array repeat '**' operator to make "ha ha ha ".
- const laugh = "ha " ???;
+ const laugh = "ha " ** 3;
// (Problem 3)
// Use the array concatenation '++' operator to make "Major Tom".
// (You'll need to add a space as well!)
const major = "Major";
@@ -21,4 +16,4 @@
+ const major_tom = major ++ " " ++ tom;
// That's all the problems. Let's see our results:
std.debug.print("d={u} {s}{s}\n", .{ d, laugh, major_tom });
std.debug.print("d={u} {s}\n", .{ d, major_tom });
+5 -3
View File
@@ -1,9 +1,11 @@
--- exercises/026_hello2.zig 2026-01-09 22:51:45.803358789 +0100
+++ answers/026_hello2.zig 2026-01-09 22:50:46.016166527 +0100
@@ -28,5 +28,5 @@
--- exercises/026_hello2.zig 2026-07-01 16:27:18.674459266 +0200
+++ answers/026_hello2.zig 2026-07-01 16:27:08.579220702 +0200
@@ -28,7 +28,7 @@
// to be able to pass it up as a return value of main().
//
// We just learned of a single statement which can accomplish this.
- stdout.print("Hello world!\n", .{});
+ try stdout.print("Hello world!\n", .{});
}
// Now you must be thinking "Why would I need to do this instead of good
+15 -3
View File
@@ -1,6 +1,6 @@
--- exercises/028_defer2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/028_defer2.zig 2023-10-05 20:04:06.966098530 +0200
@@ -18,7 +18,7 @@
--- exercises/028_defer2.zig 2026-06-02 06:08:12.713672612 +0200
+++ answers/028_defer2.zig 2026-06-02 06:08:43.262234023 +0200
@@ -20,7 +20,7 @@
fn printAnimal(animal: u8) void {
std.debug.print("(", .{});
@@ -9,3 +9,15 @@
if (animal == 'g') {
std.debug.print("Goat", .{});
@@ -51,9 +51,9 @@
// Try reordering the statements to get the answer 42
{
- defer x = x / 10;
- defer x = x + 11;
defer x = x * 2;
+ defer x = x + 11;
+ defer x = x / 10;
}
return x;
+2 -2
View File
@@ -1,5 +1,5 @@
--- exercises/040_pointers2.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/040_pointers2.zig 2023-10-05 20:04:07.022766257 +0200
--- exercises/040_pointers2.zig 2026-05-22 21:57:28.601255748 +0200
+++ answers/040_pointers2.zig 2026-05-22 21:57:27.672235943 +0200
@@ -23,7 +23,7 @@
pub fn main() void {
+3 -3
View File
@@ -1,6 +1,6 @@
--- exercises/047_methods.zig 2023-10-03 22:15:22.122241138 +0200
+++ answers/047_methods.zig 2023-10-05 20:04:07.056100214 +0200
@@ -88,7 +88,7 @@
--- exercises/047_methods.zig 2026-06-21 16:11:59.110876971 +0200
+++ answers/047_methods.zig 2026-06-21 16:13:07.066363558 +0200
@@ -106,7 +106,7 @@
for (&aliens) |*alien| {
// *** Zap the alien with the heat ray here! ***
+4 -4
View File
@@ -1,5 +1,5 @@
--- exercises/058_quiz7.zig 2024-10-28 09:06:49.448505460 +0100
+++ answers/058_quiz7.zig 2024-10-28 09:35:14.631932322 +0100
--- exercises/058_quiz7.zig 2026-05-04 16:34:31.692458399 +0200
+++ answers/058_quiz7.zig 2026-05-04 16:34:29.239406323 +0200
@@ -192,8 +192,8 @@
// Oops! The hermit forgot how to capture the union values
// in a switch statement. Please capture each value as
@@ -11,7 +11,7 @@
}
}
};
@@ -255,7 +255,7 @@
@@ -254,7 +254,7 @@
// dereference and optional value "unwrapping" look
// together. Remember that you return the address with the
// "&" operator.
@@ -20,7 +20,7 @@
// Try to make your answer this long:__________;
}
return null;
@@ -309,7 +309,7 @@
@@ -308,7 +308,7 @@
//
// Looks like the hermit forgot something in the return value of
// this function. What could that be?
+4 -4
View File
@@ -1,6 +1,6 @@
--- exercises/060_floats.zig 2026-04-03 14:40:17.582344768 +0200
+++ answers/060_floats.zig 2026-04-03 14:49:26.997886326 +0200
@@ -43,7 +43,7 @@
--- exercises/060_floats.zig 2026-05-02 19:22:46.225370223 +0200
+++ answers/060_floats.zig 2026-05-02 19:22:47.523142218 +0200
@@ -50,7 +50,7 @@
//
// We'll convert this weight from pounds to metric units at a
// conversion of 0.453592 kg to the pound.
@@ -9,7 +9,7 @@
// By default, float values are formatted in standard decimal
// notation. Experiment with '{d}' and '{d:.3}' to see how
@@ -51,7 +51,7 @@
@@ -58,7 +58,7 @@
// scientific notation.
// NOTE: The weight of the shuttle is a huge number, a scientific notation
// may be more appropriate.
+18 -14
View File
@@ -1,5 +1,5 @@
--- exercises/065_builtins2.zig 2026-02-27 13:10:36
+++ answers/065_builtins2.zig 2026-02-27 13:10:52
--- exercises/065_builtins2.zig 2026-06-01 15:33:16.617432671 +0200
+++ answers/065_builtins2.zig 2026-06-01 15:33:31.104018108 +0200
@@ -58,7 +58,7 @@
// Oops! We cannot leave the 'me' and 'myself' fields
// undefined. Please set them here:
@@ -13,27 +13,31 @@
//
// The fix for this is very subtle, but it makes a big
// difference!
- const Type2 = narcissus.fetchTheMostBeautifulType();
+ const Type2 = Narcissus.fetchTheMostBeautifulType();
- const Type2 = narcissus.FetchTheMostBeautifulType();
+ const Type2 = Narcissus.FetchTheMostBeautifulType();
// Now we print a pithy statement about Narcissus.
print("A {s} loves all {s}es. ", .{
@@ -113,15 +113,15 @@
@@ -102,16 +102,16 @@
// Please complete these 'if' statements so that the field
// 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!):
- if (fields[0].??? != void) {
+ if (fields[0].type != void) {
print(" {s}", .{fields[0].name});
- if (field_???[???] != void) {
- print(" {s}", .{field_???[???]});
+ if (field_types[0] != void) {
+ print(" {s}", .{field_names[0]});
}
- if (fields[1].??? != void) {
+ if (fields[1].type != void) {
print(" {s}", .{fields[1].name});
- if (field_???[???] != void) {
- print(" {s}", .{field_???[???]});
+ if (field_types[1] != void) {
+ print(" {s}", .{field_names[1]});
}
- if (fields[2].??? != void) {
+ if (fields[2].type != void) {
print(" {s}", .{fields[2].name});
- if (field_???[???] != void) {
- print(" {s}", .{field_???[???]});
+ if (field_types[2] != void) {
+ print(" {s}", .{field_names[2]});
}
// Yuck, look at all that repeated code above! I don't know
+3 -3
View File
@@ -1,5 +1,5 @@
--- exercises/067_comptime2.zig 2025-12-07 22:51:24.396031248 +0100
+++ answers/067_comptime2.zig 2025-12-07 22:50:18.721616293 +0100
--- exercises/067_comptime2.zig 2026-05-04 15:38:52.565144012 +0200
+++ answers/067_comptime2.zig 2026-05-04 15:37:20.257213463 +0200
@@ -36,7 +36,7 @@
// In this contrived example, we've decided to allocate some
// arrays using a variable count! But something's missing...
@@ -8,4 +8,4 @@
+ comptime var count = 0;
count += 1;
const a1: [count]u8 = .{'A'} ** count;
const a1: [count]u8 = @splat('A');
+8 -8
View File
@@ -1,11 +1,11 @@
--- exercises/071_comptime6.zig 2024-09-02 19:21:50.250454978 +0200
+++ answers/071_comptime6.zig 2024-09-02 19:21:23.553250563 +0200
@@ -40,7 +40,7 @@
const fields = @typeInfo(Narcissus).@"struct".fields;
--- exercises/071_comptime6.zig 2026-06-01 15:35:27.223400223 +0200
+++ answers/071_comptime6.zig 2026-06-01 15:36:35.349728561 +0200
@@ -41,7 +41,7 @@
const field_names = @typeInfo(Narcissus).@"struct".field_names;
const field_types = @typeInfo(Narcissus).@"struct".field_types;
- ??? {
+ inline for (fields) |field| {
if (field.type != void) {
print(" {s}", .{field.name});
+ inline for (field_names, field_types) |field_name, field_type| {
if (field_type != void) {
print(" {s}", .{field_name});
}
+7 -7
View File
@@ -1,6 +1,6 @@
--- exercises/074_comptime9.zig 2026-04-11 21:35:08.132459373 -0700
+++ answers/074_comptime9.zig 2026-04-12 07:13:37.971010827 -0700
@@ -27,12 +27,12 @@
--- exercises/074_comptime9.zig 2026-05-04 17:11:05.144118157 +0200
+++ answers/074_comptime9.zig 2026-05-04 17:10:36.778519877 +0200
@@ -28,12 +28,12 @@
start, // Ready to start a new animal.
l, // This means we've seen an "l", so if we see an "m", we know it's a Llama.
};
@@ -9,22 +9,22 @@
// We return an array of animals representing the creature. (This is why we
// really needed the 'count' parameter. Arrays need a size.)
var animals: [count]Animal = .{undefined} ** count;
var animals: [count]Animal = undefined;
- var next_animal: usize = 0;
+ comptime var next_animal: usize = 0;
inline for (fmt) |char| {
@@ -56,7 +56,7 @@
@@ -57,7 +57,7 @@
//
// What do you think happens with Gators? Do they join with
// other animals or is this an error?
- 'g' => ???,
+ 'g' => @compileError("Gators refuse to join with other animals."),
else => @compileError("No animal starts with '" ++ char ++ "'!"),
else => @compileError(std.fmt.comptimePrint("No animal starts with '{c}'!", .{char})),
},
@@ -68,7 +68,7 @@
@@ -69,7 +69,7 @@
next_animal += 1;
// Something is missing here. After we finish a Llama, we
// need to be ready to _start_ over with a new animal...
+8 -6
View File
@@ -1,15 +1,17 @@
--- exercises/075_quiz8.zig 2023-11-21 14:48:15.440702720 +0100
+++ answers/075_quiz8.zig 2023-11-21 14:50:23.453311616 +0100
@@ -49,7 +49,11 @@
--- exercises/075_quiz8.zig 2026-05-04 15:51:48.254371574 +0200
+++ answers/075_quiz8.zig 2026-05-04 15:49:28.426445382 +0200
@@ -48,7 +48,13 @@
// instead.
//
// Please fill in the body of this function!
fn makePath(from: *Place, to: *Place, dist: u8) Path {
-
-fn makePath(from: *Place, to: *Place, dist: u8) Path {}
+fn makePath(from: *Place, to: *Place, dist: u8) Path {
+ return Path{
+ .from = from,
+ .to = to,
+ .dist = dist,
+ };
}
+}
// Using our new function, these path definitions take up considerably less
// space in our program now!
+18 -18
View File
@@ -1,32 +1,32 @@
--- exercises/082_anonymous_structs3.zig 2026-02-27 13:05:46
+++ answers/082_anonymous_structs3.zig 2026-02-27 13:07:22
@@ -82,14 +82,14 @@
// @typeInfo(Circle).@"struct".fields
--- exercises/082_anonymous_structs3.zig 2026-06-01 15:59:11.872467805 +0200
+++ answers/082_anonymous_structs3.zig 2026-06-01 15:58:38.004730144 +0200
@@ -82,17 +82,17 @@
// @typeInfo(Circle).@"struct".field_types
//
// This will be an array of StructFields.
- const fields = ???;
+ const fields = @typeInfo(@TypeOf(tuple)).@"struct".fields;
// This will be an array of field types.
- const field_types = ???;
+ const field_types = @typeInfo(@TypeOf(tuple)).@"struct".field_types;
// This will be an array of field names.
- const field_names = ???;
+ const field_names = @typeInfo(@TypeOf(tuple)).@"struct".field_names;
// 2. Loop through each field. This must be done at compile
// time.
//
// Hint: remember 'inline' loops?
//
- for (fields) |field| {
+ inline for (fields) |field| {
- for (???, ???) |???, ???| {
+ inline for (field_types, field_names) |field_type, field_name| {
// 3. Print the field's name, type, and value.
//
// Each 'field' in this loop is one of these:
@@ -123,9 +123,9 @@
// for declarations. If it's a value, it looks for data.
//
// You'll need this builtin:
@@ -116,7 +116,7 @@
print("\"{s}\"({any}):{any} ", .{
- field.???,
- field.???,
field_name,
field_type,
- ???,
+ field.name,
+ field.type,
+ @field(tuple, field.name),
+ @field(tuple, field_name),
});
}
}
+4 -4
View File
@@ -1,5 +1,5 @@
--- exercises/091_async7.zig 2026-04-02 10:50:08.142508099 +0200
+++ answers/091_async7.zig 2026-04-02 10:49:59.629341593 +0200
--- exercises/091_async7.zig 2026-04-18 23:30:40.963951835 +0200
+++ answers/091_async7.zig 2026-04-18 23:33:47.313340585 +0200
@@ -49,8 +49,8 @@
for (0..times) |_| {
// Acquire the lock before modifying shared state.
@@ -9,5 +9,5 @@
+ state.mutex.lock(io) catch return;
+ defer state.mutex.unlock(io);
state.counter += 1;
}
// Sleep to give the other tasks a chance to run in the meantime.
// We do this here only to make nondeterminism more visible.
+4 -5
View File
@@ -1,13 +1,12 @@
--- exercises/108_threading2.zig 2026-04-14 06:44:18.848246237 +0200
+++ answers/108_threading2.zig 2026-04-14 08:15:30.894485037 +0200
@@ -81,8 +81,8 @@
--- exercises/108_threading2.zig 2026-06-29 23:25:53
+++ answers/108_threading2.zig 2026-06-29 23:25:25
@@ -82,7 +82,8 @@
defer handle1.join();
// Second thread to calculate the minus numbers.
- ???
-
+ const handle2 = try std.Thread.spawn(.{}, thread_pi, .{ &pi_minus, 3, count });
+ defer handle2.join();
}
// Here we add up the results.
std.debug.print("PI ≈ {d:.8}\n", .{4 + pi_plus - pi_minus});
+11 -2
View File
@@ -1,5 +1,5 @@
--- exercises/109_files.zig 2026-03-20 19:23:48.874150121 +0100
+++ answers/109_files.zig 2026-04-02 10:51:15.813831695 +0200
--- exercises/109_files.zig 2026-06-29 17:08:04.124224900 +0000
+++ answers/109_files.zig 2026-06-29 17:13:20.696220533 +0000
@@ -41,7 +41,7 @@
// by doing nothing
//
@@ -9,6 +9,15 @@
// if there's any other unexpected error we just propagate it through
else => return e,
};
@@ -50,7 +50,7 @@
// wait a minute...
// opening a directory might fail!
// what should we do here?
- var output_dir: std.Io.Dir = cwd.openDir(io, "output", .{});
+ var output_dir: std.Io.Dir = try cwd.openDir(io, "output", .{});
defer output_dir.close(io);
// we try to open the file `zigling.txt`,
@@ -61,7 +61,7 @@
// but here we are not yet done writing to the file
// if only there were a keyword in Zig that
+6 -6
View File
@@ -1,11 +1,11 @@
--- exercises/110_files2.zig 2026-03-20 19:23:48.874150121 +0100
+++ answers/110_files2.zig 2026-04-02 10:51:15.815831734 +0200
--- exercises/110_files2.zig 2026-05-31 14:54:55.061019159 +0200
+++ answers/110_files2.zig 2026-05-31 14:54:42.655691238 +0200
@@ -39,7 +39,7 @@
// initialize an array of u8 with all letter 'A'
// initialize an array of u8 entirely with the letter 'A'
// we need to pick the size of the array, 64 seems like a good number
// fix the initialization below
- var content = ['A']*64;
+ var content = [_]u8{'A'} ** 64;
// do you remember the array repetition function?
- var content: ??? = ???('A');
+ var content: [64]u8 = @splat('A');
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
std.debug.print("{s}\n", .{content});
+1241
View File
File diff suppressed because it is too large Load Diff
-413
View File
@@ -1,413 +0,0 @@
const std = @import("std");
const root = @import("../build.zig");
const debug = std.debug;
const fmt = std.fmt;
const mem = std.mem;
const Allocator = std.mem.Allocator;
const Process = std.process;
const Build = std.Build;
const Step = Build.Step;
const RunStep = Build.RunStep;
const LazyPath = Build.LazyPath;
const Exercise = root.Exercise;
pub fn addCliTests(b: *std.Build, exercises: []const Exercise) *Step {
const step = b.step("test-cli", "Test the command line interface");
{
// Test that `zig build -Dhealed -Dn=n` selects the nth exercise.
const case_step = createCase(b, "case-1");
const tmp_path = createTempPath(b) catch |err| {
return fail(step, "unable to make tmp path: {s}\n", .{@errorName(err)});
};
defer deleteTmpPath(b, tmp_path);
const heal_step = HealStep.create(b, exercises, tmp_path);
for (exercises[0 .. exercises.len - 1]) |ex| {
const n = ex.number();
const cmd = b.addSystemCommand(&.{
b.graph.zig_exe,
"build",
"-Dhealed",
b.fmt("-Dhealed-path={s}", .{tmp_path}),
b.fmt("-Dn={}", .{n}),
});
cmd.setName(b.fmt("zig build -Dhealed -Dn={}", .{n}));
cmd.expectExitCode(0);
cmd.step.dependOn(&heal_step.step);
const stderr = cmd.captureStdErr(.{});
const verify = CheckNamedStep.create(b, ex, stderr);
verify.step.dependOn(&cmd.step);
case_step.dependOn(&verify.step);
}
}
{
// Test that `zig build -Dhealed` processes all the exercises in order.
const case_step = createCase(b, "case-2");
const tmp_path = createTempPath(b) catch |err| {
return fail(step, "unable to make tmp path: {s}\n", .{@errorName(err)});
};
defer deleteTmpPath(b, tmp_path);
const heal_step = HealStep.create(b, exercises, tmp_path);
heal_step.step.dependOn(case_step);
// TODO: when an exercise is modified, the cache is not invalidated.
const cmd = b.addSystemCommand(&.{
b.graph.zig_exe,
"build",
"-Dhealed",
b.fmt("-Dhealed-path={s}", .{tmp_path}),
});
cmd.setName("zig build -Dhealed");
cmd.expectExitCode(0);
cmd.step.dependOn(&heal_step.step);
const stderr = cmd.captureStdErr(.{});
const verify = CheckStep.create(b, exercises, stderr);
verify.step.dependOn(&cmd.step);
}
{
// Test that `zig build -Dn=n` prints the hint.
const case_step = createCase(b, "case-3");
for (exercises[0 .. exercises.len - 1]) |ex| {
if (ex.skip) continue;
if (ex.hint) |hint| {
const n = ex.number();
const cmd = b.addSystemCommand(&.{
b.graph.zig_exe,
"build",
b.fmt("-Dn={}", .{n}),
});
cmd.setName(b.fmt("zig build -Dn={}", .{n}));
cmd.expectExitCode(2);
cmd.addCheck(.{ .expect_stderr_match = hint });
case_step.dependOn(&cmd.step);
}
}
step.dependOn(case_step);
}
return step;
}
fn createCase(b: *Build, name: []const u8) *Step {
const case_step = b.allocator.create(Step) catch @panic("OOM");
case_step.* = Step.init(.{
.id = .custom,
.name = name,
.owner = b,
});
return case_step;
}
/// Checks the output of `zig build -Dn=n`.
const CheckNamedStep = struct {
step: Step,
exercise: Exercise,
stderr: LazyPath,
pub fn create(owner: *Build, exercise: Exercise, stderr: LazyPath) *CheckNamedStep {
const self = owner.allocator.create(CheckNamedStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = "check-named",
.owner = owner,
.makeFn = make,
}),
.exercise = exercise,
.stderr = stderr,
};
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
const b = step.owner;
const io = b.graph.io;
const self: *CheckNamedStep = @alignCast(@fieldParentPtr("step", step));
const ex = self.exercise;
const stderr_file = try std.Io.Dir.cwd().openFile(
io,
self.stderr.getPath(b),
.{ .mode = .read_only },
);
defer stderr_file.close(io);
var stderr = stderr_file.readerStreaming(io, &.{});
{
// Skip the logo.
const nlines = mem.count(u8, root.logo, "\n");
var buf: [80]u8 = undefined;
var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) {
_ = try readLine(&stderr, &buf);
}
}
try check_output(step, ex, &stderr);
}
};
/// Checks the output of `zig build`.
const CheckStep = struct {
step: Step,
exercises: []const Exercise,
stderr: LazyPath,
pub fn create(
owner: *Build,
exercises: []const Exercise,
stderr: LazyPath,
) *CheckStep {
const self = owner.allocator.create(CheckStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = "check",
.owner = owner,
.makeFn = make,
}),
.exercises = exercises,
.stderr = stderr,
};
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
const b = step.owner;
const io = b.graph.io;
const self: *CheckStep = @alignCast(@fieldParentPtr("step", step));
const exercises = self.exercises;
const stderr_file = try std.Io.Dir.cwd().openFile(
io,
self.stderr.getPath(b),
.{ .mode = .read_only },
);
defer stderr_file.close(io);
var stderr = stderr_file.readerStreaming(io, &.{});
for (exercises) |ex| {
if (ex.number() == 1) {
// Skip the logo.
const nlines = mem.count(u8, root.logo, "\n");
var buf: [80]u8 = undefined;
var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) {
_ = try readLine(&stderr, &buf);
}
}
try check_output(step, ex, &stderr);
}
}
};
fn check_output(step: *Step, exercise: Exercise, reader: *std.Io.File.Reader) !void {
const b = step.owner;
var buf: [1024]u8 = undefined;
if (exercise.skip) {
{
const actual = try readLine(reader, &buf) orelse "EOF";
const expect = b.fmt("Skipping {s}", .{exercise.main_file});
try check(step, exercise, expect, actual);
}
{
const actual = try readLine(reader, &buf) orelse "EOF";
try check(step, exercise, "", actual);
}
return;
}
{
const actual = try readLine(reader, &buf) orelse "EOF";
const expect = b.fmt("Compiling {s}...", .{exercise.main_file});
try check(step, exercise, expect, actual);
}
{
const actual = try readLine(reader, &buf) orelse "EOF";
const expect = b.fmt("Checking {s}...", .{exercise.main_file});
try check(step, exercise, expect, actual);
}
{
const actual = try readLine(reader, &buf) orelse "EOF";
const expect = switch (exercise.kind) {
.exe => "PASSED:",
.@"test" => "PASSED",
};
try check(step, exercise, expect, actual);
}
// Skip the exercise output.
const nlines = switch (exercise.kind) {
.exe => 1 + mem.count(u8, exercise.output, "\n") + 1,
.@"test" => 1,
};
var lineno: usize = 0;
while (lineno < nlines) : (lineno += 1) {
_ = try readLine(reader, &buf) orelse @panic("EOF");
}
}
fn check(
step: *Step,
exercise: Exercise,
expect: []const u8,
actual: []const u8,
) !void {
if (!mem.eql(u8, expect, actual)) {
return step.fail("{s}: expected to see \"{s}\", found \"{s}\"", .{
exercise.main_file,
expect,
actual,
});
}
}
fn readLine(reader: *std.Io.File.Reader, buf: []u8) !?[]const u8 {
try reader.interface.readSliceAll(buf);
return mem.trimEnd(u8, buf, " \r\n");
}
/// Fails with a custom error message.
const FailStep = struct {
step: Step,
error_msg: []const u8,
pub fn create(owner: *Build, error_msg: []const u8) *FailStep {
const self = owner.allocator.create(FailStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = "fail",
.owner = owner,
.makeFn = make,
}),
.error_msg = error_msg,
};
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
const b = step.owner;
const self: *FailStep = @alignCast(@fieldParentPtr("step", step));
try step.result_error_msgs.append(b.allocator, self.error_msg);
return error.MakeFailed;
}
};
/// A variant of `std.Build.Step.fail` that does not return an error so that it
/// can be used in the configuration phase. It returns a FailStep, so that the
/// error will be cleanly handled by the build runner.
fn fail(step: *Step, comptime format: []const u8, args: anytype) *Step {
const b = step.owner;
const fail_step = FailStep.create(b, b.fmt(format, args));
step.dependOn(&fail_step.step);
return step;
}
/// Heals the exercises.
const HealStep = struct {
step: Step,
exercises: []const Exercise,
work_path: []const u8,
pub fn create(owner: *Build, exercises: []const Exercise, work_path: []const u8) *HealStep {
const self = owner.allocator.create(HealStep) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = .custom,
.name = "heal",
.owner = owner,
.makeFn = make,
}),
.exercises = exercises,
.work_path = work_path,
};
return self;
}
fn make(step: *Step, _: Step.MakeOptions) !void {
const b = step.owner;
const self: *HealStep = @alignCast(@fieldParentPtr("step", step));
return heal(b.allocator, self.exercises, self.work_path);
}
};
/// Heals all the exercises.
fn heal(allocator: Allocator, exercises: []const Exercise, work_path: []const u8) !void {
const io = std.Options.debug_io;
const sep = std.Io.Dir.path.sep_str;
const join = std.Io.Dir.path.join;
const exercises_path = "exercises";
const patches_path = "patches" ++ sep ++ "patches";
for (exercises) |ex| {
const name = ex.name();
const file = try join(allocator, &.{ exercises_path, ex.main_file });
const patch = b: {
const patch_name = try fmt.allocPrint(allocator, "{s}.patch", .{name});
break :b try join(allocator, &.{ patches_path, patch_name });
};
const output = try join(allocator, &.{ work_path, ex.main_file });
const argv = &.{ "patch", "-i", patch, "-o", output, "-s", file };
_ = try Process.run(allocator, io, .{ .argv = argv });
}
}
fn createTempPath(b: *Build) ![]const u8 {
const io = b.graph.io;
const rand_int = r: {
var x: u64 = undefined;
io.random(@ptrCast(&x));
break :r x;
};
const tmp_dir_sub_path = "tmp" ++ std.Io.Dir.path.sep_str ++ std.fmt.hex(rand_int);
const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");
try b.cache_root.handle.createDirPath(io, tmp_dir_sub_path);
return result_path;
}
fn deleteTmpPath(b: *Build, path: []const u8) void {
const io = b.graph.io;
std.Io.Dir.cwd().deleteTree(io, path) catch |err| {
std.log.warn("failed to delete {s}: {t}", .{ path, err });
};
}
-108
View File
@@ -1,108 +0,0 @@
const std = @import("std");
const print = std.debug.print;
const string = []const u8;
const cwd = std.fs.cwd();
const Dir = std.fs.Dir;
const Allocator = std.mem.Allocator;
const EXERCISES_PATH = "exercises";
const HEALED_PATH = "patches/healed";
const TEMP_PATH = "patches/healed/tmp";
const PATCHES_PATH = "patches/patches";
// Heals all the exercises.
fn heal(alloc: Allocator) !void {
try cwd.makePath(HEALED_PATH);
const org_path = try cwd.realpathAlloc(alloc, EXERCISES_PATH);
const patch_path = try cwd.realpathAlloc(alloc, PATCHES_PATH);
const healed_path = try cwd.realpathAlloc(alloc, HEALED_PATH);
var idir = try cwd.openIterableDir(EXERCISES_PATH, Dir.OpenDirOptions{});
defer idir.close();
var it = idir.iterate();
while (try it.next()) |entry| {
// create filenames
const healed_file = try concat(alloc, &.{ healed_path, "/", entry.name });
const patch_file = try concat(alloc, &.{ patch_path, "/", try patch_name(alloc, entry.name) });
// patch file
const result = try std.ChildProcess.exec(.{
.allocator = alloc,
.argv = &.{ "patch", "-i", patch_file, "-o", healed_file, entry.name },
.cwd = org_path,
});
print("{s}", .{result.stderr});
}
}
// Yields all the healed exercises that are not correctly formatted.
fn check_healed(alloc: Allocator) !void {
try cwd.makePath(TEMP_PATH);
const temp_path = try cwd.realpathAlloc(alloc, TEMP_PATH);
const healed_path = try cwd.realpathAlloc(alloc, HEALED_PATH);
var idir = try cwd.openIterableDir(HEALED_PATH, Dir.OpenDirOptions{});
defer idir.close();
var it = idir.iterate();
while (try it.next()) |entry| {
// Check the healed file
const result = try std.ChildProcess.exec(.{
.allocator = alloc,
.argv = &.{ "zig", "fmt", "--check", entry.name },
.cwd = healed_path,
});
// Is there something to fix?
if (result.stdout.len > 0) {
const temp_file = try concat(alloc, &.{ temp_path, "/", entry.name });
const healed_file = try concat(alloc, &.{ healed_path, "/", entry.name });
try std.fs.copyFileAbsolute(healed_file, temp_file, std.fs.CopyFileOptions{});
// Formats the temp file
_ = try std.ChildProcess.exec(.{
.allocator = alloc,
.argv = &.{ "zig", "fmt", entry.name },
.cwd = temp_path,
});
// Show the differences
const diff = try std.ChildProcess.exec(.{
.allocator = alloc,
.argv = &.{ "diff", "-c", healed_file, entry.name },
.cwd = temp_path,
});
print("{s}", .{diff.stdout});
try std.fs.deleteFileAbsolute(temp_file);
}
}
}
fn concat(alloc: Allocator, slices: []const string) !string {
const buf = try std.mem.concat(alloc, u8, slices);
return buf;
}
fn patch_name(alloc: Allocator, path: string) !string {
var filename = path;
const index = std.mem.lastIndexOfScalar(u8, path, '.') orelse return path;
if (index > 0) filename = path[0..index];
return try concat(alloc, &.{ filename, ".patch" });
}
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
try heal(alloc);
try check_healed(alloc);
}