ergotree_ir/mir/
expr.rs

1//! IR expression
2
3use std::convert::TryFrom;
4use std::convert::TryInto;
5
6use crate::pretty_printer::PosTrackingWriter;
7use crate::pretty_printer::Print;
8use crate::source_span::Spanned;
9use crate::types::stype::LiftIntoSType;
10use crate::types::stype::SType;
11
12use super::and::And;
13use super::apply::Apply;
14use super::bin_op::BinOp;
15use super::bit_inversion::BitInversion;
16use super::block::BlockValue;
17use super::bool_to_sigma::BoolToSigmaProp;
18use super::byte_array_to_long::ByteArrayToLong;
19use super::calc_blake2b256::CalcBlake2b256;
20use super::calc_sha256::CalcSha256;
21use super::coll_append::Append;
22use super::coll_by_index::ByIndex;
23use super::coll_exists::Exists;
24use super::coll_filter::Filter;
25use super::coll_fold::Fold;
26use super::coll_forall::ForAll;
27use super::coll_map::Map;
28use super::coll_size::SizeOf;
29use super::coll_slice::Slice;
30use super::collection::Collection;
31use super::constant::Constant;
32use super::constant::ConstantPlaceholder;
33use super::constant::Literal;
34use super::constant::TryExtractFrom;
35use super::constant::TryExtractFromError;
36use super::create_avl_tree::CreateAvlTree;
37use super::create_provedlog::CreateProveDlog;
38use super::decode_point::DecodePoint;
39use super::exponentiate::Exponentiate;
40use super::extract_amount::ExtractAmount;
41use super::extract_bytes::ExtractBytes;
42use super::extract_bytes_with_no_ref::ExtractBytesWithNoRef;
43use super::extract_creation_info::ExtractCreationInfo;
44use super::extract_id::ExtractId;
45use super::extract_reg_as::ExtractRegisterAs;
46use super::extract_script_bytes::ExtractScriptBytes;
47use super::func_value::FuncValue;
48use super::global_vars::GlobalVars;
49use super::if_op::If;
50use super::logical_not::LogicalNot;
51use super::long_to_byte_array::LongToByteArray;
52use super::method_call::MethodCall;
53use super::multiply_group::MultiplyGroup;
54use super::negation::Negation;
55use super::option_get::OptionGet;
56use super::option_get_or_else::OptionGetOrElse;
57use super::option_is_defined::OptionIsDefined;
58use super::or::Or;
59use super::property_call::PropertyCall;
60use super::select_field::SelectField;
61use super::sigma_and::SigmaAnd;
62use super::sigma_or::SigmaOr;
63use super::sigma_prop_bytes::SigmaPropBytes;
64use super::subst_const::SubstConstants;
65use super::tree_lookup::TreeLookup;
66use super::tuple::Tuple;
67use super::upcast::Upcast;
68use super::val_def::ValDef;
69use super::val_use::ValUse;
70use super::xor::Xor;
71
72extern crate derive_more;
73use crate::mir::atleast::Atleast;
74use crate::mir::byte_array_to_bigint::ByteArrayToBigInt;
75use crate::mir::create_prove_dh_tuple::CreateProveDhTuple;
76use crate::mir::deserialize_context::DeserializeContext;
77use crate::mir::deserialize_register::DeserializeRegister;
78use crate::mir::downcast::Downcast;
79use crate::mir::get_var::GetVar;
80use crate::mir::xor_of::XorOf;
81use bounded_vec::BoundedVecOutOfBounds;
82use derive_more::From;
83use derive_more::TryInto;
84use thiserror::Error;
85
86#[derive(PartialEq, Eq, Debug, Clone, From, TryInto)]
87/// Expression in ErgoTree
88pub enum Expr {
89    /// Append - Concatenation of two collections
90    Append(Spanned<Append>),
91    /// Constant value
92    Const(Constant),
93    /// Placeholder for a constant
94    ConstPlaceholder(ConstantPlaceholder),
95    /// Substitute constants in serialized ergo tree
96    SubstConstants(Spanned<SubstConstants>),
97    /// Convert byte array to SLong
98    ByteArrayToLong(Spanned<ByteArrayToLong>),
99    /// Convert byte array to SLong
100    ByteArrayToBigInt(Spanned<ByteArrayToBigInt>),
101    /// Convert SLong to a byte array
102    LongToByteArray(LongToByteArray),
103    /// Collection declaration (array of expressions of the same type)
104    Collection(Collection),
105    /// Tuple declaration
106    Tuple(Tuple),
107    /// Predefined functions (global)
108    /// Blake2b256 hash calculation
109    CalcBlake2b256(CalcBlake2b256),
110    /// Sha256 hash calculation
111    CalcSha256(CalcSha256),
112    /// Context variables (external)
113    Context,
114    /// Special global value which is used to define methods
115    Global,
116    /// Predefined global variables
117    GlobalVars(GlobalVars),
118    /// Function definition
119    FuncValue(FuncValue),
120    /// Function application
121    Apply(Apply),
122    /// Method call
123    MethodCall(Spanned<MethodCall>),
124    /// Property call
125    PropertyCall(Spanned<PropertyCall>),
126    /// Block (statements, followed by an expression)
127    BlockValue(Spanned<BlockValue>),
128    /// let-bound expression
129    ValDef(Spanned<ValDef>),
130    /// Reference to ValDef
131    ValUse(ValUse),
132    /// If, non-lazy - evaluate both branches
133    If(If),
134    /// Binary operation
135    BinOp(Spanned<BinOp>),
136    /// Logical AND
137    And(Spanned<And>),
138    /// Logical OR
139    Or(Spanned<Or>),
140    /// Byte-wise XOR
141    Xor(Xor),
142    /// THRESHOLD composition for sigma expressions
143    Atleast(Atleast),
144    /// LogicalNot
145    LogicalNot(Spanned<LogicalNot>),
146    /// Negation on numeric type
147    Negation(Spanned<Negation>),
148    /// Bit inversion on numeric type
149    BitInversion(BitInversion),
150    /// Option.get method
151    OptionGet(Spanned<OptionGet>),
152    /// Option.isDefined method
153    OptionIsDefined(Spanned<OptionIsDefined>),
154    /// Returns the option's value if the option is nonempty, otherwise return the result of evaluating `default`.
155    OptionGetOrElse(Spanned<OptionGetOrElse>),
156    /// Box monetary value
157    ExtractAmount(ExtractAmount),
158    /// Extract register's value (box.RX properties)
159    ExtractRegisterAs(Spanned<ExtractRegisterAs>),
160    /// Extract serialized box bytes
161    ExtractBytes(ExtractBytes),
162    /// Extract serialized box bytes excluding transaction_id & index
163    ExtractBytesWithNoRef(ExtractBytesWithNoRef),
164    /// Extract box's guarding script serialized to bytes
165    ExtractScriptBytes(ExtractScriptBytes),
166    /// Tuple of height when block got included into the blockchain and transaction identifier with
167    /// box index in the transaction outputs serialized to the byte array.
168    ExtractCreationInfo(ExtractCreationInfo),
169    /// Box id, Blake2b256 hash of this box's content, basically equals to `blake2b256(bytes)`
170    ExtractId(ExtractId),
171    /// Collection, get element by index
172    ByIndex(Spanned<ByIndex>),
173    /// Collection size
174    SizeOf(SizeOf),
175    /// Collection slice
176    Slice(Spanned<Slice>),
177    /// Collection fold op
178    Fold(Spanned<Fold>),
179    /// Collection map op
180    Map(Spanned<Map>),
181    /// Collection filter op
182    Filter(Spanned<Filter>),
183    /// Tests whether a predicate holds for at least one element of this collection
184    Exists(Spanned<Exists>),
185    /// Tests whether a predicate holds for all elements of this collection.
186    ForAll(Spanned<ForAll>),
187    /// Tuple field access
188    SelectField(Spanned<SelectField>),
189    /// Bool to SigmaProp
190    BoolToSigmaProp(BoolToSigmaProp),
191    /// Upcast numeric value
192    Upcast(Upcast),
193    /// Downcast numeric value
194    Downcast(Downcast),
195    /// Create proveDlog from GroupElement(PK)
196    CreateProveDlog(CreateProveDlog),
197    /// Create proveDlog from GroupElement(PK)
198    CreateProveDhTuple(CreateProveDhTuple),
199    /// Extract serialized bytes of a SigmaProp value
200    SigmaPropBytes(SigmaPropBytes),
201    /// Decode byte array to EC point
202    DecodePoint(DecodePoint),
203    /// AND conjunction for sigma propositions
204    SigmaAnd(SigmaAnd),
205    /// OR conjunction for sigma propositions
206    SigmaOr(SigmaOr),
207    /// Extracts Context variable by id and type
208    GetVar(Spanned<GetVar>),
209    /// Extract register of SELF box as `Coll[Byte]`, deserialize it into Value and inline into
210    /// the executing script.
211    DeserializeRegister(DeserializeRegister),
212    /// Extracts context variable as `Coll[Byte]`, deserializes it to script and then executes
213    /// this script in the current context. The original `Coll[Byte]` of the script is
214    /// available as `getVar[Coll[Byte]](id)` On evaluation returns the result of the
215    /// script execution in the current context
216    DeserializeContext(DeserializeContext),
217    /// MultiplyGroup op for GroupElement
218    MultiplyGroup(MultiplyGroup),
219    /// Exponentiate op for GroupElement
220    Exponentiate(Exponentiate),
221    /// XOR for collection of booleans
222    XorOf(XorOf),
223    /// Perform a lookup by key in an AVL tree
224    TreeLookup(Spanned<TreeLookup>),
225    /// Create an AVL tree
226    CreateAvlTree(CreateAvlTree),
227}
228
229impl Expr {
230    /// Type of the expression
231    pub fn tpe(&self) -> SType {
232        match self {
233            Expr::Append(ap) => ap.expr().tpe(),
234            Expr::Const(v) => v.tpe.clone(),
235            Expr::Collection(v) => v.tpe(),
236            Expr::SubstConstants(v) => v.expr().tpe(),
237            Expr::ByteArrayToLong(v) => v.expr().tpe(),
238            Expr::ByteArrayToBigInt(v) => v.expr().tpe(),
239            Expr::LongToByteArray(v) => v.tpe(),
240            Expr::ConstPlaceholder(v) => v.tpe.clone(),
241            Expr::CalcBlake2b256(v) => v.tpe(),
242            Expr::CalcSha256(v) => v.tpe(),
243            Expr::Global => SType::SGlobal,
244            Expr::Context => SType::SContext,
245            Expr::GlobalVars(v) => v.tpe(),
246            Expr::FuncValue(v) => v.tpe(),
247            Expr::Apply(v) => v.tpe(),
248            Expr::MethodCall(v) => v.expr().tpe(),
249            Expr::PropertyCall(v) => v.expr().tpe(),
250            Expr::BlockValue(v) => v.expr().tpe(),
251            Expr::ValDef(v) => v.expr().tpe(),
252            Expr::ValUse(v) => v.tpe.clone(),
253            Expr::BinOp(v) => v.expr().tpe(),
254            Expr::OptionGet(v) => v.expr().tpe(),
255            Expr::ExtractRegisterAs(v) => v.expr().tpe(),
256            Expr::Fold(v) => v.expr().tpe(),
257            Expr::SelectField(v) => v.expr().tpe(),
258            Expr::ExtractAmount(v) => v.tpe(),
259            Expr::And(v) => v.expr().tpe(),
260            Expr::Or(v) => v.expr().tpe(),
261            Expr::Xor(v) => v.tpe(),
262            Expr::Atleast(v) => v.tpe(),
263            Expr::LogicalNot(v) => v.expr().tpe(),
264            Expr::Map(v) => v.expr().tpe(),
265            Expr::Filter(v) => v.expr().tpe(),
266            Expr::BoolToSigmaProp(v) => v.tpe(),
267            Expr::Upcast(v) => v.tpe(),
268            Expr::Downcast(v) => v.tpe(),
269            Expr::If(v) => v.tpe(),
270            Expr::ByIndex(v) => v.expr().tpe(),
271            Expr::ExtractScriptBytes(v) => v.tpe(),
272            Expr::SizeOf(v) => v.tpe(),
273            Expr::Slice(v) => v.expr().tpe(),
274            Expr::CreateProveDlog(v) => v.tpe(),
275            Expr::CreateProveDhTuple(v) => v.tpe(),
276            Expr::ExtractCreationInfo(v) => v.tpe(),
277            Expr::Exists(v) => v.expr().tpe(),
278            Expr::ExtractId(v) => v.tpe(),
279            Expr::SigmaPropBytes(v) => v.tpe(),
280            Expr::OptionIsDefined(v) => v.expr().tpe(),
281            Expr::OptionGetOrElse(v) => v.expr().tpe(),
282            Expr::Negation(v) => v.expr().tpe(),
283            Expr::BitInversion(v) => v.tpe(),
284            Expr::ForAll(v) => v.expr().tpe(),
285            Expr::Tuple(v) => v.tpe(),
286            Expr::DecodePoint(v) => v.tpe(),
287            Expr::SigmaAnd(v) => v.tpe(),
288            Expr::SigmaOr(v) => v.tpe(),
289            Expr::DeserializeRegister(v) => v.tpe(),
290            Expr::DeserializeContext(v) => v.tpe(),
291            Expr::GetVar(v) => v.expr().tpe(),
292            Expr::MultiplyGroup(v) => v.tpe(),
293            Expr::Exponentiate(v) => v.tpe(),
294            Expr::XorOf(v) => v.tpe(),
295            Expr::ExtractBytes(v) => v.tpe(),
296            Expr::ExtractBytesWithNoRef(v) => v.tpe(),
297            Expr::TreeLookup(v) => v.expr().tpe(),
298            Expr::CreateAvlTree(v) => v.tpe(),
299        }
300    }
301
302    /// Type expected after the evaluation
303    pub fn post_eval_tpe(&self) -> SType {
304        match self.tpe() {
305            SType::SFunc(sfunc) => *sfunc.t_range,
306            tpe => tpe,
307        }
308    }
309
310    /// Check if given expected_tpe type is the same as the expression's post-evaluation type
311    pub fn check_post_eval_tpe(
312        &self,
313        expected_tpe: &SType,
314    ) -> Result<(), InvalidExprEvalTypeError> {
315        let expr_tpe = self.post_eval_tpe();
316        if &expr_tpe == expected_tpe {
317            Ok(())
318        } else {
319            use std::backtrace::Backtrace;
320            let backtrace = Backtrace::capture();
321            Err(InvalidExprEvalTypeError(format!(
322                "expected: {0:?}, got: {1:?}\nBacktrace:\n{backtrace}",
323                expected_tpe, expr_tpe
324            )))
325        }
326    }
327
328    /// Prints the tree with newlines
329    pub fn debug_tree(&self) -> String {
330        let tree = format!("{:#?}", self);
331        tree
332    }
333
334    /// Pretty prints the tree
335    pub fn to_string_pretty(&self) -> String {
336        let mut printer = PosTrackingWriter::new();
337        #[allow(clippy::unwrap_used)] // it only fail due to formatting errors
338        let _spanned_expr = self.print(&mut printer).unwrap();
339        printer.as_string()
340    }
341}
342
343impl<T: Into<Literal> + LiftIntoSType> From<T> for Expr {
344    fn from(t: T) -> Self {
345        Expr::Const(Constant {
346            tpe: T::stype(),
347            v: t.into(),
348        })
349    }
350}
351
352/// Unexpected argument on node construction (i.e non-Option input in OptionGet)
353#[derive(Error, PartialEq, Eq, Debug, Clone)]
354#[error("InvalidArgumentError: {0}")]
355pub struct InvalidArgumentError(pub String);
356
357/// Invalid (unexpected) expr type
358#[derive(PartialEq, Eq, Debug, Clone, Error)]
359#[error("InvalidExprEvalTypeError: {0}")]
360pub struct InvalidExprEvalTypeError(pub String);
361
362impl From<InvalidExprEvalTypeError> for InvalidArgumentError {
363    fn from(e: InvalidExprEvalTypeError) -> Self {
364        InvalidArgumentError(format!("InvalidExprEvalTypeError: {0}", e))
365    }
366}
367
368impl From<BoundedVecOutOfBounds> for InvalidArgumentError {
369    fn from(e: BoundedVecOutOfBounds) -> Self {
370        InvalidArgumentError(format!("BoundedVecOutOfBounds: {0}", e))
371    }
372}
373
374impl<T: TryFrom<Expr>> TryExtractFrom<Expr> for T {
375    fn try_extract_from(v: Expr) -> Result<Self, TryExtractFromError> {
376        let res: Result<Self, TryExtractFromError> = v.clone().try_into().map_err(|_| {
377            TryExtractFromError(format!(
378                "Cannot extract {0:?} from {1:?}",
379                std::any::type_name::<T>(),
380                v
381            ))
382        });
383        res
384    }
385}
386
387#[cfg(feature = "arbitrary")]
388#[allow(clippy::unwrap_used)]
389#[allow(clippy::panic)]
390#[allow(clippy::todo)]
391/// Arbitrary impl
392pub(crate) mod arbitrary {
393    use super::*;
394    use crate::mir::func_value::FuncArg;
395    use crate::sigma_protocol::sigma_boolean::ProveDlog;
396    use crate::types::sfunc::SFunc;
397    use proptest::collection::*;
398    use proptest::prelude::*;
399    use std::sync::Arc;
400
401    /// Parameters for arbitrary Expr generation
402    #[derive(PartialEq, Eq, Debug, Clone)]
403    pub struct ArbExprParams {
404        /// Expr type
405        pub tpe: SType,
406        /// Expr tree depth (levels)
407        pub depth: usize,
408    }
409
410    impl Default for ArbExprParams {
411        fn default() -> Self {
412            ArbExprParams {
413                tpe: SType::SBoolean,
414                depth: 1,
415            }
416        }
417    }
418
419    fn numeric_nested_expr(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
420        prop_oneof![any_with::<BinOp>(ArbExprParams {
421            tpe: elem_tpe.clone(),
422            depth
423        })
424        .prop_map_into(),]
425        .boxed()
426    }
427
428    fn bool_nested_expr(depth: usize) -> BoxedStrategy<Expr> {
429        prop_oneof![
430            any_with::<BinOp>(ArbExprParams {
431                tpe: SType::SBoolean,
432                depth
433            })
434            .prop_map_into(),
435            any_with::<And>(depth).prop_map_into(),
436            any_with::<Or>(depth).prop_map_into(),
437            any_with::<LogicalNot>(depth).prop_map_into(),
438        ]
439        .boxed()
440    }
441
442    fn coll_nested_numeric(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
443        let ty = elem_tpe.clone();
444        vec(numeric_nested_expr(depth, elem_tpe), 0..10)
445            .prop_map(move |items| Collection::new(ty.clone(), items).unwrap())
446            .prop_map_into()
447            .boxed()
448    }
449
450    fn sigma_prop_nested_expr(_depth: usize) -> BoxedStrategy<Expr> {
451        prop_oneof![
452            any::<ProveDlog>().prop_map(|pk| Expr::Const(pk.into())),
453            any::<SigmaAnd>().prop_map_into(),
454            any::<SigmaOr>().prop_map_into(),
455        ]
456        .boxed()
457    }
458
459    fn coll_nested_expr(depth: usize, elem_tpe: &SType) -> BoxedStrategy<Expr> {
460        match elem_tpe {
461            SType::SBoolean => vec(bool_nested_expr(depth), 0..10)
462                .prop_map(|items| Collection::new(SType::SBoolean, items).unwrap())
463                .prop_map_into()
464                .boxed(),
465            SType::SByte => coll_nested_numeric(depth, elem_tpe),
466            SType::SShort => coll_nested_numeric(depth, elem_tpe),
467            SType::SInt => coll_nested_numeric(depth, elem_tpe),
468            SType::SLong => coll_nested_numeric(depth, elem_tpe),
469            SType::SBigInt => coll_nested_numeric(depth, elem_tpe),
470
471            SType::STypeVar(_) => prop_oneof![
472                vec(bool_nested_expr(depth), 0..10).prop_map(|items| Collection::new(
473                    SType::SBoolean,
474                    items
475                )
476                .unwrap()),
477                vec(numeric_nested_expr(depth, &SType::SInt), 0..10)
478                    .prop_map(|items| Collection::new(SType::SInt, items).unwrap())
479            ]
480            .prop_map_into()
481            .boxed(),
482            SType::SSigmaProp => vec(sigma_prop_nested_expr(depth), 0..10)
483                .prop_map(|items| Collection::new(SType::SSigmaProp, items).unwrap())
484                .prop_map_into()
485                .boxed(),
486
487            _ => panic!("Nested expression not implemented for {:?}", &elem_tpe),
488        }
489    }
490
491    fn any_nested_expr(depth: usize) -> BoxedStrategy<Expr> {
492        prop_oneof![
493            bool_nested_expr(depth),
494            numeric_nested_expr(depth, &SType::SByte),
495            numeric_nested_expr(depth, &SType::SShort),
496            numeric_nested_expr(depth, &SType::SInt),
497            numeric_nested_expr(depth, &SType::SLong),
498            numeric_nested_expr(depth, &SType::SBigInt),
499        ]
500        .boxed()
501    }
502
503    fn nested_expr(tpe: SType, depth: usize) -> BoxedStrategy<Expr> {
504        match tpe {
505            SType::SAny => any_nested_expr(depth),
506            SType::SBoolean => bool_nested_expr(depth),
507            SType::SByte => numeric_nested_expr(depth, &tpe),
508            SType::SShort => numeric_nested_expr(depth, &tpe),
509            SType::SInt => numeric_nested_expr(depth, &tpe),
510            SType::SLong => numeric_nested_expr(depth, &tpe),
511            SType::SBigInt => numeric_nested_expr(depth, &tpe),
512            SType::SColl(elem_type) => coll_nested_expr(depth, elem_type.as_ref()),
513            SType::SSigmaProp => sigma_prop_nested_expr(depth),
514            _ => todo!("nested expr is not implemented for type: {:?}", tpe),
515        }
516        .boxed()
517    }
518
519    fn int_non_nested_expr() -> BoxedStrategy<Expr> {
520        prop_oneof![Just(GlobalVars::Height.into()),].boxed()
521    }
522
523    fn constant(tpe: &SType) -> BoxedStrategy<Expr> {
524        any_with::<Constant>(tpe.clone().into())
525            .prop_map_into()
526            .boxed()
527    }
528
529    fn bool_non_nested_expr() -> BoxedStrategy<Expr> {
530        prop_oneof![any_with::<Constant>(SType::SBoolean.into()).prop_map_into()].boxed()
531    }
532
533    fn any_non_nested_expr() -> BoxedStrategy<Expr> {
534        prop_oneof![int_non_nested_expr(), bool_non_nested_expr()].boxed()
535    }
536
537    fn coll_non_nested_expr(elem_tpe: &SType) -> BoxedStrategy<Expr> {
538        match elem_tpe {
539            SType::SBoolean => any_with::<Constant>(SType::SColl(Arc::new(SType::SBoolean)).into())
540                .prop_map(Expr::Const)
541                .boxed(),
542            SType::SByte => any_with::<Constant>(SType::SColl(Arc::new(SType::SByte)).into())
543                .prop_map(Expr::Const)
544                .boxed(),
545            SType::SShort => any_with::<Constant>(SType::SColl(Arc::new(SType::SShort)).into())
546                .prop_map(Expr::Const)
547                .boxed(),
548            SType::SInt => any_with::<Constant>(SType::SColl(Arc::new(SType::SInt)).into())
549                .prop_map(Expr::Const)
550                .boxed(),
551            SType::SLong => any_with::<Constant>(SType::SColl(Arc::new(SType::SLong)).into())
552                .prop_map(Expr::Const)
553                .boxed(),
554            _ => todo!("Collection of {0:?} is not yet implemented", elem_tpe),
555        }
556    }
557
558    fn non_nested_expr(tpe: &SType) -> BoxedStrategy<Expr> {
559        match tpe {
560            SType::SAny => any_non_nested_expr(),
561            SType::SInt => int_non_nested_expr(),
562            SType::SBoolean => bool_non_nested_expr(),
563            SType::SColl(elem_type) => coll_non_nested_expr(elem_type),
564            t => constant(t),
565        }
566    }
567
568    fn sfunc_expr(sfunc: SFunc) -> BoxedStrategy<Expr> {
569        match (sfunc.t_dom.first().unwrap(), *sfunc.t_range) {
570            (SType::SBoolean, SType::SBoolean) => any_with::<Expr>(ArbExprParams {
571                tpe: SType::SBoolean,
572                depth: 2,
573            })
574            .prop_map(|expr| {
575                Expr::FuncValue(FuncValue::new(
576                    vec![FuncArg {
577                        idx: 1.into(),
578                        tpe: SType::SBoolean,
579                    }],
580                    expr,
581                ))
582            })
583            .boxed(),
584            _ => todo!(),
585        }
586    }
587
588    impl Arbitrary for Expr {
589        type Parameters = ArbExprParams;
590        type Strategy = BoxedStrategy<Self>;
591
592        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
593            if args.depth == 0 {
594                match args.tpe {
595                    SType::SFunc(sfunc) => sfunc_expr(sfunc),
596                    _ => prop_oneof![
597                        any_with::<Constant>(args.tpe.clone().into())
598                            .prop_map(Expr::Const)
599                            .boxed(),
600                        non_nested_expr(&args.tpe)
601                    ]
602                    .boxed(),
603                }
604            } else {
605                nested_expr(args.tpe, args.depth - 1)
606            }
607        }
608    }
609}
610
611#[cfg(test)]
612mod tests {}