ergotree_ir/mir/
apply.rs

1//! Application of function
2
3use crate::serialization::op_code::OpCode;
4use crate::serialization::sigma_byte_reader::SigmaByteRead;
5use crate::serialization::sigma_byte_writer::SigmaByteWrite;
6use crate::serialization::SigmaParsingError;
7use crate::serialization::SigmaSerializable;
8use crate::serialization::SigmaSerializeResult;
9use crate::types::stype::SType;
10
11use super::expr::Expr;
12use super::expr::InvalidArgumentError;
13use crate::has_opcode::HasStaticOpCode;
14
15/// Application of function `func` to given arguments `args`
16#[derive(PartialEq, Eq, Debug, Clone)]
17pub struct Apply {
18    /// Function
19    pub func: Box<Expr>,
20    /// Arguments
21    pub args: Vec<Expr>,
22    tpe: SType,
23}
24
25impl Apply {
26    /// Create new object, returns an error if any of the requirements failed
27    pub fn new(func: Expr, args: Vec<Expr>) -> Result<Self, InvalidArgumentError> {
28        match func.tpe() {
29            SType::SFunc(sfunc) => {
30                let arg_types: Vec<SType> = args.iter().map(|a| a.tpe()).collect();
31                if sfunc.t_dom != arg_types {
32                    Err(InvalidArgumentError(format!(
33                        "Expected args: {0:?}, got: {1:?}",
34                        sfunc.t_dom, args
35                    )))
36                } else {
37                    Ok(Apply {
38                        func: Box::new(func),
39                        args,
40                        tpe: *sfunc.t_range,
41                    })
42                }
43            }
44            _ => Err(InvalidArgumentError(format!(
45                "unexpected Apply::func: {0:?}",
46                func.tpe(),
47            ))),
48        }
49    }
50
51    /// Type
52    pub fn tpe(&self) -> SType {
53        self.tpe.clone()
54    }
55}
56
57impl HasStaticOpCode for Apply {
58    const OP_CODE: OpCode = OpCode::APPLY;
59}
60
61impl SigmaSerializable for Apply {
62    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
63        self.func.sigma_serialize(w)?;
64        self.args.sigma_serialize(w)
65    }
66
67    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SigmaParsingError> {
68        let func = Expr::sigma_parse(r)?;
69        let args = Vec::<Expr>::sigma_parse(r)?;
70        Ok(Apply::new(func, args)?)
71    }
72}
73
74/// Arbitrary impl
75#[cfg(feature = "arbitrary")]
76#[allow(clippy::unwrap_used)]
77pub mod arbitrary {
78    use crate::mir::func_value::*;
79
80    use super::*;
81
82    use proptest::collection::vec;
83    use proptest::prelude::*;
84
85    impl Arbitrary for Apply {
86        type Strategy = BoxedStrategy<Self>;
87        type Parameters = ();
88
89        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
90            (any::<Expr>(), vec(any::<Expr>(), 1..10))
91                .prop_map(|(body, args)| {
92                    let func = FuncValue::new(
93                        args.iter()
94                            .enumerate()
95                            .map(|(idx, arg)| FuncArg {
96                                idx: (idx as u32).into(),
97                                tpe: arg.tpe(),
98                            })
99                            .collect(),
100                        body,
101                    )
102                    .into();
103                    Self::new(func, args).unwrap()
104                })
105                .boxed()
106        }
107    }
108}
109
110#[cfg(test)]
111#[allow(clippy::panic)]
112#[allow(clippy::unwrap_used)]
113mod tests {
114
115    use crate::serialization::sigma_serialize_roundtrip;
116
117    use super::*;
118
119    use proptest::prelude::*;
120
121    proptest! {
122
123        #[test]
124        fn ser_roundtrip(v in any::<Apply>()) {
125            let expr: Expr = v.into();
126            prop_assert_eq![sigma_serialize_roundtrip(&expr), expr];
127        }
128    }
129}