1use std::marker::PhantomData;
4
5use crate::{
6 BinaryType, LowerCompatible, NullOf, Nullable, Numeric, Orderable, PredicateType,
7 expression::{
8 Column, Expression, PostfixOperator, Scalar,
9 op::{And, Or},
10 },
11 instr::RpnInstr,
12 lower::{Instructions, LowerCtx},
13 ty::{Bool, Boolean, Comparable, Nullability, TypeMeta},
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum Operator {
19 Eq,
20 Neq,
21 Lt,
22 Lte,
23 Gt,
24 Gte,
25 Like { sensitive: bool, negated: bool },
26 And,
27 Or,
28 Add,
29 Mul,
30 Rem,
31 Sub,
32 Div,
33 BitAnd,
34 BitOr,
35 Shl,
36 Shr,
37}
38
39pub struct Binary<T, L, R> {
41 pub(crate) left: L,
42 pub(crate) op: PhantomData<T>,
43 pub(crate) right: R,
44}
45
46impl<T, L, R> Binary<T, L, R> {
47 pub fn into_parts(self) -> (L, R) {
49 (self.left, self.right)
50 }
51}
52
53pub trait BinaryOp<L: TypeMeta, R: TypeMeta> {
55 type Output: TypeMeta;
57 const INSTRUCTION: RpnInstr;
59}
60
61pub mod op {
63 pub struct Eq;
64 pub struct And;
65 pub struct Or;
66 pub struct Neq;
67 pub struct Gt;
68 pub struct Gte;
69 pub struct Lt;
70 pub struct Lte;
71 pub struct Add;
72 pub struct Rem;
73 pub struct Mul;
74 pub struct Sub;
75 pub struct Div;
76 pub struct BitAnd;
77 pub struct BitOr;
78 pub struct Shl;
79 pub struct Shr;
80}
81
82macro_rules! impl_predicate_binary {
83 ($op:ty, $variant:expr, $bound:ident) => {
84 impl<T> BinaryOp<T, T> for $op
85 where
86 T: $bound + Nullability,
87 BinaryType<NullOf<T>, NullOf<T>>: PredicateType,
88 {
89 type Output = <BinaryType<NullOf<T>, NullOf<T>> as PredicateType>::Output;
90 const INSTRUCTION: RpnInstr = RpnInstr::Binary {
91 op: $variant,
92 lhs: 0,
93 rhs: 0,
94 };
95 }
96 };
97}
98
99impl_predicate_binary!(op::Eq, Operator::Eq, Comparable);
100impl_predicate_binary!(op::Neq, Operator::Neq, Comparable);
101impl_predicate_binary!(op::Gt, Operator::Gt, Orderable);
102impl_predicate_binary!(op::Lt, Operator::Lt, Orderable);
103impl_predicate_binary!(op::Gte, Operator::Gte, Orderable);
104impl_predicate_binary!(op::Lte, Operator::Lte, Orderable);
105impl_predicate_binary!(op::And, Operator::And, Boolean);
106impl_predicate_binary!(op::Or, Operator::Or, Boolean);
107
108macro_rules! impl_numeric_binary {
109 ($op:ty, $variant:expr) => {
110 impl<T> BinaryOp<T, T> for $op
111 where
112 T: Numeric,
113 {
114 type Output = T;
115 const INSTRUCTION: RpnInstr = RpnInstr::Binary {
116 op: $variant,
117 lhs: 0,
118 rhs: 0,
119 };
120 }
121 };
122}
123
124impl_numeric_binary!(op::Add, Operator::Add);
125impl_numeric_binary!(op::Rem, Operator::Rem);
126impl_numeric_binary!(op::Mul, Operator::Mul);
127impl_numeric_binary!(op::Sub, Operator::Sub);
128impl_numeric_binary!(op::Div, Operator::Div);
129impl_numeric_binary!(op::BitAnd, Operator::BitAnd);
130impl_numeric_binary!(op::BitOr, Operator::BitOr);
131impl_numeric_binary!(op::Shl, Operator::Shl);
132impl_numeric_binary!(op::Shr, Operator::Shr);
133
134#[qraft_expression_macro::as_expression]
135impl<T, L, R> Expression for Binary<T, L, R>
136where
137 L: Expression,
138 R: LowerCompatible<L::Type>,
139 T: BinaryOp<L::Type, L::Type>,
140{
141 type Type = T::Output;
142
143 fn lower(&self, ctx: &mut LowerCtx) -> usize {
144 let lhs = self.left.lower(ctx);
145 let rhs = self.right.lower_compatible(ctx);
146 let RpnInstr::Binary { op, .. } = T::INSTRUCTION else {
147 unreachable!("binary instruction");
148 };
149 ctx.instrs.push_binary(op, lhs, rhs);
150 lhs + rhs + 1
151 }
152}
153
154pub trait EqExt<T: Comparable>: Sized + Expression {
156 fn eq<E>(self, other: E) -> Binary<op::Eq, Self, E>
157 where
158 E: LowerCompatible<T>,
159 {
160 Binary {
161 left: self,
162 right: other,
163 op: PhantomData,
164 }
165 }
166
167 fn neq<E>(self, other: E) -> Binary<op::Neq, Self, E>
168 where
169 E: LowerCompatible<T>,
170 {
171 Binary {
172 left: self,
173 right: other,
174 op: PhantomData,
175 }
176 }
177}
178
179#[derive(Debug)]
181pub struct Postfix<L> {
182 pub lhs: L,
183 pub operator: PostfixOperator,
184}
185
186#[qraft_expression_macro::as_expression]
187impl<E: Expression> Expression for Postfix<E> {
188 type Type = Bool;
189
190 fn lower(&self, ctx: &mut LowerCtx) -> usize {
191 let lhs = self.lhs.lower(ctx);
192 ctx.lower_postfix(self.operator, lhs)
193 }
194}
195
196pub trait IsNull<T>: Sized {
198 #[allow(clippy::wrong_self_convention)]
199 fn is_null(self) -> Postfix<Self> {
200 Postfix {
201 lhs: self,
202 operator: PostfixOperator::Null { negated: false },
203 }
204 }
205
206 #[allow(clippy::wrong_self_convention)]
207 fn is_not_null(self) -> Postfix<Self> {
208 Postfix {
209 lhs: self,
210 operator: PostfixOperator::Null { negated: true },
211 }
212 }
213}
214
215pub trait IsPredicate<T>: Sized {
217 #[allow(clippy::wrong_self_convention)]
218 fn is_true(self) -> Postfix<Self> {
219 Postfix {
220 lhs: self,
221 operator: PostfixOperator::True,
222 }
223 }
224
225 #[allow(clippy::wrong_self_convention)]
226 fn is_false(self) -> Postfix<Self> {
227 Postfix {
228 lhs: self,
229 operator: PostfixOperator::False,
230 }
231 }
232}
233
234impl<T: Comparable, V> IsNull<T> for V where V: Expression<Type = Nullable<T>> {}
235impl<T: Boolean, V> IsPredicate<T> for V where V: Expression<Type = T> {}
236
237impl<T: Comparable, V> EqExt<T> for V where V: Expression<Type = T> {}
238
239pub trait OrderExt<T: Orderable>: Sized + Expression {
241 fn lt<E>(self, other: E) -> Binary<op::Lt, Self, E>
242 where
243 E: LowerCompatible<T>,
244 {
245 Binary {
246 left: self,
247 right: other,
248 op: PhantomData,
249 }
250 }
251
252 fn lte<E>(self, other: E) -> Binary<op::Lte, Self, E>
253 where
254 E: LowerCompatible<T>,
255 {
256 Binary {
257 left: self,
258 right: other,
259 op: PhantomData,
260 }
261 }
262
263 fn gt<E>(self, other: E) -> Binary<op::Gt, Self, E>
264 where
265 E: LowerCompatible<T>,
266 {
267 Binary {
268 left: self,
269 right: other,
270 op: PhantomData,
271 }
272 }
273
274 fn gte<E>(self, other: E) -> Binary<op::Gte, Self, E>
275 where
276 E: LowerCompatible<T>,
277 {
278 Binary {
279 left: self,
280 right: other,
281 op: PhantomData,
282 }
283 }
284}
285
286impl<T: Orderable, V> OrderExt<T> for V where V: Expression<Type = T> {}
287
288pub trait PredicateExt<T: Boolean>: Sized + Expression {
290 fn and<E>(self, other: E) -> Binary<And, Self, E>
291 where
292 E: LowerCompatible<T>,
293 {
294 Binary {
295 left: self,
296 right: other,
297 op: PhantomData,
298 }
299 }
300
301 fn or<E>(self, other: E) -> Binary<Or, Self, E>
302 where
303 E: LowerCompatible<T>,
304 {
305 Binary {
306 left: self,
307 right: other,
308 op: PhantomData,
309 }
310 }
311}
312
313impl<T: Boolean, V> PredicateExt<T> for V where V: Expression<Type = T> {}
314
315macro_rules! impl_binop_family {
316 (trait = $trait:ident,method = $method:ident,op = $op:path) => {
317 impl<T: Numeric, Op, L, R, E> core::ops::$trait<E> for Binary<Op, L, R>
318 where
319 E: Expression<Type = T>,
320 {
321 type Output = Binary<$op, Self, E>;
322
323 fn $method(self, rhs: E) -> Self::Output {
324 Binary {
325 left: self,
326 op: PhantomData,
327 right: rhs,
328 }
329 }
330 }
331
332 impl<T: Numeric, E> core::ops::$trait<E> for Scalar<T>
333 where
334 E: Expression<Type = T>,
335 {
336 type Output = Binary<$op, Self, E>;
337
338 fn $method(self, rhs: E) -> Self::Output {
339 Binary {
340 left: self,
341 op: PhantomData,
342 right: rhs,
343 }
344 }
345 }
346
347 impl<M, T: Numeric, E> core::ops::$trait<E> for Column<M, T>
348 where
349 E: Expression<Type = T>,
350 {
351 type Output = Binary<$op, Self, E>;
352
353 fn $method(self, rhs: E) -> Self::Output {
354 Binary {
355 left: self,
356 op: PhantomData,
357 right: rhs,
358 }
359 }
360 }
361 };
362}
363
364impl_binop_family!(trait = BitAnd, method = bitand, op = op::BitAnd);
365impl_binop_family!(trait = BitOr, method = bitor, op = op::BitOr);
366impl_binop_family!(trait = Shl, method = shl, op = op::Shl);
367impl_binop_family!(trait = Shr, method = shr, op = op::Shr);