Skip to main content

tx3_lang/
ast.rs

1//! The Tx3 language abstract syntax tree (AST).
2//!
3//! This module defines the abstract syntax tree (AST) for the Tx3 language.
4//! It provides the structure for representing Tx3 programs, including
5//! transactions, types, assets, and other constructs.
6//!
7//! This module is not intended to be used directly by end-users. See
8//! [`parse_file`](crate::parse_file) and [`parse_string`](crate::parse_string)
9//! for parsing Tx3 source code into an AST.
10
11use serde::{Deserialize, Serialize};
12use std::{collections::HashMap, rc::Rc};
13
14#[derive(Debug, PartialEq, Eq)]
15pub struct Scope {
16    pub(crate) symbols: HashMap<String, Symbol>,
17    pub(crate) parent: Option<Rc<Scope>>,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum Symbol {
22    EnvVar(String, Box<Type>),
23    ParamVar(String, Box<Type>),
24    LocalExpr(Box<DataExpr>),
25    Output(usize),
26    Input(Box<InputBlock>),
27    Reference(Box<ReferenceBlock>),
28    PartyDef(Box<PartyDef>),
29    PolicyDef(Box<PolicyDef>),
30    AssetDef(Box<AssetDef>),
31    TypeDef(Box<TypeDef>),
32    AliasDef(Box<AliasDef>),
33    RecordField(Box<RecordField>),
34    VariantCase(Box<VariantCase>),
35    Function(String),
36    Fees,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct Span {
41    dummy: bool,
42    pub start: usize,
43    pub end: usize,
44}
45
46impl Default for Span {
47    fn default() -> Self {
48        Self::DUMMY
49    }
50}
51
52impl Eq for Span {}
53
54impl PartialEq for Span {
55    fn eq(&self, other: &Self) -> bool {
56        if self.dummy || other.dummy {
57            return true;
58        }
59
60        self.start == other.start && self.end == other.end
61    }
62}
63
64impl std::hash::Hash for Span {
65    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
66        self.start.hash(state);
67        self.end.hash(state);
68    }
69}
70
71impl Span {
72    pub const DUMMY: Self = Self {
73        dummy: true,
74        start: 0,
75        end: 0,
76    };
77
78    pub fn new(start: usize, end: usize) -> Self {
79        Self {
80            dummy: false,
81            start,
82            end,
83        }
84    }
85}
86
87impl Symbol {
88    pub fn as_type_def(&self) -> Option<&TypeDef> {
89        match self {
90            Symbol::TypeDef(x) => Some(x.as_ref()),
91            _ => None,
92        }
93    }
94
95    pub fn as_alias_def(&self) -> Option<&AliasDef> {
96        match self {
97            Symbol::AliasDef(x) => Some(x.as_ref()),
98            _ => None,
99        }
100    }
101
102    pub fn as_variant_case(&self) -> Option<&VariantCase> {
103        match self {
104            Symbol::VariantCase(x) => Some(x.as_ref()),
105            _ => None,
106        }
107    }
108
109    pub fn as_field_def(&self) -> Option<&RecordField> {
110        match self {
111            Symbol::RecordField(x) => Some(x.as_ref()),
112            _ => None,
113        }
114    }
115
116    pub fn as_policy_def(&self) -> Option<&PolicyDef> {
117        match self {
118            Symbol::PolicyDef(x) => Some(x.as_ref()),
119            _ => None,
120        }
121    }
122
123    pub fn target_type(&self) -> Option<Type> {
124        match self {
125            Symbol::ParamVar(_, ty) => Some(ty.as_ref().clone()),
126            Symbol::RecordField(x) => Some(x.r#type.clone()),
127            Symbol::Input(x) => x.datum_is().cloned(),
128            Symbol::Reference(x) => x.datum_is.clone(),
129            x => {
130                dbg!(x);
131                None
132            }
133        }
134    }
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
138pub struct Identifier {
139    pub value: String,
140    pub span: Span,
141
142    // analysis
143    #[serde(skip)]
144    pub(crate) symbol: Option<Symbol>,
145}
146
147impl Identifier {
148    pub fn new(value: impl Into<String>) -> Self {
149        Self {
150            value: value.into(),
151            symbol: None,
152            span: Span::DUMMY,
153        }
154    }
155
156    pub fn try_symbol(&self) -> Result<&Symbol, crate::lowering::Error> {
157        match &self.symbol {
158            Some(symbol) => Ok(symbol),
159            None => Err(crate::lowering::Error::MissingAnalyzePhase(
160                self.value.clone(),
161            )),
162        }
163    }
164
165    pub fn target_type(&self) -> Option<Type> {
166        self.symbol.as_ref().and_then(|x| x.target_type())
167    }
168}
169
170impl AsRef<str> for Identifier {
171    fn as_ref(&self) -> &str {
172        &self.value
173    }
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
177pub struct Program {
178    pub env: Option<EnvDef>,
179    pub txs: Vec<TxDef>,
180    pub types: Vec<TypeDef>,
181    pub aliases: Vec<AliasDef>,
182    pub assets: Vec<AssetDef>,
183    pub parties: Vec<PartyDef>,
184    pub policies: Vec<PolicyDef>,
185    pub span: Span,
186
187    // analysis
188    #[serde(skip)]
189    pub(crate) scope: Option<Rc<Scope>>,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
193pub struct EnvField {
194    pub name: String,
195    pub r#type: Type,
196    pub span: Span,
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
200pub struct EnvDef {
201    pub fields: Vec<EnvField>,
202    pub span: Span,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
206pub struct ParameterList {
207    pub parameters: Vec<ParamDef>,
208    pub span: Span,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
212pub struct TxDef {
213    pub name: Identifier,
214    pub parameters: ParameterList,
215    pub locals: Option<LocalsBlock>,
216    pub references: Vec<ReferenceBlock>,
217    pub inputs: Vec<InputBlock>,
218    pub outputs: Vec<OutputBlock>,
219    pub validity: Option<ValidityBlock>,
220    pub mints: Vec<MintBlock>,
221    pub burns: Vec<MintBlock>,
222    pub signers: Option<SignersBlock>,
223    pub adhoc: Vec<ChainSpecificBlock>,
224    pub span: Span,
225    pub collateral: Vec<CollateralBlock>,
226    pub metadata: Option<MetadataBlock>,
227
228    // analysis
229    #[serde(skip)]
230    pub(crate) scope: Option<Rc<Scope>>,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
234pub struct LocalsAssign {
235    pub name: Identifier,
236    pub value: DataExpr,
237    pub span: Span,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
241pub struct LocalsBlock {
242    pub assigns: Vec<LocalsAssign>,
243    pub span: Span,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
247pub struct StringLiteral {
248    pub value: String,
249    pub span: Span,
250}
251
252impl StringLiteral {
253    pub fn new(value: impl Into<String>) -> Self {
254        Self {
255            value: value.into(),
256            span: Span::DUMMY,
257        }
258    }
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
262pub struct HexStringLiteral {
263    pub value: String,
264    pub span: Span,
265}
266
267impl HexStringLiteral {
268    pub fn new(value: impl Into<String>) -> Self {
269        Self {
270            value: value.into(),
271            span: Span::DUMMY,
272        }
273    }
274}
275
276#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
277pub enum CollateralBlockField {
278    From(DataExpr),
279    MinAmount(DataExpr),
280    Ref(DataExpr),
281}
282
283impl CollateralBlockField {
284    fn key(&self) -> &str {
285        match self {
286            CollateralBlockField::From(_) => "from",
287            CollateralBlockField::MinAmount(_) => "min_amount",
288            CollateralBlockField::Ref(_) => "ref",
289        }
290    }
291
292    pub fn as_data_expr(&self) -> Option<&DataExpr> {
293        match self {
294            CollateralBlockField::Ref(x) => Some(x),
295            _ => None,
296        }
297    }
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
301pub struct CollateralBlock {
302    pub fields: Vec<CollateralBlockField>,
303    pub span: Span,
304}
305
306impl CollateralBlock {
307    pub(crate) fn find(&self, key: &str) -> Option<&CollateralBlockField> {
308        self.fields.iter().find(|x| x.key() == key)
309    }
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
313pub enum InputBlockField {
314    From(DataExpr),
315    DatumIs(Type),
316    MinAmount(DataExpr),
317    Redeemer(DataExpr),
318    Ref(DataExpr),
319}
320
321impl InputBlockField {
322    fn key(&self) -> &str {
323        match self {
324            InputBlockField::From(_) => "from",
325            InputBlockField::DatumIs(_) => "datum_is",
326            InputBlockField::MinAmount(_) => "min_amount",
327            InputBlockField::Redeemer(_) => "redeemer",
328            InputBlockField::Ref(_) => "ref",
329        }
330    }
331
332    pub fn as_data_expr(&self) -> Option<&DataExpr> {
333        match self {
334            InputBlockField::Redeemer(x) => Some(x),
335            InputBlockField::Ref(x) => Some(x),
336            _ => None,
337        }
338    }
339
340    pub fn as_datum_type(&self) -> Option<&Type> {
341        match self {
342            InputBlockField::DatumIs(x) => Some(x),
343            _ => None,
344        }
345    }
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
349pub struct ReferenceBlock {
350    pub name: String,
351    pub r#ref: DataExpr,
352    pub datum_is: Option<Type>,
353    pub span: Span,
354}
355
356#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
357pub struct MetadataBlockField {
358    pub key: DataExpr,
359    pub value: DataExpr,
360    pub span: Span,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
364pub struct MetadataBlock {
365    pub fields: Vec<MetadataBlockField>,
366    pub span: Span,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
370pub struct InputBlock {
371    pub name: String,
372    pub many: bool,
373    pub fields: Vec<InputBlockField>,
374    pub span: Span,
375}
376
377impl InputBlock {
378    pub(crate) fn find(&self, key: &str) -> Option<&InputBlockField> {
379        self.fields.iter().find(|x| x.key() == key)
380    }
381
382    pub(crate) fn datum_is(&self) -> Option<&Type> {
383        self.find("datum_is").and_then(|x| x.as_datum_type())
384    }
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
388pub enum OutputBlockField {
389    To(Box<DataExpr>),
390    Amount(Box<DataExpr>),
391    Datum(Box<DataExpr>),
392}
393
394impl OutputBlockField {
395    fn key(&self) -> &str {
396        match self {
397            OutputBlockField::To(_) => "to",
398            OutputBlockField::Amount(_) => "amount",
399            OutputBlockField::Datum(_) => "datum",
400        }
401    }
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
405pub struct OutputBlock {
406    pub name: Option<Identifier>,
407    pub optional: bool,
408    pub fields: Vec<OutputBlockField>,
409    pub span: Span,
410}
411
412impl OutputBlock {
413    pub(crate) fn find(&self, key: &str) -> Option<&OutputBlockField> {
414        self.fields.iter().find(|x| x.key() == key)
415    }
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
419pub enum ValidityBlockField {
420    UntilSlot(Box<DataExpr>),
421    SinceSlot(Box<DataExpr>),
422}
423
424impl ValidityBlockField {
425    fn key(&self) -> &str {
426        match self {
427            ValidityBlockField::UntilSlot(_) => "until_slot",
428            ValidityBlockField::SinceSlot(_) => "since_slot",
429        }
430    }
431}
432
433#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
434pub struct ValidityBlock {
435    pub fields: Vec<ValidityBlockField>,
436    pub span: Span,
437}
438
439impl ValidityBlock {
440    pub(crate) fn find(&self, key: &str) -> Option<&ValidityBlockField> {
441        self.fields.iter().find(|x| x.key() == key)
442    }
443}
444
445#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
446pub enum MintBlockField {
447    Amount(Box<DataExpr>),
448    Redeemer(Box<DataExpr>),
449}
450
451impl MintBlockField {
452    fn key(&self) -> &str {
453        match self {
454            MintBlockField::Amount(_) => "amount",
455            MintBlockField::Redeemer(_) => "redeemer",
456        }
457    }
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
461pub struct MintBlock {
462    pub fields: Vec<MintBlockField>,
463    pub span: Span,
464}
465
466#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
467pub struct SignersBlock {
468    pub signers: Vec<DataExpr>,
469    pub span: Span,
470}
471
472impl MintBlock {
473    pub(crate) fn find(&self, key: &str) -> Option<&MintBlockField> {
474        self.fields.iter().find(|x| x.key() == key)
475    }
476}
477
478#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
479pub struct RecordField {
480    pub name: Identifier,
481    pub r#type: Type,
482    pub span: Span,
483}
484
485impl RecordField {
486    pub fn new(name: &str, r#type: Type) -> Self {
487        Self {
488            name: Identifier::new(name),
489            r#type,
490            span: Span::DUMMY,
491        }
492    }
493}
494
495#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
496pub struct PartyDef {
497    pub name: Identifier,
498    pub span: Span,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
502pub struct PartyField {
503    pub name: String,
504    pub party_type: String,
505}
506
507#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
508pub struct PolicyDef {
509    pub name: Identifier,
510    pub value: PolicyValue,
511    pub span: Span,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
515pub enum PolicyField {
516    Hash(DataExpr),
517    Script(DataExpr),
518    Ref(DataExpr),
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
522pub struct PolicyConstructor {
523    pub fields: Vec<PolicyField>,
524    pub span: Span,
525}
526
527impl PolicyConstructor {
528    pub(crate) fn find_field(&self, field: &str) -> Option<&PolicyField> {
529        self.fields.iter().find(|x| match x {
530            PolicyField::Hash(_) => field == "hash",
531            PolicyField::Script(_) => field == "script",
532            PolicyField::Ref(_) => field == "ref",
533        })
534    }
535}
536
537#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
538pub enum PolicyValue {
539    Constructor(PolicyConstructor),
540    Assign(HexStringLiteral),
541}
542
543#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
544pub struct AnyAssetConstructor {
545    pub policy: Box<DataExpr>,
546    pub asset_name: Box<DataExpr>,
547    pub amount: Box<DataExpr>,
548    pub span: Span,
549}
550
551impl AnyAssetConstructor {
552    pub fn target_type(&self) -> Option<Type> {
553        Some(Type::AnyAsset)
554    }
555}
556
557#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
558pub struct RecordConstructorField {
559    pub name: Identifier,
560    pub value: Box<DataExpr>,
561    pub span: Span,
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
565pub struct StructConstructor {
566    pub r#type: Identifier,
567    pub case: VariantCaseConstructor,
568    pub span: Span,
569
570    // analysis
571    #[serde(skip)]
572    pub scope: Option<Rc<Scope>>,
573}
574
575impl StructConstructor {
576    pub fn target_type(&self) -> Option<Type> {
577        self.r#type.symbol.as_ref().and_then(|x| x.target_type())
578    }
579}
580
581#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
582pub struct VariantCaseConstructor {
583    pub name: Identifier,
584    pub fields: Vec<RecordConstructorField>,
585    pub spread: Option<Box<DataExpr>>,
586    pub span: Span,
587
588    // analysis
589    #[serde(skip)]
590    pub scope: Option<Rc<Scope>>,
591}
592
593impl VariantCaseConstructor {
594    pub fn find_field_value(&self, field: &str) -> Option<&DataExpr> {
595        self.fields
596            .iter()
597            .find(|x| x.name.value == field)
598            .map(|x| x.value.as_ref())
599    }
600}
601
602#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
603pub struct ListConstructor {
604    pub elements: Vec<DataExpr>,
605    pub span: Span,
606}
607
608impl ListConstructor {
609    pub fn target_type(&self) -> Option<Type> {
610        self.elements.first().and_then(|x| x.target_type())
611    }
612}
613
614#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
615pub struct MapField {
616    pub key: DataExpr,
617    pub value: DataExpr,
618    pub span: Span,
619}
620
621impl MapField {
622    pub fn target_type(&self) -> Option<Type> {
623        self.key.target_type()
624    }
625}
626
627#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
628pub struct MapConstructor {
629    pub fields: Vec<MapField>,
630    pub span: Span,
631}
632
633impl MapConstructor {
634    pub fn target_type(&self) -> Option<Type> {
635        if let Some(first_field) = self.fields.first() {
636            let key_type = first_field.key.target_type()?;
637            let value_type = first_field.value.target_type()?;
638            Some(Type::Map(Box::new(key_type), Box::new(value_type)))
639        } else {
640            None
641        }
642    }
643}
644
645#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
646pub struct UtxoRef {
647    pub txid: Vec<u8>,
648    pub index: u64,
649    pub span: Span,
650}
651
652#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
653pub struct NegateOp {
654    pub operand: Box<DataExpr>,
655    pub span: Span,
656}
657
658impl NegateOp {
659    pub fn target_type(&self) -> Option<Type> {
660        self.operand.target_type()
661    }
662}
663
664#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
665pub struct PropertyOp {
666    pub operand: Box<DataExpr>,
667    pub property: Box<DataExpr>,
668    pub span: Span,
669
670    // analysis
671    #[serde(skip)]
672    pub(crate) scope: Option<Rc<Scope>>,
673}
674
675impl PropertyOp {
676    pub fn target_type(&self) -> Option<Type> {
677        self.property.target_type()
678    }
679}
680
681#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
682pub struct AddOp {
683    pub lhs: Box<DataExpr>,
684    pub rhs: Box<DataExpr>,
685    pub span: Span,
686}
687
688impl AddOp {
689    pub fn target_type(&self) -> Option<Type> {
690        self.lhs.target_type()
691    }
692}
693
694#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
695pub struct SubOp {
696    pub lhs: Box<DataExpr>,
697    pub rhs: Box<DataExpr>,
698    pub span: Span,
699}
700
701impl SubOp {
702    pub fn target_type(&self) -> Option<Type> {
703        self.lhs.target_type()
704    }
705}
706
707#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
708pub struct ConcatOp {
709    pub lhs: Box<DataExpr>,
710    pub rhs: Box<DataExpr>,
711    pub span: Span,
712}
713
714impl ConcatOp {
715    pub fn target_type(&self) -> Option<Type> {
716        self.lhs.target_type()
717    }
718}
719
720#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
721pub struct FnCall {
722    pub callee: Identifier,
723    pub args: Vec<DataExpr>,
724    pub span: Span,
725}
726
727#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
728pub enum DataExpr {
729    None,
730    Unit,
731    Number(i64),
732    Bool(bool),
733    String(StringLiteral),
734    HexString(HexStringLiteral),
735    StructConstructor(StructConstructor),
736    ListConstructor(ListConstructor),
737    MapConstructor(MapConstructor),
738    AnyAssetConstructor(AnyAssetConstructor),
739    Identifier(Identifier),
740    MinUtxo(Identifier),
741    ComputeTipSlot,
742    SlotToTime(Box<DataExpr>),
743    TimeToSlot(Box<DataExpr>),
744    AddOp(AddOp),
745    SubOp(SubOp),
746    ConcatOp(ConcatOp),
747    NegateOp(NegateOp),
748    PropertyOp(PropertyOp),
749    UtxoRef(UtxoRef),
750    FnCall(FnCall),
751}
752
753impl DataExpr {
754    pub fn as_identifier(&self) -> Option<&Identifier> {
755        match self {
756            DataExpr::Identifier(x) => Some(x),
757            _ => None,
758        }
759    }
760
761    pub fn target_type(&self) -> Option<Type> {
762        match self {
763            DataExpr::Identifier(x) => x.target_type(),
764            DataExpr::None => Some(Type::Undefined),
765            DataExpr::Unit => Some(Type::Unit),
766            DataExpr::Number(_) => Some(Type::Int),
767            DataExpr::Bool(_) => Some(Type::Bool),
768            DataExpr::String(_) => Some(Type::Bytes),
769            DataExpr::HexString(_) => Some(Type::Bytes),
770            DataExpr::StructConstructor(x) => x.target_type(),
771            DataExpr::MapConstructor(x) => x.target_type(),
772            DataExpr::ListConstructor(x) => match x.target_type() {
773                Some(inner) => Some(Type::List(Box::new(inner))),
774                None => None,
775            },
776            DataExpr::AddOp(x) => x.target_type(),
777            DataExpr::SubOp(x) => x.target_type(),
778            DataExpr::ConcatOp(x) => x.target_type(),
779            DataExpr::NegateOp(x) => x.target_type(),
780            DataExpr::PropertyOp(x) => x.target_type(),
781            DataExpr::AnyAssetConstructor(x) => x.target_type(),
782            DataExpr::UtxoRef(_) => Some(Type::UtxoRef),
783            DataExpr::MinUtxo(_) => Some(Type::AnyAsset),
784            DataExpr::ComputeTipSlot => Some(Type::Int),
785            DataExpr::SlotToTime(_) => Some(Type::Int),
786            DataExpr::TimeToSlot(_) => Some(Type::Int),
787            DataExpr::FnCall(_) => None, // Function call return type determined by symbol resolution
788        }
789    }
790}
791
792#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
793pub enum AddressExpr {
794    String(StringLiteral),
795    HexString(HexStringLiteral),
796    Identifier(Identifier),
797}
798
799impl AddressExpr {
800    pub fn as_identifier(&self) -> Option<&Identifier> {
801        match self {
802            AddressExpr::Identifier(x) => Some(x),
803            _ => None,
804        }
805    }
806}
807
808#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
809pub enum Type {
810    Undefined,
811    Unit,
812    Int,
813    Bool,
814    Bytes,
815    Address,
816    Utxo,
817    UtxoRef,
818    AnyAsset,
819    List(Box<Type>),
820    Map(Box<Type>, Box<Type>),
821    Custom(Identifier),
822}
823
824impl std::fmt::Display for Type {
825    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
826        match self {
827            Type::Undefined => write!(f, "Undefined"),
828            Type::Unit => write!(f, "Unit"),
829            Type::Int => write!(f, "Int"),
830            Type::Bool => write!(f, "Bool"),
831            Type::Bytes => write!(f, "Bytes"),
832            Type::Address => write!(f, "Address"),
833            Type::UtxoRef => write!(f, "UtxoRef"),
834            Type::AnyAsset => write!(f, "AnyAsset"),
835            Type::Utxo => write!(f, "Utxo"),
836            Type::Map(key, value) => write!(f, "Map<{}, {}>", key, value),
837            Type::List(inner) => write!(f, "List<{inner}>"),
838            Type::Custom(id) => write!(f, "{}", id.value),
839        }
840    }
841}
842
843impl Type {
844    pub fn properties(&self) -> Vec<(String, Type)> {
845        match self {
846            Type::AnyAsset => {
847                vec![
848                    ("amount".to_string(), Type::Int),
849                    ("policy".to_string(), Type::Bytes),
850                    ("asset_name".to_string(), Type::Bytes),
851                ]
852            }
853            Type::UtxoRef => {
854                vec![
855                    ("tx_hash".to_string(), Type::Bytes),
856                    ("output_index".to_string(), Type::Int),
857                ]
858            }
859            Type::Custom(identifier) => {
860                let def = identifier.symbol.as_ref().and_then(|s| s.as_type_def());
861
862                match def {
863                    Some(ty) if ty.cases.len() == 1 => ty.cases[0]
864                        .fields
865                        .iter()
866                        .map(|f| (f.name.value.clone(), f.r#type.clone()))
867                        .collect(),
868                    _ => vec![],
869                }
870            }
871            _ => vec![],
872        }
873    }
874
875    pub fn property_index(&self, property: DataExpr) -> Option<DataExpr> {
876        match self {
877            Type::AnyAsset | Type::UtxoRef | Type::Custom(_) => {
878                let identifier = property.as_identifier()?;
879                let properties = Self::properties(self);
880                properties
881                    .iter()
882                    .position(|(name, _)| name == &identifier.value)
883                    .map(|index| DataExpr::Number(index as i64))
884            }
885            Type::List(_) => property
886                .target_type()
887                .filter(|ty| *ty == Type::Int)
888                .map(|_| property),
889            _ => None,
890        }
891    }
892}
893
894#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
895pub struct ParamDef {
896    pub name: Identifier,
897    pub r#type: Type,
898}
899
900#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
901pub struct AliasDef {
902    pub name: Identifier,
903    pub alias_type: Type,
904    pub span: Span,
905}
906
907impl AliasDef {
908    pub fn resolve_alias_chain(&self) -> Option<&TypeDef> {
909        match &self.alias_type {
910            Type::Custom(identifier) => match &identifier.symbol {
911                Some(Symbol::TypeDef(type_def)) => Some(type_def),
912                Some(Symbol::AliasDef(next_alias)) => next_alias.resolve_alias_chain(),
913                _ => None,
914            },
915            _ => None,
916        }
917    }
918
919    pub fn is_alias_chain_resolved(&self) -> bool {
920        self.resolve_alias_chain().is_some()
921    }
922}
923
924#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
925pub struct TypeDef {
926    pub name: Identifier,
927    pub cases: Vec<VariantCase>,
928    pub span: Span,
929}
930
931impl TypeDef {
932    pub(crate) fn find_case_index(&self, case: &str) -> Option<usize> {
933        self.cases.iter().position(|x| x.name.value == case)
934    }
935
936    #[allow(dead_code)]
937    pub(crate) fn find_case(&self, case: &str) -> Option<&VariantCase> {
938        self.cases.iter().find(|x| x.name.value == case)
939    }
940}
941
942#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
943pub struct VariantCase {
944    pub name: Identifier,
945    pub fields: Vec<RecordField>,
946    pub span: Span,
947}
948
949impl VariantCase {
950    #[allow(dead_code)]
951    pub(crate) fn find_field_index(&self, field: &str) -> Option<usize> {
952        self.fields.iter().position(|x| x.name.value == field)
953    }
954
955    #[allow(dead_code)]
956    pub(crate) fn find_field(&self, field: &str) -> Option<&RecordField> {
957        self.fields.iter().find(|x| x.name.value == field)
958    }
959}
960
961#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
962pub struct AssetDef {
963    pub name: Identifier,
964    pub policy: DataExpr,
965    pub asset_name: DataExpr,
966    pub span: Span,
967}
968
969#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
970pub enum ChainSpecificBlock {
971    Cardano(crate::cardano::CardanoBlock),
972}