| author | |
| committer | |
| log | 91570cc42d4fe973d15ac05cdecff42051f154ad |
| tree | 933200ee149c2557d3739ed7332e59b4e7ca341e |
| parent | 1b0b46a8a9f5ed3ebaf35e3018fd5402957552ae |
Perform these transformations in this priority order:
1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already.
2. If the `then` block is empty, replace the condition with `if (false)` if it is not already.
3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression.
4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression.3 files changed, 85 insertions(+), 13 deletions(-)
lib/std/zig/render.zig+12-5| ... | ... | @@ -25,7 +25,9 @@ pub const Fixups = struct { |
| 25 | 25 | /// These global declarations will be omitted. |
| 26 | 26 | omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{}, |
| 27 | 27 | /// These expressions will be replaced with the string value. |
| 28 | replace_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, []const u8) = .{}, | |
| 28 | replace_nodes_with_string: std.AutoHashMapUnmanaged(Ast.Node.Index, []const u8) = .{}, | |
| 29 | /// These nodes will be replaced with a different node. | |
| 30 | replace_nodes_with_node: std.AutoHashMapUnmanaged(Ast.Node.Index, Ast.Node.Index) = .{}, | |
| 29 | 31 | /// Change all identifier names matching the key to be value instead. |
| 30 | 32 | rename_identifiers: std.StringArrayHashMapUnmanaged([]const u8) = .{}, |
| 31 | 33 | |
| ... | ... | @@ -37,7 +39,8 @@ pub const Fixups = struct { |
| 37 | 39 | return f.unused_var_decls.count() + |
| 38 | 40 | f.gut_functions.count() + |
| 39 | 41 | f.omit_nodes.count() + |
| 40 | f.replace_nodes.count() + | |
| 42 | f.replace_nodes_with_string.count() + | |
| 43 | f.replace_nodes_with_node.count() + | |
| 41 | 44 | f.rename_identifiers.count() + |
| 42 | 45 | @intFromBool(f.rebase_imported_paths != null); |
| 43 | 46 | } |
| ... | ... | @@ -46,7 +49,8 @@ pub const Fixups = struct { |
| 46 | 49 | f.unused_var_decls.clearRetainingCapacity(); |
| 47 | 50 | f.gut_functions.clearRetainingCapacity(); |
| 48 | 51 | f.omit_nodes.clearRetainingCapacity(); |
| 49 | f.replace_nodes.clearRetainingCapacity(); | |
| 52 | f.replace_nodes_with_string.clearRetainingCapacity(); | |
| 53 | f.replace_nodes_with_node.clearRetainingCapacity(); | |
| 50 | 54 | f.rename_identifiers.clearRetainingCapacity(); |
| 51 | 55 | |
| 52 | 56 | f.rebase_imported_paths = null; |
| ... | ... | @@ -56,7 +60,8 @@ pub const Fixups = struct { |
| 56 | 60 | f.unused_var_decls.deinit(gpa); |
| 57 | 61 | f.gut_functions.deinit(gpa); |
| 58 | 62 | f.omit_nodes.deinit(gpa); |
| 59 | f.replace_nodes.deinit(gpa); | |
| 63 | f.replace_nodes_with_string.deinit(gpa); | |
| 64 | f.replace_nodes_with_node.deinit(gpa); | |
| 60 | 65 | f.rename_identifiers.deinit(gpa); |
| 61 | 66 | f.* = undefined; |
| 62 | 67 | } |
| ... | ... | @@ -329,10 +334,12 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void { |
| 329 | 334 | const main_tokens = tree.nodes.items(.main_token); |
| 330 | 335 | const node_tags = tree.nodes.items(.tag); |
| 331 | 336 | const datas = tree.nodes.items(.data); |
| 332 | if (r.fixups.replace_nodes.get(node)) |replacement| { | |
| 337 | if (r.fixups.replace_nodes_with_string.get(node)) |replacement| { | |
| 333 | 338 | try ais.writer().writeAll(replacement); |
| 334 | 339 | try renderOnlySpace(r, space); |
| 335 | 340 | return; |
| 341 | } else if (r.fixups.replace_nodes_with_node.get(node)) |replacement| { | |
| 342 | return renderExpression(r, replacement, space); | |
| 336 | 343 | } |
| 337 | 344 | switch (node_tags[node]) { |
| 338 | 345 | .identifier => { |
src/reduce.zig+13-4| ... | ... | @@ -226,7 +226,7 @@ pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 226 | 226 | } |
| 227 | 227 | |
| 228 | 228 | try std.fs.cwd().writeFile(root_source_file_path, rendered.items); |
| 229 | //std.debug.print("trying this code:\n{s}\n", .{rendered.items}); | |
| 229 | // std.debug.print("trying this code:\n{s}\n", .{rendered.items}); | |
| 230 | 230 | |
| 231 | 231 | const interestingness = try runCheck(arena, interestingness_argv.items); |
| 232 | 232 | std.debug.print("{d} random transformations: {s}. {d}/{d}\n", .{ |
| ... | ... | @@ -324,11 +324,20 @@ fn transformationsToFixups( |
| 324 | 324 | .delete_var_decl => |delete_var_decl| { |
| 325 | 325 | try fixups.omit_nodes.put(gpa, delete_var_decl.var_decl_node, {}); |
| 326 | 326 | for (delete_var_decl.references.items) |ident_node| { |
| 327 | try fixups.replace_nodes.put(gpa, ident_node, "undefined"); | |
| 327 | try fixups.replace_nodes_with_string.put(gpa, ident_node, "undefined"); | |
| 328 | 328 | } |
| 329 | 329 | }, |
| 330 | 330 | .replace_with_undef => |node| { |
| 331 | try fixups.replace_nodes.put(gpa, node, "undefined"); | |
| 331 | try fixups.replace_nodes_with_string.put(gpa, node, "undefined"); | |
| 332 | }, | |
| 333 | .replace_with_true => |node| { | |
| 334 | try fixups.replace_nodes_with_string.put(gpa, node, "true"); | |
| 335 | }, | |
| 336 | .replace_with_false => |node| { | |
| 337 | try fixups.replace_nodes_with_string.put(gpa, node, "false"); | |
| 338 | }, | |
| 339 | .replace_node => |r| { | |
| 340 | try fixups.replace_nodes_with_node.put(gpa, r.to_replace, r.replacement); | |
| 332 | 341 | }, |
| 333 | 342 | .inline_imported_file => |inline_imported_file| { |
| 334 | 343 | const full_imported_path = try std.fs.path.join(gpa, &.{ |
| ... | ... | @@ -371,7 +380,7 @@ fn transformationsToFixups( |
| 371 | 380 | try other_file_ast.renderToArrayList(&other_source, inlined_fixups); |
| 372 | 381 | try other_source.appendSlice("}"); |
| 373 | 382 | |
| 374 | try fixups.replace_nodes.put( | |
| 383 | try fixups.replace_nodes_with_string.put( | |
| 375 | 384 | gpa, |
| 376 | 385 | inline_imported_file.builtin_call_node, |
| 377 | 386 | try arena.dupe(u8, other_source.items), |
src/reduce/Walk.zig+60-4| ... | ... | @@ -27,6 +27,15 @@ pub const Transformation = union(enum) { |
| 27 | 27 | }, |
| 28 | 28 | /// Replace an expression with `undefined`. |
| 29 | 29 | replace_with_undef: Ast.Node.Index, |
| 30 | /// Replace an expression with `true`. | |
| 31 | replace_with_true: Ast.Node.Index, | |
| 32 | /// Replace an expression with `false`. | |
| 33 | replace_with_false: Ast.Node.Index, | |
| 34 | /// Replace a node with another node. | |
| 35 | replace_node: struct { | |
| 36 | to_replace: Ast.Node.Index, | |
| 37 | replacement: Ast.Node.Index, | |
| 38 | }, | |
| 30 | 39 | /// Replace an `@import` with the imported file contents wrapped in a struct. |
| 31 | 40 | inline_imported_file: InlineImportedFile, |
| 32 | 41 | |
| ... | ... | @@ -558,7 +567,7 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 558 | 567 | |
| 559 | 568 | .if_simple, |
| 560 | 569 | .@"if", |
| 561 | => return walkIf(w, ast.fullIf(node).?), | |
| 570 | => return walkIf(w, node, ast.fullIf(node).?), | |
| 562 | 571 | |
| 563 | 572 | .asm_simple, |
| 564 | 573 | .@"asm", |
| ... | ... | @@ -861,8 +870,6 @@ fn walkWhile(w: *Walk, while_node: Ast.full.While) Error!void { |
| 861 | 870 | try walkExpression(w, while_node.ast.cont_expr); |
| 862 | 871 | } |
| 863 | 872 | |
| 864 | try walkExpression(w, while_node.ast.cond_expr); // condition | |
| 865 | ||
| 866 | 873 | if (while_node.ast.then_expr != 0) { |
| 867 | 874 | try walkExpression(w, while_node.ast.then_expr); |
| 868 | 875 | } |
| ... | ... | @@ -881,7 +888,37 @@ fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void { |
| 881 | 888 | } |
| 882 | 889 | } |
| 883 | 890 | |
| 884 | fn walkIf(w: *Walk, if_node: Ast.full.If) Error!void { | |
| 891 | fn walkIf(w: *Walk, node_index: Ast.Node.Index, if_node: Ast.full.If) Error!void { | |
| 892 | assert(if_node.ast.cond_expr != 0); | |
| 893 | assert(if_node.ast.then_expr != 0); | |
| 894 | ||
| 895 | // Perform these transformations in this priority order: | |
| 896 | // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already. | |
| 897 | // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already. | |
| 898 | // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression. | |
| 899 | // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression. | |
| 900 | if (!isTrueIdent(w.ast, if_node.ast.cond_expr) and | |
| 901 | (if_node.ast.else_expr == 0 or isEmptyBlock(w.ast, if_node.ast.else_expr))) | |
| 902 | { | |
| 903 | try w.transformations.ensureUnusedCapacity(1); | |
| 904 | w.transformations.appendAssumeCapacity(.{ .replace_with_true = if_node.ast.cond_expr }); | |
| 905 | } else if (!isFalseIdent(w.ast, if_node.ast.cond_expr) and isEmptyBlock(w.ast, if_node.ast.then_expr)) { | |
| 906 | try w.transformations.ensureUnusedCapacity(1); | |
| 907 | w.transformations.appendAssumeCapacity(.{ .replace_with_false = if_node.ast.cond_expr }); | |
| 908 | } else if (isTrueIdent(w.ast, if_node.ast.cond_expr)) { | |
| 909 | try w.transformations.ensureUnusedCapacity(1); | |
| 910 | w.transformations.appendAssumeCapacity(.{ .replace_node = .{ | |
| 911 | .to_replace = node_index, | |
| 912 | .replacement = if_node.ast.then_expr, | |
| 913 | } }); | |
| 914 | } else if (isFalseIdent(w.ast, if_node.ast.cond_expr)) { | |
| 915 | try w.transformations.ensureUnusedCapacity(1); | |
| 916 | w.transformations.appendAssumeCapacity(.{ .replace_node = .{ | |
| 917 | .to_replace = node_index, | |
| 918 | .replacement = if_node.ast.else_expr, | |
| 919 | } }); | |
| 920 | } | |
| 921 | ||
| 885 | 922 | try walkExpression(w, if_node.ast.cond_expr); // condition |
| 886 | 923 | |
| 887 | 924 | if (if_node.ast.then_expr != 0) { |
| ... | ... | @@ -1002,6 +1039,14 @@ fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| 1002 | 1039 | return isMatchingIdent(ast, node, "undefined"); |
| 1003 | 1040 | } |
| 1004 | 1041 | |
| 1042 | fn isTrueIdent(ast: *const Ast, node: Ast.Node.Index) bool { | |
| 1043 | return isMatchingIdent(ast, node, "true"); | |
| 1044 | } | |
| 1045 | ||
| 1046 | fn isFalseIdent(ast: *const Ast, node: Ast.Node.Index) bool { | |
| 1047 | return isMatchingIdent(ast, node, "false"); | |
| 1048 | } | |
| 1049 | ||
| 1005 | 1050 | fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool { |
| 1006 | 1051 | const node_tags = ast.nodes.items(.tag); |
| 1007 | 1052 | const main_tokens = ast.nodes.items(.main_token); |
| ... | ... | @@ -1014,3 +1059,14 @@ fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bo |
| 1014 | 1059 | else => return false, |
| 1015 | 1060 | } |
| 1016 | 1061 | } |
| 1062 | ||
| 1063 | fn isEmptyBlock(ast: *const Ast, node: Ast.Node.Index) bool { | |
| 1064 | const node_tags = ast.nodes.items(.tag); | |
| 1065 | const node_data = ast.nodes.items(.data); | |
| 1066 | switch (node_tags[node]) { | |
| 1067 | .block_two => { | |
| 1068 | return node_data[node].lhs == 0 and node_data[node].rhs == 0; | |
| 1069 | }, | |
| 1070 | else => return false, | |
| 1071 | } | |
| 1072 | } |