Ensure the exercises use the canonical format

Add the check-exercises.py tool in the new tools directory.  It is used
to check that the exercises are correctly formatted, printing on stderr
the invalid ones and the diff in the unified format.

Update the exercises that don't use the canonical zig fmt format.

Update some patches that cause the generated zig file to be incorrectly
formatted.
This commit is contained in:
Manlio Perillo 2023-04-16 12:23:10 +02:00
parent b59bef29b9
commit 6b17a18893
10 changed files with 127 additions and 30 deletions

View file

@ -107,7 +107,7 @@ const Path = struct {
const a_paths = [_]Path{
Path{
.from = &a, // from: Archer's Point
.to = &b, // to: Bridge
.to = &b, // to: Bridge
.dist = 2,
},
};
@ -115,12 +115,12 @@ const a_paths = [_]Path{
const b_paths = [_]Path{
Path{
.from = &b, // from: Bridge
.to = &a, // to: Archer's Point
.to = &a, // to: Archer's Point
.dist = 2,
},
Path{
.from = &b, // from: Bridge
.to = &d, // to: Dogwood Grove
.to = &d, // to: Dogwood Grove
.dist = 1,
},
};
@ -128,12 +128,12 @@ const b_paths = [_]Path{
const c_paths = [_]Path{
Path{
.from = &c, // from: Cottage
.to = &d, // to: Dogwood Grove
.to = &d, // to: Dogwood Grove
.dist = 3,
},
Path{
.from = &c, // from: Cottage
.to = &e, // to: East Pond
.to = &e, // to: East Pond
.dist = 2,
},
};
@ -141,17 +141,17 @@ const c_paths = [_]Path{
const d_paths = [_]Path{
Path{
.from = &d, // from: Dogwood Grove
.to = &b, // to: Bridge
.to = &b, // to: Bridge
.dist = 1,
},
Path{
.from = &d, // from: Dogwood Grove
.to = &c, // to: Cottage
.to = &c, // to: Cottage
.dist = 3,
},
Path{
.from = &d, // from: Dogwood Grove
.to = &f, // to: Fox Pond
.to = &f, // to: Fox Pond
.dist = 7,
},
};
@ -159,20 +159,20 @@ const d_paths = [_]Path{
const e_paths = [_]Path{
Path{
.from = &e, // from: East Pond
.to = &c, // to: Cottage
.to = &c, // to: Cottage
.dist = 2,
},
Path{
.from = &e, // from: East Pond
.to = &f, // to: Fox Pond
.dist = 1, // (one-way down a short waterfall!)
.to = &f, // to: Fox Pond
.dist = 1, // (one-way down a short waterfall!)
},
};
const f_paths = [_]Path{
Path{
.from = &f, // from: Fox Pond
.to = &d, // to: Dogwood Grove
.to = &d, // to: Dogwood Grove
.dist = 7,
},
};
@ -355,8 +355,8 @@ pub fn main() void {
// Here's where the hermit decides where he would like to go. Once
// you get the program working, try some different Places on the
// map!
const start = &a; // Archer's Point
const destination = &f; // Fox Pond
const start = &a; // Archer's Point
const destination = &f; // Fox Pond
// Store each Path array as a slice in each Place. As mentioned
// above, we needed to delay making these references to avoid

View file

@ -18,10 +18,10 @@
const print = @import("std").debug.print;
pub fn main() void {
var zig = [_]u8 {
0o131, // octal
var zig = [_]u8{
0o131, // octal
0b1101000, // binary
0x66, // hex
0x66, // hex
};
print("{s} is cool.\n", .{zig});

View file

@ -16,7 +16,7 @@ const print = @import("std").debug.print;
pub fn main() void {
// Here we declare arrays of three different types and sizes
// at compile time from a function call. Neat!
const s1 = makeSequence(u8, 3); // creates a [3]u8
const s1 = makeSequence(u8, 3); // creates a [3]u8
const s2 = makeSequence(u32, 5); // creates a [5]u32
const s3 = makeSequence(i64, 7); // creates a [7]i64

View file

@ -151,8 +151,8 @@ const HermitsNotebook = struct {
};
pub fn main() void {
const start = &a; // Archer's Point
const destination = &f; // Fox Pond
const start = &a; // Archer's Point
const destination = &f; // Fox Pond
// We could either have this:
//

View file

@ -18,8 +18,8 @@ pub fn main() void {
//
// Don't change this part:
//
// = .{'h', 'e', 'l', 'l', 'o'};
// = .{ 'h', 'e', 'l', 'l', 'o' };
//
const hello = .{'h', 'e', 'l', 'l', 'o'};
const hello = .{ 'h', 'e', 'l', 'l', 'o' };
print("I say {s}!\n", .{hello});
}

View file

@ -99,7 +99,7 @@ pub fn main() !void {
var my_insects = [_]Insect{
Insect{ .ant = Ant{ .still_alive = true } },
Insect{ .bee = Bee{ .flowers_visited = 17 } },
Insect{ .grasshopper = Grasshopper{ .distance_hopped = 32 }, },
Insect{ .grasshopper = Grasshopper{ .distance_hopped = 32 } },
};
std.debug.print("Daily Insect Report:\n", .{});

View file

@ -5,6 +5,6 @@
> if (err == MyNumberError.TooSmall) {
> return 10;
> }
>
>
> return err;
> };

View file

@ -1,8 +1,8 @@
22,24c22,24
< 0o131, // octal
< 0o131, // octal
< 0b1101000, // binary
< 0x66, // hex
< 0x66, // hex
---
> 0o132, // octal
> 0o132, // octal
> 0b1101001, // binary
> 0x67, // hex
> 0x67, // hex

View file

@ -1,4 +1,4 @@
23c23
< const hello = .{'h', 'e', 'l', 'l', 'o'};
< const hello = .{ 'h', 'e', 'l', 'l', 'o' };
---
> const hello: [5]u8 = .{'h', 'e', 'l', 'l', 'o'};
> const hello: [5]u8 = .{ 'h', 'e', 'l', 'l', 'o' };

97
tools/check-exercises.py Executable file
View file

@ -0,0 +1,97 @@
#!/usr/bin/env python
import difflib
import io
import os
import os.path
import subprocess
import sys
IGNORE = subprocess.DEVNULL
PIPE = subprocess.PIPE
EXERCISES_PATH = "exercises"
HEALED_PATH = "patches/healed"
PATCHES_PATH = "patches/patches"
# Heals all the exercises.
def heal():
maketree(HEALED_PATH)
with os.scandir(EXERCISES_PATH) as it:
for entry in it:
name = entry.name
original_path = entry.path
patch_path = os.path.join(PATCHES_PATH, patch_name(name))
output_path = os.path.join(HEALED_PATH, name)
patch(original_path, patch_path, output_path)
# Yields all the healed exercises that are not correctly formatted.
def check_healed():
term = subprocess.run(
["zig", "fmt", "--check", HEALED_PATH], stdout=PIPE, text=True
)
if term.stdout == "" and term.returncode != 0:
term.check_returncode()
stream = io.StringIO(term.stdout)
for line in stream:
yield line.strip()
def main():
heal()
# Show the unified diff between the original example and the correctly
# formatted one.
for i, original in enumerate(check_healed()):
if i > 0:
print()
name = os.path.basename(original)
print(f"checking exercise {name}...\n")
from_file = open(original)
to_file = zig_fmt_file(original)
diff = difflib.unified_diff(
from_file.readlines(), to_file.readlines(), name, name + "-fmt"
)
sys.stderr.writelines(diff)
def maketree(path):
return os.makedirs(path, exist_ok=True)
# Returns path with the patch extension.
def patch_name(path):
name, _ = os.path.splitext(path)
return name + ".patch"
# Applies patch to original, and write the file to output.
def patch(original, patch, output):
subprocess.run(
["patch", "-i", patch, "-o", output, original], stdout=IGNORE, check=True
)
# Formats the Zig file at path, and returns the possibly reformatted file as a
# file object.
def zig_fmt_file(path):
with open(path) as stdin:
term = subprocess.run(
["zig", "fmt", "--stdin"], stdin=stdin, stdout=PIPE, check=True, text=True
)
return io.StringIO(term.stdout)
main()