mirror of
https://codeberg.org/ziglings/exercises.git
synced 2026-06-07 23:40:00 +00:00
Merge branch 'main' into fix-build-update
This commit is contained in:
@@ -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. That’s where our little helper Gollum comes in:
|
||||
|
||||
@@ -30,7 +30,7 @@ comptime {
|
||||
}
|
||||
}
|
||||
|
||||
// Elrond owns the entire Ziglings logic now!
|
||||
// Elrond the Wise owns the entire Ziglings logic now!
|
||||
// build.zig only builds it and forwards the chosen options as CLI flags.
|
||||
// Building just this one Run step keeps the build output readable and lets
|
||||
// Elrond iterate without the configure-phase cache getting in the way.
|
||||
@@ -46,6 +46,7 @@ pub fn build(b: *Build) !void {
|
||||
const rand = b.option(bool, "random", "Select random exercise");
|
||||
const start = b.option(usize, "s", "Start at exercise");
|
||||
const reset = b.option(bool, "reset", "Reset exercise progress");
|
||||
const logo = b.option(bool, "logo", "Display Ziglings logo");
|
||||
|
||||
const sep = std.fs.path.sep_str;
|
||||
const healed_path = if (override_healed_path) |path|
|
||||
@@ -57,7 +58,7 @@ pub fn build(b: *Build) !void {
|
||||
const elrond = b.addExecutable(.{
|
||||
.name = "elrond",
|
||||
.root_module = b.createModule(.{
|
||||
.root_source_file = b.path("src/elrond.zig"),
|
||||
.root_source_file = b.path("rivendell/elrond.zig"),
|
||||
.target = b.graph.host,
|
||||
}),
|
||||
});
|
||||
@@ -87,6 +88,8 @@ pub fn build(b: *Build) !void {
|
||||
run.addArg("--random");
|
||||
} else if (start) |s| {
|
||||
run.addArg(b.fmt("--start={d}", .{s}));
|
||||
} else if (logo) |_| {
|
||||
run.addArg("--logo");
|
||||
}
|
||||
|
||||
const ziglings_step = b.step("ziglings", "Run ziglings");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
--- exercises/110_files2.zig 2026-05-04 17:08:38.913033915 +0200
|
||||
+++ answers/110_files2.zig 2026-05-04 17:08:37.112995948 +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;
|
||||
// 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});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
// Elrond: Ziglings' exercise checker and guide.
|
||||
//
|
||||
// "You have a long way to go,
|
||||
// and the path is not easy."
|
||||
// Elrond, The Hobbit or The Fellowship of the Ring
|
||||
//
|
||||
//
|
||||
// In the reworked Zig build system (configurer/maker split) a build Step no
|
||||
// longer carries a `makeFn`, and the configure phase is cached and not re-run
|
||||
@@ -33,10 +37,9 @@ pub const logo =
|
||||
\\
|
||||
;
|
||||
|
||||
// How Elrond was invoked.
|
||||
// How Elrond was called.
|
||||
const Mode = enum {
|
||||
// `zig build`: iterate from after the last solved exercise to the first
|
||||
// unsolved one (or the end).
|
||||
// `zig build`: iterate from after the last solved exercise to the first unsolved one (or the end).
|
||||
normal,
|
||||
// `zig build -Dn=n`: check exactly one exercise.
|
||||
named,
|
||||
@@ -57,8 +60,7 @@ pub const Exercise = struct {
|
||||
// main_file must have the format key_name.zig.
|
||||
main_file: []const u8,
|
||||
|
||||
// Desired output. A program passes if its output, excluding trailing
|
||||
// whitespace, equals this string.
|
||||
// Desired output. A program passes if its output, excluding trailing whitespace, equals this string.
|
||||
output: []const u8,
|
||||
|
||||
// Optional hint shown if the program does not succeed.
|
||||
@@ -86,11 +88,9 @@ pub const Exercise = struct {
|
||||
return std.fs.path.stem(self.main_file);
|
||||
}
|
||||
|
||||
// Key of the main file: the string before the '_' with zero padding
|
||||
// removed. "001_hello.zig" -> "1".
|
||||
// Key of the main file: the string before the '_' with zero padding removed. "001_hello.zig" -> "1".
|
||||
pub fn key(self: Exercise) []const u8 {
|
||||
const end_index = std.mem.indexOfScalar(u8, self.main_file, '_') orelse
|
||||
unreachable;
|
||||
const end_index = std.mem.indexOfScalar(u8, self.main_file, '_') orelse unreachable;
|
||||
var start_index: usize = 0;
|
||||
while (self.main_file[start_index] == '0') start_index += 1;
|
||||
return self.main_file[start_index..end_index];
|
||||
@@ -102,11 +102,13 @@ pub const Exercise = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// Ansi colors.
|
||||
var use_color_escapes = false;
|
||||
var red_text: []const u8 = "";
|
||||
var red_bold_text: []const u8 = "";
|
||||
var red_dim_text: []const u8 = "";
|
||||
var green_text: []const u8 = "";
|
||||
var yellow_text: []const u8 = "";
|
||||
var bold_text: []const u8 = "";
|
||||
var reset_text: []const u8 = "";
|
||||
|
||||
@@ -126,6 +128,7 @@ fn setupColors(io: std.Io) void {
|
||||
red_bold_text = "\x1b[31;1m";
|
||||
red_dim_text = "\x1b[31;2m";
|
||||
green_text = "\x1b[32m";
|
||||
yellow_text = "\x1b[33m";
|
||||
bold_text = "\x1b[1m";
|
||||
reset_text = "\x1b[0m";
|
||||
}
|
||||
@@ -133,9 +136,9 @@ fn setupColors(io: std.Io) void {
|
||||
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
const io = init.io;
|
||||
const gpa = init.arena.allocator();
|
||||
const arena = init.arena.allocator();
|
||||
|
||||
const args = try init.minimal.args.toSlice(gpa);
|
||||
const args = try init.minimal.args.toSlice(arena);
|
||||
|
||||
setupColors(io);
|
||||
|
||||
@@ -150,17 +153,7 @@ pub fn main(init: std.process.Init) !void {
|
||||
for (1..args.len) |n| {
|
||||
const arg = args[n];
|
||||
if (std.mem.eql(u8, arg, "--logo")) {
|
||||
print("{s}", .{logo});
|
||||
return;
|
||||
} else if (std.mem.eql(u8, arg, "--reset")) {
|
||||
std.Io.Dir.cwd().deleteFile(io, progress_filename) catch |err| switch (err) {
|
||||
error.FileNotFound => {},
|
||||
else => {
|
||||
print("Unable to remove progress file: {}\n", .{err});
|
||||
std.process.exit(1);
|
||||
},
|
||||
};
|
||||
print("Progress reset, {s} removed.\n", .{progress_filename});
|
||||
print("{s}{s}{s}", .{ yellow_text, logo, reset_text });
|
||||
return;
|
||||
} else if (prefix(arg, "--zig=")) |v| {
|
||||
zig_exe = v;
|
||||
@@ -188,7 +181,7 @@ pub fn main(init: std.process.Init) !void {
|
||||
|
||||
print("{s}", .{logo});
|
||||
|
||||
const ctx: Context = .{ .io = io, .gpa = gpa, .zig_exe = zig_exe, .work_path = work_path };
|
||||
const ctx: Context = .{ .io = io, .arena = arena, .zig_exe = zig_exe, .work_path = work_path };
|
||||
|
||||
switch (mode) {
|
||||
.named => {
|
||||
@@ -220,7 +213,7 @@ pub fn main(init: std.process.Init) !void {
|
||||
},
|
||||
.normal => {
|
||||
// Start after the last solved exercise recorded in .progress.txt.
|
||||
const solved = readProgress(io, gpa);
|
||||
const solved = readProgress(io, arena);
|
||||
var start_index: usize = 0;
|
||||
for (exercises, 0..) |ex, idx| {
|
||||
if (solved < ex.number()) {
|
||||
@@ -245,15 +238,15 @@ fn prefix(arg: []const u8, pre: []const u8) ?[]const u8 {
|
||||
// Shared, read-only run context threaded through the helpers.
|
||||
const Context = struct {
|
||||
io: std.Io,
|
||||
gpa: std.mem.Allocator,
|
||||
arena: std.mem.Allocator,
|
||||
zig_exe: []const u8,
|
||||
work_path: []const u8,
|
||||
};
|
||||
|
||||
const Error = error{Failed};
|
||||
|
||||
// Iterates exercises from `start_index` to the end, stopping at the first
|
||||
// failure. Progress is written after each passed exercise.
|
||||
// Iterates exercises from `start_index` to the end, stopping at the first failure.
|
||||
// Progress is written after each passed exercise.
|
||||
fn iterateFrom(ctx: Context, start_index: usize) Error!void {
|
||||
for (exercises[start_index..]) |ex| {
|
||||
try runOne(ctx, ex, .normal);
|
||||
@@ -284,12 +277,12 @@ fn runOne(ctx: Context, ex: Exercise, mode: Mode) Error!void {
|
||||
},
|
||||
}
|
||||
|
||||
writeProgress(ctx.io, ctx.gpa, ex.number()) catch {};
|
||||
writeProgress(ctx.io, ctx.arena, ex.number()) catch {};
|
||||
}
|
||||
|
||||
fn hintAndHelp(ex: Exercise, mode: Mode) void {
|
||||
if (ex.hint) |hint|
|
||||
print("\n{s}Ziglings hint: {s}{s}", .{ bold_text, hint, reset_text });
|
||||
print("\n{s}{s}Ziglings hint: {s}{s}", .{ bold_text, green_text, hint, reset_text });
|
||||
help(ex, mode);
|
||||
}
|
||||
|
||||
@@ -313,24 +306,22 @@ fn printProgress(num: usize, max: usize) void {
|
||||
|
||||
fn runExe(ctx: Context, ex: Exercise) !void {
|
||||
const io = ctx.io;
|
||||
const gpa = ctx.gpa;
|
||||
const arena = ctx.arena;
|
||||
print("Compiling {s}...\n", .{ex.main_file});
|
||||
|
||||
const path = std.fs.path.join(gpa, &.{ ctx.work_path, ex.main_file }) catch
|
||||
@panic("OOM");
|
||||
const path = std.fs.path.join(arena, &.{ ctx.work_path, ex.main_file }) catch @panic("OOM");
|
||||
|
||||
var argv = std.ArrayList([]const u8).initCapacity(gpa, 8) catch @panic("OOM");
|
||||
defer argv.deinit(gpa);
|
||||
argv.append(gpa, ctx.zig_exe) catch @panic("OOM");
|
||||
argv.append(gpa, "run") catch @panic("OOM");
|
||||
var argv = std.ArrayList([]const u8).initCapacity(arena, 8) catch @panic("OOM");
|
||||
argv.append(arena, ctx.zig_exe) catch @panic("OOM");
|
||||
argv.append(arena, "run") catch @panic("OOM");
|
||||
if (ex.link_libc) {
|
||||
argv.append(gpa, "-lc") catch @panic("OOM");
|
||||
argv.append(gpa, "-fllvm") catch @panic("OOM");
|
||||
argv.append(arena, "-lc") catch @panic("OOM");
|
||||
argv.append(arena, "-fllvm") catch @panic("OOM");
|
||||
}
|
||||
argv.append(gpa, path) catch @panic("OOM");
|
||||
argv.append(arena, path) catch @panic("OOM");
|
||||
|
||||
// `zig run` compiles and runs in one step using Zig's own cache.
|
||||
const result = Process.run(gpa, io, .{
|
||||
const result = Process.run(arena, io, .{
|
||||
.argv = argv.items,
|
||||
.stdout_limit = .limited(1024 * 1024),
|
||||
.stderr_limit = .limited(1024 * 1024),
|
||||
@@ -344,28 +335,27 @@ fn runExe(ctx: Context, ex: Exercise) !void {
|
||||
resetLine();
|
||||
print("Checking {s}...\n", .{ex.main_file});
|
||||
|
||||
return checkOutput(io, gpa, ex, result);
|
||||
return checkOutput(io, arena, ex, result);
|
||||
}
|
||||
|
||||
fn runTest(ctx: Context, ex: Exercise) !void {
|
||||
const io = ctx.io;
|
||||
const gpa = ctx.gpa;
|
||||
const arena = ctx.arena;
|
||||
print("Compiling {s}...\n", .{ex.main_file});
|
||||
|
||||
const path = std.fs.path.join(gpa, &.{ ctx.work_path, ex.main_file }) catch
|
||||
const path = std.fs.path.join(arena, &.{ ctx.work_path, ex.main_file }) catch
|
||||
@panic("OOM");
|
||||
|
||||
var argv = std.ArrayList([]const u8).initCapacity(gpa, 8) catch @panic("OOM");
|
||||
defer argv.deinit(gpa);
|
||||
argv.append(gpa, ctx.zig_exe) catch @panic("OOM");
|
||||
argv.append(gpa, "test") catch @panic("OOM");
|
||||
var argv = std.ArrayList([]const u8).initCapacity(arena, 8) catch @panic("OOM");
|
||||
argv.append(arena, ctx.zig_exe) catch @panic("OOM");
|
||||
argv.append(arena, "test") catch @panic("OOM");
|
||||
if (ex.link_libc) {
|
||||
argv.append(gpa, "-lc") catch @panic("OOM");
|
||||
argv.append(gpa, "-fllvm") catch @panic("OOM");
|
||||
argv.append(arena, "-lc") catch @panic("OOM");
|
||||
argv.append(arena, "-fllvm") catch @panic("OOM");
|
||||
}
|
||||
argv.append(gpa, path) catch @panic("OOM");
|
||||
argv.append(arena, path) catch @panic("OOM");
|
||||
|
||||
const result = Process.run(gpa, io, .{
|
||||
const result = Process.run(arena, io, .{
|
||||
.argv = argv.items,
|
||||
.stdout_limit = .limited(1024 * 1024),
|
||||
.stderr_limit = .limited(1024 * 1024),
|
||||
@@ -382,7 +372,7 @@ fn runTest(ctx: Context, ex: Exercise) !void {
|
||||
return checkTest(ex, result);
|
||||
}
|
||||
|
||||
fn checkOutput(io: std.Io, gpa: std.mem.Allocator, ex: Exercise, result: Process.RunResult) !void {
|
||||
fn checkOutput(io: std.Io, arena: std.mem.Allocator, ex: Exercise, result: Process.RunResult) !void {
|
||||
switch (result.term) {
|
||||
.exited => |code| if (code != 0) {
|
||||
// `zig run` puts both compile errors and runtime panics on stderr;
|
||||
@@ -400,7 +390,7 @@ fn checkOutput(io: std.Io, gpa: std.mem.Allocator, ex: Exercise, result: Process
|
||||
}
|
||||
|
||||
const raw_output = if (ex.check_stdout) result.stdout else result.stderr;
|
||||
const output = trimLines(gpa, raw_output) catch @panic("OOM");
|
||||
const output = trimLines(arena, raw_output) catch @panic("OOM");
|
||||
|
||||
var exercise_output = ex.output;
|
||||
if (ex.timestamp) {
|
||||
@@ -477,28 +467,27 @@ fn resetLine() void {
|
||||
}
|
||||
|
||||
// Removes trailing whitespace per line and any trailing LF at the end.
|
||||
fn trimLines(gpa: std.mem.Allocator, buf: []const u8) ![]const u8 {
|
||||
var list = try std.ArrayList(u8).initCapacity(gpa, buf.len);
|
||||
errdefer list.deinit(gpa);
|
||||
fn trimLines(arena: std.mem.Allocator, buf: []const u8) ![]const u8 {
|
||||
var list = try std.ArrayList(u8).initCapacity(arena, buf.len);
|
||||
|
||||
var iter = std.mem.splitSequence(u8, buf, " \n");
|
||||
while (iter.next()) |line| {
|
||||
const data = std.mem.trimEnd(u8, line, " \r");
|
||||
try list.appendSlice(gpa, data);
|
||||
try list.append(gpa, '\n');
|
||||
try list.appendSlice(arena, data);
|
||||
try list.append(arena, '\n');
|
||||
}
|
||||
const result = try list.toOwnedSlice(gpa);
|
||||
const result = try list.toOwnedSlice(arena);
|
||||
return std.mem.trimEnd(u8, result, "\n");
|
||||
}
|
||||
|
||||
// Reads the last solved exercise number from .progress.txt; 0 if absent.
|
||||
fn readProgress(io: std.Io, gpa: std.mem.Allocator) u32 {
|
||||
fn readProgress(io: std.Io, arena: std.mem.Allocator) u32 {
|
||||
const file = std.Io.Dir.cwd().openFile(io, progress_filename, .{}) catch return 0;
|
||||
defer file.close(io);
|
||||
|
||||
const size = file.length(io) catch return 0;
|
||||
if (size == 0) return 0;
|
||||
const contents = gpa.alloc(u8, size) catch return 0;
|
||||
const contents = arena.alloc(u8, size) catch return 0;
|
||||
var file_buffer: [1024]u8 = undefined;
|
||||
var reader = file.reader(io, &file_buffer);
|
||||
const n = reader.interface.readSliceShort(contents) catch return 0;
|
||||
@@ -506,8 +495,8 @@ fn readProgress(io: std.Io, gpa: std.mem.Allocator) u32 {
|
||||
return std.fmt.parseInt(u32, trimmed, 10) catch 0;
|
||||
}
|
||||
|
||||
fn writeProgress(io: std.Io, gpa: std.mem.Allocator, number: usize) !void {
|
||||
const progress = try std.fmt.allocPrint(gpa, "{d}", .{number});
|
||||
fn writeProgress(io: std.Io, arena: std.mem.Allocator, number: usize) !void {
|
||||
const progress = try std.fmt.allocPrint(arena, "{d}", .{number});
|
||||
const file = try std.Io.Dir.cwd().createFile(
|
||||
io,
|
||||
progress_filename,
|
||||
@@ -693,7 +682,10 @@ const exercises = [_]Exercise{
|
||||
},
|
||||
.{
|
||||
.main_file = "028_defer2.zig",
|
||||
.output = "(Goat) (Cat) (Dog) (Dog) (Goat) (Unknown) done.",
|
||||
.output =
|
||||
\\(Goat) (Cat) (Dog) (Dog) (Goat) (Unknown) done.
|
||||
\\Answer to everything? 42
|
||||
, // pay attention to the comma
|
||||
},
|
||||
.{
|
||||
.main_file = "029_errdefer.zig",
|
||||
@@ -965,9 +957,6 @@ const exercises = [_]Exercise{
|
||||
\\Grasshopper hopped 32 meters.
|
||||
, // pay attention to the comma
|
||||
},
|
||||
|
||||
// Skipped because of https://github.com/ratfactor/ziglings/issues/163
|
||||
// direct link: https://github.com/ziglang/zig/issues/6025
|
||||
.{
|
||||
.main_file = "085_async.zig",
|
||||
.output = "Current time: <timestamp>s since epoch",
|
||||
Reference in New Issue
Block a user