tx3_lang/
parsing.rs

1//! Parses the Tx3 language.
2//!
3//! This module takes a string and parses it into Tx3 AST.
4
5use miette::SourceOffset;
6use pest::{iterators::Pair, Parser};
7use pest_derive::Parser;
8
9use crate::ast::*;
10
11#[derive(Parser)]
12#[grammar = "tx3.pest"]
13pub(crate) struct Tx3Grammar;
14
15#[derive(Debug, thiserror::Error, miette::Diagnostic)]
16#[error("Parsing error: {message}")]
17#[diagnostic(code(tx3::parsing))]
18pub struct Error {
19    pub message: String,
20
21    #[source_code]
22    pub src: String,
23
24    #[label]
25    pub span: Span,
26}
27
28impl From<pest::error::Error<Rule>> for Error {
29    fn from(error: pest::error::Error<Rule>) -> Self {
30        match &error.variant {
31            pest::error::ErrorVariant::ParsingError { positives, .. } => Error {
32                message: format!("expected {:?}", positives),
33                src: error.line().to_string(),
34                span: error.location.into(),
35            },
36            pest::error::ErrorVariant::CustomError { message } => Error {
37                message: message.clone(),
38                src: error.line().to_string(),
39                span: error.location.into(),
40            },
41        }
42    }
43}
44
45impl From<pest::error::InputLocation> for Span {
46    fn from(value: pest::error::InputLocation) -> Self {
47        match value {
48            pest::error::InputLocation::Pos(pos) => Self::new(pos, pos),
49            pest::error::InputLocation::Span((start, end)) => Self::new(start, end),
50        }
51    }
52}
53
54impl From<pest::Span<'_>> for Span {
55    fn from(span: pest::Span<'_>) -> Self {
56        Self::new(span.start(), span.end())
57    }
58}
59
60impl From<Span> for miette::SourceSpan {
61    fn from(span: Span) -> Self {
62        miette::SourceSpan::new(SourceOffset::from(span.start), span.end - span.start)
63    }
64}
65
66pub trait AstNode: Sized {
67    const RULE: Rule;
68
69    fn parse(pair: Pair<Rule>) -> Result<Self, Error>;
70
71    fn span(&self) -> &Span;
72}
73
74impl AstNode for Program {
75    const RULE: Rule = Rule::program;
76
77    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
78        let span = pair.as_span().into();
79        let inner = pair.into_inner();
80
81        let mut program = Self {
82            txs: Vec::new(),
83            assets: Vec::new(),
84            types: Vec::new(),
85            parties: Vec::new(),
86            policies: Vec::new(),
87            scope: None,
88            span,
89        };
90
91        for pair in inner {
92            match pair.as_rule() {
93                Rule::tx_def => program.txs.push(TxDef::parse(pair)?),
94                Rule::asset_def => program.assets.push(AssetDef::parse(pair)?),
95                Rule::record_def => program.types.push(TypeDef::parse(pair)?),
96                Rule::variant_def => program.types.push(TypeDef::parse(pair)?),
97                Rule::party_def => program.parties.push(PartyDef::parse(pair)?),
98                Rule::policy_def => program.policies.push(PolicyDef::parse(pair)?),
99                Rule::EOI => break,
100                x => unreachable!("Unexpected rule in program: {:?}", x),
101            }
102        }
103
104        Ok(program)
105    }
106
107    fn span(&self) -> &Span {
108        &self.span
109    }
110}
111
112impl AstNode for ParameterList {
113    const RULE: Rule = Rule::parameter_list;
114
115    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
116        let span = pair.as_span().into();
117        let inner = pair.into_inner();
118
119        let mut parameters = Vec::new();
120
121        for param in inner {
122            let mut inner = param.into_inner();
123            let name = inner.next().unwrap().as_str().to_string();
124            let r#type = Type::parse(inner.next().unwrap())?;
125
126            parameters.push(ParamDef { name, r#type });
127        }
128
129        Ok(ParameterList { parameters, span })
130    }
131
132    fn span(&self) -> &Span {
133        &self.span
134    }
135}
136
137impl AstNode for TxDef {
138    const RULE: Rule = Rule::tx_def;
139
140    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
141        let span = pair.as_span().into();
142        let mut inner = pair.into_inner();
143
144        let name = inner.next().unwrap().as_str().to_string();
145        let parameters = ParameterList::parse(inner.next().unwrap())?;
146
147        let mut inputs = Vec::new();
148        let mut outputs = Vec::new();
149        let mut burn = None;
150        let mut mint = None;
151        let mut adhoc = Vec::new();
152
153        for item in inner {
154            match item.as_rule() {
155                Rule::input_block => inputs.push(InputBlock::parse(item)?),
156                Rule::output_block => outputs.push(OutputBlock::parse(item)?),
157                Rule::burn_block => burn = Some(BurnBlock::parse(item)?),
158                Rule::mint_block => mint = Some(MintBlock::parse(item)?),
159                Rule::chain_specific_block => adhoc.push(ChainSpecificBlock::parse(item)?),
160                x => unreachable!("Unexpected rule in tx_def: {:?}", x),
161            }
162        }
163
164        Ok(TxDef {
165            name,
166            parameters,
167            inputs,
168            outputs,
169            burn,
170            mint,
171            adhoc,
172            scope: None,
173            span,
174        })
175    }
176
177    fn span(&self) -> &Span {
178        &self.span
179    }
180}
181
182impl AstNode for Identifier {
183    const RULE: Rule = Rule::identifier;
184
185    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
186        Ok(Identifier {
187            value: pair.as_str().to_string(),
188            symbol: None,
189            span: pair.as_span().into(),
190        })
191    }
192
193    fn span(&self) -> &Span {
194        &self.span
195    }
196}
197
198impl AstNode for StringLiteral {
199    const RULE: Rule = Rule::string;
200
201    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
202        Ok(StringLiteral {
203            value: pair.as_str()[1..pair.as_str().len() - 1].to_string(),
204            span: pair.as_span().into(),
205        })
206    }
207
208    fn span(&self) -> &Span {
209        &self.span
210    }
211}
212
213impl AstNode for HexStringLiteral {
214    const RULE: Rule = Rule::hex_string;
215
216    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
217        Ok(HexStringLiteral {
218            value: pair.as_str()[2..].to_string(),
219            span: pair.as_span().into(),
220        })
221    }
222
223    fn span(&self) -> &Span {
224        &self.span
225    }
226}
227
228impl AstNode for PartyDef {
229    const RULE: Rule = Rule::party_def;
230
231    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
232        let span = pair.as_span().into();
233        let mut inner = pair.into_inner();
234        let identifier = inner.next().unwrap().as_str().to_string();
235
236        Ok(PartyDef {
237            name: identifier,
238            span,
239        })
240    }
241
242    fn span(&self) -> &Span {
243        &self.span
244    }
245}
246
247impl AstNode for InputBlockField {
248    const RULE: Rule = Rule::input_block_field;
249
250    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
251        match pair.as_rule() {
252            Rule::input_block_from => {
253                let pair = pair.into_inner().next().unwrap();
254                let x = InputBlockField::From(AddressExpr::parse(pair)?);
255                Ok(x)
256            }
257            Rule::input_block_datum_is => {
258                let pair = pair.into_inner().next().unwrap();
259                let x = InputBlockField::DatumIs(Type::parse(pair)?);
260                Ok(x)
261            }
262            Rule::input_block_min_amount => {
263                let pair = pair.into_inner().next().unwrap();
264                let x = InputBlockField::MinAmount(AssetExpr::parse(pair)?);
265                Ok(x)
266            }
267            Rule::input_block_redeemer => {
268                let pair = pair.into_inner().next().unwrap();
269                let x = InputBlockField::Redeemer(DataExpr::parse(pair)?);
270                Ok(x)
271            }
272            Rule::input_block_ref => {
273                let pair = pair.into_inner().next().unwrap();
274                let x = InputBlockField::Ref(DataExpr::parse(pair)?);
275                Ok(x)
276            }
277            x => unreachable!("Unexpected rule in input_block: {:?}", x),
278        }
279    }
280
281    fn span(&self) -> &Span {
282        match self {
283            Self::From(x) => x.span(),
284            Self::DatumIs(x) => x.span(),
285            Self::MinAmount(x) => x.span(),
286            Self::Redeemer(x) => x.span(),
287            Self::Ref(x) => x.span(),
288        }
289    }
290}
291
292impl AstNode for InputBlock {
293    const RULE: Rule = Rule::input_block;
294
295    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
296        let span = pair.as_span().into();
297        let mut inner = pair.into_inner();
298
299        let name = inner.next().unwrap().as_str().to_string();
300
301        let fields = inner
302            .map(|x| InputBlockField::parse(x))
303            .collect::<Result<Vec<_>, _>>()?;
304
305        Ok(InputBlock {
306            name,
307            is_many: false,
308            fields,
309            span,
310        })
311    }
312
313    fn span(&self) -> &Span {
314        &self.span
315    }
316}
317
318impl AstNode for OutputBlockField {
319    const RULE: Rule = Rule::output_block_field;
320
321    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
322        match pair.as_rule() {
323            Rule::output_block_to => {
324                let pair = pair.into_inner().next().unwrap();
325                let x = OutputBlockField::To(Box::new(AddressExpr::parse(pair)?));
326                Ok(x)
327            }
328            Rule::output_block_amount => {
329                let pair = pair.into_inner().next().unwrap();
330                let x = OutputBlockField::Amount(AssetExpr::parse(pair)?.into());
331                Ok(x)
332            }
333            Rule::output_block_datum => {
334                let pair = pair.into_inner().next().unwrap();
335                let x = OutputBlockField::Datum(DataExpr::parse(pair)?.into());
336                Ok(x)
337            }
338            x => unreachable!("Unexpected rule in output_block_field: {:?}", x),
339        }
340    }
341
342    fn span(&self) -> &Span {
343        match self {
344            Self::To(x) => x.span(),
345            Self::Amount(x) => x.span(),
346            Self::Datum(x) => x.span(),
347        }
348    }
349}
350
351impl AstNode for OutputBlock {
352    const RULE: Rule = Rule::output_block;
353
354    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
355        let span = pair.as_span().into();
356        let mut inner = pair.into_inner();
357
358        let has_name = inner
359            .peek()
360            .map(|x| x.as_rule() == Rule::identifier)
361            .unwrap_or_default();
362
363        let name = has_name.then(|| inner.next().unwrap().as_str().to_string());
364
365        let fields = inner
366            .map(|x| OutputBlockField::parse(x))
367            .collect::<Result<Vec<_>, _>>()?;
368
369        Ok(OutputBlock { name, fields, span })
370    }
371
372    fn span(&self) -> &Span {
373        &self.span
374    }
375}
376
377impl AstNode for MintBlockField {
378    const RULE: Rule = Rule::mint_block_field;
379
380    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
381        match pair.as_rule() {
382            Rule::mint_block_amount => {
383                let pair = pair.into_inner().next().unwrap();
384                let x = MintBlockField::Amount(AssetExpr::parse(pair)?.into());
385                Ok(x)
386            }
387            Rule::mint_block_redeemer => {
388                let pair = pair.into_inner().next().unwrap();
389                let x = MintBlockField::Redeemer(DataExpr::parse(pair)?.into());
390                Ok(x)
391            }
392            x => unreachable!("Unexpected rule in output_block_field: {:?}", x),
393        }
394    }
395
396    fn span(&self) -> &Span {
397        match self {
398            Self::Amount(x) => x.span(),
399            Self::Redeemer(x) => x.span(),
400        }
401    }
402}
403
404impl AstNode for MintBlock {
405    const RULE: Rule = Rule::mint_block;
406
407    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
408        let span = pair.as_span().into();
409        let inner = pair.into_inner();
410
411        let fields = inner
412            .map(|x| MintBlockField::parse(x))
413            .collect::<Result<Vec<_>, _>>()?;
414
415        Ok(MintBlock { fields, span })
416    }
417
418    fn span(&self) -> &Span {
419        &self.span
420    }
421}
422
423impl AstNode for BurnBlock {
424    const RULE: Rule = Rule::burn_block;
425
426    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
427        let span = pair.as_span().into();
428        let inner = pair.into_inner();
429
430        let fields = inner
431            .map(|x| MintBlockField::parse(x))
432            .collect::<Result<Vec<_>, _>>()?;
433
434        Ok(BurnBlock { fields, span })
435    }
436
437    fn span(&self) -> &Span {
438        &self.span
439    }
440}
441
442impl AstNode for RecordField {
443    const RULE: Rule = Rule::record_field;
444
445    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
446        let span = pair.as_span().into();
447        let mut inner = pair.into_inner();
448        let identifier = inner.next().unwrap().as_str().to_string();
449        let r#type = Type::parse(inner.next().unwrap())?;
450
451        Ok(RecordField {
452            name: identifier,
453            r#type,
454            span,
455        })
456    }
457
458    fn span(&self) -> &Span {
459        &self.span
460    }
461}
462
463impl AstNode for PolicyField {
464    const RULE: Rule = Rule::policy_def_field;
465
466    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
467        match pair.as_rule() {
468            Rule::policy_def_hash => Ok(PolicyField::Hash(DataExpr::parse(
469                pair.into_inner().next().unwrap(),
470            )?)),
471            Rule::policy_def_script => Ok(PolicyField::Script(DataExpr::parse(
472                pair.into_inner().next().unwrap(),
473            )?)),
474            Rule::policy_def_ref => Ok(PolicyField::Ref(DataExpr::parse(
475                pair.into_inner().next().unwrap(),
476            )?)),
477            x => unreachable!("Unexpected rule in policy_field: {:?}", x),
478        }
479    }
480
481    fn span(&self) -> &Span {
482        match self {
483            Self::Hash(x) => x.span(),
484            Self::Script(x) => x.span(),
485            Self::Ref(x) => x.span(),
486        }
487    }
488}
489
490impl AstNode for PolicyConstructor {
491    const RULE: Rule = Rule::policy_def_constructor;
492
493    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
494        let span = pair.as_span().into();
495        let inner = pair.into_inner();
496
497        let fields = inner
498            .map(|x| PolicyField::parse(x))
499            .collect::<Result<Vec<_>, _>>()?;
500
501        Ok(PolicyConstructor { fields, span })
502    }
503
504    fn span(&self) -> &Span {
505        &self.span
506    }
507}
508
509impl AstNode for PolicyValue {
510    const RULE: Rule = Rule::policy_def_value;
511
512    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
513        match pair.as_rule() {
514            Rule::policy_def_constructor => {
515                Ok(PolicyValue::Constructor(PolicyConstructor::parse(pair)?))
516            }
517            Rule::policy_def_assign => Ok(PolicyValue::Assign(HexStringLiteral::parse(
518                pair.into_inner().next().unwrap(),
519            )?)),
520            x => unreachable!("Unexpected rule in policy_value: {:?}", x),
521        }
522    }
523
524    fn span(&self) -> &Span {
525        match self {
526            Self::Constructor(x) => x.span(),
527            Self::Assign(x) => x.span(),
528        }
529    }
530}
531
532impl AstNode for PolicyDef {
533    const RULE: Rule = Rule::policy_def;
534
535    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
536        let span = pair.as_span().into();
537        let mut inner = pair.into_inner();
538        let name = inner.next().unwrap().as_str().to_string();
539        let value = PolicyValue::parse(inner.next().unwrap())?;
540
541        Ok(PolicyDef { name, value, span })
542    }
543
544    fn span(&self) -> &Span {
545        &self.span
546    }
547}
548
549impl AstNode for AddressExpr {
550    const RULE: Rule = Rule::address_expr;
551
552    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
553        let mut inner = pair.into_inner();
554
555        let value = inner.next().unwrap();
556
557        match value.as_rule() {
558            Rule::string => Ok(AddressExpr::String(StringLiteral::parse(value)?)),
559            Rule::hex_string => Ok(AddressExpr::HexString(HexStringLiteral::parse(value)?)),
560            Rule::identifier => Ok(AddressExpr::Identifier(Identifier::parse(value)?)),
561            x => unreachable!("Unexpected rule in address_expr: {:?}", x),
562        }
563    }
564
565    fn span(&self) -> &Span {
566        match self {
567            Self::String(x) => x.span(),
568            Self::HexString(x) => x.span(),
569            Self::Identifier(x) => x.span(),
570        }
571    }
572}
573
574impl AstNode for StaticAssetConstructor {
575    const RULE: Rule = Rule::static_asset_constructor;
576
577    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
578        let span = pair.as_span().into();
579        let mut inner = pair.into_inner();
580
581        let r#type = Identifier::parse(inner.next().unwrap())?;
582        let amount = DataExpr::parse(inner.next().unwrap())?;
583
584        Ok(StaticAssetConstructor {
585            r#type,
586            amount: Box::new(amount),
587            span,
588        })
589    }
590
591    fn span(&self) -> &Span {
592        &self.span
593    }
594}
595
596impl AstNode for AnyAssetConstructor {
597    const RULE: Rule = Rule::any_asset_constructor;
598
599    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
600        let span = pair.as_span().into();
601        let mut inner = pair.into_inner();
602
603        let policy = DataExpr::parse(inner.next().unwrap())?;
604        let asset_name = DataExpr::parse(inner.next().unwrap())?;
605        let amount = DataExpr::parse(inner.next().unwrap())?;
606
607        Ok(AnyAssetConstructor {
608            policy: Box::new(policy),
609            asset_name: Box::new(asset_name),
610            amount: Box::new(amount),
611            span,
612        })
613    }
614
615    fn span(&self) -> &Span {
616        &self.span
617    }
618}
619
620impl AssetExpr {
621    fn identifier_parse(pair: Pair<Rule>) -> Result<Self, Error> {
622        Ok(AssetExpr::Identifier(Identifier::parse(pair)?))
623    }
624
625    fn static_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
626        Ok(AssetExpr::StaticConstructor(StaticAssetConstructor::parse(
627            pair,
628        )?))
629    }
630
631    fn any_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
632        Ok(AssetExpr::AnyConstructor(AnyAssetConstructor::parse(pair)?))
633    }
634
635    fn property_access_parse(pair: Pair<Rule>) -> Result<Self, Error> {
636        Ok(AssetExpr::PropertyAccess(PropertyAccess::parse(pair)?))
637    }
638
639    fn term_parse(pair: Pair<Rule>) -> Result<Self, Error> {
640        match pair.as_rule() {
641            Rule::static_asset_constructor => AssetExpr::static_constructor_parse(pair),
642            Rule::any_asset_constructor => AssetExpr::any_constructor_parse(pair),
643            Rule::property_access => AssetExpr::property_access_parse(pair),
644            Rule::identifier => AssetExpr::identifier_parse(pair),
645            x => unreachable!("Unexpected rule in asset_expr: {:?}", x),
646        }
647    }
648}
649
650impl AstNode for AssetExpr {
651    const RULE: Rule = Rule::asset_expr;
652
653    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
654        let mut inner = pair.into_inner();
655
656        let mut final_expr = Self::term_parse(inner.next().unwrap())?;
657
658        while let Some(term) = inner.next() {
659            let span = term.as_span().into();
660            let operator = BinaryOperator::parse(term)?;
661            let next_expr = Self::term_parse(inner.next().unwrap())?;
662
663            final_expr = AssetExpr::BinaryOp(AssetBinaryOp {
664                operator,
665                left: Box::new(final_expr),
666                right: Box::new(next_expr),
667                span,
668            });
669        }
670
671        Ok(final_expr)
672    }
673
674    fn span(&self) -> &Span {
675        match self {
676            AssetExpr::StaticConstructor(x) => x.span(),
677            AssetExpr::AnyConstructor(x) => x.span(),
678            AssetExpr::BinaryOp(x) => &x.span,
679            AssetExpr::PropertyAccess(x) => x.span(),
680            AssetExpr::Identifier(x) => x.span(),
681        }
682    }
683}
684
685impl AstNode for PropertyAccess {
686    const RULE: Rule = Rule::property_access;
687
688    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
689        let span = pair.as_span().into();
690        let mut inner = pair.into_inner();
691
692        let object = Identifier::parse(inner.next().unwrap())?;
693
694        let mut identifiers = Vec::new();
695        identifiers.push(Identifier::parse(inner.next().unwrap())?);
696
697        for identifier in inner {
698            identifiers.push(Identifier::parse(identifier)?);
699        }
700
701        Ok(PropertyAccess {
702            object,
703            path: identifiers,
704            scope: None,
705            span,
706        })
707    }
708
709    fn span(&self) -> &Span {
710        &self.span
711    }
712}
713
714impl AstNode for RecordConstructorField {
715    const RULE: Rule = Rule::record_constructor_field;
716
717    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
718        let span = pair.as_span().into();
719        let mut inner = pair.into_inner();
720
721        let name = Identifier::parse(inner.next().unwrap())?;
722        let value = DataExpr::parse(inner.next().unwrap())?;
723
724        Ok(RecordConstructorField {
725            name,
726            value: Box::new(value),
727            span,
728        })
729    }
730
731    fn span(&self) -> &Span {
732        &self.span
733    }
734}
735
736impl AstNode for DatumConstructor {
737    const RULE: Rule = Rule::datum_constructor;
738
739    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
740        let span = pair.as_span().into();
741        let mut inner = pair.into_inner();
742
743        let r#type = Identifier::parse(inner.next().unwrap())?;
744        let case = VariantCaseConstructor::parse(inner.next().unwrap())?;
745
746        Ok(DatumConstructor {
747            r#type,
748            case,
749            scope: None,
750            span,
751        })
752    }
753
754    fn span(&self) -> &Span {
755        &self.span
756    }
757}
758
759impl VariantCaseConstructor {
760    fn implicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
761        let span = pair.as_span().into();
762        let inner = pair.into_inner();
763
764        let mut fields = Vec::new();
765        let mut spread = None;
766
767        for pair in inner {
768            match pair.as_rule() {
769                Rule::record_constructor_field => {
770                    fields.push(RecordConstructorField::parse(pair)?);
771                }
772                Rule::spread_expression => {
773                    spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
774                }
775                x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
776            }
777        }
778
779        Ok(VariantCaseConstructor {
780            name: Identifier::new("Default"),
781            fields,
782            spread: spread.map(Box::new),
783            scope: None,
784            span,
785        })
786    }
787
788    fn explicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
789        let span = pair.as_span().into();
790        let mut inner = pair.into_inner();
791
792        let name = Identifier::parse(inner.next().unwrap())?;
793
794        let mut fields = Vec::new();
795        let mut spread = None;
796
797        for pair in inner {
798            match pair.as_rule() {
799                Rule::record_constructor_field => {
800                    fields.push(RecordConstructorField::parse(pair)?);
801                }
802                Rule::spread_expression => {
803                    spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
804                }
805                x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
806            }
807        }
808
809        Ok(VariantCaseConstructor {
810            name,
811            fields,
812            spread: spread.map(Box::new),
813            scope: None,
814            span,
815        })
816    }
817}
818
819impl AstNode for VariantCaseConstructor {
820    const RULE: Rule = Rule::variant_case_constructor;
821
822    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
823        match pair.as_rule() {
824            Rule::implicit_variant_case_constructor => Self::implicit_parse(pair),
825            Rule::explicit_variant_case_constructor => Self::explicit_parse(pair),
826            x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
827        }
828    }
829
830    fn span(&self) -> &Span {
831        &self.span
832    }
833}
834
835impl DataExpr {
836    fn number_parse(pair: Pair<Rule>) -> Result<Self, Error> {
837        Ok(DataExpr::Number(pair.as_str().parse().unwrap()))
838    }
839
840    fn bool_parse(pair: Pair<Rule>) -> Result<Self, Error> {
841        Ok(DataExpr::Bool(pair.as_str().parse().unwrap()))
842    }
843
844    fn identifier_parse(pair: Pair<Rule>) -> Result<Self, Error> {
845        Ok(DataExpr::Identifier(Identifier::parse(pair)?))
846    }
847
848    fn property_access_parse(pair: Pair<Rule>) -> Result<Self, Error> {
849        Ok(DataExpr::PropertyAccess(PropertyAccess::parse(pair)?))
850    }
851
852    fn constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
853        Ok(DataExpr::Constructor(DatumConstructor::parse(pair)?))
854    }
855
856    fn term_parse(pair: Pair<Rule>) -> Result<Self, Error> {
857        match pair.as_rule() {
858            Rule::number => DataExpr::number_parse(pair),
859            Rule::string => Ok(DataExpr::String(StringLiteral::parse(pair)?)),
860            Rule::bool => DataExpr::bool_parse(pair),
861            Rule::hex_string => Ok(DataExpr::HexString(HexStringLiteral::parse(pair)?)),
862            Rule::datum_constructor => DataExpr::constructor_parse(pair),
863            Rule::unit => Ok(DataExpr::Unit),
864            Rule::identifier => DataExpr::identifier_parse(pair),
865            Rule::property_access => DataExpr::property_access_parse(pair),
866            x => unreachable!("Unexpected rule in data_expr: {:?}", x),
867        }
868    }
869}
870
871impl AstNode for DataExpr {
872    const RULE: Rule = Rule::data_expr;
873
874    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
875        let mut inner = pair.into_inner();
876
877        let mut final_expr = Self::term_parse(inner.next().unwrap())?;
878
879        while let Some(term) = inner.next() {
880            let span = term.as_span().into();
881            let operator = BinaryOperator::parse(term)?;
882            let next_expr = Self::term_parse(inner.next().unwrap())?;
883
884            final_expr = DataExpr::BinaryOp(DataBinaryOp {
885                operator,
886                left: Box::new(final_expr),
887                right: Box::new(next_expr),
888                span,
889            });
890        }
891
892        Ok(final_expr)
893    }
894
895    fn span(&self) -> &Span {
896        match self {
897            DataExpr::None => &Span::DUMMY,      // TODO
898            DataExpr::Unit => &Span::DUMMY,      // TODO
899            DataExpr::Number(_) => &Span::DUMMY, // TODO
900            DataExpr::Bool(_) => &Span::DUMMY,   // TODO
901            DataExpr::String(x) => x.span(),
902            DataExpr::HexString(x) => x.span(),
903            DataExpr::Constructor(x) => x.span(),
904            DataExpr::Identifier(x) => x.span(),
905            DataExpr::PropertyAccess(x) => x.span(),
906            DataExpr::BinaryOp(x) => &x.span,
907        }
908    }
909}
910
911impl AstNode for BinaryOperator {
912    const RULE: Rule = Rule::binary_operator;
913
914    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
915        match pair.as_str() {
916            "+" => Ok(BinaryOperator::Add),
917            "-" => Ok(BinaryOperator::Subtract),
918            x => unreachable!("Unexpected string in binary_operator: {:?}", x),
919        }
920    }
921
922    fn span(&self) -> &Span {
923        &Span::DUMMY // TODO
924    }
925}
926
927impl AstNode for Type {
928    const RULE: Rule = Rule::r#type;
929
930    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
931        match pair.as_str() {
932            "Int" => Ok(Type::Int),
933            "Bool" => Ok(Type::Bool),
934            "Bytes" => Ok(Type::Bytes),
935            "Address" => Ok(Type::Address),
936            "UtxoRef" => Ok(Type::UtxoRef),
937            "AnyAsset" => Ok(Type::AnyAsset),
938            t => Ok(Type::Custom(Identifier::new(t.to_string()))),
939        }
940    }
941
942    fn span(&self) -> &Span {
943        &Span::DUMMY // TODO
944    }
945}
946
947impl TypeDef {
948    fn parse_variant_format(pair: Pair<Rule>) -> Result<Self, Error> {
949        let span = pair.as_span().into();
950        let mut inner = pair.into_inner();
951
952        let identifier = inner.next().unwrap().as_str().to_string();
953
954        let cases = inner
955            .map(VariantCase::parse)
956            .collect::<Result<Vec<_>, _>>()?;
957
958        Ok(TypeDef {
959            name: identifier,
960            cases,
961            span,
962        })
963    }
964
965    fn parse_record_format(pair: Pair<Rule>) -> Result<Self, Error> {
966        let span: Span = pair.as_span().into();
967        let mut inner = pair.into_inner();
968
969        let identifier = inner.next().unwrap().as_str().to_string();
970
971        let fields = inner
972            .map(RecordField::parse)
973            .collect::<Result<Vec<_>, _>>()?;
974
975        Ok(TypeDef {
976            name: identifier.clone(),
977            cases: vec![VariantCase {
978                name: "Default".to_string(),
979                fields,
980                span: span.clone(),
981            }],
982            span,
983        })
984    }
985}
986
987impl AstNode for TypeDef {
988    const RULE: Rule = Rule::type_def;
989
990    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
991        match pair.as_rule() {
992            Rule::variant_def => Ok(Self::parse_variant_format(pair)?),
993            Rule::record_def => Ok(Self::parse_record_format(pair)?),
994            x => unreachable!("Unexpected rule in type_def: {:?}", x),
995        }
996    }
997
998    fn span(&self) -> &Span {
999        &self.span
1000    }
1001}
1002
1003impl VariantCase {
1004    fn struct_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
1005        let span = pair.as_span().into();
1006        let mut inner = pair.into_inner();
1007
1008        let identifier = inner.next().unwrap().as_str().to_string();
1009
1010        let fields = inner
1011            .map(RecordField::parse)
1012            .collect::<Result<Vec<_>, _>>()?;
1013
1014        Ok(Self {
1015            name: identifier,
1016            fields,
1017            span,
1018        })
1019    }
1020
1021    fn unit_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
1022        let span = pair.as_span().into();
1023        let mut inner = pair.into_inner();
1024
1025        let identifier = inner.next().unwrap().as_str().to_string();
1026
1027        Ok(Self {
1028            name: identifier,
1029            fields: vec![],
1030            span,
1031        })
1032    }
1033}
1034
1035impl AstNode for VariantCase {
1036    const RULE: Rule = Rule::variant_case;
1037
1038    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1039        let case = match pair.as_rule() {
1040            Rule::variant_case_struct => Self::struct_case_parse(pair),
1041            Rule::variant_case_tuple => todo!("parse variant case tuple"),
1042            Rule::variant_case_unit => Self::unit_case_parse(pair),
1043            x => unreachable!("Unexpected rule in datum_variant: {:?}", x),
1044        }?;
1045
1046        Ok(case)
1047    }
1048
1049    fn span(&self) -> &Span {
1050        &self.span
1051    }
1052}
1053
1054impl AstNode for AssetDef {
1055    const RULE: Rule = Rule::asset_def;
1056
1057    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1058        let span = pair.as_span().into();
1059        let mut inner = pair.into_inner();
1060
1061        let identifier = inner.next().unwrap().as_str().to_string();
1062        let policy = HexStringLiteral::parse(inner.next().unwrap())?;
1063        let asset_name = inner.next().unwrap().as_str().to_string();
1064
1065        Ok(AssetDef {
1066            name: identifier,
1067            policy: Some(policy),
1068            asset_name: Some(asset_name),
1069            span,
1070        })
1071    }
1072
1073    fn span(&self) -> &Span {
1074        &self.span
1075    }
1076}
1077
1078impl AstNode for ChainSpecificBlock {
1079    const RULE: Rule = Rule::chain_specific_block;
1080
1081    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1082        let mut inner = pair.into_inner();
1083
1084        let block = inner.next().unwrap();
1085
1086        match block.as_rule() {
1087            Rule::cardano_block => {
1088                let block = crate::cardano::CardanoBlock::parse(block)?;
1089                Ok(ChainSpecificBlock::Cardano(block))
1090            }
1091            x => unreachable!("Unexpected rule in chain_specific_block: {:?}", x),
1092        }
1093    }
1094
1095    fn span(&self) -> &Span {
1096        match self {
1097            Self::Cardano(x) => x.span(),
1098        }
1099    }
1100}
1101
1102/// Parses a Tx3 source string into a Program AST.
1103///
1104/// # Arguments
1105///
1106/// * `input` - String containing Tx3 source code
1107///
1108/// # Returns
1109///
1110/// * `Result<Program, Error>` - The parsed Program AST or an error
1111///
1112/// # Errors
1113///
1114/// Returns an error if:
1115/// - The input string is not valid Tx3 syntax
1116/// - The AST construction fails
1117///
1118/// # Example
1119///
1120/// ```
1121/// use tx3_lang::parsing::parse_string;
1122/// let program = parse_string("tx swap() {}").unwrap();
1123/// ```
1124pub fn parse_string(input: &str) -> Result<Program, Error> {
1125    let pairs = Tx3Grammar::parse(Rule::program, input)?;
1126    Program::parse(pairs.into_iter().next().unwrap())
1127}
1128
1129#[cfg(test)]
1130pub fn parse_well_known_example(example: &str) -> Program {
1131    let manifest_dir = env!("CARGO_MANIFEST_DIR");
1132    let test_file = format!("{}/../../examples/{}.tx3", manifest_dir, example);
1133    let input = std::fs::read_to_string(&test_file).unwrap();
1134    parse_string(&input).unwrap()
1135}
1136
1137#[cfg(test)]
1138mod tests {
1139    use super::*;
1140    use assert_json_diff::assert_json_eq;
1141    use paste::paste;
1142    use pest::Parser;
1143
1144    #[test]
1145    fn smoke_test_parse_string() {
1146        let _ = parse_string("tx swap() {}").unwrap();
1147    }
1148
1149    macro_rules! input_to_ast_check {
1150        ($ast:ty, $name:expr, $input:expr, $expected:expr) => {
1151            paste::paste! {
1152                #[test]
1153                fn [<test_parse_ $ast:snake _ $name>]() {
1154                    let pairs = super::Tx3Grammar::parse(<$ast>::RULE, $input).unwrap();
1155                    let single_match = pairs.into_iter().next().unwrap();
1156                    let result = <$ast>::parse(single_match).unwrap();
1157
1158                    assert_eq!(result, $expected);
1159                }
1160            }
1161        };
1162    }
1163
1164    input_to_ast_check!(BinaryOperator, "plus", "+", BinaryOperator::Add);
1165
1166    input_to_ast_check!(Type, "int", "Int", Type::Int);
1167
1168    input_to_ast_check!(Type, "bool", "Bool", Type::Bool);
1169
1170    input_to_ast_check!(Type, "bytes", "Bytes", Type::Bytes);
1171
1172    input_to_ast_check!(Type, "address", "Address", Type::Address);
1173
1174    input_to_ast_check!(Type, "utxo_ref", "UtxoRef", Type::UtxoRef);
1175
1176    input_to_ast_check!(Type, "any_asset", "AnyAsset", Type::AnyAsset);
1177
1178    input_to_ast_check!(
1179        TypeDef,
1180        "type_def_record",
1181        "type MyRecord {
1182            field1: Int,
1183            field2: Bytes,
1184        }",
1185        TypeDef {
1186            name: "MyRecord".to_string(),
1187            cases: vec![VariantCase {
1188                name: "Default".to_string(),
1189                fields: vec![
1190                    RecordField::new("field1", Type::Int),
1191                    RecordField::new("field2", Type::Bytes)
1192                ],
1193                span: Span::DUMMY,
1194            }],
1195            span: Span::DUMMY,
1196        }
1197    );
1198
1199    input_to_ast_check!(
1200        TypeDef,
1201        "type_def_variant",
1202        "type MyVariant {
1203            Case1 {
1204                field1: Int,
1205                field2: Bytes,
1206            },
1207            Case2,
1208        }",
1209        TypeDef {
1210            name: "MyVariant".to_string(),
1211            cases: vec![
1212                VariantCase {
1213                    name: "Case1".to_string(),
1214                    fields: vec![
1215                        RecordField::new("field1", Type::Int),
1216                        RecordField::new("field2", Type::Bytes)
1217                    ],
1218                    span: Span::DUMMY,
1219                },
1220                VariantCase {
1221                    name: "Case2".to_string(),
1222                    fields: vec![],
1223                    span: Span::DUMMY,
1224                },
1225            ],
1226            span: Span::DUMMY,
1227        }
1228    );
1229
1230    input_to_ast_check!(
1231        StringLiteral,
1232        "literal_string",
1233        "\"Hello, world!\"",
1234        StringLiteral::new("Hello, world!".to_string())
1235    );
1236
1237    input_to_ast_check!(
1238        HexStringLiteral,
1239        "hex_string",
1240        "0xAFAFAF",
1241        HexStringLiteral::new("AFAFAF".to_string())
1242    );
1243
1244    input_to_ast_check!(
1245        StringLiteral,
1246        "literal_string_address",
1247        "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1248        StringLiteral::new("addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string())
1249    );
1250
1251    input_to_ast_check!(
1252        DataExpr,
1253        "identifier",
1254        "my_party",
1255        DataExpr::Identifier(Identifier::new("my_party".to_string()))
1256    );
1257
1258    input_to_ast_check!(DataExpr, "literal_bool_true", "true", DataExpr::Bool(true));
1259
1260    input_to_ast_check!(
1261        DataExpr,
1262        "literal_bool_false",
1263        "false",
1264        DataExpr::Bool(false)
1265    );
1266
1267    input_to_ast_check!(DataExpr, "unit_value", "())", DataExpr::Unit);
1268
1269    input_to_ast_check!(
1270        PropertyAccess,
1271        "single_property",
1272        "subject.property",
1273        PropertyAccess::new("subject", &["property"])
1274    );
1275
1276    input_to_ast_check!(
1277        PropertyAccess,
1278        "multiple_properties",
1279        "subject.property.subproperty",
1280        PropertyAccess::new("subject", &["property", "subproperty"])
1281    );
1282
1283    input_to_ast_check!(
1284        PolicyDef,
1285        "policy_def_assign",
1286        "policy MyPolicy = 0xAFAFAF;",
1287        PolicyDef {
1288            name: "MyPolicy".to_string(),
1289            value: PolicyValue::Assign(HexStringLiteral::new("AFAFAF".to_string())),
1290            span: Span::DUMMY,
1291        }
1292    );
1293
1294    input_to_ast_check!(
1295        PolicyDef,
1296        "policy_def_constructor",
1297        "policy MyPolicy {
1298            hash: 0x1234567890,
1299            script: 0x1234567890,
1300            ref: 0x1234567890,
1301        };",
1302        PolicyDef {
1303            name: "MyPolicy".to_string(),
1304            value: PolicyValue::Constructor(PolicyConstructor {
1305                fields: vec![
1306                    PolicyField::Hash(DataExpr::HexString(HexStringLiteral::new(
1307                        "1234567890".to_string()
1308                    ))),
1309                    PolicyField::Script(DataExpr::HexString(HexStringLiteral::new(
1310                        "1234567890".to_string()
1311                    ))),
1312                    PolicyField::Ref(DataExpr::HexString(HexStringLiteral::new(
1313                        "1234567890".to_string()
1314                    ))),
1315                ],
1316                span: Span::DUMMY,
1317            }),
1318            span: Span::DUMMY,
1319        }
1320    );
1321
1322    input_to_ast_check!(
1323        AssetDef,
1324        "hex_hex",
1325        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.0xef7a1ceb;",
1326        AssetDef {
1327            name: "MyToken".to_string(),
1328            policy: Some(HexStringLiteral::new(
1329                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
1330            )),
1331            asset_name: Some("0xef7a1ceb".to_string()),
1332            span: Span::DUMMY,
1333        }
1334    );
1335
1336    input_to_ast_check!(
1337        AssetDef,
1338        "hex_ascii",
1339        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.MYTOKEN;",
1340        AssetDef {
1341            name: "MyToken".to_string(),
1342            policy: Some(HexStringLiteral::new(
1343                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
1344            )),
1345            asset_name: Some("MYTOKEN".to_string()),
1346            span: Span::DUMMY,
1347        }
1348    );
1349
1350    input_to_ast_check!(
1351        StaticAssetConstructor,
1352        "type_and_literal",
1353        "MyToken(15)",
1354        StaticAssetConstructor {
1355            r#type: Identifier::new("MyToken"),
1356            amount: Box::new(DataExpr::Number(15)),
1357            span: Span::DUMMY,
1358        }
1359    );
1360
1361    input_to_ast_check!(
1362        AnyAssetConstructor,
1363        "any_asset_constructor",
1364        "AnyAsset(0x1234567890, \"MyToken\", 15)",
1365        AnyAssetConstructor {
1366            policy: Box::new(DataExpr::HexString(HexStringLiteral::new(
1367                "1234567890".to_string()
1368            ))),
1369            asset_name: Box::new(DataExpr::String(StringLiteral::new("MyToken".to_string()))),
1370            amount: Box::new(DataExpr::Number(15)),
1371            span: Span::DUMMY,
1372        }
1373    );
1374
1375    input_to_ast_check!(
1376        AnyAssetConstructor,
1377        "any_asset_identifiers",
1378        "AnyAsset(my_policy, my_token, my_amount)",
1379        AnyAssetConstructor {
1380            policy: Box::new(DataExpr::Identifier(Identifier::new("my_policy"))),
1381            asset_name: Box::new(DataExpr::Identifier(Identifier::new("my_token"))),
1382            amount: Box::new(DataExpr::Identifier(Identifier::new("my_amount"))),
1383            span: Span::DUMMY,
1384        }
1385    );
1386
1387    input_to_ast_check!(
1388        AnyAssetConstructor,
1389        "any_asset_property_access",
1390        "AnyAsset(input1.policy, input1.asset_name, input1.amount)",
1391        AnyAssetConstructor {
1392            policy: Box::new(DataExpr::PropertyAccess(PropertyAccess::new(
1393                "input1",
1394                &["policy"],
1395            ))),
1396            asset_name: Box::new(DataExpr::PropertyAccess(PropertyAccess::new(
1397                "input1",
1398                &["asset_name"],
1399            ))),
1400            amount: Box::new(DataExpr::PropertyAccess(PropertyAccess::new(
1401                "input1",
1402                &["amount"],
1403            ))),
1404            span: Span::DUMMY,
1405        }
1406    );
1407
1408    input_to_ast_check!(
1409        DataExpr,
1410        "addition",
1411        "5 + var1",
1412        DataExpr::BinaryOp(DataBinaryOp {
1413            operator: BinaryOperator::Add,
1414            left: Box::new(DataExpr::Number(5)),
1415            right: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
1416            span: Span::DUMMY,
1417        })
1418    );
1419
1420    input_to_ast_check!(
1421        AddressExpr,
1422        "address_string",
1423        "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1424        AddressExpr::String(StringLiteral::new(
1425            "addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string()
1426        ))
1427    );
1428
1429    input_to_ast_check!(
1430        AddressExpr,
1431        "address_hex_string",
1432        "0x1234567890abcdef",
1433        AddressExpr::HexString(HexStringLiteral::new("1234567890abcdef".to_string()))
1434    );
1435
1436    input_to_ast_check!(
1437        AddressExpr,
1438        "address_identifier",
1439        "my_address",
1440        AddressExpr::Identifier(Identifier::new("my_address"))
1441    );
1442
1443    input_to_ast_check!(
1444        DatumConstructor,
1445        "datum_constructor_record",
1446        "MyRecord {
1447            field1: 10,
1448            field2: abc,
1449        }",
1450        DatumConstructor {
1451            r#type: Identifier::new("MyRecord"),
1452            case: VariantCaseConstructor {
1453                name: Identifier::new("Default"),
1454                fields: vec![
1455                    RecordConstructorField {
1456                        name: Identifier::new("field1"),
1457                        value: Box::new(DataExpr::Number(10)),
1458                        span: Span::DUMMY,
1459                    },
1460                    RecordConstructorField {
1461                        name: Identifier::new("field2"),
1462                        value: Box::new(DataExpr::Identifier(Identifier::new("abc"))),
1463                        span: Span::DUMMY,
1464                    },
1465                ],
1466                spread: None,
1467                scope: None,
1468                span: Span::DUMMY,
1469            },
1470            scope: None,
1471            span: Span::DUMMY,
1472        }
1473    );
1474
1475    input_to_ast_check!(
1476        DatumConstructor,
1477        "datum_constructor_variant",
1478        "ShipCommand::MoveShip {
1479            delta_x: delta_x,
1480            delta_y: delta_y,
1481        }",
1482        DatumConstructor {
1483            r#type: Identifier::new("ShipCommand"),
1484            case: VariantCaseConstructor {
1485                name: Identifier::new("MoveShip"),
1486                fields: vec![
1487                    RecordConstructorField {
1488                        name: Identifier::new("delta_x"),
1489                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
1490                        span: Span::DUMMY,
1491                    },
1492                    RecordConstructorField {
1493                        name: Identifier::new("delta_y"),
1494                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
1495                        span: Span::DUMMY,
1496                    },
1497                ],
1498                spread: None,
1499                scope: None,
1500                span: Span::DUMMY,
1501            },
1502            scope: None,
1503            span: Span::DUMMY,
1504        }
1505    );
1506
1507    input_to_ast_check!(
1508        DatumConstructor,
1509        "datum_constructor_variant_with_spread",
1510        "ShipCommand::MoveShip {
1511            delta_x: delta_x,
1512            delta_y: delta_y,
1513            ...abc
1514        }",
1515        DatumConstructor {
1516            r#type: Identifier::new("ShipCommand"),
1517            case: VariantCaseConstructor {
1518                name: Identifier::new("MoveShip"),
1519                fields: vec![
1520                    RecordConstructorField {
1521                        name: Identifier::new("delta_x"),
1522                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
1523                        span: Span::DUMMY,
1524                    },
1525                    RecordConstructorField {
1526                        name: Identifier::new("delta_y"),
1527                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
1528                        span: Span::DUMMY,
1529                    },
1530                ],
1531                spread: Some(Box::new(DataExpr::Identifier(Identifier::new(
1532                    "abc".to_string()
1533                )))),
1534                scope: None,
1535                span: Span::DUMMY,
1536            },
1537            scope: None,
1538            span: Span::DUMMY,
1539        }
1540    );
1541
1542    input_to_ast_check!(
1543        OutputBlock,
1544        "output_block_anonymous",
1545        r#"output {
1546            to: my_party,
1547            amount: Ada(100),
1548        }"#,
1549        OutputBlock {
1550            name: None,
1551            fields: vec![
1552                OutputBlockField::To(Box::new(AddressExpr::Identifier(Identifier::new(
1553                    "my_party".to_string(),
1554                )))),
1555                OutputBlockField::Amount(Box::new(AssetExpr::StaticConstructor(
1556                    StaticAssetConstructor {
1557                        r#type: Identifier::new("Ada"),
1558                        amount: Box::new(DataExpr::Number(100)),
1559                        span: Span::DUMMY,
1560                    },
1561                ))),
1562            ],
1563            span: Span::DUMMY,
1564        }
1565    );
1566
1567    input_to_ast_check!(
1568        ChainSpecificBlock,
1569        "chain_specific_block_cardano",
1570        "cardano::vote_delegation_certificate {
1571            drep: 0x1234567890,
1572            stake: 0x1234567890,
1573        }",
1574        ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::VoteDelegationCertificate(
1575            crate::cardano::VoteDelegationCertificate {
1576                drep: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
1577                stake: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
1578                span: Span::DUMMY,
1579            },
1580        ))
1581    );
1582
1583    #[test]
1584    fn test_spans_are_respected() {
1585        let program = parse_well_known_example("lang_tour");
1586        assert_eq!(program.span, Span::new(0, 904));
1587
1588        assert_eq!(program.parties[0].span, Span::new(0, 14));
1589
1590        assert_eq!(program.types[0].span, Span::new(16, 88));
1591    }
1592
1593    fn make_snapshot_if_missing(example: &str, program: &Program) {
1594        let manifest_dir = env!("CARGO_MANIFEST_DIR");
1595        let path = format!("{}/../../examples/{}.ast", manifest_dir, example);
1596
1597        if !std::fs::exists(&path).unwrap() {
1598            let ast = serde_json::to_string_pretty(program).unwrap();
1599            std::fs::write(&path, ast).unwrap();
1600        }
1601    }
1602
1603    fn test_parsing_example(example: &str) {
1604        let program = parse_well_known_example(example);
1605
1606        make_snapshot_if_missing(example, &program);
1607
1608        let manifest_dir = env!("CARGO_MANIFEST_DIR");
1609        let ast_file = format!("{}/../../examples/{}.ast", manifest_dir, example);
1610        let ast = std::fs::read_to_string(ast_file).unwrap();
1611
1612        let expected: Program = serde_json::from_str(&ast).unwrap();
1613
1614        assert_json_eq!(program, expected);
1615    }
1616
1617    #[macro_export]
1618    macro_rules! test_parsing {
1619        ($name:ident) => {
1620            paste! {
1621                #[test]
1622                fn [<test_example_ $name>]() {
1623                    test_parsing_example(stringify!($name));
1624                }
1625            }
1626        };
1627    }
1628
1629    test_parsing!(lang_tour);
1630
1631    test_parsing!(transfer);
1632
1633    test_parsing!(swap);
1634
1635    test_parsing!(asteria);
1636
1637    test_parsing!(vesting);
1638
1639    test_parsing!(faucet);
1640}