mirror of
https://codeberg.org/ziglings/exercises.git
synced 2026-06-08 15:59:59 +00:00
Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83a89702b2 | ||
|
|
72a441a6bd | ||
|
|
6aaf048935 | ||
|
|
febc5e8bd2 | ||
|
|
b7ff71cb9e | ||
|
|
a403436fe8 | ||
|
|
beeca8d510 | ||
|
|
01ad296114 | ||
|
|
9b87962595 | ||
|
|
9d6ef1f5ba | ||
|
|
fadd2ea4c7 | ||
|
|
e69a865ce3 | ||
|
|
f6dda8181a | ||
|
|
bd55c4ac5a | ||
|
|
e61bedaa25 | ||
|
|
ac9f96459c | ||
|
|
014560c3f5 | ||
|
|
3b865a0c17 | ||
|
|
63c798637c | ||
|
|
4480762e83 | ||
|
|
4ce3ed23f3 | ||
|
|
8db82ef84e | ||
|
|
6d23c876d6 | ||
|
|
fa36a4520f | ||
|
|
7889d15b84 | ||
|
|
8a0077e83b | ||
|
|
762d8b8fa5 | ||
|
|
7300e87028 | ||
|
|
1c3238d619 | ||
|
|
000e9448d5 | ||
|
|
f434dbfc01 | ||
|
|
8468040a8e | ||
|
|
1d965491c1 | ||
|
|
926fc04d81 | ||
|
|
6b8bbfd980 | ||
|
|
e96ce4da8e | ||
|
|
42a417f9a5 | ||
|
|
dde51b3126 | ||
|
|
8af3372cf2 | ||
|
|
1ba1e301a8 | ||
|
|
1c897e1951 | ||
|
|
6048e6ef21 | ||
|
|
3a782a96d5 | ||
|
|
4bdcf195b9 | ||
|
|
656d6824d9 | ||
|
|
0d9652d867 | ||
|
|
945d9b84b0 | ||
|
|
c7afea6ef5 | ||
|
|
6c2531b824 | ||
|
|
42db1f05a7 | ||
|
|
333ca33e78 | ||
|
|
5eb9a30abe | ||
|
|
0603b66cc7 | ||
|
|
a79e923768 | ||
|
|
234e829b15 | ||
|
|
823b4987f7 | ||
|
|
ca8aee84ad | ||
|
|
c4cb76ea8e | ||
|
|
e0470c4f45 | ||
|
|
f35c9419d2 | ||
|
|
656e1ba1e7 | ||
|
|
8e2cbf7b5a | ||
|
|
d918dbae59 | ||
|
|
4746ccc1c8 |
@@ -111,7 +111,7 @@ directory.
|
|||||||
|
|
||||||
Every Ziglings exercise contains mistakes on purpose.
|
Every Ziglings exercise contains mistakes on purpose.
|
||||||
To keep our automated tests happy, each exercise also
|
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
|
When you change an exercise, you will usually need to update
|
||||||
its patch too. That’s where our little helper Gollum comes in:
|
its patch too. That’s where our little helper Gollum comes in:
|
||||||
@@ -119,12 +119,12 @@ its patch too. That’s where our little helper Gollum comes in:
|
|||||||
1. In the project root, create a folder called `answers/`
|
1. In the project root, create a folder called `answers/`
|
||||||
2. Put your solved version of the exercise file in there
|
2. Put your solved version of the exercise file in there
|
||||||
3. Back in the root, run:
|
3. Back in the root, run:
|
||||||
`./patches/gollum <exercise-number>`<br>
|
`./patches/gollum.sh <exercise-number>`<br>
|
||||||
For example: `./patches/gollum 106`
|
For example: `./patches/gollum.sh 106`
|
||||||
This will generate a shiny new patch.
|
This will generate a shiny new patch.
|
||||||
|
|
||||||
Double-check everything by asking the magical Eowyn:
|
Double-check everything by asking the magical Eowyn:
|
||||||
`./patches/eowyn`<br>
|
`./patches/eowyn.sh`<br>
|
||||||
If all tests pass: You are done!
|
If all tests pass: You are done!
|
||||||
|
|
||||||
Don’t forget to commit the patch file.
|
Don’t forget to commit the patch file.
|
||||||
|
|||||||
33
README.md
33
README.md
@@ -1,7 +1,7 @@
|
|||||||
# Ziglings
|
# Ziglings
|
||||||
|
|
||||||
Welcome to Ziglings! This project contains a series of tiny
|
Welcome to Ziglings! This project contains a series of tiny
|
||||||
broken programs (and one nasty surprise). By fixing them, you'll
|
broken programs (and one nasty surprise). By fixing them, you'll
|
||||||
learn how to read and write [Zig](https://ziglang.org/) code.
|
learn how to read and write [Zig](https://ziglang.org/) code.
|
||||||
|
|
||||||

|

|
||||||
@@ -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:
|
Verify the installation and build number of `zig` like so:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ zig version
|
zig version
|
||||||
0.16.0-dev.xxxx+xxxxxxxxx
|
0.17.0-dev.xxxx+xxxxxxxxx
|
||||||
```
|
```
|
||||||
|
|
||||||
Clone this repository with Git:
|
Clone this repository with Git:
|
||||||
@@ -73,11 +73,11 @@ 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.15.2`, but Ziglings needs a dev build with
|
stable release was `0.16`, but Ziglings needs a dev build with
|
||||||
pre-release version "0.16.0" and a build number at least as high
|
pre-release version "0.17.0-dev" 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.
|
||||||
|
|
||||||
**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
|
It is likely that you'll download a build which is _greater_ than
|
||||||
the minimum.
|
the minimum.
|
||||||
@@ -106,12 +106,6 @@ Or let Ziglings pick an exercise for you:
|
|||||||
zig build -Drandom
|
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
|
Or skip the build system entirely and interact directly with the
|
||||||
compiler if you're into that sort of thing:
|
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
|
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:
|
To get a list of all possible options, run:
|
||||||
|
|
||||||
```
|
```
|
||||||
zig build -Dn=19 -l
|
zig build -h
|
||||||
|
|
||||||
install Install 019_functions2.zig to prefix path
|
|
||||||
uninstall Uninstall 019_functions2.zig from prefix path
|
|
||||||
test Run 019_functions2.zig without checking output
|
|
||||||
...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
To reset the progress (have it run all the exercises that have already been completed):
|
To reset the progress (have it run all the exercises that have already been completed):
|
||||||
@@ -211,6 +193,7 @@ Zig Standard Library
|
|||||||
|
|
||||||
### Version Changes
|
### 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-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-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)
|
* 2026-01-09 zig 0.16.0-dev.2075 - move randomness API to `std.Io`, see [#30709](https://codeberg.org/ziglang/zig/pulls/30709)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Zig has some fun array operators.
|
// Zig has one array operator.
|
||||||
//
|
//
|
||||||
// You can use '++' to concatenate two arrays:
|
// You can use '++' to concatenate two arrays:
|
||||||
//
|
//
|
||||||
@@ -7,12 +7,8 @@
|
|||||||
// const b = [_]u8{ 3,4 };
|
// const b = [_]u8{ 3,4 };
|
||||||
// const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5
|
// const c = a ++ b ++ [_]u8{ 5 }; // equals 1 2 3 4 5
|
||||||
//
|
//
|
||||||
// You can use '**' to repeat an array:
|
// Note that '++' only operates on arrays while your program is
|
||||||
//
|
// _being compiled_. This special time is known in Zig
|
||||||
// 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
|
|
||||||
// parlance as "comptime" and we'll learn plenty more about that
|
// parlance as "comptime" and we'll learn plenty more about that
|
||||||
// later.
|
// later.
|
||||||
//
|
//
|
||||||
@@ -30,7 +26,8 @@ pub fn main() void {
|
|||||||
// (Problem 2)
|
// (Problem 2)
|
||||||
// Please set this array using repetition.
|
// Please set this array using repetition.
|
||||||
// It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
|
// 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 = [_]u8{ ??? };
|
||||||
|
const bit_pattern: [3 * bit_pattern_unit.len]u8 = @bitCast(@as([3][bit_pattern_unit.len]u8, @splat(bit_pattern_unit)));
|
||||||
|
|
||||||
// Okay, that's all of the problems. Let's see the results.
|
// Okay, that's all of the problems. Let's see the results.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -27,10 +27,6 @@ pub fn main() void {
|
|||||||
const d: u8 = ziggy[???];
|
const d: u8 = ziggy[???];
|
||||||
|
|
||||||
// (Problem 2)
|
// (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".
|
// Use the array concatenation '++' operator to make "Major Tom".
|
||||||
// (You'll need to add a space as well!)
|
// (You'll need to add a space as well!)
|
||||||
const major = "Major";
|
const major = "Major";
|
||||||
@@ -38,7 +34,7 @@ pub fn main() void {
|
|||||||
const major_tom = major ??? tom;
|
const major_tom = major ??? tom;
|
||||||
|
|
||||||
// That's all the problems. Let's see our results:
|
// 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 '{}'
|
// Keen eyes will notice that we've put 'u' and 's' inside the '{}'
|
||||||
// placeholders in the format string above. This tells the
|
// placeholders in the format string above. This tells the
|
||||||
// print() function to format the values as a UTF-8 character and
|
// print() function to format the values as a UTF-8 character and
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ pub fn main() void {
|
|||||||
for (animals) |a| printAnimal(a);
|
for (animals) |a| printAnimal(a);
|
||||||
|
|
||||||
std.debug.print("done.\n", .{});
|
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
|
// This function is _supposed_ to print an animal name in parentheses
|
||||||
@@ -35,3 +37,24 @@ fn printAnimal(animal: u8) void {
|
|||||||
|
|
||||||
std.debug.print("Unknown", .{});
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,3 +27,19 @@ pub fn main() void {
|
|||||||
|
|
||||||
std.debug.print("a: {}, b: {}\n", .{ a, b.* });
|
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.
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ var global_wizard = Character{};
|
|||||||
// an extremely efficient place for memory storage.
|
// an extremely efficient place for memory storage.
|
||||||
//
|
//
|
||||||
// Also, when a function executes, the input arguments are often
|
// 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
|
// Our main() function here has no input parameters, but it will have
|
||||||
// a stack entry (called a "frame").
|
// a stack entry (called a "frame").
|
||||||
|
|||||||
@@ -224,11 +224,10 @@ const NotebookEntry = struct {
|
|||||||
// +---+----------------+----------------+----------+
|
// +---+----------------+----------------+----------+
|
||||||
//
|
//
|
||||||
const HermitsNotebook = struct {
|
const HermitsNotebook = struct {
|
||||||
// Remember the array repetition operator `**`? It is no mere
|
// Remember the array repetition function @splat()? It is a great way
|
||||||
// novelty, it's also a great way to assign multiple items in an
|
// to assign multiple items in an array without having to list them
|
||||||
// array without having to list them one by one. Here we use it to
|
// one by one. Here we use it to initialize an array with null values.
|
||||||
// initialize an array with null values.
|
entries: [place_count]?NotebookEntry = @splat(null),
|
||||||
entries: [place_count]?NotebookEntry = .{null} ** place_count,
|
|
||||||
|
|
||||||
// The next entry keeps track of where we are in our "todo" list.
|
// The next entry keeps track of where we are in our "todo" list.
|
||||||
next_entry: u8 = 0,
|
next_entry: u8 = 0,
|
||||||
@@ -409,7 +408,7 @@ pub fn main() void {
|
|||||||
// aside memory for the trip and have the hermit's notebook fill
|
// aside memory for the trip and have the hermit's notebook fill
|
||||||
// in the trip from the destination back to the path. Note that
|
// in the trip from the destination back to the path. Note that
|
||||||
// this is the first time we've actually used the destination!
|
// 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| {
|
notebook.getTripTo(trip[0..], destination) catch |err| {
|
||||||
print("Oh no! {}\n", .{err});
|
print("Oh no! {}\n", .{err});
|
||||||
|
|||||||
@@ -27,10 +27,17 @@
|
|||||||
// the types match. Zig does not perform unsafe type coercions
|
// the types match. Zig does not perform unsafe type coercions
|
||||||
// behind your back:
|
// behind your back:
|
||||||
//
|
//
|
||||||
// var foo: f16 = 5; // NO ERROR
|
// var foo: f16 = 5; // NO 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)
|
||||||
//
|
//
|
||||||
// var foo: u16 = 5; // A literal of a different type
|
|
||||||
// var bar: f16 = foo; // ERROR
|
|
||||||
//
|
//
|
||||||
// Please fix the two float problems with this program and
|
// Please fix the two float problems with this program and
|
||||||
// display the result as a whole number.
|
// display the result as a whole number.
|
||||||
|
|||||||
@@ -93,36 +93,25 @@ pub fn main() void {
|
|||||||
|
|
||||||
print("He has room in his heart for:", .{});
|
print("He has room in his heart for:", .{});
|
||||||
|
|
||||||
// A StructFields array
|
// `field_names` is a slice of strings and it holds the names of the struct's fields
|
||||||
const fields = @typeInfo(Narcissus).@"struct".fields;
|
// `field_types` is a slice of strings 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
|
// 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 (field_???[???] != void) {
|
||||||
print(" {s}", .{fields[0].name});
|
print(" {s}", .{field_???[???]});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields[1].??? != void) {
|
if (field_???[???] != void) {
|
||||||
print(" {s}", .{fields[1].name});
|
print(" {s}", .{field_???[???]});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fields[2].??? != void) {
|
if (field_???[???] != void) {
|
||||||
print(" {s}", .{fields[2].name});
|
print(" {s}", .{field_???[???]});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Yuck, look at all that repeated code above! I don't know
|
// Yuck, look at all that repeated code above! I don't know
|
||||||
|
|||||||
@@ -39,16 +39,16 @@ pub fn main() void {
|
|||||||
var count = 0;
|
var count = 0;
|
||||||
|
|
||||||
count += 1;
|
count += 1;
|
||||||
const a1: [count]u8 = .{'A'} ** count;
|
const a1: [count]u8 = @splat('A');
|
||||||
|
|
||||||
count += 1;
|
count += 1;
|
||||||
const a2: [count]u8 = .{'B'} ** count;
|
const a2: [count]u8 = @splat('B');
|
||||||
|
|
||||||
count += 1;
|
count += 1;
|
||||||
const a3: [count]u8 = .{'C'} ** count;
|
const a3: [count]u8 = @splat('C');
|
||||||
|
|
||||||
count += 1;
|
count += 1;
|
||||||
const a4: [count]u8 = .{'D'} ** count;
|
const a4: [count]u8 = @splat('D');
|
||||||
|
|
||||||
print("{s} {s} {s} {s}\n", .{ a1, a2, a3, a4 });
|
print("{s} {s} {s} {s}\n", .{ a1, a2, a3, a4 });
|
||||||
|
|
||||||
|
|||||||
@@ -36,13 +36,14 @@ pub fn main() void {
|
|||||||
// statement was repeated three times almost verbatim. Yuck!
|
// statement was repeated three times almost verbatim. Yuck!
|
||||||
//
|
//
|
||||||
// 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 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) {
|
if (field_type != void) {
|
||||||
print(" {s}", .{field.name});
|
print(" {s}", .{field_name});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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!
|
// 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.
|
// 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
|
// We return an array of animals representing the creature. (This is why we
|
||||||
// really needed the 'count' parameter. Arrays need a size.)
|
// 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;
|
var next_animal: usize = 0;
|
||||||
|
|
||||||
inline for (fmt) |char| {
|
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?
|
// other animals or is this an error?
|
||||||
'g' => ???,
|
'g' => ???,
|
||||||
|
|
||||||
else => @compileError("No animal starts with '" ++ char ++ "'!"),
|
else => @compileError(std.fmt.comptimePrint("No animal starts with '{c}'!", .{char})),
|
||||||
},
|
},
|
||||||
|
|
||||||
.l => switch (char) {
|
.l => switch (char) {
|
||||||
|
|||||||
@@ -48,9 +48,7 @@ const Path = struct {
|
|||||||
// instead.
|
// instead.
|
||||||
//
|
//
|
||||||
// Please fill in the body of this function!
|
// 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
|
// Using our new function, these path definitions take up considerably less
|
||||||
// space in our program now!
|
// space in our program now!
|
||||||
@@ -97,7 +95,7 @@ const NotebookEntry = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const HermitsNotebook = struct {
|
const HermitsNotebook = struct {
|
||||||
entries: [place_count]?NotebookEntry = .{null} ** place_count,
|
entries: [place_count]?NotebookEntry = @splat(null),
|
||||||
next_entry: u8 = 0,
|
next_entry: u8 = 0,
|
||||||
end_of_entries: 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| {
|
notebook.getTripTo(trip[0..], destination) catch |err| {
|
||||||
print("Oh no! {}\n", .{err});
|
print("Oh no! {}\n", .{err});
|
||||||
|
|||||||
@@ -74,36 +74,27 @@ fn printTuple(tuple: anytype) void {
|
|||||||
// @typeInfo() - takes a type, returns a TypeInfo union
|
// @typeInfo() - takes a type, returns a TypeInfo union
|
||||||
// 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 field types can be found in
|
||||||
// TypeInfo's @"struct".fields.
|
// TypeInfo's @"struct".field_types.
|
||||||
//
|
//
|
||||||
// Example:
|
// Example:
|
||||||
//
|
//
|
||||||
// @typeInfo(Circle).@"struct".fields
|
// @typeInfo(Circle).@"struct".field_types
|
||||||
//
|
//
|
||||||
// This will be an array of StructFields.
|
// This will be an array of field types.
|
||||||
const fields = ???;
|
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
|
// 2. Loop through each field. This must be done at compile
|
||||||
// time.
|
// time.
|
||||||
//
|
//
|
||||||
// Hint: remember 'inline' loops?
|
// Hint: remember 'inline' loops?
|
||||||
//
|
//
|
||||||
for (fields) |field| {
|
for (???, ???) |???, ???| {
|
||||||
// 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:
|
|
||||||
//
|
|
||||||
// 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:
|
// You'll need this builtin:
|
||||||
//
|
//
|
||||||
// @field(lhs: anytype, comptime field_name: []const u8)
|
// @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.
|
// for declarations. If it's a value, it looks for data.
|
||||||
//
|
//
|
||||||
print("\"{s}\"({any}):{any} ", .{
|
print("\"{s}\"({any}):{any} ", .{
|
||||||
field.???,
|
field_name,
|
||||||
field.???,
|
field_type,
|
||||||
???,
|
???,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
//
|
//
|
||||||
// // In a task:
|
// // In a task:
|
||||||
// try mutex.lock(io); // blocks until lock is acquired
|
// try mutex.lock(io); // blocks until lock is acquired
|
||||||
// defer mutex.unlock();
|
// defer mutex.unlock(io);
|
||||||
// // ... critical section: safe to modify shared data ...
|
// // ... critical section: safe to modify shared data ...
|
||||||
//
|
//
|
||||||
// Without the mutex, concurrent tasks could read and write the
|
// 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;
|
state.mutex.??? catch return;
|
||||||
defer state.mutex.unlock(); // <-- what's missing here?
|
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;
|
state.counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,3 +93,12 @@ pub fn main() !void {
|
|||||||
//
|
//
|
||||||
// For Crypto it is better not to use this, but in sorting algorithms like
|
// For Crypto it is better not to use this, but in sorting algorithms like
|
||||||
// Bubble Sort it works very well.
|
// 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.
|
||||||
|
|||||||
@@ -51,12 +51,3 @@ pub fn main() void {
|
|||||||
//
|
//
|
||||||
// You are perhaps wondering what happens if one of the two lists
|
// You are perhaps wondering what happens if one of the two lists
|
||||||
// is longer than the other? Try it!
|
// 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.
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
// exercises.
|
// exercises.
|
||||||
//
|
//
|
||||||
// A nice example of this has been published on the Zig homepage,
|
// 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
|
// 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,
|
// to the elegance of Zig and that's a pity, if someone takes a short,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// Prerequisite :
|
// Prerequisite :
|
||||||
// - exercise/106_files.zig, or
|
// - exercise/109_files.zig, or
|
||||||
// - create a file {project_root}/output/zigling.txt
|
// - create a file {project_root}/output/zigling.txt
|
||||||
// with content `It's zigling time!`(18 bytes total)
|
// 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
|
// Get the current working directory
|
||||||
const cwd = std.Io.Dir.cwd();
|
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", .{});
|
var output_dir = try cwd.openDir(io, "output", .{});
|
||||||
defer output_dir.close(io);
|
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", .{});
|
const file = try output_dir.openFile(io, "zigling.txt", .{});
|
||||||
defer file.close(io);
|
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
|
// we need to pick the size of the array, 64 seems like a good number
|
||||||
// fix the initialization below
|
// do you remember the array repetition function?
|
||||||
var content = ['A']*64;
|
var content: ??? = ???('A');
|
||||||
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
|
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
|
||||||
std.debug.print("{s}\n", .{content});
|
std.debug.print("{s}\n", .{content});
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
// Operations performed on vectors in Zig will be done in parallel using
|
// Operations performed on vectors in Zig will be done in parallel using
|
||||||
// SIMD instructions, whenever possible.
|
// 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 v1 = @Vector(3, i32){ 1, 10, 100 };
|
||||||
const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };
|
const v2 = @Vector(3, f32){ 2.0, 3.0, 5.0 };
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//
|
//
|
||||||
// We've already learned plenty about bit manipulation using bitwise operations
|
// 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 097 and 098 and in quiz 110. The techniques we already know work
|
||||||
// just fine, but creating masks and shifting individual bits around can become
|
// just fine, but creating masks and shifting individual bits around can become
|
||||||
// quite tedious and unwieldy pretty quickly.
|
// 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:
|
// Luckily, Zig has a keyword for exactly this purpose:
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
--- exercises/005_arrays2.zig 2023-10-03 22:15:22.122241138 +0200
|
--- exercises/005_arrays2.zig 2026-05-04 16:26:32.778330847 +0200
|
||||||
+++ answers/005_arrays2.zig 2023-10-05 20:04:06.862763262 +0200
|
+++ answers/005_arrays2.zig 2026-05-04 16:26:13.082917974 +0200
|
||||||
@@ -25,12 +25,12 @@
|
@@ -21,12 +21,12 @@
|
||||||
// (Problem 1)
|
// (Problem 1)
|
||||||
// Please set this array concatenating the two arrays above.
|
// Please set this array concatenating the two arrays above.
|
||||||
// It should result in: 1 3 3 7
|
// It should result in: 1 3 3 7
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
// (Problem 2)
|
// (Problem 2)
|
||||||
// Please set this array using repetition.
|
// Please set this array using repetition.
|
||||||
// It should result in: 1 0 0 1 1 0 0 1 1 0 0 1
|
// 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 = [_]u8{ ??? };
|
||||||
+ const bit_pattern = [_]u8{ 1, 0, 0, 1 } ** 3;
|
+ const bit_pattern_unit = [_]u8{ 1, 0, 0, 1 };
|
||||||
|
const bit_pattern: [3 * bit_pattern_unit.len]u8 = @bitCast(@as([3][bit_pattern_unit.len]u8, @splat(bit_pattern_unit)));
|
||||||
|
|
||||||
// Okay, that's all of the problems. Let's see the results.
|
// Okay, that's all of the problems. Let's see the results.
|
||||||
//
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
--- exercises/006_strings.zig 2023-10-03 22:15:22.122241138 +0200
|
--- exercises/006_strings.zig 2026-05-04 17:04:31.763821070 +0200
|
||||||
+++ answers/006_strings.zig 2023-10-05 20:04:06.869430053 +0200
|
+++ answers/006_strings.zig 2026-05-04 17:02:11.672866263 +0200
|
||||||
@@ -24,18 +24,18 @@
|
@@ -24,14 +24,14 @@
|
||||||
// (Problem 1)
|
// (Problem 1)
|
||||||
// Use array square bracket syntax to get the letter 'd' from
|
// Use array square bracket syntax to get the letter 'd' from
|
||||||
// the string "stardust" above.
|
// the string "stardust" above.
|
||||||
@@ -8,11 +8,6 @@
|
|||||||
+ const d: u8 = ziggy[4];
|
+ const d: u8 = ziggy[4];
|
||||||
|
|
||||||
// (Problem 2)
|
// (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".
|
// Use the array concatenation '++' operator to make "Major Tom".
|
||||||
// (You'll need to add a space as well!)
|
// (You'll need to add a space as well!)
|
||||||
const major = "Major";
|
const major = "Major";
|
||||||
@@ -21,4 +16,4 @@
|
|||||||
+ const major_tom = major ++ " " ++ tom;
|
+ const major_tom = major ++ " " ++ tom;
|
||||||
|
|
||||||
// That's all the problems. Let's see our results:
|
// 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 });
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
--- exercises/028_defer2.zig 2023-10-03 22:15:22.122241138 +0200
|
--- exercises/028_defer2.zig 2026-06-02 06:08:12.713672612 +0200
|
||||||
+++ answers/028_defer2.zig 2023-10-05 20:04:06.966098530 +0200
|
+++ answers/028_defer2.zig 2026-06-02 06:08:43.262234023 +0200
|
||||||
@@ -18,7 +18,7 @@
|
@@ -20,7 +20,7 @@
|
||||||
fn printAnimal(animal: u8) void {
|
fn printAnimal(animal: u8) void {
|
||||||
std.debug.print("(", .{});
|
std.debug.print("(", .{});
|
||||||
|
|
||||||
@@ -9,3 +9,15 @@
|
|||||||
|
|
||||||
if (animal == 'g') {
|
if (animal == 'g') {
|
||||||
std.debug.print("Goat", .{});
|
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;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
--- exercises/040_pointers2.zig 2023-10-03 22:15:22.122241138 +0200
|
--- exercises/040_pointers2.zig 2026-05-22 21:57:28.601255748 +0200
|
||||||
+++ answers/040_pointers2.zig 2023-10-05 20:04:07.022766257 +0200
|
+++ answers/040_pointers2.zig 2026-05-22 21:57:27.672235943 +0200
|
||||||
@@ -23,7 +23,7 @@
|
@@ -23,7 +23,7 @@
|
||||||
|
|
||||||
pub fn main() void {
|
pub fn main() void {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
--- exercises/058_quiz7.zig 2024-10-28 09:06:49.448505460 +0100
|
--- exercises/058_quiz7.zig 2026-05-04 16:34:31.692458399 +0200
|
||||||
+++ answers/058_quiz7.zig 2024-10-28 09:35:14.631932322 +0100
|
+++ answers/058_quiz7.zig 2026-05-04 16:34:29.239406323 +0200
|
||||||
@@ -192,8 +192,8 @@
|
@@ -192,8 +192,8 @@
|
||||||
// Oops! The hermit forgot how to capture the union values
|
// Oops! The hermit forgot how to capture the union values
|
||||||
// in a switch statement. Please capture each value as
|
// in a switch statement. Please capture each value as
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -255,7 +255,7 @@
|
@@ -254,7 +254,7 @@
|
||||||
// dereference and optional value "unwrapping" look
|
// dereference and optional value "unwrapping" look
|
||||||
// together. Remember that you return the address with the
|
// together. Remember that you return the address with the
|
||||||
// "&" operator.
|
// "&" operator.
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
// Try to make your answer this long:__________;
|
// Try to make your answer this long:__________;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -309,7 +309,7 @@
|
@@ -308,7 +308,7 @@
|
||||||
//
|
//
|
||||||
// Looks like the hermit forgot something in the return value of
|
// Looks like the hermit forgot something in the return value of
|
||||||
// this function. What could that be?
|
// this function. What could that be?
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
--- exercises/060_floats.zig 2026-04-03 14:40:17.582344768 +0200
|
--- exercises/060_floats.zig 2026-05-02 19:22:46.225370223 +0200
|
||||||
+++ answers/060_floats.zig 2026-04-03 14:49:26.997886326 +0200
|
+++ answers/060_floats.zig 2026-05-02 19:22:47.523142218 +0200
|
||||||
@@ -43,7 +43,7 @@
|
@@ -50,7 +50,7 @@
|
||||||
//
|
//
|
||||||
// We'll convert this weight from pounds to metric units 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.
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
// By default, float values are formatted in standard decimal
|
// By default, float values are formatted in standard decimal
|
||||||
// notation. Experiment with '{d}' and '{d:.3}' to see how
|
// notation. Experiment with '{d}' and '{d:.3}' to see how
|
||||||
@@ -51,7 +51,7 @@
|
@@ -58,7 +58,7 @@
|
||||||
// scientific notation.
|
// scientific notation.
|
||||||
// NOTE: The weight of the shuttle is a huge number, a scientific notation
|
// NOTE: The weight of the shuttle is a huge number, a scientific notation
|
||||||
// may be more appropriate.
|
// may be more appropriate.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
--- exercises/065_builtins2.zig 2026-02-27 13:10:36
|
--- exercises/065_builtins2.zig 2026-06-01 15:33:16.617432671 +0200
|
||||||
+++ answers/065_builtins2.zig 2026-02-27 13:10:52
|
+++ answers/065_builtins2.zig 2026-06-01 15:33:31.104018108 +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,26 @@
|
|||||||
|
|
||||||
// Now we print a pithy statement about Narcissus.
|
// Now we print a pithy statement about Narcissus.
|
||||||
print("A {s} loves all {s}es. ", .{
|
print("A {s} loves all {s}es. ", .{
|
||||||
@@ -113,15 +113,15 @@
|
@@ -102,16 +102,16 @@
|
||||||
// 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 (field_???[???] != void) {
|
||||||
+ if (fields[0].type != void) {
|
- print(" {s}", .{field_???[???]});
|
||||||
print(" {s}", .{fields[0].name});
|
+ if (field_types[0] != void) {
|
||||||
|
+ print(" {s}", .{field_names[0]});
|
||||||
}
|
}
|
||||||
|
|
||||||
- if (fields[1].??? != void) {
|
- if (field_???[???] != void) {
|
||||||
+ if (fields[1].type != void) {
|
- print(" {s}", .{field_???[???]});
|
||||||
print(" {s}", .{fields[1].name});
|
+ if (field_types[1] != void) {
|
||||||
|
+ print(" {s}", .{field_names[1]});
|
||||||
}
|
}
|
||||||
|
|
||||||
- if (fields[2].??? != void) {
|
- if (field_???[???] != void) {
|
||||||
+ if (fields[2].type != void) {
|
- print(" {s}", .{field_???[???]});
|
||||||
print(" {s}", .{fields[2].name});
|
+ if (field_types[2] != void) {
|
||||||
|
+ print(" {s}", .{field_names[2]});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Yuck, look at all that repeated code above! I don't know
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
--- exercises/067_comptime2.zig 2025-12-07 22:51:24.396031248 +0100
|
--- exercises/067_comptime2.zig 2026-05-04 15:38:52.565144012 +0200
|
||||||
+++ answers/067_comptime2.zig 2025-12-07 22:50:18.721616293 +0100
|
+++ answers/067_comptime2.zig 2026-05-04 15:37:20.257213463 +0200
|
||||||
@@ -36,7 +36,7 @@
|
@@ -36,7 +36,7 @@
|
||||||
// In this contrived example, we've decided to allocate some
|
// In this contrived example, we've decided to allocate some
|
||||||
// arrays using a variable count! But something's missing...
|
// arrays using a variable count! But something's missing...
|
||||||
@@ -8,4 +8,4 @@
|
|||||||
+ comptime var count = 0;
|
+ comptime var count = 0;
|
||||||
|
|
||||||
count += 1;
|
count += 1;
|
||||||
const a1: [count]u8 = .{'A'} ** count;
|
const a1: [count]u8 = @splat('A');
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
--- exercises/071_comptime6.zig 2024-09-02 19:21:50.250454978 +0200
|
--- exercises/071_comptime6.zig 2026-06-01 15:35:27.223400223 +0200
|
||||||
+++ answers/071_comptime6.zig 2024-09-02 19:21:23.553250563 +0200
|
+++ answers/071_comptime6.zig 2026-06-01 15:36:35.349728561 +0200
|
||||||
@@ -40,7 +40,7 @@
|
@@ -41,7 +41,7 @@
|
||||||
|
const field_names = @typeInfo(Narcissus).@"struct".field_names;
|
||||||
const fields = @typeInfo(Narcissus).@"struct".fields;
|
const field_types = @typeInfo(Narcissus).@"struct".field_types;
|
||||||
|
|
||||||
- ??? {
|
- ??? {
|
||||||
+ inline for (fields) |field| {
|
+ inline for (field_names, field_types) |field_name, field_type| {
|
||||||
if (field.type != void) {
|
if (field_type != void) {
|
||||||
print(" {s}", .{field.name});
|
print(" {s}", .{field_name});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
--- exercises/074_comptime9.zig 2026-04-11 21:35:08.132459373 -0700
|
--- exercises/074_comptime9.zig 2026-05-04 17:11:05.144118157 +0200
|
||||||
+++ answers/074_comptime9.zig 2026-04-12 07:13:37.971010827 -0700
|
+++ answers/074_comptime9.zig 2026-05-04 17:10:36.778519877 +0200
|
||||||
@@ -27,12 +27,12 @@
|
@@ -28,12 +28,12 @@
|
||||||
start, // Ready to start a new animal.
|
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.
|
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
|
// We return an array of animals representing the creature. (This is why we
|
||||||
// really needed the 'count' parameter. Arrays need a size.)
|
// 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;
|
- var next_animal: usize = 0;
|
||||||
+ comptime var next_animal: usize = 0;
|
+ comptime var next_animal: usize = 0;
|
||||||
|
|
||||||
inline for (fmt) |char| {
|
inline for (fmt) |char| {
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@
|
@@ -57,7 +57,7 @@
|
||||||
//
|
//
|
||||||
// What do you think happens with Gators? Do they join with
|
// What do you think happens with Gators? Do they join with
|
||||||
// other animals or is this an error?
|
// other animals or is this an error?
|
||||||
- 'g' => ???,
|
- 'g' => ???,
|
||||||
+ 'g' => @compileError("Gators refuse to join with other animals."),
|
+ '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;
|
next_animal += 1;
|
||||||
// Something is missing here. After we finish a Llama, we
|
// Something is missing here. After we finish a Llama, we
|
||||||
// need to be ready to _start_ over with a new animal...
|
// need to be ready to _start_ over with a new animal...
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
--- exercises/075_quiz8.zig 2023-11-21 14:48:15.440702720 +0100
|
--- exercises/075_quiz8.zig 2026-05-04 15:51:48.254371574 +0200
|
||||||
+++ answers/075_quiz8.zig 2023-11-21 14:50:23.453311616 +0100
|
+++ answers/075_quiz8.zig 2026-05-04 15:49:28.426445382 +0200
|
||||||
@@ -49,7 +49,11 @@
|
@@ -48,7 +48,13 @@
|
||||||
|
// instead.
|
||||||
//
|
//
|
||||||
// Please fill in the body of this function!
|
// 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{
|
+ return Path{
|
||||||
+ .from = from,
|
+ .from = from,
|
||||||
+ .to = to,
|
+ .to = to,
|
||||||
+ .dist = dist,
|
+ .dist = dist,
|
||||||
+ };
|
+ };
|
||||||
}
|
+}
|
||||||
|
|
||||||
// Using our new function, these path definitions take up considerably less
|
// Using our new function, these path definitions take up considerably less
|
||||||
|
// space in our program now!
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
--- exercises/082_anonymous_structs3.zig 2026-02-27 13:05:46
|
--- exercises/082_anonymous_structs3.zig 2026-06-01 15:59:11.872467805 +0200
|
||||||
+++ answers/082_anonymous_structs3.zig 2026-02-27 13:07:22
|
+++ answers/082_anonymous_structs3.zig 2026-06-01 15:58:38.004730144 +0200
|
||||||
@@ -82,14 +82,14 @@
|
@@ -82,17 +82,17 @@
|
||||||
// @typeInfo(Circle).@"struct".fields
|
// @typeInfo(Circle).@"struct".field_types
|
||||||
//
|
//
|
||||||
// This will be an array of StructFields.
|
// This will be an array of field types.
|
||||||
- const fields = ???;
|
- const field_types = ???;
|
||||||
+ const fields = @typeInfo(@TypeOf(tuple)).@"struct".fields;
|
+ 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
|
// 2. Loop through each field. This must be done at compile
|
||||||
// time.
|
// time.
|
||||||
//
|
//
|
||||||
// Hint: remember 'inline' loops?
|
// Hint: remember 'inline' loops?
|
||||||
//
|
//
|
||||||
- for (fields) |field| {
|
- for (???, ???) |???, ???| {
|
||||||
+ inline for (fields) |field| {
|
+ inline for (field_types, field_names) |field_type, field_name| {
|
||||||
// 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:
|
// You'll need this builtin:
|
||||||
@@ -123,9 +123,9 @@
|
@@ -116,7 +116,7 @@
|
||||||
// for declarations. If it's a value, it looks for data.
|
|
||||||
//
|
|
||||||
print("\"{s}\"({any}):{any} ", .{
|
print("\"{s}\"({any}):{any} ", .{
|
||||||
- field.???,
|
field_name,
|
||||||
- field.???,
|
field_type,
|
||||||
- ???,
|
- ???,
|
||||||
+ field.name,
|
+ @field(tuple, field_name),
|
||||||
+ field.type,
|
|
||||||
+ @field(tuple, field.name),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
--- exercises/091_async7.zig 2026-04-02 10:50:08.142508099 +0200
|
--- exercises/091_async7.zig 2026-04-18 23:30:40.963951835 +0200
|
||||||
+++ answers/091_async7.zig 2026-04-02 10:49:59.629341593 +0200
|
+++ answers/091_async7.zig 2026-04-18 23:33:47.313340585 +0200
|
||||||
@@ -49,8 +49,8 @@
|
@@ -49,8 +49,8 @@
|
||||||
for (0..times) |_| {
|
for (0..times) |_| {
|
||||||
// Acquire the lock before modifying shared state.
|
// Acquire the lock before modifying shared state.
|
||||||
@@ -9,5 +9,5 @@
|
|||||||
+ state.mutex.lock(io) catch return;
|
+ state.mutex.lock(io) catch return;
|
||||||
+ defer state.mutex.unlock(io);
|
+ 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.
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
--- exercises/110_files2.zig 2026-03-20 19:23:48.874150121 +0100
|
--- exercises/110_files2.zig 2026-05-31 14:54:55.061019159 +0200
|
||||||
+++ answers/110_files2.zig 2026-04-02 10:51:15.815831734 +0200
|
+++ answers/110_files2.zig 2026-05-31 14:54:42.655691238 +0200
|
||||||
@@ -39,7 +39,7 @@
|
@@ -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
|
// we need to pick the size of the array, 64 seems like a good number
|
||||||
// fix the initialization below
|
// do you remember the array repetition function?
|
||||||
- var content = ['A']*64;
|
- var content: ??? = ???('A');
|
||||||
+ var content = [_]u8{'A'} ** 64;
|
+ var content: [64]u8 = @splat('A');
|
||||||
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
|
// this should print out : `AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`
|
||||||
std.debug.print("{s}\n", .{content});
|
std.debug.print("{s}\n", .{content});
|
||||||
|
|
||||||
|
|||||||
1224
rivendell/elrond.zig
Normal file
1224
rivendell/elrond.zig
Normal file
File diff suppressed because it is too large
Load Diff
413
test/tests.zig
413
test/tests.zig
@@ -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 });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user