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 MapField {
631    pub key: DataExpr,
632    pub value: DataExpr,
633    pub span: Span,
634}
635
636impl MapField {
637    pub fn target_type(&self) -> Option<Type> {
638        self.key.target_type()
639    }
640}
641
642#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
643pub struct MapConstructor {
644    pub fields: Vec<MapField>,
645    pub span: Span,
646}
647
648impl MapConstructor {
649    pub fn target_type(&self) -> Option<Type> {
650        if let Some(first_field) = self.fields.first() {
651            let key_type = first_field.key.target_type()?;
652            let value_type = first_field.value.target_type()?;
653            Some(Type::Map(Box::new(key_type), Box::new(value_type)))
654        } else {
655            None
656        }
657    }
658}
659
660#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
661pub struct UtxoRef {
662    pub txid: Vec<u8>,
663    pub index: u64,
664    pub span: Span,
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
668pub struct NegateOp {
669    pub operand: Box<DataExpr>,
670    pub span: Span,
671}
672
673impl NegateOp {
674    pub fn target_type(&self) -> Option<Type> {
675        self.operand.target_type()
676    }
677}
678
679#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
680pub struct PropertyOp {
681    pub operand: Box<DataExpr>,
682    pub property: Box<DataExpr>,
683    pub span: Span,
684
685    // analysis
686    #[serde(skip)]
687    pub(crate) scope: Option<Rc<Scope>>,
688}
689
690impl PropertyOp {
691    pub fn target_type(&self) -> Option<Type> {
692        self.property.target_type()
693    }
694}
695
696#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
697pub struct AddOp {
698    pub lhs: Box<DataExpr>,
699    pub rhs: Box<DataExpr>,
700    pub span: Span,
701}
702
703impl AddOp {
704    pub fn target_type(&self) -> Option<Type> {
705        self.lhs.target_type()
706    }
707}
708
709#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
710pub struct SubOp {
711    pub lhs: Box<DataExpr>,
712    pub rhs: Box<DataExpr>,
713    pub span: Span,
714}
715
716impl SubOp {
717    pub fn target_type(&self) -> Option<Type> {
718        self.lhs.target_type()
719    }
720}
721
722#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
723pub struct ConcatOp {
724    pub lhs: Box<DataExpr>,
725    pub rhs: Box<DataExpr>,
726    pub span: Span,
727}
728
729impl ConcatOp {
730    pub fn target_type(&self) -> Option<Type> {
731        self.lhs.target_type()
732    }
733}
734
735#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
736pub struct FnCall {
737    pub callee: Identifier,
738    pub args: Vec<DataExpr>,
739    pub span: Span,
740}
741
742impl FnCall {
743    /// The static type of the call: the callee function's declared return type,
744    /// or `None` until the callee is resolved (ยง6.3).
745    pub fn target_type(&self) -> Option<Type> {
746        let fn_def = self.callee.symbol.as_ref()?.as_fn_def()?;
747        Some(fn_def.return_type.clone())
748    }
749}
750
751#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
752pub enum DataExpr {
753    None,
754    Unit,
755    Number(i64),
756    Bool(bool),
757    String(StringLiteral),
758    HexString(HexStringLiteral),
759    StructConstructor(StructConstructor),
760    ListConstructor(ListConstructor),
761    MapConstructor(MapConstructor),
762    AnyAssetConstructor(AnyAssetConstructor),
763    Identifier(Identifier),
764    AddOp(AddOp),
765    SubOp(SubOp),
766    ConcatOp(ConcatOp),
767    NegateOp(NegateOp),
768    PropertyOp(PropertyOp),
769    UtxoRef(UtxoRef),
770    FnCall(FnCall),
771}
772
773impl DataExpr {
774    pub fn as_identifier(&self) -> Option<&Identifier> {
775        match self {
776            DataExpr::Identifier(x) => Some(x),
777            _ => None,
778        }
779    }
780
781    pub fn target_type(&self) -> Option<Type> {
782        match self {
783            DataExpr::Identifier(x) => x.target_type(),
784            DataExpr::None => Some(Type::Undefined),
785            DataExpr::Unit => Some(Type::Unit),
786            DataExpr::Number(_) => Some(Type::Int),
787            DataExpr::Bool(_) => Some(Type::Bool),
788            DataExpr::String(_) => Some(Type::Bytes),
789            DataExpr::HexString(_) => Some(Type::Bytes),
790            DataExpr::StructConstructor(x) => x.target_type(),
791            DataExpr::MapConstructor(x) => x.target_type(),
792            DataExpr::ListConstructor(x) => match x.target_type() {
793                Some(inner) => Some(Type::List(Box::new(inner))),
794                None => None,
795            },
796            DataExpr::AddOp(x) => x.target_type(),
797            DataExpr::SubOp(x) => x.target_type(),
798            DataExpr::ConcatOp(x) => x.target_type(),
799            DataExpr::NegateOp(x) => x.target_type(),
800            DataExpr::PropertyOp(x) => x.target_type(),
801            DataExpr::AnyAssetConstructor(x) => x.target_type(),
802            DataExpr::UtxoRef(_) => Some(Type::UtxoRef),
803            DataExpr::FnCall(x) => x.target_type(),
804        }
805    }
806}
807
808#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
809pub enum AddressExpr {
810    String(StringLiteral),
811    HexString(HexStringLiteral),
812    Identifier(Identifier),
813}
814
815impl AddressExpr {
816    pub fn as_identifier(&self) -> Option<&Identifier> {
817        match self {
818            AddressExpr::Identifier(x) => Some(x),
819            _ => None,
820        }
821    }
822}
823
824#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
825pub enum Type {
826    Undefined,
827    Unit,
828    Int,
829    Bool,
830    Bytes,
831    Address,
832    Utxo,
833    UtxoRef,
834    AnyAsset,
835    List(Box<Type>),
836    Map(Box<Type>, Box<Type>),
837    Custom(Identifier),
838}
839
840impl std::fmt::Display for Type {
841    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
842        match self {
843            Type::Undefined => write!(f, "Undefined"),
844            Type::Unit => write!(f, "Unit"),
845            Type::Int => write!(f, "Int"),
846            Type::Bool => write!(f, "Bool"),
847            Type::Bytes => write!(f, "Bytes"),
848            Type::Address => write!(f, "Address"),
849            Type::UtxoRef => write!(f, "UtxoRef"),
850            Type::AnyAsset => write!(f, "AnyAsset"),
851            Type::Utxo => write!(f, "Utxo"),
852            Type::Map(key, value) => write!(f, "Map<{}, {}>", key, value),
853            Type::List(inner) => write!(f, "List<{inner}>"),
854            Type::Custom(id) => write!(f, "{}", id.value),
855        }
856    }
857}
858
859impl Type {
860    pub fn properties(&self) -> Vec<(String, Type)> {
861        match self {
862            Type::AnyAsset => {
863                vec![
864                    ("amount".to_string(), Type::Int),
865                    ("policy".to_string(), Type::Bytes),
866                    ("asset_name".to_string(), Type::Bytes),
867                ]
868            }
869            Type::UtxoRef => {
870                vec![
871                    ("tx_hash".to_string(), Type::Bytes),
872                    ("output_index".to_string(), Type::Int),
873                ]
874            }
875            Type::Custom(identifier) => {
876                let def = identifier.symbol.as_ref().and_then(|s| s.as_type_def());
877
878                match def {
879                    Some(ty) if ty.cases.len() == 1 => ty.cases[0]
880                        .fields
881                        .iter()
882                        .map(|f| (f.name.value.clone(), f.r#type.clone()))
883                        .collect(),
884                    _ => vec![],
885                }
886            }
887            _ => vec![],
888        }
889    }
890
891    pub fn property_index(&self, property: DataExpr) -> Option<DataExpr> {
892        match self {
893            Type::AnyAsset | Type::UtxoRef | Type::Custom(_) => {
894                let identifier = property.as_identifier()?;
895                let properties = Self::properties(self);
896                properties
897                    .iter()
898                    .position(|(name, _)| name == &identifier.value)
899                    .map(|index| DataExpr::Number(index as i64))
900            }
901            Type::List(_) => property
902                .target_type()
903                .filter(|ty| *ty == Type::Int)
904                .map(|_| property),
905            _ => None,
906        }
907    }
908}
909
910#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
911pub struct ParamDef {
912    pub name: Identifier,
913    pub r#type: Type,
914    #[serde(default, skip_serializing_if = "Option::is_none")]
915    pub docstring: Option<String>,
916}
917
918#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
919pub struct AliasDef {
920    pub name: Identifier,
921    pub alias_type: Type,
922    pub span: Span,
923}
924
925impl AliasDef {
926    pub fn resolve_alias_chain(&self) -> Option<&TypeDef> {
927        match &self.alias_type {
928            Type::Custom(identifier) => match &identifier.symbol {
929                Some(Symbol::TypeDef(type_def)) => Some(type_def),
930                Some(Symbol::AliasDef(next_alias)) => next_alias.resolve_alias_chain(),
931                _ => None,
932            },
933            _ => None,
934        }
935    }
936
937    pub fn is_alias_chain_resolved(&self) -> bool {
938        self.resolve_alias_chain().is_some()
939    }
940}
941
942#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
943pub struct TypeDef {
944    pub name: Identifier,
945    pub cases: Vec<VariantCase>,
946    pub span: Span,
947}
948
949impl TypeDef {
950    pub(crate) fn find_case_index(&self, case: &str) -> Option<usize> {
951        self.cases.iter().position(|x| x.name.value == case)
952    }
953
954    #[allow(dead_code)]
955    pub(crate) fn find_case(&self, case: &str) -> Option<&VariantCase> {
956        self.cases.iter().find(|x| x.name.value == case)
957    }
958}
959
960#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
961pub struct VariantCase {
962    pub name: Identifier,
963    pub fields: Vec<RecordField>,
964    pub span: Span,
965}
966
967impl VariantCase {
968    #[allow(dead_code)]
969    pub(crate) fn find_field_index(&self, field: &str) -> Option<usize> {
970        self.fields.iter().position(|x| x.name.value == field)
971    }
972
973    #[allow(dead_code)]
974    pub(crate) fn find_field(&self, field: &str) -> Option<&RecordField> {
975        self.fields.iter().find(|x| x.name.value == field)
976    }
977}
978
979#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
980pub struct AssetDef {
981    pub name: Identifier,
982    pub policy: DataExpr,
983    pub asset_name: DataExpr,
984    pub span: Span,
985}
986
987#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
988pub struct LetBinding {
989    pub name: Identifier,
990    pub value: DataExpr,
991    pub span: Span,
992}
993
994#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
995pub struct FnBody {
996    pub let_bindings: Vec<LetBinding>,
997    pub result: Box<DataExpr>,
998    pub span: Span,
999}
1000
1001/// A function provided by the compiler rather than declared in source. This is
1002/// only the serializable *key* carried on `FnDef`; each variant's signature,
1003/// analysis, and lowering live with its [`crate::builtins::Builtin`] impl.
1004#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
1005pub enum BuiltinFn {
1006    MinUtxo,
1007    TipSlot,
1008    SlotToTime,
1009    TimeToSlot,
1010}
1011
1012impl BuiltinFn {
1013    /// Every built-in key. `crate::builtins::resolve` maps each to its
1014    /// implementation (exhaustively, so this list and the registry stay in
1015    /// sync at compile time).
1016    pub const ALL: [BuiltinFn; 4] = [
1017        BuiltinFn::MinUtxo,
1018        BuiltinFn::TipSlot,
1019        BuiltinFn::SlotToTime,
1020        BuiltinFn::TimeToSlot,
1021    ];
1022}
1023
1024#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1025pub struct FnDef {
1026    pub name: Identifier,
1027    pub parameters: ParameterList,
1028    pub return_type: Type,
1029    /// The inline body of a user-defined function. `None` for built-ins, which
1030    /// carry a `builtin` kind instead.
1031    pub body: Option<FnBody>,
1032    /// Set when this is a compiler-provided function; mutually exclusive with
1033    /// `body`. User-defined functions always parse with `builtin: None`.
1034    #[serde(default, skip_serializing_if = "Option::is_none")]
1035    pub builtin: Option<BuiltinFn>,
1036    pub span: Span,
1037
1038    // analysis
1039    #[serde(skip)]
1040    pub(crate) scope: Option<Rc<Scope>>,
1041}
1042
1043#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1044pub enum ChainSpecificBlock {
1045    Cardano(crate::cardano::CardanoBlock),
1046}