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    ParamVar(String, Box<Type>),
23    Input(String, Box<Type>),
24    PartyDef(Box<PartyDef>),
25    PolicyDef(Box<PolicyDef>),
26    AssetDef(Box<AssetDef>),
27    TypeDef(Box<TypeDef>),
28    RecordField(Box<RecordField>),
29    VariantCase(Box<VariantCase>),
30    Fees,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct Span {
35    dummy: bool,
36    pub start: usize,
37    pub end: usize,
38}
39
40impl Eq for Span {}
41
42impl PartialEq for Span {
43    fn eq(&self, other: &Self) -> bool {
44        if self.dummy || other.dummy {
45            return true;
46        }
47
48        self.start == other.start && self.end == other.end
49    }
50}
51
52impl std::hash::Hash for Span {
53    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
54        self.start.hash(state);
55        self.end.hash(state);
56    }
57}
58
59impl Span {
60    pub const DUMMY: Self = Self {
61        dummy: true,
62        start: 0,
63        end: 0,
64    };
65
66    pub fn new(start: usize, end: usize) -> Self {
67        Self {
68            dummy: false,
69            start,
70            end,
71        }
72    }
73}
74
75impl Symbol {
76    pub fn as_type_def(&self) -> Option<&TypeDef> {
77        match self {
78            Symbol::TypeDef(x) => Some(x.as_ref()),
79            _ => None,
80        }
81    }
82
83    pub fn as_variant_case(&self) -> Option<&VariantCase> {
84        match self {
85            Symbol::VariantCase(x) => Some(x.as_ref()),
86            _ => None,
87        }
88    }
89
90    pub fn as_field_def(&self) -> Option<&RecordField> {
91        match self {
92            Symbol::RecordField(x) => Some(x.as_ref()),
93            _ => None,
94        }
95    }
96
97    pub fn as_policy_def(&self) -> Option<&PolicyDef> {
98        match self {
99            Symbol::PolicyDef(x) => Some(x.as_ref()),
100            _ => None,
101        }
102    }
103
104    pub fn target_type(&self) -> Option<Type> {
105        match self {
106            Symbol::ParamVar(_, ty) => Some(ty.as_ref().clone()),
107            Symbol::RecordField(x) => Some(x.r#type.clone()),
108            Symbol::Input(_, ty) => Some(ty.as_ref().clone()),
109            x => {
110                dbg!(x);
111                None
112            }
113        }
114    }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
118pub struct Identifier {
119    pub value: String,
120    pub span: Span,
121
122    // analysis
123    #[serde(skip)]
124    pub(crate) symbol: Option<Symbol>,
125}
126
127impl Identifier {
128    pub fn new(value: impl Into<String>) -> Self {
129        Self {
130            value: value.into(),
131            symbol: None,
132            span: Span::DUMMY,
133        }
134    }
135
136    pub fn try_symbol(&self) -> Result<&Symbol, crate::lowering::Error> {
137        match &self.symbol {
138            Some(symbol) => Ok(symbol),
139            None => Err(crate::lowering::Error::MissingAnalyzePhase),
140        }
141    }
142}
143
144impl AsRef<str> for Identifier {
145    fn as_ref(&self) -> &str {
146        &self.value
147    }
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
151pub struct Program {
152    pub txs: Vec<TxDef>,
153    pub types: Vec<TypeDef>,
154    pub assets: Vec<AssetDef>,
155    pub parties: Vec<PartyDef>,
156    pub policies: Vec<PolicyDef>,
157    pub span: Span,
158
159    // analysis
160    #[serde(skip)]
161    pub(crate) scope: Option<Rc<Scope>>,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
165pub struct ParameterList {
166    pub parameters: Vec<ParamDef>,
167    pub span: Span,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
171pub struct TxDef {
172    pub name: String,
173    pub parameters: ParameterList,
174    pub inputs: Vec<InputBlock>,
175    pub outputs: Vec<OutputBlock>,
176    pub burn: Option<BurnBlock>,
177    pub mint: Option<MintBlock>,
178    pub adhoc: Vec<ChainSpecificBlock>,
179    pub span: Span,
180
181    // analysis
182    #[serde(skip)]
183    pub(crate) scope: Option<Rc<Scope>>,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
187pub struct StringLiteral {
188    pub value: String,
189    pub span: Span,
190}
191
192impl StringLiteral {
193    pub fn new(value: impl Into<String>) -> Self {
194        Self {
195            value: value.into(),
196            span: Span::DUMMY,
197        }
198    }
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
202pub struct HexStringLiteral {
203    pub value: String,
204    pub span: Span,
205}
206
207impl HexStringLiteral {
208    pub fn new(value: impl Into<String>) -> Self {
209        Self {
210            value: value.into(),
211            span: Span::DUMMY,
212        }
213    }
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
217pub enum InputBlockField {
218    From(AddressExpr),
219    DatumIs(Type),
220    MinAmount(AssetExpr),
221    Redeemer(DataExpr),
222    Ref(DataExpr),
223}
224
225impl InputBlockField {
226    fn key(&self) -> &str {
227        match self {
228            InputBlockField::From(_) => "from",
229            InputBlockField::DatumIs(_) => "datum_is",
230            InputBlockField::MinAmount(_) => "min_amount",
231            InputBlockField::Redeemer(_) => "redeemer",
232            InputBlockField::Ref(_) => "ref",
233        }
234    }
235
236    pub fn as_address_expr(&self) -> Option<&AddressExpr> {
237        match self {
238            InputBlockField::From(x) => Some(x),
239            _ => None,
240        }
241    }
242
243    pub fn as_asset_expr(&self) -> Option<&AssetExpr> {
244        match self {
245            InputBlockField::MinAmount(x) => Some(x),
246            _ => None,
247        }
248    }
249
250    pub fn as_data_expr(&self) -> Option<&DataExpr> {
251        match self {
252            InputBlockField::Redeemer(x) => Some(x),
253            InputBlockField::Ref(x) => Some(x),
254            _ => None,
255        }
256    }
257
258    pub fn as_datum_type(&self) -> Option<&Type> {
259        match self {
260            InputBlockField::DatumIs(x) => Some(x),
261            _ => None,
262        }
263    }
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
267pub struct InputBlock {
268    pub name: String,
269    pub is_many: bool,
270    pub fields: Vec<InputBlockField>,
271    pub span: Span,
272}
273
274impl InputBlock {
275    pub(crate) fn find(&self, key: &str) -> Option<&InputBlockField> {
276        self.fields.iter().find(|x| x.key() == key)
277    }
278
279    pub(crate) fn find_mut(&mut self, key: &str) -> Option<&mut InputBlockField> {
280        self.fields.iter_mut().find(|x| x.key() == key)
281    }
282
283    pub(crate) fn datum_is(&self) -> Option<&Type> {
284        self.find("datum_is").and_then(|x| x.as_datum_type())
285    }
286}
287
288#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
289pub enum OutputBlockField {
290    To(Box<AddressExpr>),
291    Amount(Box<AssetExpr>),
292    Datum(Box<DataExpr>),
293}
294
295impl OutputBlockField {
296    fn key(&self) -> &str {
297        match self {
298            OutputBlockField::To(_) => "to",
299            OutputBlockField::Amount(_) => "amount",
300            OutputBlockField::Datum(_) => "datum",
301        }
302    }
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
306pub struct OutputBlock {
307    pub name: Option<String>,
308    pub fields: Vec<OutputBlockField>,
309    pub span: Span,
310}
311
312impl OutputBlock {
313    pub(crate) fn find(&self, key: &str) -> Option<&OutputBlockField> {
314        self.fields.iter().find(|x| x.key() == key)
315    }
316}
317
318#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
319pub enum MintBlockField {
320    Amount(Box<AssetExpr>),
321    Redeemer(Box<DataExpr>),
322}
323
324impl MintBlockField {
325    fn key(&self) -> &str {
326        match self {
327            MintBlockField::Amount(_) => "amount",
328            MintBlockField::Redeemer(_) => "redeemer",
329        }
330    }
331}
332
333#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
334pub struct MintBlock {
335    pub fields: Vec<MintBlockField>,
336    pub span: Span,
337}
338
339impl MintBlock {
340    pub(crate) fn find(&self, key: &str) -> Option<&MintBlockField> {
341        self.fields.iter().find(|x| x.key() == key)
342    }
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
346pub struct BurnBlock {
347    pub fields: Vec<MintBlockField>,
348    pub span: Span,
349}
350
351#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
352pub struct RecordField {
353    pub name: String,
354    pub r#type: Type,
355    pub span: Span,
356}
357
358impl RecordField {
359    pub fn new(name: &str, r#type: Type) -> Self {
360        Self {
361            name: name.to_string(),
362            r#type,
363            span: Span::DUMMY,
364        }
365    }
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
369pub struct PartyDef {
370    pub name: String,
371    pub span: Span,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
375pub struct PartyField {
376    pub name: String,
377    pub party_type: String,
378}
379
380#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
381pub struct PolicyDef {
382    pub name: String,
383    pub value: PolicyValue,
384    pub span: Span,
385}
386
387#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
388pub enum PolicyField {
389    Hash(DataExpr),
390    Script(DataExpr),
391    Ref(DataExpr),
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
395pub struct PolicyConstructor {
396    pub fields: Vec<PolicyField>,
397    pub span: Span,
398}
399
400impl PolicyConstructor {
401    pub(crate) fn find_field(&self, field: &str) -> Option<&PolicyField> {
402        self.fields.iter().find(|x| match x {
403            PolicyField::Hash(_) => field == "hash",
404            PolicyField::Script(_) => field == "script",
405            PolicyField::Ref(_) => field == "ref",
406        })
407    }
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
411pub enum PolicyValue {
412    Constructor(PolicyConstructor),
413    Assign(HexStringLiteral),
414}
415
416#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
417pub struct StaticAssetConstructor {
418    pub r#type: Identifier,
419    pub amount: Box<DataExpr>,
420    pub span: Span,
421}
422
423#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
424pub struct AnyAssetConstructor {
425    pub policy: Box<DataExpr>,
426    pub asset_name: Box<DataExpr>,
427    pub amount: Box<DataExpr>,
428    pub span: Span,
429}
430
431#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
432pub struct AssetBinaryOp {
433    pub left: Box<AssetExpr>,
434    pub operator: BinaryOperator,
435    pub right: Box<AssetExpr>,
436    pub span: Span,
437}
438
439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
440pub enum AssetExpr {
441    StaticConstructor(StaticAssetConstructor),
442    AnyConstructor(AnyAssetConstructor),
443    BinaryOp(AssetBinaryOp),
444    PropertyAccess(PropertyAccess),
445    Identifier(Identifier),
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
449pub struct PropertyAccess {
450    pub object: Identifier,
451    pub path: Vec<Identifier>,
452    pub span: Span,
453
454    // analysis
455    #[serde(skip)]
456    pub(crate) scope: Option<Rc<Scope>>,
457}
458
459impl PropertyAccess {
460    pub fn new(object: &str, path: &[&str]) -> Self {
461        Self {
462            object: Identifier::new(object),
463            path: path.iter().map(|x| Identifier::new(*x)).collect(),
464            scope: None,
465            span: Span::DUMMY,
466        }
467    }
468}
469
470impl PropertyAccess {
471    /// Shift the property access to the next property in the path.
472    pub fn shift(mut self) -> Option<Self> {
473        if self.path.is_empty() {
474            return None;
475        }
476
477        let new_object = self.path.remove(0);
478        self.object = new_object;
479
480        Some(self)
481    }
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
485pub struct RecordConstructorField {
486    pub name: Identifier,
487    pub value: Box<DataExpr>,
488    pub span: Span,
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
492pub struct DatumConstructor {
493    pub r#type: Identifier,
494    pub case: VariantCaseConstructor,
495    pub span: Span,
496
497    // analysis
498    #[serde(skip)]
499    pub scope: Option<Rc<Scope>>,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
503pub struct VariantCaseConstructor {
504    pub name: Identifier,
505    pub fields: Vec<RecordConstructorField>,
506    pub spread: Option<Box<DataExpr>>,
507    pub span: Span,
508
509    // analysis
510    #[serde(skip)]
511    pub scope: Option<Rc<Scope>>,
512}
513
514impl VariantCaseConstructor {
515    pub fn find_field_value(&self, field: &str) -> Option<&DataExpr> {
516        self.fields
517            .iter()
518            .find(|x| x.name.value == field)
519            .map(|x| x.value.as_ref())
520    }
521}
522
523#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
524pub struct DataBinaryOp {
525    pub left: Box<DataExpr>,
526    pub operator: BinaryOperator,
527    pub right: Box<DataExpr>,
528    pub span: Span,
529}
530
531#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
532pub enum DataExpr {
533    None,
534    Unit,
535    Number(i64),
536    Bool(bool),
537    String(StringLiteral),
538    HexString(HexStringLiteral),
539    Constructor(DatumConstructor),
540    Identifier(Identifier),
541    PropertyAccess(PropertyAccess),
542    BinaryOp(DataBinaryOp),
543}
544
545impl DataExpr {
546    pub fn as_identifier(&self) -> Option<&Identifier> {
547        match self {
548            DataExpr::Identifier(x) => Some(x),
549            _ => None,
550        }
551    }
552}
553
554#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
555pub enum AddressExpr {
556    String(StringLiteral),
557    HexString(HexStringLiteral),
558    Identifier(Identifier),
559}
560
561impl AddressExpr {
562    pub fn as_identifier(&self) -> Option<&Identifier> {
563        match self {
564            AddressExpr::Identifier(x) => Some(x),
565            _ => None,
566        }
567    }
568}
569#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
570pub enum BinaryOperator {
571    Add,
572    Subtract,
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
576pub enum Type {
577    Undefined,
578    Unit,
579    Int,
580    Bool,
581    Bytes,
582    Address,
583    UtxoRef,
584    AnyAsset,
585    Custom(Identifier),
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
589pub struct ParamDef {
590    pub name: String,
591    pub r#type: Type,
592}
593
594#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
595pub struct TypeDef {
596    pub name: String,
597    pub cases: Vec<VariantCase>,
598    pub span: Span,
599}
600
601impl TypeDef {
602    pub(crate) fn find_case_index(&self, case: &str) -> Option<usize> {
603        self.cases.iter().position(|x| x.name == case)
604    }
605
606    #[allow(dead_code)]
607    pub(crate) fn find_case(&self, case: &str) -> Option<&VariantCase> {
608        self.cases.iter().find(|x| x.name == case)
609    }
610}
611
612#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
613pub struct VariantCase {
614    pub name: String,
615    pub fields: Vec<RecordField>,
616    pub span: Span,
617}
618
619impl VariantCase {
620    #[allow(dead_code)]
621    pub(crate) fn find_field_index(&self, field: &str) -> Option<usize> {
622        self.fields.iter().position(|x| x.name == field)
623    }
624
625    #[allow(dead_code)]
626    pub(crate) fn find_field(&self, field: &str) -> Option<&RecordField> {
627        self.fields.iter().find(|x| x.name == field)
628    }
629}
630
631#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
632pub struct AssetDef {
633    pub name: String,
634    pub policy: Option<HexStringLiteral>,
635    pub asset_name: Option<String>,
636    pub span: Span,
637}
638
639#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
640pub enum ChainSpecificBlock {
641    Cardano(crate::cardano::CardanoBlock),
642}