| ... | @@ -3,20 +3,38 @@ const Allocator = std.mem.Allocator; | ... | @@ -3,20 +3,38 @@ const Allocator = std.mem.Allocator; |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | | 4 | |
| 5 | const usage = | 5 | const usage = |
| 6 | \\zig reduce [source_file] [transformation] | 6 | \\zig reduce [source_file] [interestingness] |
| 7 | \\ | 7 | \\ |
| 8 | ; | 8 | ; |
| 9 | | 9 | |
| | 10 | const Interestingness = enum { interesting, boring, unknown }; |
| | 11 | |
| 10 | // Roadmap: | 12 | // Roadmap: |
| 11 | // - add the main loop that checks for interestingness | | |
| 12 | // - add transformations | | |
| 13 | // - add thread pool | 13 | // - add thread pool |
| 14 | // - add support for `@import` detection and other files | 14 | // - add support for `@import` detection and other files |
| | 15 | // - more fancy transformations |
| 15 | // - reduce flags sent to the compiler | 16 | // - reduce flags sent to the compiler |
| | 17 | // - @import inlining |
| | 18 | // - deleting unused functions and other globals |
| | 19 | // - removing statements or blocks of code |
| | 20 | // - replacing operands of `and` and `or` with `true` and `false` |
| | 21 | // - replacing if conditions with `true` and `false` |
| | 22 | // - integrate the build system? |
| 16 | | 23 | |
| 17 | pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | 24 | pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 18 | const file_path = args[2]; | 25 | const file_path = args[2]; |
| 19 | const transformation_index = try std.fmt.parseInt(u32, args[3], 0); | 26 | const interestingness_argv_template = args[3..]; |
| | 27 | |
| | 28 | 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); |
| | 32 | |
| | 33 | var rendered = std.ArrayList(u8).init(gpa); |
| | 34 | defer rendered.deinit(); |
| | 35 | |
| | 36 | var prev_rendered = std.ArrayList(u8).init(gpa); |
| | 37 | defer prev_rendered.deinit(); |
| 20 | | 38 | |
| 21 | const source_code = try std.fs.cwd().readFileAllocOptions( | 39 | const source_code = try std.fs.cwd().readFileAllocOptions( |
| 22 | arena, | 40 | arena, |
| ... | @@ -33,19 +51,56 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | ... | @@ -33,19 +51,56 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 33 | if (tree.errors.len != 0) { | 51 | if (tree.errors.len != 0) { |
| 34 | @panic("syntax errors occurred"); | 52 | @panic("syntax errors occurred"); |
| 35 | } | 53 | } |
| 36 | var rendered = std.ArrayList(u8).init(gpa); | | |
| 37 | defer rendered.deinit(); | | |
| 38 | rendered.clearRetainingCapacity(); | | |
| 39 | | 54 | |
| 40 | var gut_functions: std.AutoHashMapUnmanaged(u32, void) = .{}; | 55 | var next_gut_fn_index: u32 = 0; |
| 41 | try gut_functions.put(arena, transformation_index, {}); | 56 | var fixups: std.zig.Ast.Fixups = .{}; |
| | 57 | |
| | 58 | while (true) { |
| | 59 | try fixups.gut_functions.put(arena, next_gut_fn_index, {}); |
| 42 | | 60 | |
| 43 | try tree.renderToArrayList(&rendered, .{ | 61 | rendered.clearRetainingCapacity(); |
| 44 | .gut_functions = gut_functions, | 62 | try tree.renderToArrayList(&rendered, fixups); |
| 45 | }); | | |
| 46 | | 63 | |
| 47 | const stdout = std.io.getStdOut(); | 64 | if (std.mem.eql(u8, rendered.items, prev_rendered.items)) { |
| 48 | try stdout.writeAll(rendered.items); | 65 | std.debug.print("no remaining transformations\n", .{}); |
| | 66 | break; |
| | 67 | } |
| | 68 | prev_rendered.clearRetainingCapacity(); |
| | 69 | try prev_rendered.appendSlice(rendered.items); |
| 49 | | 70 | |
| | 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 | }; |
| | 90 | std.debug.print("{s}\n", .{@tagName(interestingness)}); |
| | 91 | switch (interestingness) { |
| | 92 | .interesting => { |
| | 93 | next_gut_fn_index += 1; |
| | 94 | }, |
| | 95 | .unknown, .boring => { |
| | 96 | // revert the change and try the next transformation |
| | 97 | assert(fixups.gut_functions.remove(next_gut_fn_index)); |
| | 98 | next_gut_fn_index += 1; |
| | 99 | |
| | 100 | rendered.clearRetainingCapacity(); |
| | 101 | try tree.renderToArrayList(&rendered, fixups); |
| | 102 | }, |
| | 103 | } |
| | 104 | } |
| 50 | return std.process.cleanExit(); | 105 | return std.process.cleanExit(); |
| 51 | } | 106 | } |