|
10 | 10 | from python_minifier.util import is_constant_node |
11 | 11 |
|
12 | 12 |
|
13 | | -class FoldConstants(SuiteTransformer): |
14 | | - """ |
15 | | - Fold Constants if it would reduce the size of the source |
| 13 | +def is_foldable_constant(node): |
16 | 14 | """ |
| 15 | + Check if a node is a constant expression that can participate in folding. |
17 | 16 |
|
18 | | - def __init__(self): |
19 | | - super(FoldConstants, self).__init__() |
| 17 | + We can asume that children have already been folded, so foldable constants are either: |
| 18 | + - Simple literals (Num, NameConstant) |
| 19 | + - UnaryOp(USub/Invert) on a Num - these don't fold to shorter forms, |
| 20 | + so they remain after child visiting. UAdd and Not would have been |
| 21 | + folded away since they always produce shorter results. |
| 22 | + """ |
| 23 | + if is_constant_node(node, (ast.Num, ast.NameConstant)): |
| 24 | + return True |
20 | 25 |
|
21 | | - def visit_BinOp(self, node): |
| 26 | + if isinstance(node, ast.UnaryOp): |
| 27 | + if isinstance(node.op, (ast.USub, ast.Invert)): |
| 28 | + return is_constant_node(node.operand, ast.Num) |
22 | 29 |
|
23 | | - node.left = self.visit(node.left) |
24 | | - node.right = self.visit(node.right) |
| 30 | + return False |
25 | 31 |
|
26 | | - # Check this is a constant expression that could be folded |
27 | | - # We don't try to fold strings or bytes, since they have probably been arranged this way to make the source shorter and we are unlikely to beat that |
28 | | - if not is_constant_node(node.left, (ast.Num, ast.NameConstant)): |
29 | | - return node |
30 | | - if not is_constant_node(node.right, (ast.Num, ast.NameConstant)): |
31 | | - return node |
32 | 32 |
|
33 | | - if isinstance(node.op, ast.Div): |
34 | | - # Folding div is subtle, since it can have different results in Python 2 and Python 3 |
35 | | - # Do this once target version options have been implemented |
36 | | - return node |
| 33 | +class FoldConstants(SuiteTransformer): |
| 34 | + """ |
| 35 | + Fold Constants if it would reduce the size of the source |
| 36 | + """ |
37 | 37 |
|
38 | | - if isinstance(node.op, ast.Pow): |
39 | | - # This can be folded, but it is unlikely to reduce the size of the source |
40 | | - # It can also be slow to evaluate |
41 | | - return node |
| 38 | + def __init__(self): |
| 39 | + super(FoldConstants, self).__init__() |
42 | 40 |
|
| 41 | + def fold(self, node): |
43 | 42 | # Evaluate the expression |
44 | 43 | try: |
45 | 44 | original_expression = unparse_expression(node) |
@@ -96,6 +95,44 @@ def visit_BinOp(self, node): |
96 | 95 | # New representation is shorter and has the same value, so use it |
97 | 96 | return self.add_child(new_node, get_parent(node), node.namespace) |
98 | 97 |
|
| 98 | + def visit_BinOp(self, node): |
| 99 | + |
| 100 | + node.left = self.visit(node.left) |
| 101 | + node.right = self.visit(node.right) |
| 102 | + |
| 103 | + # Check this is a constant expression that could be folded |
| 104 | + # We don't try to fold strings or bytes, since they have probably been arranged this way to make the source shorter and we are unlikely to beat that |
| 105 | + if not is_foldable_constant(node.left): |
| 106 | + return node |
| 107 | + if not is_foldable_constant(node.right): |
| 108 | + return node |
| 109 | + |
| 110 | + if isinstance(node.op, ast.Div): |
| 111 | + # Folding div is subtle, since it can have different results in Python 2 and Python 3 |
| 112 | + # Do this once target version options have been implemented |
| 113 | + return node |
| 114 | + |
| 115 | + if isinstance(node.op, ast.Pow): |
| 116 | + # This can be folded, but it is unlikely to reduce the size of the source |
| 117 | + # It can also be slow to evaluate |
| 118 | + return node |
| 119 | + |
| 120 | + return self.fold(node) |
| 121 | + |
| 122 | + def visit_UnaryOp(self, node): |
| 123 | + |
| 124 | + node.operand = self.visit(node.operand) |
| 125 | + |
| 126 | + # Only fold if the operand is a foldable constant |
| 127 | + if not is_foldable_constant(node.operand): |
| 128 | + return node |
| 129 | + |
| 130 | + # Only fold these unary operators |
| 131 | + if not isinstance(node.op, (ast.USub, ast.UAdd, ast.Invert, ast.Not)): |
| 132 | + return node |
| 133 | + |
| 134 | + return self.fold(node) |
| 135 | + |
99 | 136 |
|
100 | 137 | def equal_value_and_type(a, b): |
101 | 138 | if type(a) != type(b): |
|
0 commit comments