Commit 815b85d8a2
Changed files (1)
src
src/reduce.zig
@@ -1,13 +1,34 @@
const std = @import("std");
+const mem = std.mem;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
+const fatal = @import("./main.zig").fatal;
const usage =
- \\zig reduce [source_file] [interestingness]
+ \\zig reduce [options] ./checker root_source_file.zig [-- [argv]]
+ \\
+ \\root_source_file.zig is relative to --main-mod-path.
+ \\
+ \\checker:
+ \\ An executable that communicates interestingness by returning these exit codes:
+ \\ exit(0): interesting
+ \\ exit(1): unknown (infinite loop or other mishap)
+ \\ exit(other): not interesting
+ \\
+ \\options:
+ \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
+ \\ deps: [dep],[dep],...
+ \\ dep: [[import=]name]
+ \\ --deps [dep],[dep],... Set dependency names for the root package
+ \\ dep: [[import=]name]
+ \\ --main-mod-path Set the directory of the root module
+ \\
+ \\argv:
+ \\ Forwarded directly to the interestingness script.
\\
;
-const Interestingness = enum { interesting, boring, unknown };
+const Interestingness = enum { interesting, unknown, boring };
// Roadmap:
// - add thread pool
@@ -22,13 +43,44 @@ const Interestingness = enum { interesting, boring, unknown };
// - integrate the build system?
pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
- const file_path = args[2];
- const interestingness_argv_template = args[3..];
+ var opt_checker_path: ?[]const u8 = null;
+ var opt_root_source_file_path: ?[]const u8 = null;
+ var argv: []const []const u8 = &.{};
+
+ {
+ var i: usize = 2; // skip over "zig" and "reduce"
+ while (i < args.len) : (i += 1) {
+ const arg = args[i];
+ if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
+ const stdout = std.io.getStdOut().writer();
+ try stdout.writeAll(usage);
+ return std.process.cleanExit();
+ } else if (mem.eql(u8, arg, "--")) {
+ argv = args[i + 1 ..];
+ break;
+ } else {
+ fatal("unrecognized parameter: '{s}'", .{arg});
+ }
+ } else if (opt_checker_path == null) {
+ opt_checker_path = arg;
+ } else if (opt_root_source_file_path == null) {
+ opt_root_source_file_path = arg;
+ } else {
+ fatal("unexpected extra parameter: '{s}'", .{arg});
+ }
+ }
+ }
+
+ const checker_path = opt_checker_path orelse
+ fatal("missing interestingness checker argument; see -h for usage", .{});
+ const root_source_file_path = opt_root_source_file_path orelse
+ fatal("missing root source file path argument; see -h for usage", .{});
var interestingness_argv: std.ArrayListUnmanaged([]const u8) = .{};
- try interestingness_argv.ensureUnusedCapacity(arena, interestingness_argv_template.len + 1);
- interestingness_argv.appendSliceAssumeCapacity(interestingness_argv_template);
- interestingness_argv.appendAssumeCapacity(file_path);
+ try interestingness_argv.ensureUnusedCapacity(arena, argv.len + 1);
+ interestingness_argv.appendAssumeCapacity(checker_path);
+ interestingness_argv.appendSliceAssumeCapacity(argv);
var rendered = std.ArrayList(u8).init(gpa);
defer rendered.deinit();
@@ -38,7 +90,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
const source_code = try std.fs.cwd().readFileAllocOptions(
arena,
- file_path,
+ root_source_file_path,
std.math.maxInt(u32),
null,
1,
@@ -55,38 +107,34 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
var next_gut_fn_index: u32 = 0;
var fixups: std.zig.Ast.Fixups = .{};
+ {
+ // smoke test the interestingness check
+ switch (try runCheck(arena, interestingness_argv.items)) {
+ .interesting => {},
+ .boring, .unknown => |t| {
+ fatal("interestingness check returned {s} for unmodified input\n", .{
+ @tagName(t),
+ });
+ },
+ }
+ }
+
while (true) {
try fixups.gut_functions.put(arena, next_gut_fn_index, {});
rendered.clearRetainingCapacity();
try tree.renderToArrayList(&rendered, fixups);
- if (std.mem.eql(u8, rendered.items, prev_rendered.items)) {
+ if (mem.eql(u8, rendered.items, prev_rendered.items)) {
std.debug.print("no remaining transformations\n", .{});
break;
}
prev_rendered.clearRetainingCapacity();
try prev_rendered.appendSlice(rendered.items);
- try std.fs.cwd().writeFile(file_path, rendered.items);
-
- const result = try std.process.Child.run(.{
- .allocator = arena,
- .argv = interestingness_argv.items,
- });
- if (result.stderr.len != 0)
- std.debug.print("{s}", .{result.stderr});
- const interestingness: Interestingness = switch (result.term) {
- .Exited => |code| switch (code) {
- 0 => .interesting,
- 1 => .unknown,
- else => .boring,
- },
- else => b: {
- std.debug.print("interestingness check aborted unexpectedly\n", .{});
- break :b .boring;
- },
- };
+ try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
+
+ const interestingness = try runCheck(arena, interestingness_argv.items);
std.debug.print("{s}\n", .{@tagName(interestingness)});
switch (interestingness) {
.interesting => {
@@ -104,3 +152,27 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
}
return std.process.cleanExit();
}
+
+fn termToInteresting(term: std.process.Child.Term) Interestingness {
+ return switch (term) {
+ .Exited => |code| switch (code) {
+ 0 => .interesting,
+ 1 => .unknown,
+ else => .boring,
+ },
+ else => b: {
+ std.debug.print("interestingness check aborted unexpectedly\n", .{});
+ break :b .boring;
+ },
+ };
+}
+
+fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness {
+ const result = try std.process.Child.run(.{
+ .allocator = arena,
+ .argv = argv,
+ });
+ if (result.stderr.len != 0)
+ std.debug.print("{s}", .{result.stderr});
+ return termToInteresting(result.term);
+}