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