diff --git a/argus-core/src/expr.rs b/argus-core/src/expr.rs index 831425a..a7b87ec 100644 --- a/argus-core/src/expr.rs +++ b/argus-core/src/expr.rs @@ -4,9 +4,11 @@ mod bool_ops; mod internal_macros; pub mod iter; mod num_ops; +mod traits; pub use bool_ops::*; pub use num_ops::*; +pub use traits::*; use crate::{ArgusResult, Error}; @@ -26,6 +28,21 @@ pub enum NumExpr { Div { dividend: Box, divisor: Box }, } +impl Expr for NumExpr { + fn as_any(&self) -> &dyn Any { + self + } + + fn args(&self) -> Vec<&dyn Expr> { + match self { + NumExpr::Neg { arg } => vec![arg.as_ref()], + NumExpr::Add { args } | NumExpr::Mul { args } => args.iter().map(|arg| arg as &dyn Expr).collect(), + NumExpr::Div { dividend, divisor } => vec![dividend.as_ref(), divisor.as_ref()], + _ => vec![], + } + } +} + /// Types of comparison operations #[derive(Clone, Copy, Debug, PartialEq)] pub enum Ordering { @@ -46,6 +63,21 @@ pub enum BoolExpr { Or { args: Vec }, } +impl Expr for BoolExpr { + fn args(&self) -> Vec<&dyn Expr> { + match self { + BoolExpr::Cmp { op: _, lhs, rhs } => vec![lhs.as_ref(), rhs.as_ref()], + BoolExpr::Not { arg } => vec![arg.as_ref()], + BoolExpr::And { args } | BoolExpr::Or { args } => args.iter().map(|arg| arg as &dyn Expr).collect(), + _ => vec![], + } + } + + fn as_any(&self) -> &dyn Any { + self + } +} + /// Expression builder /// /// The `ExprBuilder` is a factory structure that deals with the creation of diff --git a/argus-core/src/expr/traits.rs b/argus-core/src/expr/traits.rs new file mode 100644 index 0000000..50ddfa9 --- /dev/null +++ b/argus-core/src/expr/traits.rs @@ -0,0 +1,53 @@ +use std::any::Any; + +use super::iter::AstIter; + +/// A trait representing expressions +pub trait Expr { + fn args(&self) -> Vec<&dyn Expr>; + + fn as_any(&self) -> &dyn Any; + + fn iter(&self) -> AstIter<'_> + where + Self: Sized, + { + AstIter::new(self) + } +} + +impl dyn Expr { + pub fn downcast_expr_ref(&self) -> Option<&T> + where + T: Any, + { + self.as_any().downcast_ref::() + } +} + +#[cfg(test)] +mod tests { + use super::super::{arbitrary, BoolExpr, NumExpr}; + use super::*; + use proptest::prelude::*; + + proptest! { + #[test] + fn downcast_expr_bool(bool_expr in arbitrary::bool_expr()) { + let expr_ref = bool_expr.as_ref() as &dyn Expr; + + let downcast_ref = expr_ref.downcast_expr_ref::().unwrap(); + assert_eq!(downcast_ref, bool_expr.as_ref()); + } + } + + proptest! { + #[test] + fn downcast_expr_num(num_expr in arbitrary::num_expr()) { + let expr_ref = num_expr.as_ref() as &dyn Expr; + + let downcast_ref = expr_ref.downcast_expr_ref::().unwrap(); + assert_eq!(downcast_ref, num_expr.as_ref()); + } + } +}