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