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 AssetConstructor {
575    const RULE: Rule = Rule::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(AssetConstructor {
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 AssetExpr {
597    fn identifier_parse(pair: Pair<Rule>) -> Result<Self, Error> {
598        Ok(AssetExpr::Identifier(Identifier::parse(pair)?))
599    }
600
601    fn constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
602        Ok(AssetExpr::Constructor(AssetConstructor::parse(pair)?))
603    }
604
605    fn property_access_parse(pair: Pair<Rule>) -> Result<Self, Error> {
606        Ok(AssetExpr::PropertyAccess(PropertyAccess::parse(pair)?))
607    }
608
609    fn term_parse(pair: Pair<Rule>) -> Result<Self, Error> {
610        match pair.as_rule() {
611            Rule::asset_constructor => AssetExpr::constructor_parse(pair),
612            Rule::property_access => AssetExpr::property_access_parse(pair),
613            Rule::identifier => AssetExpr::identifier_parse(pair),
614            x => unreachable!("Unexpected rule in asset_expr: {:?}", x),
615        }
616    }
617}
618
619impl AstNode for AssetExpr {
620    const RULE: Rule = Rule::asset_expr;
621
622    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
623        let mut inner = pair.into_inner();
624
625        let mut final_expr = Self::term_parse(inner.next().unwrap())?;
626
627        while let Some(term) = inner.next() {
628            let span = term.as_span().into();
629            let operator = BinaryOperator::parse(term)?;
630            let next_expr = Self::term_parse(inner.next().unwrap())?;
631
632            final_expr = AssetExpr::BinaryOp(AssetBinaryOp {
633                operator,
634                left: Box::new(final_expr),
635                right: Box::new(next_expr),
636                span,
637            });
638        }
639
640        Ok(final_expr)
641    }
642
643    fn span(&self) -> &Span {
644        match self {
645            AssetExpr::Constructor(x) => x.span(),
646            AssetExpr::BinaryOp(x) => &x.span,
647            AssetExpr::PropertyAccess(x) => x.span(),
648            AssetExpr::Identifier(x) => x.span(),
649        }
650    }
651}
652
653impl AstNode for PropertyAccess {
654    const RULE: Rule = Rule::property_access;
655
656    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
657        let span = pair.as_span().into();
658        let mut inner = pair.into_inner();
659
660        let object = Identifier::parse(inner.next().unwrap())?;
661
662        let mut identifiers = Vec::new();
663        identifiers.push(Identifier::parse(inner.next().unwrap())?);
664
665        for identifier in inner {
666            identifiers.push(Identifier::parse(identifier)?);
667        }
668
669        Ok(PropertyAccess {
670            object,
671            path: identifiers,
672            scope: None,
673            span,
674        })
675    }
676
677    fn span(&self) -> &Span {
678        &self.span
679    }
680}
681
682impl AstNode for RecordConstructorField {
683    const RULE: Rule = Rule::record_constructor_field;
684
685    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
686        let span = pair.as_span().into();
687        let mut inner = pair.into_inner();
688
689        let name = Identifier::parse(inner.next().unwrap())?;
690        let value = DataExpr::parse(inner.next().unwrap())?;
691
692        Ok(RecordConstructorField {
693            name,
694            value: Box::new(value),
695            span,
696        })
697    }
698
699    fn span(&self) -> &Span {
700        &self.span
701    }
702}
703
704impl AstNode for DatumConstructor {
705    const RULE: Rule = Rule::datum_constructor;
706
707    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
708        let span = pair.as_span().into();
709        let mut inner = pair.into_inner();
710
711        let r#type = Identifier::parse(inner.next().unwrap())?;
712        let case = VariantCaseConstructor::parse(inner.next().unwrap())?;
713
714        Ok(DatumConstructor {
715            r#type,
716            case,
717            scope: None,
718            span,
719        })
720    }
721
722    fn span(&self) -> &Span {
723        &self.span
724    }
725}
726
727impl VariantCaseConstructor {
728    fn implicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
729        let span = pair.as_span().into();
730        let inner = pair.into_inner();
731
732        let mut fields = Vec::new();
733        let mut spread = None;
734
735        for pair in inner {
736            match pair.as_rule() {
737                Rule::record_constructor_field => {
738                    fields.push(RecordConstructorField::parse(pair)?);
739                }
740                Rule::spread_expression => {
741                    spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
742                }
743                x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
744            }
745        }
746
747        Ok(VariantCaseConstructor {
748            name: Identifier::new("Default"),
749            fields,
750            spread: spread.map(Box::new),
751            scope: None,
752            span,
753        })
754    }
755
756    fn explicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
757        let span = pair.as_span().into();
758        let mut inner = pair.into_inner();
759
760        let name = Identifier::parse(inner.next().unwrap())?;
761
762        let mut fields = Vec::new();
763        let mut spread = None;
764
765        for pair in inner {
766            match pair.as_rule() {
767                Rule::record_constructor_field => {
768                    fields.push(RecordConstructorField::parse(pair)?);
769                }
770                Rule::spread_expression => {
771                    spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
772                }
773                x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
774            }
775        }
776
777        Ok(VariantCaseConstructor {
778            name,
779            fields,
780            spread: spread.map(Box::new),
781            scope: None,
782            span,
783        })
784    }
785}
786
787impl AstNode for VariantCaseConstructor {
788    const RULE: Rule = Rule::variant_case_constructor;
789
790    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
791        match pair.as_rule() {
792            Rule::implicit_variant_case_constructor => Self::implicit_parse(pair),
793            Rule::explicit_variant_case_constructor => Self::explicit_parse(pair),
794            x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
795        }
796    }
797
798    fn span(&self) -> &Span {
799        &self.span
800    }
801}
802
803impl DataExpr {
804    fn number_parse(pair: Pair<Rule>) -> Result<Self, Error> {
805        Ok(DataExpr::Number(pair.as_str().parse().unwrap()))
806    }
807
808    fn bool_parse(pair: Pair<Rule>) -> Result<Self, Error> {
809        Ok(DataExpr::Bool(pair.as_str().parse().unwrap()))
810    }
811
812    fn identifier_parse(pair: Pair<Rule>) -> Result<Self, Error> {
813        Ok(DataExpr::Identifier(Identifier::parse(pair)?))
814    }
815
816    fn property_access_parse(pair: Pair<Rule>) -> Result<Self, Error> {
817        Ok(DataExpr::PropertyAccess(PropertyAccess::parse(pair)?))
818    }
819
820    fn constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
821        Ok(DataExpr::Constructor(DatumConstructor::parse(pair)?))
822    }
823
824    fn term_parse(pair: Pair<Rule>) -> Result<Self, Error> {
825        match pair.as_rule() {
826            Rule::number => DataExpr::number_parse(pair),
827            Rule::string => Ok(DataExpr::String(StringLiteral::parse(pair)?)),
828            Rule::bool => DataExpr::bool_parse(pair),
829            Rule::hex_string => Ok(DataExpr::HexString(HexStringLiteral::parse(pair)?)),
830            Rule::datum_constructor => DataExpr::constructor_parse(pair),
831            Rule::unit => Ok(DataExpr::Unit),
832            Rule::identifier => DataExpr::identifier_parse(pair),
833            Rule::property_access => DataExpr::property_access_parse(pair),
834            x => unreachable!("Unexpected rule in data_expr: {:?}", x),
835        }
836    }
837}
838
839impl AstNode for DataExpr {
840    const RULE: Rule = Rule::data_expr;
841
842    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
843        let mut inner = pair.into_inner();
844
845        let mut final_expr = Self::term_parse(inner.next().unwrap())?;
846
847        while let Some(term) = inner.next() {
848            let span = term.as_span().into();
849            let operator = BinaryOperator::parse(term)?;
850            let next_expr = Self::term_parse(inner.next().unwrap())?;
851
852            final_expr = DataExpr::BinaryOp(DataBinaryOp {
853                operator,
854                left: Box::new(final_expr),
855                right: Box::new(next_expr),
856                span,
857            });
858        }
859
860        Ok(final_expr)
861    }
862
863    fn span(&self) -> &Span {
864        match self {
865            DataExpr::None => &Span::DUMMY,      // TODO
866            DataExpr::Unit => &Span::DUMMY,      // TODO
867            DataExpr::Number(_) => &Span::DUMMY, // TODO
868            DataExpr::Bool(_) => &Span::DUMMY,   // TODO
869            DataExpr::String(x) => x.span(),
870            DataExpr::HexString(x) => x.span(),
871            DataExpr::Constructor(x) => x.span(),
872            DataExpr::Identifier(x) => x.span(),
873            DataExpr::PropertyAccess(x) => x.span(),
874            DataExpr::BinaryOp(x) => &x.span,
875        }
876    }
877}
878
879impl AstNode for BinaryOperator {
880    const RULE: Rule = Rule::binary_operator;
881
882    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
883        match pair.as_str() {
884            "+" => Ok(BinaryOperator::Add),
885            "-" => Ok(BinaryOperator::Subtract),
886            x => unreachable!("Unexpected string in binary_operator: {:?}", x),
887        }
888    }
889
890    fn span(&self) -> &Span {
891        &Span::DUMMY // TODO
892    }
893}
894
895impl AstNode for Type {
896    const RULE: Rule = Rule::r#type;
897
898    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
899        match pair.as_str() {
900            "Int" => Ok(Type::Int),
901            "Bool" => Ok(Type::Bool),
902            "Bytes" => Ok(Type::Bytes),
903            "Address" => Ok(Type::Address),
904            "UtxoRef" => Ok(Type::UtxoRef),
905            "AnyAsset" => Ok(Type::AnyAsset),
906            t => Ok(Type::Custom(Identifier::new(t.to_string()))),
907        }
908    }
909
910    fn span(&self) -> &Span {
911        &Span::DUMMY // TODO
912    }
913}
914
915impl TypeDef {
916    fn parse_variant_format(pair: Pair<Rule>) -> Result<Self, Error> {
917        let span = pair.as_span().into();
918        let mut inner = pair.into_inner();
919
920        let identifier = inner.next().unwrap().as_str().to_string();
921
922        let cases = inner
923            .map(VariantCase::parse)
924            .collect::<Result<Vec<_>, _>>()?;
925
926        Ok(TypeDef {
927            name: identifier,
928            cases,
929            span,
930        })
931    }
932
933    fn parse_record_format(pair: Pair<Rule>) -> Result<Self, Error> {
934        let span: Span = pair.as_span().into();
935        let mut inner = pair.into_inner();
936
937        let identifier = inner.next().unwrap().as_str().to_string();
938
939        let fields = inner
940            .map(RecordField::parse)
941            .collect::<Result<Vec<_>, _>>()?;
942
943        Ok(TypeDef {
944            name: identifier.clone(),
945            cases: vec![VariantCase {
946                name: "Default".to_string(),
947                fields,
948                span: span.clone(),
949            }],
950            span,
951        })
952    }
953}
954
955impl AstNode for TypeDef {
956    const RULE: Rule = Rule::type_def;
957
958    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
959        match pair.as_rule() {
960            Rule::variant_def => Ok(Self::parse_variant_format(pair)?),
961            Rule::record_def => Ok(Self::parse_record_format(pair)?),
962            x => unreachable!("Unexpected rule in type_def: {:?}", x),
963        }
964    }
965
966    fn span(&self) -> &Span {
967        &self.span
968    }
969}
970
971impl VariantCase {
972    fn struct_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
973        let span = pair.as_span().into();
974        let mut inner = pair.into_inner();
975
976        let identifier = inner.next().unwrap().as_str().to_string();
977
978        let fields = inner
979            .map(RecordField::parse)
980            .collect::<Result<Vec<_>, _>>()?;
981
982        Ok(Self {
983            name: identifier,
984            fields,
985            span,
986        })
987    }
988
989    fn unit_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
990        let span = pair.as_span().into();
991        let mut inner = pair.into_inner();
992
993        let identifier = inner.next().unwrap().as_str().to_string();
994
995        Ok(Self {
996            name: identifier,
997            fields: vec![],
998            span,
999        })
1000    }
1001}
1002
1003impl AstNode for VariantCase {
1004    const RULE: Rule = Rule::variant_case;
1005
1006    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1007        let case = match pair.as_rule() {
1008            Rule::variant_case_struct => Self::struct_case_parse(pair),
1009            Rule::variant_case_tuple => todo!("parse variant case tuple"),
1010            Rule::variant_case_unit => Self::unit_case_parse(pair),
1011            x => unreachable!("Unexpected rule in datum_variant: {:?}", x),
1012        }?;
1013
1014        Ok(case)
1015    }
1016
1017    fn span(&self) -> &Span {
1018        &self.span
1019    }
1020}
1021
1022impl AstNode for AssetDef {
1023    const RULE: Rule = Rule::asset_def;
1024
1025    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1026        let span = pair.as_span().into();
1027        let mut inner = pair.into_inner();
1028
1029        let identifier = inner.next().unwrap().as_str().to_string();
1030        let policy = HexStringLiteral::parse(inner.next().unwrap())?;
1031        let asset_name = inner.next().unwrap().as_str().to_string();
1032
1033        Ok(AssetDef {
1034            name: identifier,
1035            policy,
1036            asset_name,
1037            span,
1038        })
1039    }
1040
1041    fn span(&self) -> &Span {
1042        &self.span
1043    }
1044}
1045
1046impl AstNode for ChainSpecificBlock {
1047    const RULE: Rule = Rule::chain_specific_block;
1048
1049    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1050        let mut inner = pair.into_inner();
1051
1052        let block = inner.next().unwrap();
1053
1054        match block.as_rule() {
1055            Rule::cardano_block => {
1056                let block = crate::cardano::CardanoBlock::parse(block)?;
1057                Ok(ChainSpecificBlock::Cardano(block))
1058            }
1059            x => unreachable!("Unexpected rule in chain_specific_block: {:?}", x),
1060        }
1061    }
1062
1063    fn span(&self) -> &Span {
1064        match self {
1065            Self::Cardano(x) => x.span(),
1066        }
1067    }
1068}
1069
1070/// Parses a Tx3 source string into a Program AST.
1071///
1072/// # Arguments
1073///
1074/// * `input` - String containing Tx3 source code
1075///
1076/// # Returns
1077///
1078/// * `Result<Program, Error>` - The parsed Program AST or an error
1079///
1080/// # Errors
1081///
1082/// Returns an error if:
1083/// - The input string is not valid Tx3 syntax
1084/// - The AST construction fails
1085///
1086/// # Example
1087///
1088/// ```
1089/// use tx3_lang::parsing::parse_string;
1090/// let program = parse_string("tx swap() {}").unwrap();
1091/// ```
1092pub fn parse_string(input: &str) -> Result<Program, Error> {
1093    let pairs = Tx3Grammar::parse(Rule::program, input)?;
1094    Program::parse(pairs.into_iter().next().unwrap())
1095}
1096
1097#[cfg(test)]
1098pub fn parse_well_known_example(example: &str) -> Program {
1099    let manifest_dir = env!("CARGO_MANIFEST_DIR");
1100    let test_file = format!("{}/../../examples/{}.tx3", manifest_dir, example);
1101    let input = std::fs::read_to_string(&test_file).unwrap();
1102    parse_string(&input).unwrap()
1103}
1104
1105#[cfg(test)]
1106mod tests {
1107    use super::*;
1108    use assert_json_diff::assert_json_eq;
1109    use paste::paste;
1110    use pest::Parser;
1111
1112    #[test]
1113    fn smoke_test_parse_string() {
1114        let _ = parse_string("tx swap() {}").unwrap();
1115    }
1116
1117    macro_rules! input_to_ast_check {
1118        ($ast:ty, $name:expr, $input:expr, $expected:expr) => {
1119            paste::paste! {
1120                #[test]
1121                fn [<test_parse_ $ast:snake _ $name>]() {
1122                    let pairs = super::Tx3Grammar::parse(<$ast>::RULE, $input).unwrap();
1123                    let single_match = pairs.into_iter().next().unwrap();
1124                    let result = <$ast>::parse(single_match).unwrap();
1125
1126                    assert_eq!(result, $expected);
1127                }
1128            }
1129        };
1130    }
1131
1132    input_to_ast_check!(BinaryOperator, "plus", "+", BinaryOperator::Add);
1133
1134    input_to_ast_check!(Type, "int", "Int", Type::Int);
1135
1136    input_to_ast_check!(Type, "bool", "Bool", Type::Bool);
1137
1138    input_to_ast_check!(Type, "bytes", "Bytes", Type::Bytes);
1139
1140    input_to_ast_check!(Type, "address", "Address", Type::Address);
1141
1142    input_to_ast_check!(Type, "utxo_ref", "UtxoRef", Type::UtxoRef);
1143
1144    input_to_ast_check!(Type, "any_asset", "AnyAsset", Type::AnyAsset);
1145
1146    input_to_ast_check!(
1147        TypeDef,
1148        "type_def_record",
1149        "type MyRecord {
1150            field1: Int,
1151            field2: Bytes,
1152        }",
1153        TypeDef {
1154            name: "MyRecord".to_string(),
1155            cases: vec![VariantCase {
1156                name: "Default".to_string(),
1157                fields: vec![
1158                    RecordField::new("field1", Type::Int),
1159                    RecordField::new("field2", Type::Bytes)
1160                ],
1161                span: Span::DUMMY,
1162            }],
1163            span: Span::DUMMY,
1164        }
1165    );
1166
1167    input_to_ast_check!(
1168        TypeDef,
1169        "type_def_variant",
1170        "type MyVariant {
1171            Case1 {
1172                field1: Int,
1173                field2: Bytes,
1174            },
1175            Case2,
1176        }",
1177        TypeDef {
1178            name: "MyVariant".to_string(),
1179            cases: vec![
1180                VariantCase {
1181                    name: "Case1".to_string(),
1182                    fields: vec![
1183                        RecordField::new("field1", Type::Int),
1184                        RecordField::new("field2", Type::Bytes)
1185                    ],
1186                    span: Span::DUMMY,
1187                },
1188                VariantCase {
1189                    name: "Case2".to_string(),
1190                    fields: vec![],
1191                    span: Span::DUMMY,
1192                },
1193            ],
1194            span: Span::DUMMY,
1195        }
1196    );
1197
1198    input_to_ast_check!(
1199        StringLiteral,
1200        "literal_string",
1201        "\"Hello, world!\"",
1202        StringLiteral::new("Hello, world!".to_string())
1203    );
1204
1205    input_to_ast_check!(
1206        HexStringLiteral,
1207        "hex_string",
1208        "0xAFAFAF",
1209        HexStringLiteral::new("AFAFAF".to_string())
1210    );
1211
1212    input_to_ast_check!(
1213        StringLiteral,
1214        "literal_string_address",
1215        "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1216        StringLiteral::new("addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string())
1217    );
1218
1219    input_to_ast_check!(
1220        DataExpr,
1221        "identifier",
1222        "my_party",
1223        DataExpr::Identifier(Identifier::new("my_party".to_string()))
1224    );
1225
1226    input_to_ast_check!(DataExpr, "literal_bool_true", "true", DataExpr::Bool(true));
1227
1228    input_to_ast_check!(
1229        DataExpr,
1230        "literal_bool_false",
1231        "false",
1232        DataExpr::Bool(false)
1233    );
1234
1235    input_to_ast_check!(DataExpr, "unit_value", "())", DataExpr::Unit);
1236
1237    input_to_ast_check!(
1238        PropertyAccess,
1239        "single_property",
1240        "subject.property",
1241        PropertyAccess::new("subject", &["property"])
1242    );
1243
1244    input_to_ast_check!(
1245        PropertyAccess,
1246        "multiple_properties",
1247        "subject.property.subproperty",
1248        PropertyAccess::new("subject", &["property", "subproperty"])
1249    );
1250
1251    input_to_ast_check!(
1252        PolicyDef,
1253        "policy_def_assign",
1254        "policy MyPolicy = 0xAFAFAF;",
1255        PolicyDef {
1256            name: "MyPolicy".to_string(),
1257            value: PolicyValue::Assign(HexStringLiteral::new("AFAFAF".to_string())),
1258            span: Span::DUMMY,
1259        }
1260    );
1261
1262    input_to_ast_check!(
1263        PolicyDef,
1264        "policy_def_constructor",
1265        "policy MyPolicy {
1266            hash: 0x1234567890,
1267            script: 0x1234567890,
1268            ref: 0x1234567890,
1269        };",
1270        PolicyDef {
1271            name: "MyPolicy".to_string(),
1272            value: PolicyValue::Constructor(PolicyConstructor {
1273                fields: vec![
1274                    PolicyField::Hash(DataExpr::HexString(HexStringLiteral::new(
1275                        "1234567890".to_string()
1276                    ))),
1277                    PolicyField::Script(DataExpr::HexString(HexStringLiteral::new(
1278                        "1234567890".to_string()
1279                    ))),
1280                    PolicyField::Ref(DataExpr::HexString(HexStringLiteral::new(
1281                        "1234567890".to_string()
1282                    ))),
1283                ],
1284                span: Span::DUMMY,
1285            }),
1286            span: Span::DUMMY,
1287        }
1288    );
1289
1290    input_to_ast_check!(
1291        AssetDef,
1292        "hex_hex",
1293        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.0xef7a1ceb;",
1294        AssetDef {
1295            name: "MyToken".to_string(),
1296            policy: HexStringLiteral::new(
1297                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
1298            ),
1299            asset_name: "0xef7a1ceb".to_string(),
1300            span: Span::DUMMY,
1301        }
1302    );
1303
1304    input_to_ast_check!(
1305        AssetDef,
1306        "hex_ascii",
1307        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.MYTOKEN;",
1308        AssetDef {
1309            name: "MyToken".to_string(),
1310            policy: HexStringLiteral::new(
1311                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
1312            ),
1313            asset_name: "MYTOKEN".to_string(),
1314            span: Span::DUMMY,
1315        }
1316    );
1317
1318    input_to_ast_check!(
1319        AssetConstructor,
1320        "type_and_literal",
1321        "MyToken(15)",
1322        AssetConstructor {
1323            r#type: Identifier::new("MyToken"),
1324            amount: Box::new(DataExpr::Number(15)),
1325            span: Span::DUMMY,
1326        }
1327    );
1328
1329    input_to_ast_check!(
1330        DataExpr,
1331        "addition",
1332        "5 + var1",
1333        DataExpr::BinaryOp(DataBinaryOp {
1334            operator: BinaryOperator::Add,
1335            left: Box::new(DataExpr::Number(5)),
1336            right: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
1337            span: Span::DUMMY,
1338        })
1339    );
1340
1341    input_to_ast_check!(
1342        AddressExpr,
1343        "address_string",
1344        "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1345        AddressExpr::String(StringLiteral::new(
1346            "addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string()
1347        ))
1348    );
1349
1350    input_to_ast_check!(
1351        AddressExpr,
1352        "address_hex_string",
1353        "0x1234567890abcdef",
1354        AddressExpr::HexString(HexStringLiteral::new("1234567890abcdef".to_string()))
1355    );
1356
1357    input_to_ast_check!(
1358        AddressExpr,
1359        "address_identifier",
1360        "my_address",
1361        AddressExpr::Identifier(Identifier::new("my_address"))
1362    );
1363
1364    input_to_ast_check!(
1365        DatumConstructor,
1366        "datum_constructor_record",
1367        "MyRecord {
1368            field1: 10,
1369            field2: abc,
1370        }",
1371        DatumConstructor {
1372            r#type: Identifier::new("MyRecord"),
1373            case: VariantCaseConstructor {
1374                name: Identifier::new("Default"),
1375                fields: vec![
1376                    RecordConstructorField {
1377                        name: Identifier::new("field1"),
1378                        value: Box::new(DataExpr::Number(10)),
1379                        span: Span::DUMMY,
1380                    },
1381                    RecordConstructorField {
1382                        name: Identifier::new("field2"),
1383                        value: Box::new(DataExpr::Identifier(Identifier::new("abc"))),
1384                        span: Span::DUMMY,
1385                    },
1386                ],
1387                spread: None,
1388                scope: None,
1389                span: Span::DUMMY,
1390            },
1391            scope: None,
1392            span: Span::DUMMY,
1393        }
1394    );
1395
1396    input_to_ast_check!(
1397        DatumConstructor,
1398        "datum_constructor_variant",
1399        "ShipCommand::MoveShip {
1400            delta_x: delta_x,
1401            delta_y: delta_y,
1402        }",
1403        DatumConstructor {
1404            r#type: Identifier::new("ShipCommand"),
1405            case: VariantCaseConstructor {
1406                name: Identifier::new("MoveShip"),
1407                fields: vec![
1408                    RecordConstructorField {
1409                        name: Identifier::new("delta_x"),
1410                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
1411                        span: Span::DUMMY,
1412                    },
1413                    RecordConstructorField {
1414                        name: Identifier::new("delta_y"),
1415                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
1416                        span: Span::DUMMY,
1417                    },
1418                ],
1419                spread: None,
1420                scope: None,
1421                span: Span::DUMMY,
1422            },
1423            scope: None,
1424            span: Span::DUMMY,
1425        }
1426    );
1427
1428    input_to_ast_check!(
1429        DatumConstructor,
1430        "datum_constructor_variant_with_spread",
1431        "ShipCommand::MoveShip {
1432            delta_x: delta_x,
1433            delta_y: delta_y,
1434            ...abc
1435        }",
1436        DatumConstructor {
1437            r#type: Identifier::new("ShipCommand"),
1438            case: VariantCaseConstructor {
1439                name: Identifier::new("MoveShip"),
1440                fields: vec![
1441                    RecordConstructorField {
1442                        name: Identifier::new("delta_x"),
1443                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
1444                        span: Span::DUMMY,
1445                    },
1446                    RecordConstructorField {
1447                        name: Identifier::new("delta_y"),
1448                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
1449                        span: Span::DUMMY,
1450                    },
1451                ],
1452                spread: Some(Box::new(DataExpr::Identifier(Identifier::new(
1453                    "abc".to_string()
1454                )))),
1455                scope: None,
1456                span: Span::DUMMY,
1457            },
1458            scope: None,
1459            span: Span::DUMMY,
1460        }
1461    );
1462
1463    input_to_ast_check!(
1464        OutputBlock,
1465        "output_block_anonymous",
1466        r#"output {
1467            to: my_party,
1468            amount: Ada(100),
1469        }"#,
1470        OutputBlock {
1471            name: None,
1472            fields: vec![
1473                OutputBlockField::To(Box::new(AddressExpr::Identifier(Identifier::new(
1474                    "my_party".to_string(),
1475                )))),
1476                OutputBlockField::Amount(Box::new(AssetExpr::Constructor(AssetConstructor {
1477                    r#type: Identifier::new("Ada"),
1478                    amount: Box::new(DataExpr::Number(100)),
1479                    span: Span::DUMMY,
1480                }))),
1481            ],
1482            span: Span::DUMMY,
1483        }
1484    );
1485
1486    input_to_ast_check!(
1487        ChainSpecificBlock,
1488        "chain_specific_block_cardano",
1489        "cardano::vote_delegation_certificate {
1490            drep: 0x1234567890,
1491            stake: 0x1234567890,
1492        }",
1493        ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::VoteDelegationCertificate(
1494            crate::cardano::VoteDelegationCertificate {
1495                drep: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
1496                stake: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
1497                span: Span::DUMMY,
1498            },
1499        ))
1500    );
1501
1502    #[test]
1503    fn test_spans_are_respected() {
1504        let program = parse_well_known_example("lang_tour");
1505        assert_eq!(program.span, Span::new(0, 738));
1506
1507        assert_eq!(program.parties[0].span, Span::new(0, 14));
1508
1509        assert_eq!(program.types[0].span, Span::new(16, 69));
1510    }
1511
1512    fn make_snapshot_if_missing(example: &str, program: &Program) {
1513        let manifest_dir = env!("CARGO_MANIFEST_DIR");
1514        let path = format!("{}/../../examples/{}.ast", manifest_dir, example);
1515
1516        if !std::fs::exists(&path).unwrap() {
1517            let ast = serde_json::to_string_pretty(program).unwrap();
1518            std::fs::write(&path, ast).unwrap();
1519        }
1520    }
1521
1522    fn test_parsing_example(example: &str) {
1523        let program = parse_well_known_example(example);
1524
1525        make_snapshot_if_missing(example, &program);
1526
1527        let manifest_dir = env!("CARGO_MANIFEST_DIR");
1528        let ast_file = format!("{}/../../examples/{}.ast", manifest_dir, example);
1529        let ast = std::fs::read_to_string(ast_file).unwrap();
1530
1531        let expected: Program = serde_json::from_str(&ast).unwrap();
1532
1533        assert_json_eq!(program, expected);
1534    }
1535
1536    #[macro_export]
1537    macro_rules! test_parsing {
1538        ($name:ident) => {
1539            paste! {
1540                #[test]
1541                fn [<test_example_ $name>]() {
1542                    test_parsing_example(stringify!($name));
1543                }
1544            }
1545        };
1546    }
1547
1548    test_parsing!(lang_tour);
1549
1550    test_parsing!(transfer);
1551
1552    test_parsing!(swap);
1553
1554    test_parsing!(asteria);
1555
1556    test_parsing!(vesting);
1557
1558    test_parsing!(faucet);
1559}