diff --git a/README.md b/README.md index a6e760f..fd45a06 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,9 @@ language such as C. Each exercise is self-contained and self-explained. However, you're encouraged to also check out these Zig language resources -for more detail: +for more details: * https://ziglang.org/learn/ -* https://ziglearn.org/ * https://ziglang.org/documentation/master/ * [Zig in Depth! (video series)](https://www.youtube.com/watch?v=MMtvGA1YhW4&list=PLtB7CL7EG7pCw7Xy1SQC53Gl8pI7aDg9t&pp=iAQB) @@ -45,7 +44,7 @@ Verify the installation and build number of `zig` like so: ``` $ zig version -0.15.0-dev.xxxx+xxxxxxxxx +0.16.0-dev.xxxx+xxxxxxxxx ``` Clone this repository with Git: @@ -73,8 +72,8 @@ 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.14.1`, but Ziglings needs a dev build with -pre-release version "0.15.0" and a build number at least as high +stable release was `0.15.1`, but Ziglings needs a dev build with +pre-release version "0.16.0" and a build number at least as high as that shown in the example version check above. It is likely that you'll download a build which is _greater_ than @@ -87,7 +86,9 @@ that if you update one, you may need to also update the other. ### Version Changes -Version-0.14.0-dev.1573 +* *2025-08-15* zig 0.15.0-dev.1519 - changes in array list, see [#24801](https://github.com/ziglang/zig/pull/24801) +* *2025-08-08* zig 0.15.0-dev.1380 - changes in build system, see [#24588](https://github.com/ziglang/zig/pull/24588) +* *2025-07-22* zig 0.15.0-dev.1092 - various changes due to new I/O API, see [#24488](https://github.com/ziglang/zig/pull/24488) * *2024-09-16* zig 0.14.0-dev.1573 - introduction of labeled switch, see [#21257](https://github.com/ziglang/zig/pull/21257) * *2024-09-02* zig 0.14.0-dev.1409 - several changes in std.builtin, see [#21225](https://github.com/ziglang/zig/pull/21225) * *2024-08-04* zig 0.14.0-dev.1224 - several changes in build system, see [#21115](https://github.com/ziglang/zig/pull/21115) diff --git a/build.zig b/build.zig index 6c76917..7e8bf7a 100644 --- a/build.zig +++ b/build.zig @@ -15,7 +15,7 @@ const print = std.debug.print; // 1) Getting Started // 2) Version Changes comptime { - const required_zig = "0.14.0-dev.1573"; + const required_zig = "0.15.0-dev.1519"; const current_zig = builtin.zig_version; const min_zig = std.SemanticVersion.parse(required_zig) catch unreachable; if (current_zig.order(min_zig) == .lt) { @@ -126,19 +126,18 @@ pub fn build(b: *Build) !void { if (!validate_exercises()) std.process.exit(2); use_color_escapes = false; - if (std.io.getStdErr().supportsAnsiEscapeCodes()) { + if (std.fs.File.stderr().supportsAnsiEscapeCodes()) { use_color_escapes = true; } else if (builtin.os.tag == .windows) { const w32 = struct { - const WINAPI = std.os.windows.WINAPI; const DWORD = std.os.windows.DWORD; const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; const STD_ERROR_HANDLE: DWORD = @bitCast(@as(i32, -12)); - extern "kernel32" fn GetStdHandle(id: DWORD) callconv(WINAPI) ?*anyopaque; - extern "kernel32" fn GetConsoleMode(console: ?*anyopaque, out_mode: *DWORD) callconv(WINAPI) u32; - extern "kernel32" fn SetConsoleMode(console: ?*anyopaque, mode: DWORD) callconv(WINAPI) u32; + const GetStdHandle = std.os.windows.kernel32.GetStdHandle; + const GetConsoleMode = std.os.windows.kernel32.GetConsoleMode; + const SetConsoleMode = std.os.windows.kernel32.SetConsoleMode; }; - const handle = w32.GetStdHandle(w32.STD_ERROR_HANDLE); + const handle = w32.GetStdHandle(w32.STD_ERROR_HANDLE).?; var mode: w32.DWORD = 0; if (w32.GetConsoleMode(handle, &mode) != 0) { mode |= w32.ENABLE_VIRTUAL_TERMINAL_PROCESSING; @@ -493,7 +492,7 @@ const ZiglingStep = struct { const path = join(b.allocator, &.{ self.work_path, exercise_path }) catch @panic("OOM"); - var zig_args = std.ArrayList([]const u8).init(b.allocator); + var zig_args = std.array_list.Managed([]const u8).init(b.allocator); defer zig_args.deinit(); zig_args.append(b.graph.zig_exe) catch @panic("OOM"); @@ -509,6 +508,10 @@ const ZiglingStep = struct { zig_args.append("-lc") catch @panic("OOM"); } + if (b.reference_trace) |rt| { + zig_args.append(b.fmt("-freference-trace={}", .{rt})) catch @panic("OOM"); + } + zig_args.append(b.pathFromRoot(path)) catch @panic("OOM"); zig_args.append("--cache-dir") catch @panic("OOM"); @@ -520,7 +523,7 @@ const ZiglingStep = struct { // NOTE: After many changes in zig build system, we need to create the cache path manually. // See https://github.com/ziglang/zig/pull/21115 // Maybe there is a better way (in the future). - const exe_dir = try self.step.evalZigProcess(zig_args.items, prog_node, false); + const exe_dir = try self.step.evalZigProcess(zig_args.items, prog_node, false, null, b.allocator); const exe_name = switch (self.exercise.kind) { .exe => self.exercise.name(), .@"test" => "test", @@ -582,17 +585,17 @@ fn resetLine() void { /// Removes trailing whitespace for each line in buf, also ensuring that there /// are no trailing LF characters at the end. pub fn trimLines(allocator: std.mem.Allocator, buf: []const u8) ![]const u8 { - var list = try std.ArrayList(u8).initCapacity(allocator, buf.len); + var list = try std.array_list.Aligned(u8, null).initCapacity(allocator, buf.len); var iter = std.mem.splitSequence(u8, buf, " \n"); while (iter.next()) |line| { // TODO: trimming CR characters is probably not necessary. const data = std.mem.trimRight(u8, line, " \r"); - try list.appendSlice(data); - try list.append('\n'); + try list.appendSlice(allocator, data); + try list.append(allocator, '\n'); } - const result = try list.toOwnedSlice(); // TODO: probably not necessary + const result = try list.toOwnedSlice(allocator); // TODO: probably not necessary // Remove the trailing LF character, that is always present in the exercise // output. @@ -1064,7 +1067,7 @@ const exercises = [_]Exercise{ .{ .main_file = "082_anonymous_structs3.zig", .output = - \\"0"(bool):true "1"(bool):false "2"(i32):42 "3"(f32):3.141592e0 + \\"0"(bool):true "1"(bool):false "2"(i32):42 "3"(f32):3.141592 , .hint = "This one is a challenge! But you have everything you need.", }, diff --git a/exercises/019_functions2.zig b/exercises/019_functions2.zig index a527ae2..5a76c7b 100644 --- a/exercises/019_functions2.zig +++ b/exercises/019_functions2.zig @@ -3,7 +3,7 @@ // example that takes two parameters. As you can see, parameters // are declared just like any other types ("name": "type"): // -// fn myFunction(number: u8, is_lucky: bool) { +// fn myFunction(number: u8, is_lucky: bool) void { // ... // } // diff --git a/exercises/026_hello2.zig b/exercises/026_hello2.zig index cd59b86..582dba9 100644 --- a/exercises/026_hello2.zig +++ b/exercises/026_hello2.zig @@ -16,12 +16,12 @@ const std = @import("std"); // pub fn main() !void { // We get a Writer for Standard Out so we can print() to it. - const stdout = std.io.getStdOut().writer(); + var stdout = std.fs.File.stdout().writer(&.{}); // Unlike std.debug.print(), the Standard Out writer can fail // with an error. We don't care _what_ the error is, we want // to be able to pass it up as a return value of main(). // // We just learned of a single statement which can accomplish this. - stdout.print("Hello world!\n", .{}); + stdout.interface.print("Hello world!\n", .{}); } diff --git a/exercises/034_quiz4.zig b/exercises/034_quiz4.zig index 2d843f2..8704397 100644 --- a/exercises/034_quiz4.zig +++ b/exercises/034_quiz4.zig @@ -10,11 +10,11 @@ const std = @import("std"); const NumError = error{IllegalNumber}; pub fn main() void { - const stdout = std.io.getStdOut().writer(); + var stdout = std.fs.File.stdout().writer(&.{}); const my_num: u32 = getNumber(); - try stdout.print("my_num={}\n", .{my_num}); + try stdout.interface.print("my_num={}\n", .{my_num}); } // This function is obviously weird and non-functional. But you will not be changing it for this quiz. diff --git a/exercises/098_bit_manipulation2.zig b/exercises/098_bit_manipulation2.zig index 979b103..8b51265 100644 --- a/exercises/098_bit_manipulation2.zig +++ b/exercises/098_bit_manipulation2.zig @@ -32,7 +32,7 @@ const print = std.debug.print; pub fn main() !void { // let's check the pangram - print("Is this a pangram? {?}!\n", .{isPangram("The quick brown fox jumps over the lazy dog.")}); + print("Is this a pangram? {}!\n", .{isPangram("The quick brown fox jumps over the lazy dog.")}); } fn isPangram(str: []const u8) bool { @@ -45,7 +45,7 @@ fn isPangram(str: []const u8) bool { // loop about all characters in the string for (str) |c| { // if the character is an alphabetical character - if (ascii.isASCII(c) and ascii.isAlphabetic(c)) { + if (ascii.isAscii(c) and ascii.isAlphabetic(c)) { // then we set the bit at the position // // to do this, we use a little trick: diff --git a/exercises/104_threading.zig b/exercises/104_threading.zig index 9c4e216..638769f 100644 --- a/exercises/104_threading.zig +++ b/exercises/104_threading.zig @@ -106,7 +106,7 @@ pub fn main() !void { // After the threads have been started, // they run in parallel and we can still do some work in between. - std.time.sleep(1500 * std.time.ns_per_ms); + std.Thread.sleep(1500 * std.time.ns_per_ms); std.debug.print("Some weird stuff, after starting the threads.\n", .{}); } // After we have left the closed area, we wait until @@ -117,12 +117,12 @@ pub fn main() !void { // This function is started with every thread that we set up. // In our example, we pass the number of the thread as a parameter. fn thread_function(num: usize) !void { - std.time.sleep(200 * num * std.time.ns_per_ms); + std.Thread.sleep(200 * num * std.time.ns_per_ms); std.debug.print("thread {d}: {s}\n", .{ num, "started." }); // This timer simulates the work of the thread. const work_time = 3 * ((5 - num % 3) - 2); - std.time.sleep(work_time * std.time.ns_per_s); + std.Thread.sleep(work_time * std.time.ns_per_s); std.debug.print("thread {d}: {s}\n", .{ num, "finished." }); } diff --git a/patches/patches/026_hello2.patch b/patches/patches/026_hello2.patch index f0224a1..f99cc67 100644 --- a/patches/patches/026_hello2.patch +++ b/patches/patches/026_hello2.patch @@ -1,9 +1,9 @@ ---- exercises/026_hello2.zig 2023-10-03 22:15:22.122241138 +0200 -+++ answers/026_hello2.zig 2023-10-05 20:04:06.959431737 +0200 +--- exercises/026_hello2.zig 2025-07-22 09:55:51.337832401 +0200 ++++ answers/026_hello2.zig 2025-07-22 10:00:11.233348058 +0200 @@ -23,5 +23,5 @@ // 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", .{}); +- stdout.interface.print("Hello world!\n", .{}); ++ try stdout.interface.print("Hello world!\n", .{}); } diff --git a/patches/patches/034_quiz4.patch b/patches/patches/034_quiz4.patch index 8c0bc0e..15c95fc 100644 --- a/patches/patches/034_quiz4.patch +++ b/patches/patches/034_quiz4.patch @@ -1,15 +1,15 @@ ---- exercises/034_quiz4.zig 2023-10-03 22:15:22.122241138 +0200 -+++ answers/034_quiz4.zig 2023-10-05 20:04:06.996099091 +0200 +--- exercises/034_quiz4.zig 2025-07-22 09:55:51.337832401 +0200 ++++ answers/034_quiz4.zig 2025-07-22 10:05:08.320323184 +0200 @@ -9,10 +9,10 @@ const NumError = error{IllegalNumber}; -pub fn main() void { +pub fn main() !void { - const stdout = std.io.getStdOut().writer(); + var stdout = std.fs.File.stdout().writer(&.{}); - const my_num: u32 = getNumber(); + const my_num: u32 = try getNumber(); - try stdout.print("my_num={}\n", .{my_num}); + try stdout.interface.print("my_num={}\n", .{my_num}); } diff --git a/patches/patches/084_async.patch b/patches/patches/084_async.patch deleted file mode 100644 index 11a9da0..0000000 --- a/patches/patches/084_async.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- exercises/084_async.zig 2023-10-03 22:15:22.125574535 +0200 -+++ answers/084_async.zig 2023-10-05 20:04:07.219436606 +0200 -@@ -48,7 +48,7 @@ - pub fn main() void { - // Additional Hint: you can assign things to '_' when you - // don't intend to do anything with them. -- foo(); -+ _ = async foo(); - } - - fn foo() void { diff --git a/patches/patches/085_async2.patch b/patches/patches/085_async2.patch deleted file mode 100644 index ba10b05..0000000 --- a/patches/patches/085_async2.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- exercises/085_async2.zig 2023-10-03 22:15:22.125574535 +0200 -+++ answers/085_async2.zig 2023-10-05 20:04:07.226103397 +0200 -@@ -19,6 +19,7 @@ - - pub fn main() void { - var foo_frame = async foo(); -+ resume foo_frame; - } - - fn foo() void { diff --git a/patches/patches/086_async3.patch b/patches/patches/086_async3.patch deleted file mode 100644 index d80d4a1..0000000 --- a/patches/patches/086_async3.patch +++ /dev/null @@ -1,16 +0,0 @@ ---- exercises/086_async3.zig 2023-10-03 22:15:22.125574535 +0200 -+++ answers/086_async3.zig 2023-10-05 20:04:07.229436793 +0200 -@@ -13,7 +13,12 @@ - const n = 5; - var foo_frame = async foo(n); - -- ??? -+ // Silly solution. You can also use a loop. -+ resume foo_frame; -+ resume foo_frame; -+ resume foo_frame; -+ resume foo_frame; -+ resume foo_frame; - - print("\n", .{}); - } diff --git a/patches/patches/087_async4.patch b/patches/patches/087_async4.patch deleted file mode 100644 index b1c1736..0000000 --- a/patches/patches/087_async4.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- exercises/087_async4.zig 2023-10-03 22:15:22.125574535 +0200 -+++ answers/087_async4.zig 2023-10-05 20:04:07.236103584 +0200 -@@ -16,7 +16,7 @@ - - while (global_counter <= 5) { - print("{} ", .{global_counter}); -- ??? -+ resume foo_frame; - } - - print("\n", .{}); -@@ -24,7 +24,7 @@ - - fn foo() void { - while (true) { -- ??? -- ??? -+ global_counter += 1; -+ suspend {} - } - } diff --git a/patches/patches/088_async5.patch b/patches/patches/088_async5.patch deleted file mode 100644 index b9d5a21..0000000 --- a/patches/patches/088_async5.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- exercises/088_async5.zig 2023-10-03 22:15:22.125574535 +0200 -+++ answers/088_async5.zig 2023-10-05 20:04:07.239436980 +0200 -@@ -36,7 +36,7 @@ - pub fn main() void { - var myframe = async getPageTitle("http://example.com"); - -- var value = ??? -+ var value = await myframe; - - print("{s}\n", .{value}); - } diff --git a/patches/patches/089_async6.patch b/patches/patches/089_async6.patch deleted file mode 100644 index 4a0687e..0000000 --- a/patches/patches/089_async6.patch +++ /dev/null @@ -1,13 +0,0 @@ ---- exercises/089_async6.zig 2023-10-03 22:15:22.125574535 +0200 -+++ answers/089_async6.zig 2023-10-05 20:04:07.242770376 +0200 -@@ -41,8 +41,8 @@ - var com_frame = async getPageTitle("http://example.com"); - var org_frame = async getPageTitle("http://example.org"); - -- var com_title = com_frame; -- var org_title = org_frame; -+ var com_title = await com_frame; -+ var org_title = await org_frame; - - print(".com: {s}, .org: {s}.\n", .{ com_title, org_title }); - } diff --git a/patches/patches/090_async7.patch b/patches/patches/090_async7.patch deleted file mode 100644 index 62ec057..0000000 --- a/patches/patches/090_async7.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- exercises/090_async7.zig 2023-10-03 22:15:22.125574535 +0200 -+++ answers/090_async7.zig 2023-10-05 20:04:07.249437167 +0200 -@@ -29,7 +29,7 @@ - // The main() function can not be async. But we know - // getBeef() will not suspend with this particular - // invocation. Please make this okay: -- var my_beef = getBeef(0); -+ var my_beef = nosuspend getBeef(0); - - print("beef? {X}!\n", .{my_beef}); - } diff --git a/patches/patches/091_async8.patch b/patches/patches/091_async8.patch deleted file mode 100644 index ddd3fce..0000000 --- a/patches/patches/091_async8.patch +++ /dev/null @@ -1,26 +0,0 @@ ---- exercises/091_async8.zig 2023-10-03 22:15:22.125574535 +0200 -+++ answers/091_async8.zig 2023-10-05 20:04:07.252770563 +0200 -@@ -17,7 +17,7 @@ - - var frame = async suspendable(); - -- print("X", .{}); -+ print("D", .{}); - - resume frame; - -@@ -25,11 +25,11 @@ - } - - fn suspendable() void { -- print("X", .{}); -+ print("B", .{}); - - suspend { -- print("X", .{}); -+ print("C", .{}); - } - -- print("X", .{}); -+ print("E", .{}); - } diff --git a/test/tests.zig b/test/tests.zig index 5f64f8e..b191610 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -161,7 +161,7 @@ const CheckNamedStep = struct { ); defer stderr_file.close(); - const stderr = stderr_file.reader(); + var stderr = stderr_file.readerStreaming(&.{}); { // Skip the logo. const nlines = mem.count(u8, root.logo, "\n"); @@ -169,10 +169,10 @@ const CheckNamedStep = struct { var lineno: usize = 0; while (lineno < nlines) : (lineno += 1) { - _ = try readLine(stderr, &buf); + _ = try readLine(&stderr, &buf); } } - try check_output(step, ex, stderr); + try check_output(step, ex, &stderr); } }; @@ -213,7 +213,7 @@ const CheckStep = struct { ); defer stderr_file.close(); - const stderr = stderr_file.reader(); + var stderr = stderr_file.readerStreaming(&.{}); for (exercises) |ex| { if (ex.number() == 1) { // Skip the logo. @@ -222,15 +222,15 @@ const CheckStep = struct { var lineno: usize = 0; while (lineno < nlines) : (lineno += 1) { - _ = try readLine(stderr, &buf); + _ = try readLine(&stderr, &buf); } } - try check_output(step, ex, stderr); + try check_output(step, ex, &stderr); } } }; -fn check_output(step: *Step, exercise: Exercise, reader: Reader) !void { +fn check_output(step: *Step, exercise: Exercise, reader: *Reader) !void { const b = step.owner; var buf: [1024]u8 = undefined; @@ -297,12 +297,9 @@ fn check( } } -fn readLine(reader: fs.File.Reader, buf: []u8) !?[]const u8 { - if (try reader.readUntilDelimiterOrEof(buf, '\n')) |line| { - return mem.trimRight(u8, line, " \r\n"); - } - - return null; +fn readLine(reader: *fs.File.Reader, buf: []u8) !?[]const u8 { + try reader.interface.readSliceAll(buf); + return mem.trimRight(u8, buf, " \r\n"); } /// Fails with a custom error message. @@ -405,7 +402,8 @@ fn heal(allocator: Allocator, exercises: []const Exercise, work_path: []const u8 /// difference that returns an error when the temp path cannot be created. pub fn makeTempPath(b: *Build) ![]const u8 { const rand_int = std.crypto.random.int(u64); - const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ Build.hex64(rand_int); + const rand_hex64 = std.fmt.hex(rand_int); + const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ rand_hex64; const path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM"); try b.cache_root.handle.makePath(tmp_dir_sub_path);