| ... | @@ -27,6 +27,15 @@ pub const Transformation = union(enum) { | ... | @@ -27,6 +27,15 @@ pub const Transformation = union(enum) { |
| 27 | }, | 27 | }, |
| 28 | /// Replace an expression with `undefined`. | 28 | /// Replace an expression with `undefined`. |
| 29 | replace_with_undef: Ast.Node.Index, | 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 | /// Replace an `@import` with the imported file contents wrapped in a struct. | 39 | /// Replace an `@import` with the imported file contents wrapped in a struct. |
| 31 | inline_imported_file: InlineImportedFile, | 40 | inline_imported_file: InlineImportedFile, |
| 32 | | 41 | |
| ... | @@ -550,7 +559,7 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { | ... | @@ -550,7 +559,7 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 550 | .while_simple, | 559 | .while_simple, |
| 551 | .while_cont, | 560 | .while_cont, |
| 552 | .@"while", | 561 | .@"while", |
| 553 | => return walkWhile(w, ast.fullWhile(node).?), | 562 | => return walkWhile(w, node, ast.fullWhile(node).?), |
| 554 | | 563 | |
| 555 | .for_simple, | 564 | .for_simple, |
| 556 | .@"for", | 565 | .@"for", |
| ... | @@ -558,7 +567,7 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { | ... | @@ -558,7 +567,7 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void { |
| 558 | | 567 | |
| 559 | .if_simple, | 568 | .if_simple, |
| 560 | .@"if", | 569 | .@"if", |
| 561 | => return walkIf(w, ast.fullIf(node).?), | 570 | => return walkIf(w, node, ast.fullIf(node).?), |
| 562 | | 571 | |
| 563 | .asm_simple, | 572 | .asm_simple, |
| 564 | .@"asm", | 573 | .@"asm", |
| ... | @@ -854,15 +863,43 @@ fn walkSwitchCase(w: *Walk, switch_case: Ast.full.SwitchCase) Error!void { | ... | @@ -854,15 +863,43 @@ fn walkSwitchCase(w: *Walk, switch_case: Ast.full.SwitchCase) Error!void { |
| 854 | try walkExpression(w, switch_case.ast.target_expr); | 863 | try walkExpression(w, switch_case.ast.target_expr); |
| 855 | } | 864 | } |
| 856 | | 865 | |
| 857 | fn walkWhile(w: *Walk, while_node: Ast.full.While) Error!void { | 866 | fn walkWhile(w: *Walk, node_index: Ast.Node.Index, while_node: Ast.full.While) Error!void { |
| | 867 | assert(while_node.ast.cond_expr != 0); |
| | 868 | assert(while_node.ast.then_expr != 0); |
| | 869 | |
| | 870 | // Perform these transformations in this priority order: |
| | 871 | // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already. |
| | 872 | // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already. |
| | 873 | // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression. |
| | 874 | // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression. |
| | 875 | if (!isTrueIdent(w.ast, while_node.ast.cond_expr) and |
| | 876 | (while_node.ast.else_expr == 0 or isEmptyBlock(w.ast, while_node.ast.else_expr))) |
| | 877 | { |
| | 878 | try w.transformations.ensureUnusedCapacity(1); |
| | 879 | w.transformations.appendAssumeCapacity(.{ .replace_with_true = while_node.ast.cond_expr }); |
| | 880 | } else if (!isFalseIdent(w.ast, while_node.ast.cond_expr) and isEmptyBlock(w.ast, while_node.ast.then_expr)) { |
| | 881 | try w.transformations.ensureUnusedCapacity(1); |
| | 882 | w.transformations.appendAssumeCapacity(.{ .replace_with_false = while_node.ast.cond_expr }); |
| | 883 | } else if (isTrueIdent(w.ast, while_node.ast.cond_expr)) { |
| | 884 | try w.transformations.ensureUnusedCapacity(1); |
| | 885 | w.transformations.appendAssumeCapacity(.{ .replace_node = .{ |
| | 886 | .to_replace = node_index, |
| | 887 | .replacement = while_node.ast.then_expr, |
| | 888 | } }); |
| | 889 | } else if (isFalseIdent(w.ast, while_node.ast.cond_expr)) { |
| | 890 | try w.transformations.ensureUnusedCapacity(1); |
| | 891 | w.transformations.appendAssumeCapacity(.{ .replace_node = .{ |
| | 892 | .to_replace = node_index, |
| | 893 | .replacement = while_node.ast.else_expr, |
| | 894 | } }); |
| | 895 | } |
| | 896 | |
| 858 | try walkExpression(w, while_node.ast.cond_expr); // condition | 897 | try walkExpression(w, while_node.ast.cond_expr); // condition |
| 859 | | 898 | |
| 860 | if (while_node.ast.cont_expr != 0) { | 899 | if (while_node.ast.cont_expr != 0) { |
| 861 | try walkExpression(w, while_node.ast.cont_expr); | 900 | try walkExpression(w, while_node.ast.cont_expr); |
| 862 | } | 901 | } |
| 863 | | 902 | |
| 864 | try walkExpression(w, while_node.ast.cond_expr); // condition | | |
| 865 | | | |
| 866 | if (while_node.ast.then_expr != 0) { | 903 | if (while_node.ast.then_expr != 0) { |
| 867 | try walkExpression(w, while_node.ast.then_expr); | 904 | try walkExpression(w, while_node.ast.then_expr); |
| 868 | } | 905 | } |
| ... | @@ -881,7 +918,37 @@ fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void { | ... | @@ -881,7 +918,37 @@ fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void { |
| 881 | } | 918 | } |
| 882 | } | 919 | } |
| 883 | | 920 | |
| 884 | fn walkIf(w: *Walk, if_node: Ast.full.If) Error!void { | 921 | fn walkIf(w: *Walk, node_index: Ast.Node.Index, if_node: Ast.full.If) Error!void { |
| | 922 | assert(if_node.ast.cond_expr != 0); |
| | 923 | assert(if_node.ast.then_expr != 0); |
| | 924 | |
| | 925 | // Perform these transformations in this priority order: |
| | 926 | // 1. If the `else` expression is missing or an empty block, replace the condition with `if (true)` if it is not already. |
| | 927 | // 2. If the `then` block is empty, replace the condition with `if (false)` if it is not already. |
| | 928 | // 3. If the condition is `if (true)`, replace the `if` expression with the contents of the `then` expression. |
| | 929 | // 4. If the condition is `if (false)`, replace the `if` expression with the contents of the `else` expression. |
| | 930 | if (!isTrueIdent(w.ast, if_node.ast.cond_expr) and |
| | 931 | (if_node.ast.else_expr == 0 or isEmptyBlock(w.ast, if_node.ast.else_expr))) |
| | 932 | { |
| | 933 | try w.transformations.ensureUnusedCapacity(1); |
| | 934 | w.transformations.appendAssumeCapacity(.{ .replace_with_true = if_node.ast.cond_expr }); |
| | 935 | } else if (!isFalseIdent(w.ast, if_node.ast.cond_expr) and isEmptyBlock(w.ast, if_node.ast.then_expr)) { |
| | 936 | try w.transformations.ensureUnusedCapacity(1); |
| | 937 | w.transformations.appendAssumeCapacity(.{ .replace_with_false = if_node.ast.cond_expr }); |
| | 938 | } else if (isTrueIdent(w.ast, if_node.ast.cond_expr)) { |
| | 939 | try w.transformations.ensureUnusedCapacity(1); |
| | 940 | w.transformations.appendAssumeCapacity(.{ .replace_node = .{ |
| | 941 | .to_replace = node_index, |
| | 942 | .replacement = if_node.ast.then_expr, |
| | 943 | } }); |
| | 944 | } else if (isFalseIdent(w.ast, if_node.ast.cond_expr)) { |
| | 945 | try w.transformations.ensureUnusedCapacity(1); |
| | 946 | w.transformations.appendAssumeCapacity(.{ .replace_node = .{ |
| | 947 | .to_replace = node_index, |
| | 948 | .replacement = if_node.ast.else_expr, |
| | 949 | } }); |
| | 950 | } |
| | 951 | |
| 885 | try walkExpression(w, if_node.ast.cond_expr); // condition | 952 | try walkExpression(w, if_node.ast.cond_expr); // condition |
| 886 | | 953 | |
| 887 | if (if_node.ast.then_expr != 0) { | 954 | if (if_node.ast.then_expr != 0) { |
| ... | @@ -1002,6 +1069,14 @@ fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool { | ... | @@ -1002,6 +1069,14 @@ fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| 1002 | return isMatchingIdent(ast, node, "undefined"); | 1069 | return isMatchingIdent(ast, node, "undefined"); |
| 1003 | } | 1070 | } |
| 1004 | | 1071 | |
| | 1072 | fn isTrueIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| | 1073 | return isMatchingIdent(ast, node, "true"); |
| | 1074 | } |
| | 1075 | |
| | 1076 | fn isFalseIdent(ast: *const Ast, node: Ast.Node.Index) bool { |
| | 1077 | return isMatchingIdent(ast, node, "false"); |
| | 1078 | } |
| | 1079 | |
| 1005 | fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool { | 1080 | fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bool { |
| 1006 | const node_tags = ast.nodes.items(.tag); | 1081 | const node_tags = ast.nodes.items(.tag); |
| 1007 | const main_tokens = ast.nodes.items(.main_token); | 1082 | const main_tokens = ast.nodes.items(.main_token); |
| ... | @@ -1014,3 +1089,14 @@ fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bo | ... | @@ -1014,3 +1089,14 @@ fn isMatchingIdent(ast: *const Ast, node: Ast.Node.Index, string: []const u8) bo |
| 1014 | else => return false, | 1089 | else => return false, |
| 1015 | } | 1090 | } |
| 1016 | } | 1091 | } |
| | 1092 | |
| | 1093 | fn isEmptyBlock(ast: *const Ast, node: Ast.Node.Index) bool { |
| | 1094 | const node_tags = ast.nodes.items(.tag); |
| | 1095 | const node_data = ast.nodes.items(.data); |
| | 1096 | switch (node_tags[node]) { |
| | 1097 | .block_two => { |
| | 1098 | return node_data[node].lhs == 0 and node_data[node].rhs == 0; |
| | 1099 | }, |
| | 1100 | else => return false, |
| | 1101 | } |
| | 1102 | } |