use std::string::ToString; use super::BaseType; use super::Quantifier; use super::GazType; /// Thin wrapper around all expression types, to avoid trait objects #[derive(Clone)] pub enum Expr { Literal(Box), Variable(Box), BinaryOperator(Box), } impl Expr { pub fn new_literal(x: Literal) -> Self { Self::Literal(Box::new(x)) } pub fn new_variable(x: Variable) -> Self { Self::Variable(Box::new(x)) } pub fn new_binary_op(x: BinaryOperator) -> Self { Self::BinaryOperator(Box::new(x)) } } impl GazType for Expr { fn get_base(&self) -> BaseType { match self { Expr::Literal(x) => x.get_base(), Expr::Variable(x) => x.get_base(), Expr::BinaryOperator(x) => x.get_base(), } } } impl ToString for Expr { fn to_string(&self) -> String { match self { Expr::Literal(x) => x.to_string(), Expr::Variable(x) => x.to_string(), Expr::BinaryOperator(x) => x.to_string(), } } } #[derive(Debug, Clone)] pub enum Literal { Int(i32), Real(f32), } impl ToString for Literal { fn to_string(&self) -> String { match *self { Literal::Int(x) => x.to_string(), Literal::Real(x) => x.to_string(), } } } impl GazType for Literal { fn get_base(&self) -> BaseType { match *self { Literal::Int(_) => BaseType::Int, Literal::Real(_) => BaseType::Real, } } } #[derive(Clone, Default, Builder)] #[builder(setter(into))] pub struct Variable { type_: BaseType, quantifier: Quantifier, name: String, } impl Variable { pub fn get_name(&self) -> String { self.name.clone() } pub fn get_quantifier(&self) -> Quantifier { self.quantifier } } impl ToString for Variable { fn to_string(&self) -> String { self.name.clone() } } impl GazType for Variable { fn get_base(&self) -> BaseType { self.type_ } } #[derive(Clone)] enum BinaryOp { Add(Expr, Expr), Subtract(Expr, Expr), Multiply(Expr, Expr), Divide(Expr, Expr), } #[derive(Clone)] pub struct BinaryOperator { basetype: BaseType, op: BinaryOp, } impl BinaryOperator { pub fn add(left: Expr, right: Expr) -> Self { let is_both_same = left.get_base() == right.get_base(); let is_one_real = left.get_base() == BaseType::Real || right.get_base() == BaseType::Real; let is_one_int = left.get_base() == BaseType::Int || right.get_base() == BaseType::Int; let basetype = if is_both_same { left.get_base() } else if is_one_real && is_one_int { BaseType::Real } else { panic!("Unsupported types being added: {:?} and {:?}", left.get_base(), right.get_base()); }; BinaryOperator { basetype, op: BinaryOp::Add(left, right), } } pub fn sub(left: Expr, right: Expr) -> Self { let is_both_same = left.get_base() == right.get_base(); let is_one_real = left.get_base() == BaseType::Real || right.get_base() == BaseType::Real; let is_one_int = left.get_base() == BaseType::Int || right.get_base() == BaseType::Int; let basetype = if is_both_same { left.get_base() } else if is_one_real && is_one_int { BaseType::Real } else { panic!("Unsupported types being subtracted: {:?} and {:?}", left.get_base(), right.get_base()); }; BinaryOperator { basetype, op: BinaryOp::Subtract(left, right), } } pub fn multiply(left: Expr, right: Expr) -> Self { let is_both_same = left.get_base() == right.get_base(); let is_one_real = left.get_base() == BaseType::Real || right.get_base() == BaseType::Real; let is_one_int = left.get_base() == BaseType::Int || right.get_base() == BaseType::Int; let basetype = if is_both_same { left.get_base() } else if is_one_real && is_one_int { BaseType::Real } else { panic!("Unsupported types being multiplied: {:?} and {:?}", left.get_base(), right.get_base()); }; BinaryOperator { basetype, op: BinaryOp::Multiply(left, right), } } pub fn divide(left: Expr, right: Expr) -> Self { let is_both_same = left.get_base() == right.get_base(); let is_one_real = left.get_base() == BaseType::Real || right.get_base() == BaseType::Real; let is_one_int = left.get_base() == BaseType::Int || right.get_base() == BaseType::Int; let basetype = if is_both_same { left.get_base() } else if is_one_real && is_one_int { BaseType::Real } else { panic!("Unsupported types being divided: {:?} and {:?}", left.get_base(), right.get_base()); }; BinaryOperator { basetype, op: BinaryOp::Divide(left, right), } } } impl ToString for BinaryOperator { fn to_string(&self) -> String { let mut s = String::new(); s.push('('); match &self.op { BinaryOp::Add(l, _) => s.push_str(&l.to_string()), BinaryOp::Subtract(l, _) => s.push_str(&l.to_string()), BinaryOp::Multiply(l, _) => s.push_str(&l.to_string()), BinaryOp::Divide(l, _) => s.push_str(&l.to_string()), } s.push(')'); s.push( match &self.op { BinaryOp::Add(_, _) => '+', BinaryOp::Subtract(_, _) => '-', BinaryOp::Multiply(_, _) => '*', BinaryOp::Divide(_, _) => '/', } ); s.push('('); match &self.op { BinaryOp::Add(_, r) => s.push_str(&r.to_string()), BinaryOp::Subtract(_, r) => s.push_str(&r.to_string()), BinaryOp::Multiply(_, r) => s.push_str(&r.to_string()), BinaryOp::Divide(_, r) => s.push_str(&r.to_string()), } s.push(')'); s } } impl GazType for BinaryOperator { fn get_base(&self) -> BaseType { self.basetype } }