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