| 1056 | } // namespace |
| 1057 | |
| 1058 | Result<Expression> Canonicalize(Expression expr, compute::ExecContext* exec_context) { |
| 1059 | if (!expr.IsBound()) { |
| 1060 | return Status::Invalid("Cannot canonicalize an unbound expression."); |
| 1061 | } |
| 1062 | |
| 1063 | if (exec_context == nullptr) { |
| 1064 | compute::ExecContext exec_context; |
| 1065 | return Canonicalize(std::move(expr), &exec_context); |
| 1066 | } |
| 1067 | |
| 1068 | // If potentially reconstructing more deeply than a call's immediate arguments |
| 1069 | // (for example, when reorganizing an associative chain), add expressions to this set to |
| 1070 | // avoid unnecessary work |
| 1071 | struct { |
| 1072 | std::unordered_set<Expression, Expression::Hash> set_; |
| 1073 | |
| 1074 | bool operator()(const Expression& expr) const { |
| 1075 | return set_.find(expr) != set_.end(); |
| 1076 | } |
| 1077 | |
| 1078 | void Add(std::vector<Expression> exprs) { |
| 1079 | std::move(exprs.begin(), exprs.end(), std::inserter(set_, set_.end())); |
| 1080 | } |
| 1081 | } AlreadyCanonicalized; |
| 1082 | |
| 1083 | return ModifyExpression( |
| 1084 | std::move(expr), |
| 1085 | [&AlreadyCanonicalized, exec_context](Expression expr) -> Result<Expression> { |
| 1086 | auto call = expr.call(); |
| 1087 | if (!call) return expr; |
| 1088 | if (!call->function->is_pure()) return expr; |
| 1089 | |
| 1090 | if (AlreadyCanonicalized(expr)) return expr; |
| 1091 | |
| 1092 | if (IsBinaryAssociativeCommutative(*call)) { |
| 1093 | struct { |
| 1094 | int Priority(const Expression& operand) const { |
| 1095 | // order literals first, starting with nulls |
| 1096 | if (operand.IsNullLiteral()) return 0; |
| 1097 | if (operand.literal()) return 1; |
| 1098 | return 2; |
| 1099 | } |
| 1100 | bool operator()(const Expression& l, const Expression& r) const { |
| 1101 | return Priority(l) < Priority(r); |
| 1102 | } |
| 1103 | } CanonicalOrdering; |
| 1104 | |
| 1105 | FlattenedAssociativeChain chain(expr); |
| 1106 | |
| 1107 | if (chain.was_left_folded && |
| 1108 | std::is_sorted(chain.fringe.begin(), chain.fringe.end(), |
| 1109 | CanonicalOrdering)) { |
| 1110 | // fast path for expressions which happen to have arrived in an |
| 1111 | // already-canonical form |
| 1112 | AlreadyCanonicalized.Add(std::move(chain.exprs)); |
| 1113 | return expr; |
| 1114 | } |
| 1115 |
no test coverage detected