authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-03 15:17:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-03 20:05:32-07:00
log815b85d8a265fb8ffb5425046d638441a5a82ab1
treebd78a0cbc2f040c46519466f949f690b8101c6b0
parentb1aaf421344c13aee87c628fe329d0bd1858988e

zig reduce: check once upfront to verify interestingness


1 files changed, 100 insertions(+), 28 deletions(-)

src/reduce.zig+100-28
......@@ -1,13 +1,34 @@
11const std = @import("std");
2const mem = std.mem;
23const Allocator = std.mem.Allocator;
34const assert = std.debug.assert;
5const fatal = @import("./main.zig").fatal;
46
57const usage =
6 \\zig reduce [source_file] [interestingness]
8 \\zig reduce [options] ./checker root_source_file.zig [-- [argv]]
9 \\
10 \\root_source_file.zig is relative to --main-mod-path.
11 \\
12 \\checker:
13 \\ An executable that communicates interestingness by returning these exit codes:
14 \\ exit(0): interesting
15 \\ exit(1): unknown (infinite loop or other mishap)
16 \\ exit(other): not interesting
17 \\
18 \\options:
19 \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
20 \\ deps: [dep],[dep],...
21 \\ dep: [[import=]name]
22 \\ --deps [dep],[dep],... Set dependency names for the root package
23 \\ dep: [[import=]name]
24 \\ --main-mod-path Set the directory of the root module
25 \\
26 \\argv:
27 \\ Forwarded directly to the interestingness script.
728 \\
829;
930
10const Interestingness = enum { interesting, boring, unknown };
31const Interestingness = enum { interesting, unknown, boring };
1132
1233// Roadmap:
1334// - add thread pool
......@@ -22,13 +43,44 @@ const Interestingness = enum { interesting, boring, unknown };
2243// - integrate the build system?
2344
2445pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
25 const file_path = args[2];
26 const interestingness_argv_template = args[3..];
46 var opt_checker_path: ?[]const u8 = null;
47 var opt_root_source_file_path: ?[]const u8 = null;
48 var argv: []const []const u8 = &.{};
49
50 {
51 var i: usize = 2; // skip over "zig" and "reduce"
52 while (i < args.len) : (i += 1) {
53 const arg = args[i];
54 if (mem.startsWith(u8, arg, "-")) {
55 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
56 const stdout = std.io.getStdOut().writer();
57 try stdout.writeAll(usage);
58 return std.process.cleanExit();
59 } else if (mem.eql(u8, arg, "--")) {
60 argv = args[i + 1 ..];
61 break;
62 } else {
63 fatal("unrecognized parameter: '{s}'", .{arg});
64 }
65 } else if (opt_checker_path == null) {
66 opt_checker_path = arg;
67 } else if (opt_root_source_file_path == null) {
68 opt_root_source_file_path = arg;
69 } else {
70 fatal("unexpected extra parameter: '{s}'", .{arg});
71 }
72 }
73 }
74
75 const checker_path = opt_checker_path orelse
76 fatal("missing interestingness checker argument; see -h for usage", .{});
77 const root_source_file_path = opt_root_source_file_path orelse
78 fatal("missing root source file path argument; see -h for usage", .{});
2779
2880 var interestingness_argv: std.ArrayListUnmanaged([]const u8) = .{};
29 try interestingness_argv.ensureUnusedCapacity(arena, interestingness_argv_template.len + 1);
30 interestingness_argv.appendSliceAssumeCapacity(interestingness_argv_template);
31 interestingness_argv.appendAssumeCapacity(file_path);
81 try interestingness_argv.ensureUnusedCapacity(arena, argv.len + 1);
82 interestingness_argv.appendAssumeCapacity(checker_path);
83 interestingness_argv.appendSliceAssumeCapacity(argv);
3284
3385 var rendered = std.ArrayList(u8).init(gpa);
3486 defer rendered.deinit();
......@@ -38,7 +90,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
3890
3991 const source_code = try std.fs.cwd().readFileAllocOptions(
4092 arena,
41 file_path,
93 root_source_file_path,
4294 std.math.maxInt(u32),
4395 null,
4496 1,
......@@ -55,38 +107,34 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
55107 var next_gut_fn_index: u32 = 0;
56108 var fixups: std.zig.Ast.Fixups = .{};
57109
110 {
111 // smoke test the interestingness check
112 switch (try runCheck(arena, interestingness_argv.items)) {
113 .interesting => {},
114 .boring, .unknown => |t| {
115 fatal("interestingness check returned {s} for unmodified input\n", .{
116 @tagName(t),
117 });
118 },
119 }
120 }
121
58122 while (true) {
59123 try fixups.gut_functions.put(arena, next_gut_fn_index, {});
60124
61125 rendered.clearRetainingCapacity();
62126 try tree.renderToArrayList(&rendered, fixups);
63127
64 if (std.mem.eql(u8, rendered.items, prev_rendered.items)) {
128 if (mem.eql(u8, rendered.items, prev_rendered.items)) {
65129 std.debug.print("no remaining transformations\n", .{});
66130 break;
67131 }
68132 prev_rendered.clearRetainingCapacity();
69133 try prev_rendered.appendSlice(rendered.items);
70134
71 try std.fs.cwd().writeFile(file_path, rendered.items);
72
73 const result = try std.process.Child.run(.{
74 .allocator = arena,
75 .argv = interestingness_argv.items,
76 });
77 if (result.stderr.len != 0)
78 std.debug.print("{s}", .{result.stderr});
79 const interestingness: Interestingness = switch (result.term) {
80 .Exited => |code| switch (code) {
81 0 => .interesting,
82 1 => .unknown,
83 else => .boring,
84 },
85 else => b: {
86 std.debug.print("interestingness check aborted unexpectedly\n", .{});
87 break :b .boring;
88 },
89 };
135 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
136
137 const interestingness = try runCheck(arena, interestingness_argv.items);
90138 std.debug.print("{s}\n", .{@tagName(interestingness)});
91139 switch (interestingness) {
92140 .interesting => {
......@@ -104,3 +152,27 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
104152 }
105153 return std.process.cleanExit();
106154}
155
156fn termToInteresting(term: std.process.Child.Term) Interestingness {
157 return switch (term) {
158 .Exited => |code| switch (code) {
159 0 => .interesting,
160 1 => .unknown,
161 else => .boring,
162 },
163 else => b: {
164 std.debug.print("interestingness check aborted unexpectedly\n", .{});
165 break :b .boring;
166 },
167 };
168}
169
170fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness {
171 const result = try std.process.Child.run(.{
172 .allocator = arena,
173 .argv = argv,
174 });
175 if (result.stderr.len != 0)
176 std.debug.print("{s}", .{result.stderr});
177 return termToInteresting(result.term);
178}