Skip to content

Commit 667bd9f

Browse files
committed
Refactor expr
1 parent 530a75a commit 667bd9f

File tree

4 files changed

+270
-165
lines changed

4 files changed

+270
-165
lines changed

src/iceberg/expression/common.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919

2020
#pragma once
2121

22-
namespace iceberg {
22+
#include <memory>
23+
#include <string>
24+
25+
#include "iceberg/result.h"
2326

24-
template <typename T>
25-
concept Bindable = requires(T expr) {
26-
{ expr.Bind() };
27-
};
27+
namespace iceberg {
28+
class Schema;
2829

29-
} // namespace iceberg
30+
} // namespace iceberg

src/iceberg/expression/expression.cc

Lines changed: 168 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,68 +25,220 @@
2525

2626
namespace iceberg {
2727

28-
// True implementation
28+
template <typename T>
29+
concept Bindable = requires(const T& expr, const Schema& schema, bool case_sensitive) {
30+
// Must have a BoundType alias that defines what type it binds to
31+
typename T::BoundType;
32+
33+
// Must have a Bind method with the correct signature
34+
{ expr.Bind(schema, case_sensitive) } -> std::same_as<Result<typename T::BoundType>>;
35+
};
36+
37+
/// \brief Concept for types that behave like predicates (bound or unbound)
38+
template <typename T>
39+
concept PredicateLike = requires(const T& pred) {
40+
// Must have an operation type
41+
{ pred.op() } -> std::same_as<Operation>;
42+
43+
// Must be convertible to string
44+
{ pred.ToString() } -> std::same_as<std::string>;
45+
46+
// Must have a Negate method that returns a shared_ptr to the same concept
47+
{ pred.Negate() } -> std::convertible_to<std::shared_ptr<T>>;
48+
49+
// Must support equality comparison
50+
{ pred.Equals(pred) } -> std::same_as<bool>;
51+
};
52+
53+
/// \brief Concept specifically for unbound predicates that can be bound
54+
template <typename T>
55+
concept UnboundPredicate = PredicateLike<T> && requires(const T& pred) {
56+
// Must have a BoundType alias
57+
typename T::BoundType;
58+
59+
// Must be bindable to a schema
60+
requires Bindable<T>;
61+
};
62+
63+
/// \brief Concept specifically for bound predicates
64+
template <typename T>
65+
concept BoundPredicateLike = PredicateLike<T> && requires(const T& pred) {
66+
// Must have type information
67+
{ pred.type() } -> std::convertible_to<std::shared_ptr<Type>>;
68+
69+
// Must report that it's bound
70+
{ pred.IsBound() } -> std::convertible_to<bool>;
71+
};
72+
73+
// Internal implementation classes
74+
75+
/// \brief An Expression that is always true.
76+
class True final : public Predicate {
77+
public:
78+
/// \brief Returns the singleton instance
79+
static const std::shared_ptr<True>& Instance();
80+
81+
Operation op() const override { return Operation::kTrue; }
82+
83+
std::string ToString() const override { return "true"; }
84+
85+
std::shared_ptr<Predicate> Negate() const override;
86+
87+
bool Equals(const Expression& other) const override {
88+
return other.op() == Operation::kTrue;
89+
}
90+
91+
private:
92+
constexpr True() = default;
93+
};
94+
95+
/// \brief An expression that is always false.
96+
class False final : public Predicate {
97+
public:
98+
/// \brief Returns the singleton instance
99+
static const std::shared_ptr<False>& Instance();
100+
101+
Operation op() const override { return Operation::kFalse; }
102+
103+
std::string ToString() const override { return "false"; }
104+
105+
std::shared_ptr<Predicate> Negate() const override;
106+
107+
bool Equals(const Expression& other) const override {
108+
return other.op() == Operation::kFalse;
109+
}
110+
111+
private:
112+
constexpr False() = default;
113+
};
114+
115+
/// \brief An Expression that represents a logical AND operation between two expressions.
116+
class AndImpl final : public Predicate {
117+
public:
118+
/// \brief Constructs an And expression from two sub-expressions.
119+
AndImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right);
120+
121+
/// \brief Returns the left operand of the AND expression.
122+
const std::shared_ptr<Predicate>& left() const { return left_; }
123+
124+
/// \brief Returns the right operand of the AND expression.
125+
const std::shared_ptr<Predicate>& right() const { return right_; }
126+
127+
Operation op() const override { return Operation::kAnd; }
128+
129+
std::string ToString() const override;
130+
131+
std::shared_ptr<Predicate> Negate() const override;
132+
133+
bool Equals(const Expression& other) const override;
134+
135+
private:
136+
std::shared_ptr<Predicate> left_;
137+
std::shared_ptr<Predicate> right_;
138+
};
139+
140+
/// \brief An Expression that represents a logical OR operation between two expressions.
141+
class OrImpl final : public Predicate {
142+
public:
143+
/// \brief Constructs an Or expression from two sub-expressions.
144+
OrImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right);
145+
146+
/// \brief Returns the left operand of the OR expression.
147+
const std::shared_ptr<Predicate>& left() const { return left_; }
148+
149+
/// \brief Returns the right operand of the OR expression.
150+
const std::shared_ptr<Predicate>& right() const { return right_; }
151+
152+
Operation op() const override { return Operation::kOr; }
153+
154+
std::string ToString() const override;
155+
156+
std::shared_ptr<Predicate> Negate() const override;
157+
158+
bool Equals(const Expression& other) const override;
159+
160+
private:
161+
std::shared_ptr<Predicate> left_;
162+
std::shared_ptr<Predicate> right_;
163+
};
164+
165+
// Implementation of True
29166
const std::shared_ptr<True>& True::Instance() {
30167
static const std::shared_ptr<True> instance{new True()};
31168
return instance;
32169
}
33170

34171
std::shared_ptr<Predicate> True::Negate() const { return False::Instance(); }
35172

36-
// False implementation
173+
// Implementation of False
37174
const std::shared_ptr<False>& False::Instance() {
38175
static const std::shared_ptr<False> instance = std::shared_ptr<False>(new False());
39176
return instance;
40177
}
41178

42179
std::shared_ptr<Predicate> False::Negate() const { return True::Instance(); }
43180

44-
// And implementation
45-
And::And(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right)
181+
// Implementation of AndImpl
182+
AndImpl::AndImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right)
46183
: left_(std::move(left)), right_(std::move(right)) {}
47184

48-
std::string And::ToString() const {
185+
std::string AndImpl::ToString() const {
49186
return std::format("({} and {})", left_->ToString(), right_->ToString());
50187
}
51188

52-
std::shared_ptr<Predicate> And::Negate() const {
189+
std::shared_ptr<Predicate> AndImpl::Negate() const {
53190
// De Morgan's law: not(A and B) = (not A) or (not B)
54191
auto left_negated = left_->Negate();
55192
auto right_negated = right_->Negate();
56-
return std::make_shared<Or>(left_negated, right_negated);
193+
return std::make_shared<OrImpl>(left_negated, right_negated);
57194
}
58195

59-
bool And::Equals(const Expression& expr) const {
196+
bool AndImpl::Equals(const Expression& expr) const {
60197
if (expr.op() == Operation::kAnd) {
61-
const auto& other = iceberg::internal::checked_cast<const And&>(expr);
198+
const auto& other = iceberg::internal::checked_cast<const AndImpl&>(expr);
62199
return (left_->Equals(*other.left()) && right_->Equals(*other.right())) ||
63200
(left_->Equals(*other.right()) && right_->Equals(*other.left()));
64201
}
65202
return false;
66203
}
67204

68-
// Or implementation
69-
Or::Or(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right)
205+
// Implementation of OrImpl
206+
OrImpl::OrImpl(std::shared_ptr<Predicate> left, std::shared_ptr<Predicate> right)
70207
: left_(std::move(left)), right_(std::move(right)) {}
71208

72-
std::string Or::ToString() const {
209+
std::string OrImpl::ToString() const {
73210
return std::format("({} or {})", left_->ToString(), right_->ToString());
74211
}
75212

76-
std::shared_ptr<Predicate> Or::Negate() const {
213+
std::shared_ptr<Predicate> OrImpl::Negate() const {
77214
// De Morgan's law: not(A or B) = (not A) and (not B)
78215
auto left_negated = left_->Negate();
79216
auto right_negated = right_->Negate();
80-
return std::make_shared<And>(left_negated, right_negated);
217+
return std::make_shared<AndImpl>(left_negated, right_negated);
81218
}
82219

83-
bool Or::Equals(const Expression& expr) const {
220+
bool OrImpl::Equals(const Expression& expr) const {
84221
if (expr.op() == Operation::kOr) {
85-
const auto& other = iceberg::internal::checked_cast<const Or&>(expr);
222+
const auto& other = iceberg::internal::checked_cast<const OrImpl&>(expr);
86223
return (left_->Equals(*other.left()) && right_->Equals(*other.right())) ||
87224
(left_->Equals(*other.right()) && right_->Equals(*other.left()));
88225
}
89226
return false;
90227
}
91228

229+
// Implementation of Predicate static factory methods
230+
std::shared_ptr<Predicate> Predicate::AlwaysTrue() { return True::Instance(); }
231+
232+
std::shared_ptr<Predicate> Predicate::AlwaysFalse() { return False::Instance(); }
233+
234+
std::shared_ptr<Predicate> Predicate::And(std::shared_ptr<Predicate> left,
235+
std::shared_ptr<Predicate> right) {
236+
return std::make_shared<AndImpl>(std::move(left), std::move(right));
237+
}
238+
239+
std::shared_ptr<Predicate> Predicate::Or(std::shared_ptr<Predicate> left,
240+
std::shared_ptr<Predicate> right) {
241+
return std::make_shared<OrImpl>(std::move(left), std::move(right));
242+
}
243+
92244
} // namespace iceberg

0 commit comments

Comments
 (0)