| ... | ... | @@ -1,13 +1,34 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; |
| 2 | 3 | const Allocator = std.mem.Allocator; |
| 3 | 4 | const assert = std.debug.assert; |
| 5 | const fatal = @import("./main.zig").fatal; |
| 4 | 6 | |
| 5 | 7 | const 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. |
| 7 | 28 | \\ |
| 8 | 29 | ; |
| 9 | 30 | |
| 10 | | const Interestingness = enum { interesting, boring, unknown }; |
| 31 | const Interestingness = enum { interesting, unknown, boring }; |
| 11 | 32 | |
| 12 | 33 | // Roadmap: |
| 13 | 34 | // - add thread pool |
| ... | ... | @@ -22,13 +43,44 @@ const Interestingness = enum { interesting, boring, unknown }; |
| 22 | 43 | // - integrate the build system? |
| 23 | 44 | |
| 24 | 45 | pub 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", .{}); |
| 27 | 79 | |
| 28 | 80 | 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); |
| 32 | 84 | |
| 33 | 85 | var rendered = std.ArrayList(u8).init(gpa); |
| 34 | 86 | defer rendered.deinit(); |
| ... | ... | @@ -38,7 +90,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 38 | 90 | |
| 39 | 91 | const source_code = try std.fs.cwd().readFileAllocOptions( |
| 40 | 92 | arena, |
| 41 | | file_path, |
| 93 | root_source_file_path, |
| 42 | 94 | std.math.maxInt(u32), |
| 43 | 95 | null, |
| 44 | 96 | 1, |
| ... | ... | @@ -55,38 +107,34 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 55 | 107 | var next_gut_fn_index: u32 = 0; |
| 56 | 108 | var fixups: std.zig.Ast.Fixups = .{}; |
| 57 | 109 | |
| 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 | |
| 58 | 122 | while (true) { |
| 59 | 123 | try fixups.gut_functions.put(arena, next_gut_fn_index, {}); |
| 60 | 124 | |
| 61 | 125 | rendered.clearRetainingCapacity(); |
| 62 | 126 | try tree.renderToArrayList(&rendered, fixups); |
| 63 | 127 | |
| 64 | | if (std.mem.eql(u8, rendered.items, prev_rendered.items)) { |
| 128 | if (mem.eql(u8, rendered.items, prev_rendered.items)) { |
| 65 | 129 | std.debug.print("no remaining transformations\n", .{}); |
| 66 | 130 | break; |
| 67 | 131 | } |
| 68 | 132 | prev_rendered.clearRetainingCapacity(); |
| 69 | 133 | try prev_rendered.appendSlice(rendered.items); |
| 70 | 134 | |
| 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); |
| 90 | 138 | std.debug.print("{s}\n", .{@tagName(interestingness)}); |
| 91 | 139 | switch (interestingness) { |
| 92 | 140 | .interesting => { |
| ... | ... | @@ -104,3 +152,27 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 104 | 152 | } |
| 105 | 153 | return std.process.cleanExit(); |
| 106 | 154 | } |
| 155 | |
| 156 | fn 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 | |
| 170 | fn 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 | } |