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    FunctionDef(Box<FnDef>),
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 as_fn_def(&self) -> Option<&FnDef> {
124        match self {
125            Symbol::FunctionDef(x) => Some(x.as_ref()),
126            _ => None,
127        }
128    }
129
130    pub fn target_type(&self) -> Option<Type> {
131        match self {
132            Symbol::ParamVar(_, ty) => Some(ty.as_ref().clone()),
133            Symbol::RecordField(x) => Some(x.r#type.clone()),
134            Symbol::Input(x) => x.datum_is().cloned(),
135            Symbol::Reference(x) => x.datum_is.clone(),
136            x => {
137                dbg!(x);
138                None
139            }
140        }
141    }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
145pub struct Identifier {
146    pub value: String,
147    pub span: Span,
148
149    // analysis
150    #[serde(skip)]
151    pub(crate) symbol: Option<Symbol>,
152}
153
154impl Identifier {
155    pub fn new(value: impl Into<String>) -> Self {
156        Self {
157            value: value.into(),
158            symbol: None,
159            span: Span::DUMMY,
160        }
161    }
162
163    pub fn try_symbol(&self) -> Result<&Symbol, crate::lowering::Error> {
164        match &self.symbol {
165            Some(symbol) => Ok(symbol),
166            None => Err(crate::lowering::Error::MissingAnalyzePhase(
167                self.value.clone(),
168            )),
169        }
170    }
171
172    pub fn target_type(&self) -> Option<Type> {
173        self.symbol.as_ref().and_then(|x| x.target_type())
174    }
175}
176
177impl AsRef<str> for Identifier {
178    fn as_ref(&self) -> &str {
179        &self.value
180    }
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
184pub struct Program {
185    pub env: Option<EnvDef>,
186    pub txs: Vec<TxDef>,
187    pub types: Vec<TypeDef>,
188    pub aliases: Vec<AliasDef>,
189    pub assets: Vec<AssetDef>,
190    pub parties: Vec<PartyDef>,
191    pub policies: Vec<PolicyDef>,
192    #[serde(default)]
193    pub functions: Vec<FnDef>,
194    pub span: Span,
195
196    // analysis
197    #[serde(skip)]
198    pub(crate) scope: Option<Rc<Scope>>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
202pub struct EnvField {
203    pub name: String,
204    pub r#type: Type,
205    #[serde(default, skip_serializing_if = "Option::is_none")]
206    pub docstring: Option<String>,
207    pub span: Span,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
211pub struct EnvDef {
212    pub fields: Vec<EnvField>,
213    pub span: Span,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
217pub struct ParameterList {
218    pub parameters: Vec<ParamDef>,
219    pub span: Span,
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
223pub struct TxDef {
224    pub name: Identifier,
225    #[serde(default, skip_serializing_if = "Option::is_none")]
226    pub docstring: Option<String>,
227    pub parameters: ParameterList,
228    pub locals: Option<LocalsBlock>,
229    pub references: Vec<ReferenceBlock>,
230    pub inputs: Vec<InputBlock>,
231    pub outputs: Vec<OutputBlock>,
232    pub validity: Option<ValidityBlock>,
233    pub mints: Vec<MintBlock>,
234    pub burns: Vec<MintBlock>,
235    pub signers: Option<SignersBlock>,
236    pub adhoc: Vec<ChainSpecificBlock>,
237    pub span: Span,
238    pub collateral: Vec<CollateralBlock>,
239    pub metadata: Option<MetadataBlock>,
240
241    // analysis
242    #[serde(skip)]
243    pub(crate) scope: Option<Rc<Scope>>,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
247pub struct LocalsAssign {
248    pub name: Identifier,
249    pub value: DataExpr,
250    pub span: Span,
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
254pub struct LocalsBlock {
255    pub assigns: Vec<LocalsAssign>,
256    pub span: Span,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
260pub struct StringLiteral {
261    pub value: String,
262    pub span: Span,
263}
264
265impl StringLiteral {
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, Hash)]
275pub struct HexStringLiteral {
276    pub value: String,
277    pub span: Span,
278}
279
280impl HexStringLiteral {
281    pub fn new(value: impl Into<String>) -> Self {
282        Self {
283            value: value.into(),
284            span: Span::DUMMY,
285        }
286    }
287}
288
289#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
290pub enum CollateralBlockField {
291    From(DataExpr),
292    MinAmount(DataExpr),
293    Ref(DataExpr),
294}
295
296impl CollateralBlockField {
297    fn key(&self) -> &str {
298        match self {
299            CollateralBlockField::From(_) => "from",
300            CollateralBlockField::MinAmount(_) => "min_amount",
301            CollateralBlockField::Ref(_) => "ref",
302        }
303    }
304
305    pub fn as_data_expr(&self) -> Option<&DataExpr> {
306        match self {
307            CollateralBlockField::Ref(x) => Some(x),
308            _ => None,
309        }
310    }
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
314pub struct CollateralBlock {
315    pub fields: Vec<CollateralBlockField>,
316    pub span: Span,
317}
318
319impl CollateralBlock {
320    pub(crate) fn find(&self, key: &str) -> Option<&CollateralBlockField> {
321        self.fields.iter().find(|x| x.key() == key)
322    }
323}
324
325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
326pub enum InputBlockField {
327    From(DataExpr),
328    DatumIs(Type),
329    MinAmount(DataExpr),
330    Redeemer(DataExpr),
331    Ref(DataExpr),
332}
333
334impl InputBlockField {
335    fn key(&self) -> &str {
336        match self {
337            InputBlockField::From(_) => "from",
338            InputBlockField::DatumIs(_) => "datum_is",
339            InputBlockField::MinAmount(_) => "min_amount",
340            InputBlockField::Redeemer(_) => "redeemer",
341            InputBlockField::Ref(_) => "ref",
342        }
343    }
344
345    pub fn as_data_expr(&self) -> Option<&DataExpr> {
346        match self {
347            InputBlockField::Redeemer(x) => Some(x),
348            InputBlockField::Ref(x) => Some(x),
349            _ => None,
350        }
351    }
352
353    pub fn as_datum_type(&self) -> Option<&Type> {
354        match self {
355            InputBlockField::DatumIs(x) => Some(x),
356            _ => None,
357        }
358    }
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
362pub struct ReferenceBlock {
363    pub name: String,
364    pub r#ref: DataExpr,
365    pub datum_is: Option<Type>,
366    pub span: Span,
367}
368
369#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
370pub struct MetadataBlockField {
371    pub key: DataExpr,
372    pub value: DataExpr,
373    pub span: Span,
374}
375
376#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
377pub struct MetadataBlock {
378    pub fields: Vec<MetadataBlockField>,
379    pub span: Span,
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
383pub struct InputBlock {
384    pub name: String,
385    pub many: bool,
386    pub fields: Vec<InputBlockField>,
387    pub span: Span,
388}
389
390impl InputBlock {
391    pub(crate) fn find(&self, key: &str) -> Option<&InputBlockField> {
392        self.fields.iter().find(|x| x.key() == key)
393    }
394
395    pub(crate) fn datum_is(&self) -> Option<&Type> {
396        self.find("datum_is").and_then(|x| x.as_datum_type())
397    }
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
401pub enum OutputBlockField {
402    To(Box<DataExpr>),
403    Amount(Box<DataExpr>),
404    Datum(Box<DataExpr>),
405}
406
407impl OutputBlockField {
408    fn key(&self) -> &str {
409        match self {
410            OutputBlockField::To(_) => "to",
411            OutputBlockField::Amount(_) => "amount",
412            OutputBlockField::Datum(_) => "datum",
413        }
414    }
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
418pub struct OutputBlock {
419    pub name: Option<Identifier>,
420    pub optional: bool,
421    pub fields: Vec<OutputBlockField>,
422    pub span: Span,
423}
424
425impl OutputBlock {
426    pub(crate) fn find(&self, key: &str) -> Option<&OutputBlockField> {
427        self.fields.iter().find(|x| x.key() == key)
428    }
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
432pub enum ValidityBlockField {
433    UntilSlot(Box<DataExpr>),
434    SinceSlot(Box<DataExpr>),
435}
436
437impl ValidityBlockField {
438    fn key(&self) -> &str {
439        match self {
440            ValidityBlockField::UntilSlot(_) => "until_slot",
441            ValidityBlockField::SinceSlot(_) => "since_slot",
442        }
443    }
444}
445
446#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
447pub struct ValidityBlock {
448    pub fields: Vec<ValidityBlockField>,
449    pub span: Span,
450}
451
452impl ValidityBlock {
453    pub(crate) fn find(&self, key: &str) -> Option<&ValidityBlockField> {
454        self.fields.iter().find(|x| x.key() == key)
455    }
456}
457
458#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
459pub enum MintBlockField {
460    Amount(Box<DataExpr>),
461    Redeemer(Box<DataExpr>),
462}
463
464impl MintBlockField {
465    fn key(&self) -> &str {
466        match self {
467            MintBlockField::Amount(_) => "amount",
468            MintBlockField::Redeemer(_) => "redeemer",
469        }
470    }
471}
472
473#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
474pub struct MintBlock {
475    pub fields: Vec<MintBlockField>,
476    pub span: Span,
477}
478
479#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
480pub struct SignersBlock {
481    pub signers: Vec<DataExpr>,
482    pub span: Span,
483}
484
485impl MintBlock {
486    pub(crate) fn find(&self, key: &str) -> Option<&MintBlockField> {
487        self.fields.iter().find(|x| x.key() == key)
488    }
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
492pub struct RecordField {
493    pub name: Identifier,
494    pub r#type: Type,
495    pub span: Span,
496}
497
498impl RecordField {
499    pub fn new(name: &str, r#type: Type) -> Self {
500        Self {
501            name: Identifier::new(name),
502            r#type,
503            span: Span::DUMMY,
504        }
505    }
506}
507
508#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
509pub struct PartyDef {
510    pub name: Identifier,
511    #[serde(default, skip_serializing_if = "Option::is_none")]
512    pub docstring: Option<String>,
513    pub span: Span,
514}
515
516#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
517pub struct PartyField {
518    pub name: String,
519    pub party_type: String,
520}
521
522#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
523pub struct PolicyDef {
524    pub name: Identifier,
525    pub value: PolicyValue,
526    pub span: Span,
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
530pub enum PolicyField {
531    Hash(DataExpr),
532    Script(DataExpr),
533    Ref(DataExpr),
534}
535
536#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
537pub struct PolicyConstructor {
538    pub fields: Vec<PolicyField>,
539    pub span: Span,
540}
541
542impl PolicyConstructor {
543    pub(crate) fn find_field(&self, field: &str) -> Option<&PolicyField> {
544        self.fields.iter().find(|x| match x {
545            PolicyField::Hash(_) => field == "hash",
546            PolicyField::Script(_) => field == "script",
547            PolicyField::Ref(_) => field == "ref",
548        })
549    }
550}
551
552#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
553pub enum PolicyValue {
554    Constructor(PolicyConstructor),
555    Assign(HexStringLiteral),
556}
557
558#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
559pub struct AnyAssetConstructor {
560    pub policy: Box<DataExpr>,
561    pub asset_name: Box<DataExpr>,
562    pub amount: Box<DataExpr>,
563    pub span: Span,
564}
565
566impl AnyAssetConstructor {
567    pub fn target_type(&self) -> Option<Type> {
568        Some(Type::AnyAsset)
569    }
570}
571
572#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
573pub struct RecordConstructorField {
574    pub name: Identifier,
575    pub value: Box<DataExpr>,
576    pub span: Span,
577}
578
579#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
580pub struct StructConstructor {
581    pub r#type: Identifier,
582    pub case: VariantCaseConstructor,
583    pub span: Span,
584
585    // analysis
586    #[serde(skip)]
587    pub scope: Option<Rc<Scope>>,
588}
589
590impl StructConstructor {
591    pub fn target_type(&self) -> Option<Type> {
592        self.r#type.symbol.as_ref().and_then(|x| x.target_type())
593    }
594}
595
596#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
597pub struct VariantCaseConstructor {
598    pub name: Identifier,
599    pub fields: Vec<RecordConstructorField>,
600    pub spread: Option<Box<DataExpr>>,
601    pub span: Span,
602
603    // analysis
604    #[serde(skip)]
605    pub scope: Option<Rc<Scope>>,
606}
607
608impl VariantCaseConstructor {
609    pub fn find_field_value(&self, field: &str) -> Option<&DataExpr> {
610        self.fields
611            .iter()
612            .find(|x| x.name.value == field)
613            .map(|x| x.value.as_ref())
614    }
615}
616
617#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
618pub struct ListConstructor {
619    pub elements: Vec<DataExpr>,
620    pub span: Span,
621}
622
623impl ListConstructor {
624    pub fn target_type(&self) -> Option<Type> {
625        self.elements.first().and_then(|x| x.target_type())
626    }
627}
628
629#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
630pub struct TupleConstructor {
631    pub elements: Vec<DataExpr>,
632    pub span: Span,
633}
634
635impl TupleConstructor {
636    pub fn target_type(&self) -> Option<Type> {
637        // A tuple's type is known only when every element's type is known.
638        let elements = self
639            .elements
640            .iter()
641            .map(|x| x.target_type())
642            .collect::<Option<Vec<_>>>()?;
643
644        Some(Type::Tuple(elements))
645    }
646}
647
648#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
649pub struct MapField {
650    pub key: DataExpr,
651    pub value: DataExpr,
652    pub span: Span,
653}
654
655impl MapField {
656    pub fn target_type(&self) -> Option<Type> {
657        self.key.target_type()
658    }
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
662pub struct MapConstructor {
663    pub fields: Vec<MapField>,
664    pub span: Span,
665}
666
667impl MapConstructor {
668    pub fn target_type(&self) -> Option<Type> {
669        if let Some(first_field) = self.fields.first() {
670            let key_type = first_field.key.target_type()?;
671            let value_type = first_field.value.target_type()?;
672            Some(Type::Map(Box::new(key_type), Box::new(value_type)))
673        } else {
674            None
675        }
676    }
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
680pub struct UtxoRef {
681    pub txid: Vec<u8>,
682    pub index: u64,
683    pub span: Span,
684}
685
686#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
687pub struct NegateOp {
688    pub operand: Box<DataExpr>,
689    pub span: Span,
690}
691
692impl NegateOp {
693    pub fn target_type(&self) -> Option<Type> {
694        self.operand.target_type()
695    }
696}
697
698#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
699pub struct PropertyOp {
700    pub operand: Box<DataExpr>,
701    pub property: Box<DataExpr>,
702    pub span: Span,
703
704    // analysis
705    #[serde(skip)]
706    pub(crate) scope: Option<Rc<Scope>>,
707}
708
709impl PropertyOp {
710    pub fn target_type(&self) -> Option<Type> {
711        // Positional tuple access (`t.0`) resolves to the element type at that
712        // index; for every other operand the property carries its own type.
713        if let (Some(Type::Tuple(elements)), DataExpr::Number(index)) =
714            (self.operand.target_type(), self.property.as_ref())
715        {
716            return elements.get(*index as usize).cloned();
717        }
718
719        self.property.target_type()
720    }
721}
722
723#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
724pub struct AddOp {
725    pub lhs: Box<DataExpr>,
726    pub rhs: Box<DataExpr>,
727    pub span: Span,
728}
729
730impl AddOp {
731    pub fn target_type(&self) -> Option<Type> {
732        self.lhs.target_type()
733    }
734}
735
736#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
737pub struct SubOp {
738    pub lhs: Box<DataExpr>,
739    pub rhs: Box<DataExpr>,
740    pub span: Span,
741}
742
743impl SubOp {
744    pub fn target_type(&self) -> Option<Type> {
745        self.lhs.target_type()
746    }
747}
748
749#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
750pub struct MulOp {
751    pub lhs: Box<DataExpr>,
752    pub rhs: Box<DataExpr>,
753    pub span: Span,
754}
755
756impl MulOp {
757    pub fn target_type(&self) -> Option<Type> {
758        self.lhs.target_type()
759    }
760}
761
762#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
763pub struct DivOp {
764    pub lhs: Box<DataExpr>,
765    pub rhs: Box<DataExpr>,
766    pub span: Span,
767}
768
769impl DivOp {
770    pub fn target_type(&self) -> Option<Type> {
771        self.lhs.target_type()
772    }
773}
774
775#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
776pub struct ConcatOp {
777    pub lhs: Box<DataExpr>,
778    pub rhs: Box<DataExpr>,
779    pub span: Span,
780}
781
782impl ConcatOp {
783    pub fn target_type(&self) -> Option<Type> {
784        self.lhs.target_type()
785    }
786}
787
788#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
789pub struct FnCall {
790    pub callee: Identifier,
791    pub args: Vec<DataExpr>,
792    pub span: Span,
793}
794
795impl FnCall {
796    /// The static type of the call: the callee function's declared return type,
797    /// or `None` until the callee is resolved (ยง6.3).
798    pub fn target_type(&self) -> Option<Type> {
799        let fn_def = self.callee.symbol.as_ref()?.as_fn_def()?;
800        Some(fn_def.return_type.clone())
801    }
802}
803
804#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
805pub enum DataExpr {
806    None,
807    Unit,
808    Number(i64),
809    Bool(bool),
810    String(StringLiteral),
811    HexString(HexStringLiteral),
812    StructConstructor(StructConstructor),
813    ListConstructor(ListConstructor),
814    MapConstructor(MapConstructor),
815    TupleConstructor(TupleConstructor),
816    AnyAssetConstructor(AnyAssetConstructor),
817    Identifier(Identifier),
818    AddOp(AddOp),
819    SubOp(SubOp),
820    MulOp(MulOp),
821    DivOp(DivOp),
822    ConcatOp(ConcatOp),
823    NegateOp(NegateOp),
824    PropertyOp(PropertyOp),
825    UtxoRef(UtxoRef),
826    FnCall(FnCall),
827}
828
829impl DataExpr {
830    pub fn as_identifier(&self) -> Option<&Identifier> {
831        match self {
832            DataExpr::Identifier(x) => Some(x),
833            _ => None,
834        }
835    }
836
837    pub fn target_type(&self) -> Option<Type> {
838        match self {
839            DataExpr::Identifier(x) => x.target_type(),
840            DataExpr::None => Some(Type::Undefined),
841            DataExpr::Unit => Some(Type::Unit),
842            DataExpr::Number(_) => Some(Type::Int),
843            DataExpr::Bool(_) => Some(Type::Bool),
844            DataExpr::String(_) => Some(Type::Bytes),
845            DataExpr::HexString(_) => Some(Type::Bytes),
846            DataExpr::StructConstructor(x) => x.target_type(),
847            DataExpr::MapConstructor(x) => x.target_type(),
848            DataExpr::ListConstructor(x) => match x.target_type() {
849                Some(inner) => Some(Type::List(Box::new(inner))),
850                None => None,
851            },
852            DataExpr::TupleConstructor(x) => x.target_type(),
853            DataExpr::AddOp(x) => x.target_type(),
854            DataExpr::SubOp(x) => x.target_type(),
855            DataExpr::MulOp(x) => x.target_type(),
856            DataExpr::DivOp(x) => x.target_type(),
857            DataExpr::ConcatOp(x) => x.target_type(),
858            DataExpr::NegateOp(x) => x.target_type(),
859            DataExpr::PropertyOp(x) => x.target_type(),
860            DataExpr::AnyAssetConstructor(x) => x.target_type(),
861            DataExpr::UtxoRef(_) => Some(Type::UtxoRef),
862            DataExpr::FnCall(x) => x.target_type(),
863        }
864    }
865}
866
867#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
868pub enum AddressExpr {
869    String(StringLiteral),
870    HexString(HexStringLiteral),
871    Identifier(Identifier),
872}
873
874impl AddressExpr {
875    pub fn as_identifier(&self) -> Option<&Identifier> {
876        match self {
877            AddressExpr::Identifier(x) => Some(x),
878            _ => None,
879        }
880    }
881}
882
883#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
884pub enum Type {
885    Undefined,
886    Unit,
887    Int,
888    Bool,
889    Bytes,
890    Address,
891    Utxo,
892    UtxoRef,
893    AnyAsset,
894    List(Box<Type>),
895    Map(Box<Type>, Box<Type>),
896    Tuple(Vec<Type>),
897    Custom(Identifier),
898}
899
900impl std::fmt::Display for Type {
901    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
902        match self {
903            Type::Undefined => write!(f, "Undefined"),
904            Type::Unit => write!(f, "Unit"),
905            Type::Int => write!(f, "Int"),
906            Type::Bool => write!(f, "Bool"),
907            Type::Bytes => write!(f, "Bytes"),
908            Type::Address => write!(f, "Address"),
909            Type::UtxoRef => write!(f, "UtxoRef"),
910            Type::AnyAsset => write!(f, "AnyAsset"),
911            Type::Utxo => write!(f, "Utxo"),
912            Type::Map(key, value) => write!(f, "Map<{}, {}>", key, value),
913            Type::List(inner) => write!(f, "List<{inner}>"),
914            Type::Tuple(elements) => {
915                let inner = elements
916                    .iter()
917                    .map(|t| t.to_string())
918                    .collect::<Vec<_>>()
919                    .join(", ");
920                write!(f, "Tuple<{inner}>")
921            }
922            Type::Custom(id) => write!(f, "{}", id.value),
923        }
924    }
925}
926
927impl Type {
928    pub fn properties(&self) -> Vec<(String, Type)> {
929        match self {
930            Type::AnyAsset => {
931                vec![
932                    ("amount".to_string(), Type::Int),
933                    ("policy".to_string(), Type::Bytes),
934                    ("asset_name".to_string(), Type::Bytes),
935                ]
936            }
937            Type::UtxoRef => {
938                vec![
939                    ("tx_hash".to_string(), Type::Bytes),
940                    ("output_index".to_string(), Type::Int),
941                ]
942            }
943            Type::Custom(identifier) => {
944                let def = identifier.symbol.as_ref().and_then(|s| s.as_type_def());
945
946                match def {
947                    Some(ty) if ty.cases.len() == 1 => ty.cases[0]
948                        .fields
949                        .iter()
950                        .map(|f| (f.name.value.clone(), f.r#type.clone()))
951                        .collect(),
952                    _ => vec![],
953                }
954            }
955            _ => vec![],
956        }
957    }
958
959    pub fn property_index(&self, property: DataExpr) -> Option<DataExpr> {
960        match self {
961            Type::AnyAsset | Type::UtxoRef | Type::Custom(_) => {
962                let identifier = property.as_identifier()?;
963                let properties = Self::properties(self);
964                properties
965                    .iter()
966                    .position(|(name, _)| name == &identifier.value)
967                    .map(|index| DataExpr::Number(index as i64))
968            }
969            Type::List(_) => property
970                .target_type()
971                .filter(|ty| *ty == Type::Int)
972                .map(|_| property),
973            // Positional tuple access (`t.0`): the property is a literal index,
974            // valid only when it falls within the tuple's arity.
975            Type::Tuple(elements) => match property {
976                DataExpr::Number(index) if (0..elements.len() as i64).contains(&index) => {
977                    Some(DataExpr::Number(index))
978                }
979                _ => None,
980            },
981            _ => None,
982        }
983    }
984}
985
986#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
987pub struct ParamDef {
988    pub name: Identifier,
989    pub r#type: Type,
990    #[serde(default, skip_serializing_if = "Option::is_none")]
991    pub docstring: Option<String>,
992}
993
994#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
995pub struct AliasDef {
996    pub name: Identifier,
997    pub alias_type: Type,
998    pub span: Span,
999}
1000
1001impl AliasDef {
1002    pub fn resolve_alias_chain(&self) -> Option<&TypeDef> {
1003        match &self.alias_type {
1004            Type::Custom(identifier) => match &identifier.symbol {
1005                Some(Symbol::TypeDef(type_def)) => Some(type_def),
1006                Some(Symbol::AliasDef(next_alias)) => next_alias.resolve_alias_chain(),
1007                _ => None,
1008            },
1009            _ => None,
1010        }
1011    }
1012
1013    pub fn is_alias_chain_resolved(&self) -> bool {
1014        self.resolve_alias_chain().is_some()
1015    }
1016}
1017
1018#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1019pub struct TypeDef {
1020    pub name: Identifier,
1021    pub cases: Vec<VariantCase>,
1022    pub span: Span,
1023}
1024
1025impl TypeDef {
1026    pub(crate) fn find_case_index(&self, case: &str) -> Option<usize> {
1027        self.cases.iter().position(|x| x.name.value == case)
1028    }
1029
1030    #[allow(dead_code)]
1031    pub(crate) fn find_case(&self, case: &str) -> Option<&VariantCase> {
1032        self.cases.iter().find(|x| x.name.value == case)
1033    }
1034}
1035
1036#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1037pub struct VariantCase {
1038    pub name: Identifier,
1039    pub fields: Vec<RecordField>,
1040    pub span: Span,
1041}
1042
1043impl VariantCase {
1044    #[allow(dead_code)]
1045    pub(crate) fn find_field_index(&self, field: &str) -> Option<usize> {
1046        self.fields.iter().position(|x| x.name.value == field)
1047    }
1048
1049    #[allow(dead_code)]
1050    pub(crate) fn find_field(&self, field: &str) -> Option<&RecordField> {
1051        self.fields.iter().find(|x| x.name.value == field)
1052    }
1053}
1054
1055#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1056pub struct AssetDef {
1057    pub name: Identifier,
1058    pub policy: DataExpr,
1059    pub asset_name: DataExpr,
1060    pub span: Span,
1061}
1062
1063#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1064pub struct LetBinding {
1065    pub name: Identifier,
1066    pub value: DataExpr,
1067    pub span: Span,
1068}
1069
1070#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1071pub struct FnBody {
1072    pub let_bindings: Vec<LetBinding>,
1073    pub result: Box<DataExpr>,
1074    pub span: Span,
1075}
1076
1077/// A function provided by the compiler rather than declared in source. This is
1078/// only the serializable *key* carried on `FnDef`; each variant's signature,
1079/// analysis, and lowering live with its [`crate::builtins::Builtin`] impl.
1080#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1081pub enum BuiltinFn {
1082    MinUtxo,
1083    TipSlot,
1084    SlotToTime,
1085    TimeToSlot,
1086}
1087
1088impl BuiltinFn {
1089    /// Every built-in key. `crate::builtins::resolve` maps each to its
1090    /// implementation (exhaustively, so this list and the registry stay in
1091    /// sync at compile time).
1092    pub const ALL: [BuiltinFn; 4] = [
1093        BuiltinFn::MinUtxo,
1094        BuiltinFn::TipSlot,
1095        BuiltinFn::SlotToTime,
1096        BuiltinFn::TimeToSlot,
1097    ];
1098}
1099
1100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1101pub struct FnDef {
1102    pub name: Identifier,
1103    pub parameters: ParameterList,
1104    pub return_type: Type,
1105    /// The inline body of a user-defined function. `None` for built-ins, which
1106    /// carry a `builtin` kind instead.
1107    pub body: Option<FnBody>,
1108    /// Set when this is a compiler-provided function; mutually exclusive with
1109    /// `body`. User-defined functions always parse with `builtin: None`.
1110    #[serde(default, skip_serializing_if = "Option::is_none")]
1111    pub builtin: Option<BuiltinFn>,
1112    pub span: Span,
1113
1114    // analysis
1115    #[serde(skip)]
1116    pub(crate) scope: Option<Rc<Scope>>,
1117}
1118
1119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1120pub enum ChainSpecificBlock {
1121    Cardano(crate::cardano::CardanoBlock),
1122}