use std::{marker::PhantomData, ops};
use crate::{
BigInt, BinaryType, Bool, Double, Float, Int, MathType, Nullable, Numeric,
expression::{As, Binary, Bound, Column, Expression, LitNumber, Scalar},
lower::{Instructions, LowerCtx},
};
macro_rules! define_math_binary {
($name:ident, $method:ident, $trait:ident, $variant:expr) => {
pub struct $name<L, R, Out> {
pub(crate) left: L,
pub(crate) right: R,
pub(crate) marker: PhantomData<Out>,
}
#[qraft_expression_macro::as_expression]
impl<L, R, Out> Expression for $name<L, R, Out>
where
L: Expression,
R: Expression,
L::Type: Numeric,
R::Type: Numeric,
Out: Numeric,
BinaryType<L::Type, R::Type>: MathType<Output = Out>,
{
type Type = Out;
fn lower(&self, ctx: &mut LowerCtx) -> usize {
let lhs = self.left.lower(ctx);
let rhs = self.right.lower(ctx);
ctx.instrs.push_binary($variant, lhs, rhs);
lhs + rhs + 1
}
}
impl<M, T, Rhs, Out> ops::$trait<Rhs> for Column<M, T>
where
T: Numeric,
Rhs: Expression,
Rhs::Type: Numeric,
Out: Numeric,
BinaryType<T, Rhs::Type>: MathType<Output = Out>,
{
type Output = $name<Self, Rhs, Out>;
fn $method(self, rhs: Rhs) -> Self::Output {
$name {
left: self,
right: rhs,
marker: PhantomData,
}
}
}
impl<M, T, Rhs, Out> ops::$trait<Rhs> for As<Column<M, T>>
where
T: Numeric,
Rhs: Expression,
Rhs::Type: Numeric,
Out: Numeric,
BinaryType<T, Rhs::Type>: MathType<Output = Out>,
{
type Output = $name<Self, Rhs, Out>;
fn $method(self, rhs: Rhs) -> Self::Output {
$name {
left: self,
right: rhs,
marker: PhantomData,
}
}
}
impl<T, Rhs, Out> ops::$trait<Rhs> for Scalar<T>
where
T: Numeric,
Rhs: Expression,
Rhs::Type: Numeric,
Out: Numeric,
BinaryType<T, Rhs::Type>: MathType<Output = Out>,
{
type Output = $name<Self, Rhs, Out>;
fn $method(self, rhs: Rhs) -> Self::Output {
$name {
left: self,
right: rhs,
marker: PhantomData,
}
}
}
impl<Op, L, R, Rhs, LeftTy, Out> ops::$trait<Rhs> for Binary<Op, L, R>
where
Self: Expression<Type = LeftTy>,
LeftTy: Numeric,
Rhs: Expression,
Rhs::Type: Numeric,
Out: Numeric,
BinaryType<LeftTy, Rhs::Type>: MathType<Output = Out>,
{
type Output = $name<Self, Rhs, Out>;
fn $method(self, rhs: Rhs) -> Self::Output {
$name {
left: self,
right: rhs,
marker: PhantomData,
}
}
}
impl<T, Rhs, Out> ops::$trait<Rhs> for LitNumber<T>
where
T: Numeric,
Rhs: Expression,
Rhs::Type: Numeric,
Out: Numeric,
BinaryType<T, Rhs::Type>: MathType<Output = Out>,
{
type Output = $name<Self, Rhs, Out>;
fn $method(self, rhs: Rhs) -> Self::Output {
$name {
left: self,
right: rhs,
marker: PhantomData,
}
}
}
impl<L, R, Current, Rhs, Out> ops::$trait<Rhs> for $name<L, R, Current>
where
Self: Expression<Type = Current>,
Current: Numeric,
Rhs: Expression,
Rhs::Type: Numeric,
Out: Numeric,
BinaryType<Current, Rhs::Type>: MathType<Output = Out>,
{
type Output = $name<Self, Rhs, Out>;
fn $method(self, rhs: Rhs) -> Self::Output {
$name {
left: self,
right: rhs,
marker: PhantomData,
}
}
}
};
}
define_math_binary!(MathAdd, add, Add, super::Operator::Add);
define_math_binary!(MathSub, sub, Sub, super::Operator::Sub);
define_math_binary!(MathMul, mul, Mul, super::Operator::Mul);
define_math_binary!(MathDiv, div, Div, super::Operator::Div);
define_math_binary!(MathRem, rem, Rem, super::Operator::Rem);
impl Expression for i32 {
type Type = Int;
fn lower(&self, ctx: &mut LowerCtx) -> usize {
Bound::<Int>::new((*self).into()).lower(ctx)
}
}
impl Expression for i64 {
type Type = BigInt;
fn lower(&self, ctx: &mut LowerCtx) -> usize {
Bound::<BigInt>::new((*self).into()).lower(ctx)
}
}
impl Expression for f32 {
type Type = Float;
fn lower(&self, ctx: &mut LowerCtx) -> usize {
Bound::<Float>::new((*self).into()).lower(ctx)
}
}
impl Expression for f64 {
type Type = Double;
fn lower(&self, ctx: &mut LowerCtx) -> usize {
Bound::<Double>::new((*self).into()).lower(ctx)
}
}
impl Expression for bool {
type Type = Bool;
fn lower(&self, ctx: &mut LowerCtx) -> usize {
Bound::<Bool>::new((*self).into()).lower(ctx)
}
}
impl Expression for Option<bool> {
type Type = Nullable<Bool>;
fn lower(&self, ctx: &mut LowerCtx) -> usize {
Bound::<Nullable<Bool>>::new((*self).into()).lower(ctx)
}
}