tx3_lang/
applying.rs

1use std::collections::{BTreeMap, HashMap, HashSet};
2
3use crate::ir::Expression;
4use crate::{backend, ir, ArgValue, CanonicalAssets, Utxo};
5
6#[derive(Debug, thiserror::Error)]
7pub enum Error {
8    #[error("invalid built-in operation {0:?}")]
9    InvalidBuiltInOp(Box<ir::BuiltInOp>),
10
11    #[error(transparent)]
12    BackendError(#[from] backend::Error),
13
14    #[error("invalid argument {0:?} for {1}")]
15    InvalidArgument(ArgValue, String),
16
17    #[error("property {0} not found in {1}")]
18    PropertyNotFound(String, String),
19
20    #[error("property index {0} not found in {1}")]
21    PropertyIndexNotFound(usize, String),
22
23    #[error("invalid {0} operation over {1:?} and {2:?}")]
24    InvalidBinaryOp(String, String, String),
25
26    #[error("invalid {0} operation over {1:?}")]
27    InvalidUnaryOp(String, String),
28
29    #[error("cannot coerce {0:?} into assets")]
30    CannotCoerceIntoAssets(ir::Expression),
31
32    #[error("cannot coerce {0:?} into datum")]
33    CannotCoerceIntoDatum(ir::Expression),
34}
35
36pub trait Indexable: std::fmt::Debug {
37    fn index(&self, index: ir::Expression) -> Option<ir::Expression>;
38
39    fn index_or_err(&self, index: ir::Expression) -> Result<ir::Expression, Error> {
40        let index_value = index.as_number().unwrap_or(0) as usize;
41        self.index(index).ok_or(Error::PropertyIndexNotFound(
42            index_value,
43            format!("{self:?}"),
44        ))
45    }
46}
47
48impl Indexable for ir::StructExpr {
49    fn index(&self, index: ir::Expression) -> Option<ir::Expression> {
50        // numeric indices represent the index of the field of the struct
51        match index {
52            ir::Expression::Number(n) => self.fields.get(n as usize).cloned(),
53            _ => return None,
54        }
55    }
56}
57
58impl Indexable for ir::Expression {
59    fn index(&self, index: ir::Expression) -> Option<ir::Expression> {
60        match self {
61            ir::Expression::None => None,
62            ir::Expression::Map(x) => x
63                .iter()
64                .find(|(k, _)| *k == index)
65                .map(|(k, v)| ir::Expression::Tuple(Box::new((k.clone(), v.clone())))),
66            ir::Expression::List(x) => x.get(index.as_number()? as usize).cloned(),
67            ir::Expression::Tuple(x) => match index.as_number()? {
68                0 => Some(x.0.clone()),
69                1 => Some(x.1.clone()),
70                _ => None,
71            },
72            ir::Expression::Struct(x) => x.index(index.clone()),
73            _ => None,
74        }
75    }
76}
77
78pub trait Concatenable {
79    fn concat(self, other: ir::Expression) -> Result<ir::Expression, Error>;
80}
81
82pub trait Arithmetic {
83    fn add(self, other: ir::Expression) -> Result<ir::Expression, Error>;
84    fn sub(self, other: ir::Expression) -> Result<ir::Expression, Error>;
85    fn neg(self) -> Result<ir::Expression, Error>;
86}
87
88impl<T> Arithmetic for T
89where
90    T: Into<CanonicalAssets> + std::fmt::Debug,
91{
92    fn add(self, other: ir::Expression) -> Result<ir::Expression, Error> {
93        let y = match other {
94            ir::Expression::Assets(x) => CanonicalAssets::from(x),
95            ir::Expression::None => CanonicalAssets::empty(),
96            other => {
97                return Err(Error::InvalidBinaryOp(
98                    "add".to_string(),
99                    format!("{self:?}"),
100                    format!("{other:?}"),
101                ))
102            }
103        };
104
105        let x = self.into();
106        let total = x + y;
107        Ok(ir::Expression::Assets(total.into()))
108    }
109
110    fn sub(self, other: ir::Expression) -> Result<ir::Expression, Error> {
111        let other_neg = other.neg()?;
112        self.add(other_neg)
113    }
114
115    fn neg(self) -> Result<ir::Expression, Error> {
116        let negated = std::ops::Neg::neg(self.into());
117        Ok(ir::Expression::Assets(negated.into()))
118    }
119}
120
121impl Arithmetic for i128 {
122    fn add(self, other: ir::Expression) -> Result<ir::Expression, Error> {
123        match other {
124            ir::Expression::Number(y) => Ok(ir::Expression::Number(self + y)),
125            ir::Expression::None => Ok(ir::Expression::Number(self)),
126            _ => Err(Error::InvalidBinaryOp(
127                "add".to_string(),
128                format!("{self:?}"),
129                format!("{other:?}"),
130            )),
131        }
132    }
133
134    fn sub(self, other: ir::Expression) -> Result<ir::Expression, Error> {
135        let other_neg = other.neg()?;
136        self.add(other_neg)
137    }
138
139    fn neg(self) -> Result<ir::Expression, Error> {
140        Ok(ir::Expression::Number(-self))
141    }
142}
143
144impl Arithmetic for ir::Expression {
145    fn add(self, other: ir::Expression) -> Result<ir::Expression, Error> {
146        match self {
147            ir::Expression::None => Ok(other),
148            ir::Expression::Number(x) => Arithmetic::add(x, other),
149            ir::Expression::Assets(x) => Arithmetic::add(x, other),
150            x => Err(Error::InvalidBinaryOp(
151                "add".to_string(),
152                format!("{x:?}"),
153                format!("{other:?}"),
154            )),
155        }
156    }
157
158    fn sub(self, other: ir::Expression) -> Result<ir::Expression, Error> {
159        match self {
160            ir::Expression::None => Ok(other),
161            ir::Expression::Number(x) => Arithmetic::sub(x, other),
162            ir::Expression::Assets(x) => Arithmetic::sub(x, other),
163            x => Err(Error::InvalidBinaryOp(
164                "sub".to_string(),
165                format!("{x:?}"),
166                format!("{other:?}"),
167            )),
168        }
169    }
170
171    fn neg(self) -> Result<ir::Expression, Error> {
172        match self {
173            ir::Expression::None => Ok(ir::Expression::None),
174            ir::Expression::Number(x) => Arithmetic::neg(x),
175            ir::Expression::Assets(x) => Arithmetic::neg(x),
176            x => Err(Error::InvalidUnaryOp("neg".to_string(), format!("{x:?}"))),
177        }
178    }
179}
180
181impl Concatenable for String {
182    fn concat(self, other: ir::Expression) -> Result<ir::Expression, Error> {
183        match other {
184            ir::Expression::String(y) => Ok(ir::Expression::String(self + &y)),
185            ir::Expression::None => Ok(ir::Expression::String(self)),
186            _ => Err(Error::InvalidBinaryOp(
187                "concat".to_string(),
188                format!("String({self:?})"),
189                format!("{other:?}"),
190            )),
191        }
192    }
193}
194
195impl Concatenable for Vec<Expression> {
196    fn concat(self, other: Expression) -> Result<Expression, Error> {
197        match other {
198            Expression::List(expressions) => {
199                Ok(Expression::List([&self[..], &expressions[..]].concat()))
200            }
201            _ => Err(Error::InvalidBinaryOp(
202                "concat".to_string(),
203                format!("List({:?})", self),
204                format!("{:?}", other),
205            )),
206        }
207    }
208}
209
210impl Concatenable for Vec<u8> {
211    fn concat(self, other: ir::Expression) -> Result<ir::Expression, Error> {
212        match other {
213            ir::Expression::Bytes(y) => {
214                let mut result = self;
215                result.extend(y);
216                Ok(ir::Expression::Bytes(result))
217            }
218            ir::Expression::None => Ok(ir::Expression::Bytes(self)),
219            _ => Err(Error::InvalidBinaryOp(
220                "concat".to_string(),
221                format!("Bytes({self:?})"),
222                format!("{other:?}"),
223            )),
224        }
225    }
226}
227
228impl Concatenable for ir::Expression {
229    fn concat(self, other: ir::Expression) -> Result<ir::Expression, Error> {
230        match self {
231            ir::Expression::None => Ok(other),
232            ir::Expression::String(x) => Concatenable::concat(x, other),
233            ir::Expression::Bytes(x) => Concatenable::concat(x, other),
234            ir::Expression::List(x) => Concatenable::concat(x, other),
235            x => Err(Error::InvalidBinaryOp(
236                "concat".to_string(),
237                format!("{x:?}"),
238                format!("{other:?}"),
239            )),
240        }
241    }
242}
243
244pub trait Coerceable {
245    fn into_assets(self) -> Result<ir::Expression, Error>;
246    fn into_datum(self) -> Result<ir::Expression, Error>;
247}
248
249impl Coerceable for ir::Expression {
250    fn into_assets(self) -> Result<ir::Expression, Error> {
251        match self {
252            ir::Expression::None => Ok(ir::Expression::None),
253            ir::Expression::Assets(x) => Ok(ir::Expression::Assets(x)),
254            ir::Expression::UtxoSet(x) => {
255                let all = x
256                    .into_iter()
257                    .map(|x| x.assets)
258                    .fold(CanonicalAssets::empty(), |acc, x| acc + x);
259
260                Ok(ir::Expression::Assets(all.into()))
261            }
262            _ => Err(Error::CannotCoerceIntoAssets(self)),
263        }
264    }
265
266    fn into_datum(self) -> Result<ir::Expression, Error> {
267        match self {
268            ir::Expression::None => Ok(ir::Expression::None),
269            ir::Expression::UtxoSet(x) => Ok(x
270                .into_iter()
271                .next()
272                .and_then(|x| x.datum)
273                .unwrap_or(ir::Expression::None)),
274            ir::Expression::List(x) => Ok(ir::Expression::List(x)),
275            ir::Expression::Map(x) => Ok(ir::Expression::Map(x)),
276            ir::Expression::Tuple(x) => Ok(ir::Expression::Tuple(x)),
277            ir::Expression::Struct(x) => Ok(ir::Expression::Struct(x)),
278            ir::Expression::Bytes(x) => Ok(ir::Expression::Bytes(x)),
279            ir::Expression::Number(x) => Ok(ir::Expression::Number(x)),
280            ir::Expression::String(x) => Ok(ir::Expression::String(x)),
281            ir::Expression::Address(x) => Ok(ir::Expression::Bytes(x)),
282            ir::Expression::Hash(x) => Ok(ir::Expression::Bytes(x)),
283            _ => Err(Error::CannotCoerceIntoDatum(self)),
284        }
285    }
286}
287
288fn arg_value_into_expr(arg: ArgValue) -> ir::Expression {
289    match arg {
290        ArgValue::Address(x) => ir::Expression::Address(x),
291        ArgValue::Int(x) => ir::Expression::Number(x),
292        ArgValue::Bool(x) => ir::Expression::Bool(x),
293        ArgValue::String(x) => ir::Expression::String(x),
294        ArgValue::Bytes(x) => ir::Expression::Bytes(x),
295        ArgValue::UtxoSet(x) => ir::Expression::UtxoSet(x),
296        ArgValue::UtxoRef(x) => ir::Expression::UtxoRefs(vec![x]),
297    }
298}
299
300pub trait Apply: Sized + std::fmt::Debug {
301    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error>;
302    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error>;
303    fn apply_fees(self, fees: u64) -> Result<Self, Error>;
304
305    fn is_constant(&self) -> bool;
306
307    fn params(&self) -> BTreeMap<String, ir::Type>;
308    fn queries(&self) -> BTreeMap<String, ir::InputQuery>;
309
310    fn reduce(self) -> Result<Self, Error>;
311}
312
313pub trait Composite: Sized {
314    fn reduce_self(self) -> Result<Self, Error> {
315        Ok(self)
316    }
317
318    fn components(&self) -> Vec<&ir::Expression>;
319
320    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
321    where
322        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone;
323
324    fn reduce_nested(self) -> Result<Self, Error> {
325        self.try_map_components(|x| x.reduce())
326    }
327}
328
329impl<T> Apply for T
330where
331    T: Composite + std::fmt::Debug,
332{
333    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
334        self.try_map_components(|x| x.apply_args(args))
335    }
336
337    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
338        self.try_map_components(|x| x.apply_inputs(args))
339    }
340
341    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
342        self.try_map_components(|x| x.apply_fees(fees))
343    }
344
345    fn is_constant(&self) -> bool {
346        self.components().iter().all(|x| x.is_constant())
347    }
348
349    fn params(&self) -> BTreeMap<String, ir::Type> {
350        self.components().iter().flat_map(|x| x.params()).collect()
351    }
352
353    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
354        self.components().iter().flat_map(|x| x.queries()).collect()
355    }
356
357    fn reduce(self) -> Result<Self, Error> {
358        let x = self.reduce_nested()?;
359
360        if x.is_constant() {
361            x.reduce_self()
362        } else {
363            Ok(x)
364        }
365    }
366}
367
368impl<T> Apply for Option<T>
369where
370    T: Apply,
371{
372    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
373        self.map(|x| x.apply_args(args)).transpose()
374    }
375
376    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
377        self.map(|x| x.apply_inputs(args)).transpose()
378    }
379
380    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
381        self.map(|x| x.apply_fees(fees)).transpose()
382    }
383
384    fn is_constant(&self) -> bool {
385        match self {
386            Some(x) => x.is_constant(),
387            None => true,
388        }
389    }
390
391    fn params(&self) -> BTreeMap<String, ir::Type> {
392        match self {
393            Some(x) => x.params(),
394            None => BTreeMap::new(),
395        }
396    }
397
398    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
399        match self {
400            Some(x) => x.queries(),
401            None => BTreeMap::new(),
402        }
403    }
404
405    fn reduce(self) -> Result<Self, Error> {
406        self.map(|x| x.reduce()).transpose()
407    }
408}
409
410impl<T> Apply for Vec<T>
411where
412    T: Apply,
413{
414    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
415        self.into_iter().map(|x| x.apply_args(args)).collect()
416    }
417
418    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
419        self.into_iter().map(|x| x.apply_inputs(args)).collect()
420    }
421
422    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
423        self.into_iter().map(|x| x.apply_fees(fees)).collect()
424    }
425
426    fn is_constant(&self) -> bool {
427        self.iter().all(|x| x.is_constant())
428    }
429
430    fn params(&self) -> BTreeMap<String, ir::Type> {
431        self.iter().flat_map(|x| x.params()).collect()
432    }
433
434    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
435        self.iter().flat_map(|x| x.queries()).collect()
436    }
437
438    fn reduce(self) -> Result<Self, Error> {
439        self.into_iter().map(|x| x.reduce()).collect()
440    }
441}
442
443impl<T> Apply for HashMap<String, T>
444where
445    T: Apply,
446{
447    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
448        self.into_iter()
449            .map(|(k, v)| v.apply_args(args).map(|v| (k, v)))
450            .collect()
451    }
452
453    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
454        self.into_iter()
455            .map(|(k, v)| v.apply_inputs(args).map(|v| (k, v)))
456            .collect()
457    }
458
459    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
460        self.into_iter()
461            .map(|(k, v)| v.apply_fees(fees).map(|v| (k, v)))
462            .collect()
463    }
464
465    fn is_constant(&self) -> bool {
466        self.values().all(|x| x.is_constant())
467    }
468
469    fn params(&self) -> BTreeMap<String, ir::Type> {
470        self.values().flat_map(|x| x.params()).collect()
471    }
472
473    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
474        self.values().flat_map(|x| x.queries()).collect()
475    }
476
477    fn reduce(self) -> Result<Self, Error> {
478        self.into_iter()
479            .map(|(k, v)| v.reduce().map(|v| (k, v)))
480            .collect()
481    }
482}
483
484impl Composite for ir::ScriptSource {
485    fn reduce_self(self) -> Result<Self, Error> {
486        Ok(self)
487    }
488
489    fn components(&self) -> Vec<&ir::Expression> {
490        match self {
491            ir::ScriptSource::Embedded(x) => vec![x],
492            ir::ScriptSource::UtxoRef { r#ref, source } => {
493                std::iter::once(r#ref).chain(source.as_ref()).collect()
494            }
495        }
496    }
497
498    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
499    where
500        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
501    {
502        match self {
503            ir::ScriptSource::Embedded(x) => Ok(ir::ScriptSource::Embedded(f(x)?)),
504            ir::ScriptSource::UtxoRef { r#ref, source } => Ok(ir::ScriptSource::UtxoRef {
505                r#ref: f(r#ref)?,
506                source: source.map(&f).transpose()?,
507            }),
508        }
509    }
510}
511
512impl TryFrom<&ArgValue> for ir::ScriptSource {
513    type Error = Error;
514
515    fn try_from(value: &ArgValue) -> Result<Self, Self::Error> {
516        match value {
517            ArgValue::Bytes(x) => Ok(ir::ScriptSource::Embedded(ir::Expression::Bytes(x.clone()))),
518            ArgValue::UtxoRef(x) => Ok(ir::ScriptSource::UtxoRef {
519                r#ref: ir::Expression::UtxoRefs(vec![x.clone()]),
520                source: None,
521            }),
522            _ => Err(Error::InvalidArgument(value.clone(), "script".to_string())),
523        }
524    }
525}
526
527impl Composite for ir::PolicyExpr {
528    fn components(&self) -> Vec<&ir::Expression> {
529        let script = self.script.components();
530        std::iter::once(&self.hash).chain(script).collect()
531    }
532
533    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
534    where
535        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
536    {
537        Ok(Self {
538            name: self.name,
539            hash: f(self.hash)?,
540            script: self.script.try_map_components(f)?,
541        })
542    }
543}
544
545impl Composite for ir::StructExpr {
546    fn components(&self) -> Vec<&ir::Expression> {
547        self.fields.iter().collect()
548    }
549
550    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
551    where
552        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
553    {
554        Ok(Self {
555            constructor: self.constructor,
556            fields: self
557                .fields
558                .into_iter()
559                .map(&f)
560                .collect::<Result<Vec<_>, _>>()?,
561        })
562    }
563}
564
565impl Composite for ir::AssetExpr {
566    fn components(&self) -> Vec<&ir::Expression> {
567        vec![&self.policy, &self.asset_name, &self.amount]
568    }
569
570    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
571    where
572        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
573    {
574        Ok(Self {
575            policy: f(self.policy)?,
576            asset_name: f(self.asset_name)?,
577            amount: f(self.amount)?,
578        })
579    }
580}
581
582impl Composite for ir::Coerce {
583    fn components(&self) -> Vec<&ir::Expression> {
584        match self {
585            Self::IntoAssets(x) => vec![x],
586            Self::IntoDatum(x) => vec![x],
587            Self::IntoScript(x) => vec![x],
588            Self::NoOp(x) => vec![x],
589        }
590    }
591
592    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
593    where
594        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
595    {
596        match self {
597            Self::IntoAssets(x) => Ok(Self::IntoAssets(f(x)?)),
598            Self::IntoDatum(x) => Ok(Self::IntoDatum(f(x)?)),
599            Self::IntoScript(x) => Ok(Self::IntoScript(f(x)?)),
600            Self::NoOp(x) => Ok(Self::NoOp(f(x)?)),
601        }
602    }
603
604    fn reduce_self(self) -> Result<Self, Error> {
605        match self {
606            Self::NoOp(x) => Ok(Self::NoOp(x)),
607            Self::IntoAssets(x) => Ok(Self::NoOp(x.into_assets()?)),
608            Self::IntoDatum(x) => Ok(Self::NoOp(x.into_datum()?)),
609            Self::IntoScript(x) => todo!(),
610        }
611    }
612}
613
614impl Composite for ir::BuiltInOp {
615    fn components(&self) -> Vec<&ir::Expression> {
616        match self {
617            Self::NoOp(x) => vec![x],
618            Self::Add(x, y) => vec![x, y],
619            Self::Sub(x, y) => vec![x, y],
620            Self::Concat(x, y) => vec![x, y],
621            Self::Negate(x) => vec![x],
622            Self::Property(x, _) => vec![x],
623        }
624    }
625
626    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
627    where
628        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
629    {
630        match self {
631            Self::NoOp(x) => Ok(Self::NoOp(f(x)?)),
632            Self::Add(x, y) => Ok(Self::Add(f(x)?, f(y)?)),
633            Self::Sub(x, y) => Ok(Self::Sub(f(x)?, f(y)?)),
634            Self::Concat(x, y) => Ok(Self::Concat(f(x)?, f(y)?)),
635            Self::Negate(x) => Ok(Self::Negate(f(x)?)),
636            Self::Property(x, prop) => Ok(Self::Property(f(x)?, prop)),
637        }
638    }
639
640    fn reduce_self(self) -> Result<Self, Error> {
641        match self {
642            Self::Add(x, y) => Ok(Self::NoOp(x.add(y)?)),
643            Self::Sub(x, y) => Ok(Self::NoOp(x.sub(y)?)),
644            Self::Concat(x, y) => Ok(Self::NoOp(x.concat(y)?)),
645            Self::Negate(x) => Ok(Self::NoOp(x.neg()?)),
646            Self::Property(x, prop) => Ok(Self::NoOp(x.index_or_err(prop)?)),
647            Self::NoOp(x) => Ok(Self::NoOp(x)),
648        }
649    }
650
651    fn reduce_nested(self) -> Result<Self, Error> {
652        match self {
653            Self::Add(x, y) => Ok(Self::Add(x.reduce()?, y.reduce()?)),
654            Self::Sub(x, y) => Ok(Self::Sub(x.reduce()?, y.reduce()?)),
655            Self::Concat(x, y) => Ok(Self::Concat(x.reduce()?, y.reduce()?)),
656            Self::Negate(x) => Ok(Self::Negate(x.reduce()?)),
657            Self::Property(x, y) => Ok(Self::Property(x.reduce()?, y.reduce()?)),
658            Self::NoOp(x) => Ok(Self::NoOp(x.reduce()?)),
659        }
660    }
661}
662
663impl From<ir::AssetExpr> for CanonicalAssets {
664    fn from(asset: ir::AssetExpr) -> Self {
665        let policy = asset.expect_constant_policy();
666        let name = asset.expect_constant_name();
667        let amount = asset.expect_constant_amount();
668
669        Self::from_asset(policy, name, amount)
670    }
671}
672
673impl From<Vec<ir::AssetExpr>> for CanonicalAssets {
674    fn from(assets: Vec<ir::AssetExpr>) -> Self {
675        let mut result = CanonicalAssets::empty();
676
677        for asset in assets {
678            let asset = asset.into();
679            result = result + asset;
680        }
681
682        result
683    }
684}
685
686impl From<CanonicalAssets> for Vec<ir::AssetExpr> {
687    fn from(assets: CanonicalAssets) -> Self {
688        let mut result = Vec::new();
689
690        for (class, amount) in assets.into_iter() {
691            result.push(ir::AssetExpr {
692                policy: class
693                    .policy()
694                    .map(|x| ir::Expression::Bytes(x.to_vec()))
695                    .unwrap_or(ir::Expression::None),
696                asset_name: class
697                    .name()
698                    .map(|x| ir::Expression::Bytes(x.to_vec()))
699                    .unwrap_or(ir::Expression::None),
700                amount: ir::Expression::Number(amount),
701            });
702        }
703
704        result
705    }
706}
707
708impl ir::AssetExpr {
709    fn expect_constant_policy(&self) -> Option<&[u8]> {
710        match &self.policy {
711            ir::Expression::None => None,
712            ir::Expression::Bytes(x) => Some(x.as_slice()),
713            _ => None,
714        }
715    }
716
717    fn expect_constant_name(&self) -> Option<&[u8]> {
718        match &self.asset_name {
719            ir::Expression::None => None,
720            ir::Expression::Bytes(x) => Some(x.as_slice()),
721            ir::Expression::String(x) => Some(x.as_bytes()),
722            _ => None,
723        }
724    }
725
726    fn expect_constant_amount(&self) -> i128 {
727        match &self.amount {
728            ir::Expression::Number(x) => *x,
729            _ => unreachable!("amount expected to be Number"),
730        }
731    }
732}
733
734impl Composite for ir::Input {
735    fn components(&self) -> Vec<&ir::Expression> {
736        vec![&self.utxos, &self.redeemer]
737    }
738
739    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
740    where
741        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
742    {
743        Ok(Self {
744            name: self.name,
745            utxos: f(self.utxos)?,
746            redeemer: f(self.redeemer)?,
747        })
748    }
749}
750
751impl Composite for ir::InputQuery {
752    fn components(&self) -> Vec<&ir::Expression> {
753        vec![&self.address, &self.min_amount, &self.r#ref]
754    }
755
756    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
757    where
758        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
759    {
760        Ok(Self {
761            address: f(self.address)?,
762            min_amount: f(self.min_amount)?,
763            r#ref: f(self.r#ref)?,
764            ..self
765        })
766    }
767}
768
769impl Apply for ir::Param {
770    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
771        match self {
772            ir::Param::ExpectValue(name, ty) => {
773                let defined = args.get(&name).cloned();
774
775                match defined {
776                    Some(x) => Ok(ir::Param::Set(arg_value_into_expr(x))),
777                    None => Ok(Self::ExpectValue(name, ty)),
778                }
779            }
780            // queries can have nested params
781            ir::Param::ExpectInput(name, query) => {
782                Ok(ir::Param::ExpectInput(name, query.apply_args(args)?))
783            }
784            x => Ok(x),
785        }
786    }
787
788    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
789        match self {
790            ir::Param::ExpectInput(name, query) => {
791                let defined = args.get(&name).cloned();
792
793                match defined {
794                    Some(x) => Ok(ir::Param::Set(ir::Expression::UtxoSet(x))),
795                    None => Ok(Self::ExpectInput(name, query)),
796                }
797            }
798            x => Ok(x),
799        }
800    }
801
802    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
803        match self {
804            ir::Param::ExpectFees => Ok(ir::Param::Set(ir::Expression::Assets(vec![
805                ir::AssetExpr {
806                    policy: ir::Expression::None,
807                    asset_name: ir::Expression::None,
808                    amount: ir::Expression::Number(fees as i128),
809                },
810            ]))),
811            // queries can have nested params
812            ir::Param::ExpectInput(name, query) => {
813                Ok(ir::Param::ExpectInput(name, query.apply_fees(fees)?))
814            }
815            x => Ok(x),
816        }
817    }
818
819    fn is_constant(&self) -> bool {
820        match self {
821            ir::Param::Set(x) => x.is_constant(),
822            _ => false,
823        }
824    }
825
826    fn params(&self) -> BTreeMap<String, ir::Type> {
827        match self {
828            ir::Param::ExpectValue(name, ty) => BTreeMap::from([(name.clone(), ty.clone())]),
829            // queries can have nested params
830            ir::Param::ExpectInput(_, x) => x.params(),
831            _ => BTreeMap::new(),
832        }
833    }
834
835    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
836        match self {
837            ir::Param::ExpectInput(name, query) => BTreeMap::from([(name.clone(), query.clone())]),
838            _ => BTreeMap::new(),
839        }
840    }
841
842    fn reduce(self) -> Result<Self, Error> {
843        match self {
844            // queries can have nested expressions that need to be reduced
845            ir::Param::ExpectInput(name, query) => {
846                Ok(ir::Param::ExpectInput(name, query.reduce()?))
847            }
848            x => Ok(x),
849        }
850    }
851}
852
853impl Apply for ir::Expression {
854    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
855        match self {
856            Self::List(x) => Ok(Self::List(
857                x.into_iter()
858                    .map(|x| x.apply_args(args))
859                    .collect::<Result<_, _>>()?,
860            )),
861            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
862                x.0.apply_args(args)?,
863                x.1.apply_args(args)?,
864            )))),
865            Self::Map(x) => Ok(Self::Map(
866                x.into_iter()
867                    .map(|(k, v)| {
868                        Ok::<(Expression, Expression), Error>((
869                            k.apply_args(args)?,
870                            v.apply_args(args)?,
871                        ))
872                    })
873                    .collect::<Result<_, _>>()?,
874            )),
875            Self::Struct(x) => Ok(Self::Struct(x.apply_args(args)?)),
876            Self::Assets(x) => Ok(Self::Assets(
877                x.into_iter()
878                    .map(|x| x.apply_args(args))
879                    .collect::<Result<_, _>>()?,
880            )),
881            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_args(args)?))),
882            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_args(args)?))),
883            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_args(args)?))),
884            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_args(args)?))),
885            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_args(args)?))),
886
887            // Don't fall into the temptation of simplifying the following cases under a single
888            // wildcard with a default implementation, it makes it really hard to detect missing
889            // implementation when adding new `Expression` variants.
890            Self::None => Ok(self),
891            Self::Bytes(_) => Ok(self),
892            Self::Number(_) => Ok(self),
893            Self::Bool(_) => Ok(self),
894            Self::String(_) => Ok(self),
895            Self::Address(_) => Ok(self),
896            Self::Hash(_) => Ok(self),
897            Self::UtxoRefs(_) => Ok(self),
898            Self::UtxoSet(_) => Ok(self),
899        }
900    }
901
902    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
903        match self {
904            Self::List(x) => Ok(Self::List(
905                x.into_iter()
906                    .map(|x| x.apply_inputs(args))
907                    .collect::<Result<_, _>>()?,
908            )),
909            Self::Map(x) => Ok(Self::Map(
910                x.into_iter()
911                    .map(|(k, v)| {
912                        Ok::<(Expression, Expression), Error>((
913                            k.apply_inputs(args)?,
914                            v.apply_inputs(args)?,
915                        ))
916                    })
917                    .collect::<Result<_, _>>()?,
918            )),
919            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
920                x.0.apply_inputs(args)?,
921                x.1.apply_inputs(args)?,
922            )))),
923            Self::Struct(x) => Ok(Self::Struct(x.apply_inputs(args)?)),
924            Self::Assets(x) => Ok(Self::Assets(
925                x.into_iter()
926                    .map(|x| x.apply_inputs(args))
927                    .collect::<Result<_, _>>()?,
928            )),
929            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_inputs(args)?))),
930            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_inputs(args)?))),
931            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_inputs(args)?))),
932            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_inputs(args)?))),
933            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_inputs(args)?))),
934
935            // Don't fall into the temptation of simplifying the following cases under a single
936            // wildcard with a default implementation, it makes it really hard to detect missing
937            // implementation when adding new `Expression` variants.
938            Self::None => Ok(self),
939            Self::Bytes(_) => Ok(self),
940            Self::Number(_) => Ok(self),
941            Self::Bool(_) => Ok(self),
942            Self::String(_) => Ok(self),
943            Self::Address(_) => Ok(self),
944            Self::Hash(_) => Ok(self),
945            Self::UtxoRefs(_) => Ok(self),
946            Self::UtxoSet(_) => Ok(self),
947        }
948    }
949
950    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
951        match self {
952            Self::List(x) => Ok(Self::List(
953                x.into_iter()
954                    .map(|x| x.apply_fees(fees))
955                    .collect::<Result<_, _>>()?,
956            )),
957            Self::Map(x) => Ok(Self::Map(
958                x.into_iter()
959                    .map(|(k, v)| {
960                        Ok::<(Expression, Expression), Error>((
961                            k.apply_fees(fees)?,
962                            v.apply_fees(fees)?,
963                        ))
964                    })
965                    .collect::<Result<_, _>>()?,
966            )),
967            Self::Tuple(x) => Ok(Self::Tuple(Box::new((
968                x.0.apply_fees(fees)?,
969                x.1.apply_fees(fees)?,
970            )))),
971            Self::Struct(x) => Ok(Self::Struct(x.apply_fees(fees)?)),
972            Self::Assets(x) => Ok(Self::Assets(
973                x.into_iter()
974                    .map(|x| x.apply_fees(fees))
975                    .collect::<Result<_, _>>()?,
976            )),
977            Self::EvalParam(x) => Ok(Self::EvalParam(Box::new(x.apply_fees(fees)?))),
978            Self::EvalBuiltIn(x) => Ok(Self::EvalBuiltIn(Box::new(x.apply_fees(fees)?))),
979            Self::EvalCoerce(x) => Ok(Self::EvalCoerce(Box::new(x.apply_fees(fees)?))),
980            Self::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.apply_fees(fees)?))),
981            Self::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.apply_fees(fees)?))),
982
983            // Don't fall into the temptation of simplifying the following cases under a single
984            // wildcard with a default implementation, it makes it really hard to detect missing
985            // implementation when adding new `Expression` variants.
986            Self::None => Ok(self),
987            Self::Bytes(_) => Ok(self),
988            Self::Number(_) => Ok(self),
989            Self::Bool(_) => Ok(self),
990            Self::String(_) => Ok(self),
991            Self::Address(_) => Ok(self),
992            Self::Hash(_) => Ok(self),
993            Self::UtxoRefs(_) => Ok(self),
994            Self::UtxoSet(_) => Ok(self),
995        }
996    }
997
998    fn is_constant(&self) -> bool {
999        match self {
1000            Self::List(x) => x.iter().all(|x| x.is_constant()),
1001            Self::Map(x) => x.iter().all(|(k, v)| k.is_constant() && v.is_constant()),
1002            Self::Tuple(x) => x.0.is_constant() && x.1.is_constant(),
1003            Self::Struct(x) => x.is_constant(),
1004            Self::Assets(x) => x.iter().all(|x| x.is_constant()),
1005            Self::EvalParam(x) => x.is_constant(),
1006            Self::EvalBuiltIn(x) => x.is_constant(),
1007            Self::EvalCoerce(x) => x.is_constant(),
1008            Self::EvalCompiler(_) => false,
1009            Self::AdHocDirective(x) => x.is_constant(),
1010
1011            // Don't fall into the temptation of simplifying the following cases under a single
1012            // wildcard with a default implementation, it makes it really hard to detect missing
1013            // implementation when adding new `Expression` variants.
1014            Self::None => true,
1015            Self::Bytes(_) => true,
1016            Self::Number(_) => true,
1017            Self::Bool(_) => true,
1018            Self::String(_) => true,
1019            Self::Address(_) => true,
1020            Self::Hash(_) => true,
1021            Self::UtxoRefs(_) => true,
1022            Self::UtxoSet(_) => true,
1023        }
1024    }
1025
1026    fn params(&self) -> BTreeMap<String, ir::Type> {
1027        match self {
1028            Self::List(x) => x.iter().flat_map(|x| x.params()).collect(),
1029            Self::Map(x) => x
1030                .iter()
1031                .flat_map(|(k, v)| [k.params(), v.params()].into_iter().flatten())
1032                .collect(),
1033            Self::Tuple(x) => [x.0.params(), x.1.params()].into_iter().flatten().collect(),
1034            Self::Struct(x) => x.params(),
1035            Self::Assets(x) => x.iter().flat_map(|x| x.params()).collect(),
1036            Self::EvalParam(x) => x.params(),
1037            Self::EvalBuiltIn(x) => x.params(),
1038            Self::EvalCoerce(x) => x.params(),
1039            Self::EvalCompiler(x) => x.params(),
1040            Self::AdHocDirective(x) => x.params(),
1041
1042            // Don't fall into the temptation of simplifying the following cases under a single
1043            // wildcard with a default implementation, it makes it really hard to detect missing
1044            // implementation when adding new `Expression` variants.
1045            Self::None => BTreeMap::new(),
1046            Self::Bytes(_) => BTreeMap::new(),
1047            Self::Number(_) => BTreeMap::new(),
1048            Self::Bool(_) => BTreeMap::new(),
1049            Self::String(_) => BTreeMap::new(),
1050            Self::Address(_) => BTreeMap::new(),
1051            Self::Hash(_) => BTreeMap::new(),
1052            Self::UtxoRefs(_) => BTreeMap::new(),
1053            Self::UtxoSet(_) => BTreeMap::new(),
1054        }
1055    }
1056
1057    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
1058        match self {
1059            Self::List(x) => x.iter().flat_map(|x| x.queries()).collect(),
1060            Self::Map(x) => x
1061                .iter()
1062                .flat_map(|(k, v)| [k.queries(), v.queries()].into_iter().flatten())
1063                .collect(),
1064            Self::Tuple(x) => [x.0.queries(), x.1.queries()]
1065                .into_iter()
1066                .flatten()
1067                .collect(),
1068            Self::Struct(x) => x.queries(),
1069            Self::Assets(x) => x.iter().flat_map(|x| x.queries()).collect(),
1070            Self::EvalParam(x) => x.queries(),
1071            Self::EvalBuiltIn(x) => x.queries(),
1072            Self::EvalCoerce(x) => x.queries(),
1073            Self::EvalCompiler(x) => x.queries(),
1074            Self::AdHocDirective(x) => x.queries(),
1075
1076            // Don't fall into the temptation of simplifying the following cases under a single
1077            // wildcard with a default implementation, it makes it really hard to detect missing
1078            // implementation when adding new `Expression` variants.
1079            Self::None => BTreeMap::new(),
1080            Self::Bytes(_) => BTreeMap::new(),
1081            Self::Number(_) => BTreeMap::new(),
1082            Self::Bool(_) => BTreeMap::new(),
1083            Self::String(_) => BTreeMap::new(),
1084            Self::Address(_) => BTreeMap::new(),
1085            Self::Hash(_) => BTreeMap::new(),
1086            Self::UtxoRefs(_) => BTreeMap::new(),
1087            Self::UtxoSet(_) => BTreeMap::new(),
1088        }
1089    }
1090
1091    fn reduce(self) -> Result<Self, Error> {
1092        match self {
1093            // the following expressions can only be reduced internally
1094            ir::Expression::List(x) => Ok(Self::List(
1095                x.into_iter()
1096                    .map(|x| x.reduce())
1097                    .collect::<Result<_, _>>()?,
1098            )),
1099            ir::Expression::Map(x) => Ok(Self::Map(
1100                x.into_iter()
1101                    .map(|(k, v)| Ok::<(Expression, Expression), Error>((k.reduce()?, v.reduce()?)))
1102                    .collect::<Result<_, _>>()?,
1103            )),
1104            ir::Expression::Tuple(x) => Ok(Self::Tuple(Box::new((x.0.reduce()?, x.1.reduce()?)))),
1105            ir::Expression::Struct(x) => Ok(Self::Struct(x.reduce()?)),
1106            ir::Expression::Assets(x) => Ok(Self::Assets(
1107                x.into_iter()
1108                    .map(|x| x.reduce())
1109                    .collect::<Result<_, _>>()?,
1110            )),
1111            ir::Expression::EvalCompiler(x) => Ok(Self::EvalCompiler(Box::new(x.reduce()?))),
1112            ir::Expression::AdHocDirective(x) => Ok(Self::AdHocDirective(Box::new(x.reduce()?))),
1113
1114            // the following ones can be turned into simpler expressions
1115            ir::Expression::EvalBuiltIn(x) => match x.reduce()? {
1116                ir::BuiltInOp::NoOp(x) => Ok(x),
1117                x => Ok(ir::Expression::EvalBuiltIn(Box::new(x.reduce()?))),
1118            },
1119            ir::Expression::EvalCoerce(x) => match x.reduce()? {
1120                ir::Coerce::NoOp(x) => Ok(x),
1121                x => Ok(ir::Expression::EvalCoerce(Box::new(x.reduce()?))),
1122            },
1123            ir::Expression::EvalParam(x) => match x.reduce()? {
1124                ir::Param::Set(x) => Ok(x),
1125                x => Ok(ir::Expression::EvalParam(Box::new(x.reduce()?))),
1126            },
1127
1128            // Don't fall into the temptation of simplifying the following cases under a single
1129            // wildcard with a default implementation, it makes it really hard to detect missing
1130            // implementation when adding new `Expression` variants.
1131            Self::None => Ok(self),
1132            Self::Bytes(_) => Ok(self),
1133            Self::Number(_) => Ok(self),
1134            Self::Bool(_) => Ok(self),
1135            Self::String(_) => Ok(self),
1136            Self::Address(_) => Ok(self),
1137            Self::Hash(_) => Ok(self),
1138            Self::UtxoRefs(_) => Ok(self),
1139            Self::UtxoSet(_) => Ok(self),
1140        }
1141    }
1142}
1143
1144impl Composite for ir::Output {
1145    fn components(&self) -> Vec<&ir::Expression> {
1146        vec![&self.address, &self.datum, &self.amount]
1147    }
1148
1149    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1150    where
1151        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1152    {
1153        Ok(Self {
1154            address: f(self.address)?,
1155            datum: f(self.datum)?,
1156            amount: f(self.amount)?,
1157            optional: self.optional,
1158        })
1159    }
1160}
1161
1162impl Composite for ir::Mint {
1163    fn components(&self) -> Vec<&ir::Expression> {
1164        vec![&self.amount, &self.redeemer]
1165    }
1166
1167    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1168    where
1169        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1170    {
1171        Ok(Self {
1172            amount: f(self.amount)?,
1173            redeemer: f(self.redeemer)?,
1174        })
1175    }
1176}
1177
1178impl Composite for ir::AdHocDirective {
1179    fn components(&self) -> Vec<&ir::Expression> {
1180        self.data.values().collect()
1181    }
1182
1183    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1184    where
1185        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1186    {
1187        Ok(Self {
1188            name: self.name,
1189            data: self
1190                .data
1191                .into_iter()
1192                .map(|(k, v)| f(v).map(|v| (k, v)))
1193                .collect::<Result<_, _>>()?,
1194        })
1195    }
1196}
1197
1198impl Composite for ir::CompilerOp {
1199    fn components(&self) -> Vec<&ir::Expression> {
1200        match self {
1201            ir::CompilerOp::BuildScriptAddress(x) => vec![x],
1202            ir::CompilerOp::ComputeMinUtxo(x) => vec![x],
1203            ir::CompilerOp::ComputeTipSlot => vec![],
1204            ir::CompilerOp::ComputeSlotToTime(x) => vec![x],
1205            ir::CompilerOp::ComputeTimeToSlot(x) => vec![x],
1206        }
1207    }
1208
1209    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1210    where
1211        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1212    {
1213        match self {
1214            ir::CompilerOp::BuildScriptAddress(x) => Ok(ir::CompilerOp::BuildScriptAddress(f(x)?)),
1215            ir::CompilerOp::ComputeMinUtxo(x) => Ok(ir::CompilerOp::ComputeMinUtxo(f(x)?)),
1216            ir::CompilerOp::ComputeTipSlot => Ok(ir::CompilerOp::ComputeTipSlot),
1217            ir::CompilerOp::ComputeSlotToTime(x) => Ok(ir::CompilerOp::ComputeSlotToTime(f(x)?)),
1218            ir::CompilerOp::ComputeTimeToSlot(x) => Ok(ir::CompilerOp::ComputeTimeToSlot(f(x)?)),
1219        }
1220    }
1221}
1222
1223impl Composite for ir::Signers {
1224    fn components(&self) -> Vec<&ir::Expression> {
1225        self.signers.iter().collect()
1226    }
1227
1228    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1229    where
1230        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1231    {
1232        Ok(Self {
1233            signers: self.signers.into_iter().map(f).collect::<Result<_, _>>()?,
1234        })
1235    }
1236}
1237
1238impl Composite for ir::Collateral {
1239    fn components(&self) -> Vec<&ir::Expression> {
1240        vec![&self.utxos]
1241    }
1242
1243    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1244    where
1245        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1246    {
1247        Ok(Self {
1248            utxos: f(self.utxos)?,
1249        })
1250    }
1251}
1252
1253impl Composite for ir::Validity {
1254    fn components(&self) -> Vec<&ir::Expression> {
1255        vec![&self.since, &self.until]
1256    }
1257
1258    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1259    where
1260        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1261    {
1262        Ok(Self {
1263            since: f(self.since)?,
1264            until: f(self.until)?,
1265        })
1266    }
1267}
1268
1269impl Composite for ir::Metadata {
1270    fn components(&self) -> Vec<&ir::Expression> {
1271        vec![&self.key, &self.value]
1272    }
1273
1274    fn try_map_components<F>(self, f: F) -> Result<Self, Error>
1275    where
1276        F: Fn(ir::Expression) -> Result<ir::Expression, Error> + Clone,
1277    {
1278        Ok(Self {
1279            key: f(self.key)?,
1280            value: f(self.value)?,
1281        })
1282    }
1283}
1284
1285impl Apply for ir::Tx {
1286    fn apply_args(self, args: &BTreeMap<String, ArgValue>) -> Result<Self, Error> {
1287        let tx = ir::Tx {
1288            references: self.references.apply_args(args)?,
1289            inputs: self.inputs.apply_args(args)?,
1290            outputs: self.outputs.apply_args(args)?,
1291            validity: self.validity.apply_args(args)?,
1292            mints: self.mints.apply_args(args)?,
1293            burns: self.burns.apply_args(args)?,
1294            fees: self.fees.apply_args(args)?,
1295            adhoc: self.adhoc.apply_args(args)?,
1296            collateral: self.collateral.apply_args(args)?,
1297            signers: self.signers.apply_args(args)?,
1298            metadata: self.metadata.apply_args(args)?,
1299        };
1300
1301        Ok(tx)
1302    }
1303
1304    fn apply_inputs(self, args: &BTreeMap<String, HashSet<Utxo>>) -> Result<Self, Error> {
1305        Ok(Self {
1306            references: self.references.apply_inputs(args)?,
1307            inputs: self.inputs.apply_inputs(args)?,
1308            outputs: self.outputs.apply_inputs(args)?,
1309            validity: self.validity.apply_inputs(args)?,
1310            mints: self.mints.apply_inputs(args)?,
1311            burns: self.burns.apply_inputs(args)?,
1312            fees: self.fees.apply_inputs(args)?,
1313            adhoc: self.adhoc.apply_inputs(args)?,
1314            collateral: self.collateral.apply_inputs(args)?,
1315            signers: self.signers.apply_inputs(args)?,
1316            metadata: self.metadata.apply_inputs(args)?,
1317        })
1318    }
1319
1320    fn apply_fees(self, fees: u64) -> Result<Self, Error> {
1321        Ok(Self {
1322            references: self.references.apply_fees(fees)?,
1323            inputs: self.inputs.apply_fees(fees)?,
1324            outputs: self.outputs.apply_fees(fees)?,
1325            validity: self.validity.apply_fees(fees)?,
1326            mints: self.mints.apply_fees(fees)?,
1327            burns: self.burns.apply_fees(fees)?,
1328            fees: self.fees.apply_fees(fees)?,
1329            adhoc: self.adhoc.apply_fees(fees)?,
1330            collateral: self.collateral.apply_fees(fees)?,
1331            signers: self.signers.apply_fees(fees)?,
1332            metadata: self.metadata.apply_fees(fees)?,
1333        })
1334    }
1335
1336    fn is_constant(&self) -> bool {
1337        self.inputs.iter().all(|x| x.is_constant())
1338            && self.outputs.iter().all(|x| x.is_constant())
1339            && self.mints.iter().all(|x| x.is_constant())
1340            && self.burns.iter().all(|x| x.is_constant())
1341            && self.fees.is_constant()
1342            && self.metadata.is_constant()
1343            && self.validity.is_constant()
1344            && self.references.is_constant()
1345            && self.collateral.is_constant()
1346            && self.adhoc.iter().all(|x| x.is_constant())
1347            && self.signers.is_constant()
1348    }
1349
1350    fn params(&self) -> BTreeMap<String, ir::Type> {
1351        // TODO: analyze if necessary to add ref_inputs
1352        let mut params = BTreeMap::new();
1353        params.extend(self.inputs.params());
1354        params.extend(self.outputs.params());
1355        params.extend(self.mints.params());
1356        params.extend(self.burns.params());
1357        params.extend(self.fees.params());
1358        params.extend(self.adhoc.params());
1359        params.extend(self.signers.params());
1360        params.extend(self.validity.params());
1361        params.extend(self.metadata.params());
1362        params.extend(self.references.params());
1363        params.extend(self.collateral.params());
1364        params
1365    }
1366
1367    fn queries(&self) -> BTreeMap<String, ir::InputQuery> {
1368        let mut queries = BTreeMap::new();
1369        queries.extend(self.inputs.queries());
1370        queries.extend(self.outputs.queries());
1371        queries.extend(self.mints.queries());
1372        queries.extend(self.burns.queries());
1373        queries.extend(self.fees.queries());
1374        queries.extend(self.adhoc.queries());
1375        queries.extend(self.signers.queries());
1376        queries.extend(self.validity.queries());
1377        queries.extend(self.metadata.queries());
1378        queries.extend(self.collateral.queries());
1379        queries.extend(self.references.queries());
1380        queries
1381    }
1382
1383    fn reduce(self) -> Result<Self, Error> {
1384        Ok(Self {
1385            references: self.references.reduce()?,
1386            inputs: self.inputs.reduce()?,
1387            outputs: self.outputs.reduce()?,
1388            validity: self.validity.reduce()?,
1389            mints: self.mints.reduce()?,
1390            burns: self.burns.reduce()?,
1391            fees: self.fees.reduce()?,
1392            adhoc: self.adhoc.reduce()?,
1393            collateral: self.collateral.reduce()?,
1394            signers: self.signers.reduce()?,
1395            metadata: self.metadata.reduce()?,
1396        })
1397    }
1398}
1399
1400pub fn apply_args(template: ir::Tx, args: &BTreeMap<String, ArgValue>) -> Result<ir::Tx, Error> {
1401    template.apply_args(args)
1402}
1403
1404pub fn apply_inputs(
1405    template: ir::Tx,
1406    args: &BTreeMap<String, HashSet<Utxo>>,
1407) -> Result<ir::Tx, Error> {
1408    template.apply_inputs(args)
1409}
1410
1411pub fn apply_fees(template: ir::Tx, fees: u64) -> Result<ir::Tx, Error> {
1412    template.apply_fees(fees)
1413}
1414
1415pub fn reduce(template: ir::Tx) -> Result<ir::Tx, Error> {
1416    template.reduce()
1417}
1418
1419pub fn find_params(template: &ir::Tx) -> BTreeMap<String, ir::Type> {
1420    template.params()
1421}
1422
1423pub fn find_queries(template: &ir::Tx) -> BTreeMap<String, ir::InputQuery> {
1424    template.queries()
1425}
1426
1427#[cfg(test)]
1428mod tests {
1429
1430    use crate::UtxoRef;
1431
1432    use super::*;
1433
1434    const SUBJECT_PROTOCOL: &str = r#"
1435        party Sender;
1436
1437        tx swap(a: Int, b: Int) {
1438            input source {
1439                from: Sender,
1440                min_amount: Ada(a) + Ada(b) + fees,
1441            }
1442        }
1443    "#;
1444
1445    #[test]
1446    fn param_expression_is_applied() {
1447        let ir = ir::Expression::EvalParam(Box::new(ir::Param::ExpectValue(
1448            "a".to_string(),
1449            ir::Type::Int,
1450        )));
1451
1452        let params = ir.params();
1453        assert_eq!(params.len(), 1);
1454        assert_eq!(params.get("a"), Some(&ir::Type::Int));
1455
1456        let args = BTreeMap::from([("a".to_string(), ArgValue::Int(100))]);
1457
1458        let after = ir.apply_args(&args).unwrap();
1459
1460        assert_eq!(
1461            after,
1462            ir::Expression::EvalParam(Box::new(ir::Param::Set(ir::Expression::Number(100),)))
1463        );
1464    }
1465
1466    #[test]
1467    fn nested_param_expression_is_applied() {
1468        let ir = ir::Expression::EvalParam(Box::new(ir::Param::ExpectInput(
1469            "out".to_string(),
1470            ir::InputQuery {
1471                address: ir::Expression::None,
1472                min_amount: ir::Expression::None,
1473                r#ref: ir::Expression::EvalParam(Box::new(ir::Param::ExpectValue(
1474                    "in".to_string(),
1475                    ir::Type::Int,
1476                ))),
1477                many: false,
1478                collateral: false,
1479            },
1480        )));
1481
1482        let params = ir.params();
1483        assert_eq!(params.len(), 1);
1484        assert_eq!(params.get("in"), Some(&ir::Type::Int));
1485
1486        let args = BTreeMap::from([("in".to_string(), ArgValue::Int(100))]);
1487        let after = ir.apply_args(&args).unwrap();
1488
1489        let after = after.reduce().unwrap();
1490
1491        let queries = after.queries();
1492        assert_eq!(queries.len(), 1);
1493        assert_eq!(
1494            queries.get("out"),
1495            Some(&ir::InputQuery {
1496                address: ir::Expression::None,
1497                min_amount: ir::Expression::None,
1498                r#ref: ir::Expression::Number(100),
1499                many: false,
1500                collateral: false,
1501            })
1502        );
1503    }
1504
1505    #[test]
1506    fn param_expression_is_reduced() {
1507        let ir = ir::Expression::EvalParam(Box::new(ir::Param::Set(ir::Expression::Number(3))));
1508
1509        let after = ir.reduce().unwrap();
1510
1511        assert_eq!(after, ir::Expression::Number(3));
1512    }
1513
1514    #[test]
1515    fn test_apply_args() {
1516        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1517        crate::analyzing::analyze(&mut ast).ok().unwrap();
1518
1519        let before = crate::lowering::lower(&ast, "swap").unwrap();
1520
1521        dbg!(&before);
1522
1523        let params = find_params(&before);
1524        assert_eq!(params.len(), 3);
1525        assert_eq!(params.get("sender"), Some(&ir::Type::Address));
1526        assert_eq!(params.get("a"), Some(&ir::Type::Int));
1527        assert_eq!(params.get("b"), Some(&ir::Type::Int));
1528
1529        let args = BTreeMap::from([
1530            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1531            ("a".to_string(), ArgValue::Int(100)),
1532            ("b".to_string(), ArgValue::Int(200)),
1533        ]);
1534
1535        let after = apply_args(before, &args).unwrap();
1536
1537        let params = find_params(&after);
1538        assert_eq!(params.len(), 0);
1539    }
1540
1541    #[test]
1542    fn test_apply_inputs() {
1543        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1544        crate::analyzing::analyze(&mut ast).ok().unwrap();
1545
1546        let before = crate::lowering::lower(&ast, "swap").unwrap();
1547
1548        let args = BTreeMap::from([
1549            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1550            ("a".to_string(), ArgValue::Int(100)),
1551            ("b".to_string(), ArgValue::Int(200)),
1552        ]);
1553
1554        let before = apply_args(before, &args).unwrap();
1555
1556        let before = before.reduce().unwrap();
1557
1558        let queries = find_queries(&before);
1559        dbg!(&queries);
1560
1561        assert_eq!(queries.len(), 1);
1562        assert!(queries.contains_key("source"));
1563
1564        let inputs = BTreeMap::from([(
1565            "source".to_string(),
1566            HashSet::from([Utxo {
1567                r#ref: UtxoRef::new(b"abc", 0),
1568                address: b"abc".to_vec(),
1569                datum: None,
1570                assets: CanonicalAssets::from_naked_amount(300),
1571                script: None,
1572            }]),
1573        )]);
1574
1575        let after = apply_inputs(before, &inputs).unwrap();
1576
1577        let queries = find_queries(&after);
1578        dbg!(&queries);
1579
1580        assert_eq!(queries.len(), 0);
1581    }
1582
1583    #[test]
1584    fn test_apply_fees() {
1585        let mut ast = crate::parsing::parse_string(SUBJECT_PROTOCOL).unwrap();
1586        crate::analyzing::analyze(&mut ast).ok().unwrap();
1587
1588        let before = crate::lowering::lower(&ast, "swap").unwrap();
1589
1590        let args = BTreeMap::from([
1591            ("sender".to_string(), ArgValue::Address(b"abc".to_vec())),
1592            ("a".to_string(), ArgValue::Int(100)),
1593            ("b".to_string(), ArgValue::Int(200)),
1594        ]);
1595
1596        let before = apply_args(before, &args).unwrap().reduce().unwrap();
1597
1598        let after = before.apply_fees(100).unwrap().reduce().unwrap();
1599
1600        let queries = find_queries(&after);
1601
1602        let query = queries.get("source").unwrap();
1603
1604        assert_eq!(
1605            query,
1606            &ir::InputQuery {
1607                address: ir::Expression::Address(b"abc".to_vec()),
1608                min_amount: ir::Expression::Assets(vec![ir::AssetExpr {
1609                    policy: ir::Expression::None,
1610                    asset_name: ir::Expression::None,
1611                    amount: ir::Expression::Number(400),
1612                }]),
1613                r#ref: ir::Expression::None,
1614                many: false,
1615                collateral: false,
1616            }
1617        );
1618    }
1619
1620    #[test]
1621    fn built_in_expression_is_reduced() {
1622        let op =
1623            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::NoOp(ir::Expression::Number(5))));
1624
1625        let after = op.reduce().unwrap();
1626
1627        assert_eq!(after, ir::Expression::Number(5))
1628    }
1629
1630    #[test]
1631    fn numeric_add_is_reduced() {
1632        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1633            ir::Expression::Number(1),
1634            ir::Expression::Number(5),
1635        )));
1636
1637        let after = op.reduce().unwrap();
1638
1639        assert_eq!(after, ir::Expression::Number(6));
1640    }
1641
1642    #[test]
1643    fn numeric_sub_is_reduced() {
1644        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Sub(
1645            ir::Expression::Number(8),
1646            ir::Expression::Number(5),
1647        )));
1648
1649        let after = op.reduce().unwrap();
1650
1651        assert_eq!(after, ir::Expression::Number(3));
1652    }
1653
1654    #[test]
1655    fn nested_numeric_binary_op_is_reduced() {
1656        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1657            ir::Expression::Number(1),
1658            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Sub(
1659                ir::Expression::Number(5),
1660                ir::Expression::Number(3),
1661            ))),
1662        )));
1663
1664        let after = op.reduce().unwrap();
1665
1666        assert_eq!(after, ir::Expression::Number(3));
1667    }
1668
1669    #[test]
1670    fn test_reduce_single_custom_asset_binary_op() {
1671        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1672            ir::Expression::Assets(vec![ir::AssetExpr {
1673                policy: ir::Expression::Bytes(b"abc".to_vec()),
1674                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1675                amount: ir::Expression::Number(100),
1676            }]),
1677            ir::Expression::Assets(vec![ir::AssetExpr {
1678                policy: ir::Expression::Bytes(b"abc".to_vec()),
1679                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1680                amount: ir::Expression::Number(200),
1681            }]),
1682        )));
1683
1684        let reduced = op.reduce().unwrap();
1685
1686        match reduced {
1687            ir::Expression::Assets(assets) => {
1688                assert_eq!(assets.len(), 1);
1689                assert_eq!(assets[0].policy, ir::Expression::Bytes(b"abc".to_vec()));
1690                assert_eq!(assets[0].asset_name, ir::Expression::Bytes(b"111".to_vec()));
1691                assert_eq!(assets[0].amount, ir::Expression::Number(300));
1692            }
1693            _ => panic!("Expected assets"),
1694        };
1695    }
1696
1697    #[test]
1698    fn test_reduce_native_asset_binary_op() {
1699        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1700            ir::Expression::Assets(vec![ir::AssetExpr {
1701                policy: ir::Expression::None,
1702                asset_name: ir::Expression::None,
1703                amount: ir::Expression::Number(100),
1704            }]),
1705            ir::Expression::Assets(vec![ir::AssetExpr {
1706                policy: ir::Expression::None,
1707                asset_name: ir::Expression::None,
1708                amount: ir::Expression::Number(200),
1709            }]),
1710        )));
1711
1712        let reduced = op.reduce().unwrap();
1713
1714        match reduced {
1715            ir::Expression::Assets(assets) => {
1716                assert_eq!(assets.len(), 1);
1717                assert_eq!(assets[0].policy, ir::Expression::None);
1718                assert_eq!(assets[0].asset_name, ir::Expression::None);
1719                assert_eq!(assets[0].amount, ir::Expression::Number(300));
1720            }
1721            _ => panic!("Expected assets"),
1722        };
1723    }
1724
1725    #[test]
1726    fn test_reduce_mixed_asset_binary_op() {
1727        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1728            ir::Expression::Assets(vec![ir::AssetExpr {
1729                policy: ir::Expression::None,
1730                asset_name: ir::Expression::None,
1731                amount: ir::Expression::Number(100),
1732            }]),
1733            ir::Expression::Assets(vec![ir::AssetExpr {
1734                policy: ir::Expression::Bytes(b"abc".to_vec()),
1735                asset_name: ir::Expression::Bytes(b"111".to_vec()),
1736                amount: ir::Expression::Number(200),
1737            }]),
1738        )));
1739
1740        let reduced = op.reduce().unwrap();
1741
1742        match reduced {
1743            ir::Expression::Assets(assets) => {
1744                assert_eq!(assets.len(), 2);
1745
1746                for asset in assets {
1747                    if asset.policy == ir::Expression::None {
1748                        assert_eq!(asset.asset_name, ir::Expression::None);
1749                        assert_eq!(asset.amount, ir::Expression::Number(100));
1750                    } else {
1751                        assert_eq!(asset.policy, ir::Expression::Bytes(b"abc".to_vec()));
1752                        assert_eq!(asset.asset_name, ir::Expression::Bytes(b"111".to_vec()));
1753                        assert_eq!(asset.amount, ir::Expression::Number(200));
1754                    }
1755                }
1756            }
1757            _ => panic!("Expected assets"),
1758        };
1759    }
1760
1761    #[test]
1762    fn test_reduce_coerce_noop() {
1763        let op = ir::Expression::EvalCoerce(Box::new(ir::Coerce::NoOp(ir::Expression::Number(5))));
1764
1765        let reduced = op.reduce().unwrap();
1766
1767        match reduced {
1768            ir::Expression::Number(5) => (),
1769            _ => panic!("Expected number 5"),
1770        };
1771    }
1772
1773    #[test]
1774    fn test_coerce_utxo_set_into_assets() {
1775        let utxos = vec![Utxo {
1776            r#ref: UtxoRef::new(b"abc", 1),
1777            address: b"abc".into(),
1778            datum: Some(ir::Expression::Number(1)),
1779            assets: CanonicalAssets::from_defined_asset(b"abc", b"111", 1),
1780            script: None,
1781        }];
1782
1783        let op = ir::Coerce::IntoAssets(ir::Expression::UtxoSet(HashSet::from_iter(
1784            utxos.clone().into_iter(),
1785        )));
1786
1787        let reduced = op.reduce().unwrap();
1788
1789        assert_eq!(
1790            reduced,
1791            ir::Coerce::NoOp(ir::Expression::Assets(utxos[0].assets.clone().into()))
1792        );
1793    }
1794
1795    #[test]
1796    fn test_coerce_utxo_set_into_datum() {
1797        let utxos = vec![Utxo {
1798            r#ref: UtxoRef::new(b"abc", 1),
1799            address: b"abc".into(),
1800            datum: Some(ir::Expression::Number(1)),
1801            assets: CanonicalAssets::from_naked_amount(1),
1802            script: None,
1803        }];
1804
1805        let op = ir::Coerce::IntoDatum(ir::Expression::UtxoSet(HashSet::from_iter(
1806            utxos.clone().into_iter(),
1807        )));
1808
1809        let reduced = op.reduce().unwrap();
1810
1811        assert_eq!(reduced, ir::Coerce::NoOp(utxos[0].datum.clone().unwrap()));
1812    }
1813
1814    #[test]
1815    fn test_reduce_struct_property_access() {
1816        let object = ir::Expression::Struct(ir::StructExpr {
1817            constructor: 0,
1818            fields: vec![
1819                ir::Expression::Number(1),
1820                ir::Expression::Number(2),
1821                ir::Expression::Number(3),
1822            ],
1823        });
1824
1825        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
1826            object.clone(),
1827            Expression::Number(1),
1828        )));
1829
1830        let reduced = op.reduce();
1831
1832        match reduced {
1833            Ok(ir::Expression::Number(2)) => (),
1834            _ => panic!("Expected number 2"),
1835        };
1836
1837        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
1838            object.clone(),
1839            Expression::Number(100),
1840        )));
1841
1842        let reduced = op.reduce();
1843
1844        match reduced {
1845            Err(Error::PropertyIndexNotFound(100, _)) => (),
1846            _ => panic!("Expected property index not found"),
1847        };
1848    }
1849
1850    #[test]
1851    fn test_reduce_list_property_access() {
1852        let object = ir::Expression::List(vec![
1853            ir::Expression::Number(1),
1854            ir::Expression::Number(2),
1855            ir::Expression::Number(3),
1856        ]);
1857
1858        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
1859            object.clone(),
1860            Expression::Number(1),
1861        )));
1862
1863        let reduced = op.reduce();
1864
1865        match reduced {
1866            Ok(ir::Expression::Number(2)) => (),
1867            _ => panic!("Expected number 2"),
1868        };
1869
1870        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
1871            object.clone(),
1872            ir::Expression::Number(100),
1873        )));
1874
1875        let reduced = op.reduce();
1876
1877        match reduced {
1878            Err(Error::PropertyIndexNotFound(100, _)) => (),
1879            _ => panic!("Expected property index not found"),
1880        };
1881    }
1882
1883    #[test]
1884    fn test_reduce_tuple_property_access() {
1885        let object = ir::Expression::Tuple(Box::new((
1886            ir::Expression::Number(1),
1887            ir::Expression::Number(2),
1888        )));
1889
1890        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
1891            object.clone(),
1892            Expression::Number(1),
1893        )));
1894
1895        let reduced = op.reduce();
1896
1897        match reduced {
1898            Ok(ir::Expression::Number(2)) => (),
1899            _ => panic!("Expected number 2"),
1900        };
1901
1902        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
1903            object.clone(),
1904            Expression::Number(100),
1905        )));
1906
1907        let reduced = op.reduce();
1908
1909        match reduced {
1910            Err(Error::PropertyIndexNotFound(100, _)) => (),
1911            _ => panic!("Expected property index not found"),
1912        };
1913    }
1914
1915    #[test]
1916    fn test_string_concat_is_reduced() {
1917        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1918            ir::Expression::String("hello".to_string()),
1919            ir::Expression::String("world".to_string()),
1920        )));
1921
1922        let reduced = op.reduce().unwrap();
1923
1924        match reduced {
1925            ir::Expression::String(s) => assert_eq!(s, "helloworld"),
1926            _ => panic!("Expected string 'helloworld'"),
1927        }
1928    }
1929
1930    #[test]
1931    fn test_bytes_concat_is_reduced() {
1932        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1933            ir::Expression::Bytes(vec![1, 2, 3]),
1934            ir::Expression::Bytes(vec![4, 5, 6]),
1935        )));
1936
1937        let reduced = op.reduce().unwrap();
1938
1939        match reduced {
1940            ir::Expression::Bytes(b) => assert_eq!(b, vec![1, 2, 3, 4, 5, 6]),
1941            _ => panic!("Expected bytes [1, 2, 3, 4, 5, 6]"),
1942        }
1943    }
1944
1945    #[test]
1946    fn test_list_concat_is_reduced() {
1947        let op = Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1948            Expression::List(vec![Expression::Number(1)]),
1949            Expression::List(vec![Expression::Number(2)]),
1950        )));
1951
1952        let reduced = op.reduce().unwrap();
1953
1954        match reduced {
1955            ir::Expression::List(b) => {
1956                assert_eq!(b, vec![Expression::Number(1), Expression::Number(2)])
1957            }
1958            _ => panic!("Expected List [Number(1), Number(2)"),
1959        }
1960    }
1961
1962    #[test]
1963    fn test_concat_type_mismatch_error() {
1964        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1965            ir::Expression::String("hello".to_string()),
1966            ir::Expression::Bytes(vec![1, 2, 3]),
1967        )));
1968
1969        let reduced = op.reduce();
1970
1971        match reduced {
1972            Err(Error::InvalidBinaryOp(op, _, _)) => assert_eq!(op, "concat"),
1973            _ => panic!("Expected InvalidBinaryOp error"),
1974        }
1975    }
1976
1977    #[test]
1978    fn test_concat_with_none() {
1979        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Concat(
1980            ir::Expression::String("hello".to_string()),
1981            ir::Expression::None,
1982        )));
1983
1984        let reduced = op.reduce().unwrap();
1985
1986        match reduced {
1987            ir::Expression::String(s) => assert_eq!(s, "hello"),
1988            _ => panic!("Expected string 'hello'"),
1989        }
1990    }
1991
1992    #[test]
1993    fn test_min_utxo_add_non_reduction() {
1994        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Add(
1995            ir::Expression::Assets(vec![ir::AssetExpr {
1996                policy: ir::Expression::None,
1997                asset_name: ir::Expression::None,
1998                amount: ir::Expression::Number(29),
1999            }]),
2000            ir::Expression::EvalCompiler(Box::new(ir::CompilerOp::ComputeMinUtxo(
2001                ir::Expression::Number(20),
2002            ))),
2003        )));
2004
2005        let reduced = op.clone().reduce().unwrap();
2006
2007        assert!(op == reduced)
2008    }
2009
2010    #[test]
2011    fn test_min_utxo_sub_non_reduction() {
2012        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Sub(
2013            ir::Expression::Assets(vec![ir::AssetExpr {
2014                policy: ir::Expression::None,
2015                asset_name: ir::Expression::None,
2016                amount: ir::Expression::Number(29),
2017            }]),
2018            ir::Expression::EvalCompiler(Box::new(ir::CompilerOp::ComputeMinUtxo(
2019                ir::Expression::Number(20),
2020            ))),
2021        )));
2022
2023        let reduced = op.clone().reduce().unwrap();
2024
2025        assert!(op == reduced)
2026    }
2027
2028    #[test]
2029    fn test_index_list_with_expression() {
2030        let list = ir::Expression::List(vec![
2031            ir::Expression::String("first".to_string()),
2032            ir::Expression::String("second".to_string()),
2033            ir::Expression::String("third".to_string()),
2034        ]);
2035
2036        let index_expr = ir::Expression::Number(1);
2037        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
2038            list.clone(),
2039            index_expr,
2040        )));
2041
2042        let reduced = op.reduce().unwrap();
2043
2044        match reduced {
2045            ir::Expression::String(s) => assert_eq!(s, "second"),
2046            _ => panic!("Expected string 'second'"),
2047        }
2048    }
2049
2050    #[test]
2051    fn test_index_list_out_of_bounds() {
2052        let list = ir::Expression::List(vec![ir::Expression::Number(1), ir::Expression::Number(2)]);
2053
2054        let index_expr = ir::Expression::Number(5);
2055        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
2056            list.clone(),
2057            index_expr,
2058        )));
2059
2060        let reduced = op.reduce();
2061
2062        match reduced {
2063            Err(Error::PropertyIndexNotFound(5, _)) => (),
2064            _ => panic!("Expected PropertyIndexNotFound error"),
2065        }
2066    }
2067
2068    #[test]
2069    fn test_index_tuple_with_expression() {
2070        let tuple = ir::Expression::Tuple(Box::new((
2071            ir::Expression::String("left".to_string()),
2072            ir::Expression::String("right".to_string()),
2073        )));
2074
2075        let index_expr = ir::Expression::Number(0);
2076        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
2077            tuple.clone(),
2078            index_expr,
2079        )));
2080
2081        let reduced = op.reduce().unwrap();
2082
2083        match reduced {
2084            ir::Expression::String(s) => assert_eq!(s, "left"),
2085            _ => panic!("Expected string 'left'"),
2086        }
2087
2088        let index_expr = ir::Expression::Number(1);
2089        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
2090            tuple.clone(),
2091            index_expr,
2092        )));
2093
2094        let reduced = op.reduce().unwrap();
2095
2096        match reduced {
2097            ir::Expression::String(s) => assert_eq!(s, "right"),
2098            _ => panic!("Expected string 'right'"),
2099        }
2100    }
2101
2102    #[test]
2103    fn test_index_struct_with_expression() {
2104        let struct_expr = ir::Expression::Struct(ir::StructExpr {
2105            constructor: 0,
2106            fields: vec![
2107                ir::Expression::String("field0".to_string()),
2108                ir::Expression::String("field1".to_string()),
2109                ir::Expression::String("field2".to_string()),
2110            ],
2111        });
2112
2113        let index_expr = ir::Expression::Number(1);
2114        let op = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
2115            struct_expr.clone(),
2116            index_expr,
2117        )));
2118
2119        let reduced = op.reduce().unwrap();
2120
2121        match reduced {
2122            ir::Expression::String(s) => assert_eq!(s, "field1"),
2123            _ => panic!("Expected string 'field1'"),
2124        }
2125    }
2126
2127    #[test]
2128    fn test_index_none_expression() {
2129        let none_expr = ir::Expression::None;
2130        let index_expr = ir::Expression::Number(0);
2131
2132        let op =
2133            ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(none_expr, index_expr)));
2134
2135        let reduced = op.reduce();
2136
2137        match reduced {
2138            Err(Error::PropertyIndexNotFound(0, _)) => (),
2139            _ => panic!("Expected PropertyIndexNotFound error for None expression"),
2140        }
2141    }
2142
2143    #[test]
2144    fn test_indexable_trait_on_expression() {
2145        let list_expr =
2146            ir::Expression::List(vec![ir::Expression::Number(10), ir::Expression::Number(20)]);
2147
2148        let result = list_expr.index(ir::Expression::Number(0));
2149        assert_eq!(result, Some(ir::Expression::Number(10)));
2150
2151        let result = list_expr.index(ir::Expression::Number(1));
2152        assert_eq!(result, Some(ir::Expression::Number(20)));
2153
2154        let result = list_expr.index(ir::Expression::Number(2));
2155        assert_eq!(result, None);
2156
2157        let tuple_expr = ir::Expression::Tuple(Box::new((
2158            ir::Expression::Number(100),
2159            ir::Expression::Number(200),
2160        )));
2161
2162        let result = tuple_expr.index(ir::Expression::Number(0));
2163        assert_eq!(result, Some(ir::Expression::Number(100)));
2164
2165        let result = tuple_expr.index(ir::Expression::Number(1));
2166        assert_eq!(result, Some(ir::Expression::Number(200)));
2167
2168        let result = tuple_expr.index(ir::Expression::Number(2));
2169        assert_eq!(result, None);
2170    }
2171
2172    #[test]
2173    fn test_nested_property_access() {
2174        let nested_expr = ir::Expression::List(vec![
2175            ir::Expression::Tuple(Box::new((
2176                ir::Expression::Number(1),
2177                ir::Expression::Number(2),
2178            ))),
2179            ir::Expression::Tuple(Box::new((
2180                ir::Expression::Number(3),
2181                ir::Expression::Number(4),
2182            ))),
2183        ]);
2184
2185        // Access nested_expr[1][0] (should be 3)
2186        let first_access = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
2187            nested_expr,
2188            ir::Expression::Number(1),
2189        )));
2190
2191        let second_access = ir::Expression::EvalBuiltIn(Box::new(ir::BuiltInOp::Property(
2192            first_access,
2193            ir::Expression::Number(0),
2194        )));
2195
2196        let reduced = second_access.reduce().unwrap();
2197
2198        match reduced {
2199            ir::Expression::Number(n) => assert_eq!(n, 3),
2200            _ => panic!("Expected number 3"),
2201        }
2202    }
2203}