Skip to main content

tx3_lang/
parsing.rs

1//! Parses the Tx3 language.
2//!
3//! This module takes a string and parses it into Tx3 AST.
4
5use std::sync::LazyLock;
6
7use miette::SourceOffset;
8use pest::{
9    iterators::Pair,
10    pratt_parser::{Assoc, Op, PrattParser},
11    Parser,
12};
13use pest_derive::Parser;
14
15use crate::ast::*;
16
17#[derive(Parser)]
18#[grammar = "tx3.pest"]
19pub(crate) struct Tx3Grammar;
20
21#[derive(Debug, thiserror::Error, miette::Diagnostic)]
22#[error("Parsing error: {message}")]
23#[diagnostic(code(tx3::parsing))]
24pub struct Error {
25    pub message: String,
26
27    #[source_code]
28    pub src: String,
29
30    #[label]
31    pub span: Span,
32}
33
34impl From<pest::error::Error<Rule>> for Error {
35    fn from(error: pest::error::Error<Rule>) -> Self {
36        match &error.variant {
37            pest::error::ErrorVariant::ParsingError { positives, .. } => Error {
38                message: format!("expected {positives:?}"),
39                src: error.line().to_string(),
40                span: error.location.into(),
41            },
42            pest::error::ErrorVariant::CustomError { message } => Error {
43                message: message.clone(),
44                src: error.line().to_string(),
45                span: error.location.into(),
46            },
47        }
48    }
49}
50
51impl From<pest::error::InputLocation> for Span {
52    fn from(value: pest::error::InputLocation) -> Self {
53        match value {
54            pest::error::InputLocation::Pos(pos) => Self::new(pos, pos),
55            pest::error::InputLocation::Span((start, end)) => Self::new(start, end),
56        }
57    }
58}
59
60impl From<pest::Span<'_>> for Span {
61    fn from(span: pest::Span<'_>) -> Self {
62        Self::new(span.start(), span.end())
63    }
64}
65
66impl From<Span> for miette::SourceSpan {
67    fn from(span: Span) -> Self {
68        miette::SourceSpan::new(SourceOffset::from(span.start), span.end - span.start)
69    }
70}
71
72pub trait AstNode: Sized {
73    const RULE: Rule;
74
75    fn parse(pair: Pair<Rule>) -> Result<Self, Error>;
76
77    fn span(&self) -> &Span;
78}
79
80/// Pulls an optional leading `Rule::docstring` pair from a `Pairs` iterator and
81/// normalizes it: each line's leading `///` (and at most one space after it) is
82/// stripped, lines are joined with `\n`, and trailing whitespace is trimmed.
83fn take_docstring(inner: &mut pest::iterators::Pairs<Rule>) -> Option<String> {
84    let next = inner.peek()?;
85    if next.as_rule() != Rule::docstring {
86        return None;
87    }
88    let pair = inner.next().unwrap();
89
90    let mut lines = Vec::new();
91    for line in pair.into_inner() {
92        debug_assert_eq!(line.as_rule(), Rule::doc_line);
93        let raw = line.as_str();
94        let trimmed = raw.strip_prefix("///").unwrap_or(raw);
95        let trimmed = trimmed.strip_prefix(' ').unwrap_or(trimmed);
96        lines.push(trimmed.trim_end().to_string());
97    }
98
99    let joined = lines.join("\n");
100    if joined.trim().is_empty() {
101        None
102    } else {
103        Some(joined)
104    }
105}
106
107impl AstNode for Program {
108    const RULE: Rule = Rule::program;
109
110    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
111        let span = pair.as_span().into();
112        let inner = pair.into_inner();
113
114        let mut program = Self {
115            env: None,
116            txs: Vec::new(),
117            assets: Vec::new(),
118            types: Vec::new(),
119            aliases: Vec::new(),
120            parties: Vec::new(),
121            policies: Vec::new(),
122            functions: Vec::new(),
123            scope: None,
124            span,
125        };
126
127        for pair in inner {
128            match pair.as_rule() {
129                Rule::env_def => program.env = Some(EnvDef::parse(pair)?),
130                Rule::tx_def => program.txs.push(TxDef::parse(pair)?),
131                Rule::asset_def => program.assets.push(AssetDef::parse(pair)?),
132                Rule::record_def => program.types.push(TypeDef::parse(pair)?),
133                Rule::variant_def => program.types.push(TypeDef::parse(pair)?),
134                Rule::alias_def => program.aliases.push(AliasDef::parse(pair)?),
135                Rule::party_def => program.parties.push(PartyDef::parse(pair)?),
136                Rule::policy_def => program.policies.push(PolicyDef::parse(pair)?),
137                Rule::fn_def => program.functions.push(FnDef::parse(pair)?),
138                Rule::EOI => break,
139                x => unreachable!("Unexpected rule in program: {:?}", x),
140            }
141        }
142
143        Ok(program)
144    }
145
146    fn span(&self) -> &Span {
147        &self.span
148    }
149}
150
151impl AstNode for EnvField {
152    const RULE: Rule = Rule::env_field;
153
154    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
155        let span = pair.as_span().into();
156        let mut inner = pair.into_inner();
157        let docstring = take_docstring(&mut inner);
158        let identifier = inner.next().unwrap().as_str().to_string();
159        let r#type = Type::parse(inner.next().unwrap())?;
160
161        Ok(EnvField {
162            name: identifier,
163            r#type,
164            docstring,
165            span,
166        })
167    }
168
169    fn span(&self) -> &Span {
170        &self.span
171    }
172}
173
174impl AstNode for EnvDef {
175    const RULE: Rule = Rule::env_def;
176
177    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
178        let span = pair.as_span().into();
179        let inner = pair.into_inner();
180
181        let fields = inner
182            .map(|x| EnvField::parse(x))
183            .collect::<Result<Vec<_>, _>>()?;
184
185        Ok(EnvDef { fields, span })
186    }
187
188    fn span(&self) -> &Span {
189        &self.span
190    }
191}
192
193impl AstNode for ParameterList {
194    const RULE: Rule = Rule::parameter_list;
195
196    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
197        let span = pair.as_span().into();
198        let inner = pair.into_inner();
199
200        let mut parameters = Vec::new();
201
202        for param in inner {
203            let mut inner = param.into_inner();
204            let docstring = take_docstring(&mut inner);
205            let name = Identifier::parse(inner.next().unwrap())?;
206            let r#type = Type::parse(inner.next().unwrap())?;
207
208            parameters.push(ParamDef {
209                name,
210                r#type,
211                docstring,
212            });
213        }
214
215        Ok(ParameterList { parameters, span })
216    }
217
218    fn span(&self) -> &Span {
219        &self.span
220    }
221}
222
223impl AstNode for TxDef {
224    const RULE: Rule = Rule::tx_def;
225
226    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
227        let span = pair.as_span().into();
228        let mut inner = pair.into_inner();
229
230        let docstring = take_docstring(&mut inner);
231        let name = Identifier::parse(inner.next().unwrap())?;
232        let parameters = ParameterList::parse(inner.next().unwrap())?;
233
234        let mut locals = None;
235        let mut references = Vec::new();
236        let mut inputs = Vec::new();
237        let mut outputs = Vec::new();
238        let mut validity = None;
239        let mut burns = Vec::new();
240        let mut mints = Vec::new();
241        let mut adhoc = Vec::new();
242        let mut collateral = Vec::new();
243        let mut signers = None;
244        let mut metadata = None;
245
246        for item in inner {
247            match item.as_rule() {
248                Rule::locals_block => locals = Some(LocalsBlock::parse(item)?),
249                Rule::reference_block => references.push(ReferenceBlock::parse(item)?),
250                Rule::input_block => inputs.push(InputBlock::parse(item)?),
251                Rule::output_block => outputs.push(OutputBlock::parse(item)?),
252                Rule::validity_block => validity = Some(ValidityBlock::parse(item)?),
253                Rule::mint_block => mints.push(MintBlock::parse(item)?),
254                Rule::burn_block => burns.push(MintBlock::parse(item)?),
255                Rule::chain_specific_block => adhoc.push(ChainSpecificBlock::parse(item)?),
256                Rule::collateral_block => collateral.push(CollateralBlock::parse(item)?),
257                Rule::signers_block => signers = Some(SignersBlock::parse(item)?),
258                Rule::metadata_block => metadata = Some(MetadataBlock::parse(item)?),
259                x => unreachable!("Unexpected rule in tx_def: {:?}", x),
260            }
261        }
262
263        Ok(TxDef {
264            name,
265            docstring,
266            parameters,
267            locals,
268            references,
269            inputs,
270            outputs,
271            validity,
272            mints,
273            burns,
274            signers,
275            adhoc,
276            scope: None,
277            span,
278            collateral,
279            metadata,
280        })
281    }
282
283    fn span(&self) -> &Span {
284        &self.span
285    }
286}
287
288impl AstNode for Identifier {
289    const RULE: Rule = Rule::identifier;
290
291    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
292        Ok(Identifier {
293            value: pair.as_str().to_string(),
294            symbol: None,
295            span: pair.as_span().into(),
296        })
297    }
298
299    fn span(&self) -> &Span {
300        &self.span
301    }
302}
303
304impl AstNode for StringLiteral {
305    const RULE: Rule = Rule::string;
306
307    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
308        Ok(StringLiteral {
309            value: pair.as_str()[1..pair.as_str().len() - 1].to_string(),
310            span: pair.as_span().into(),
311        })
312    }
313
314    fn span(&self) -> &Span {
315        &self.span
316    }
317}
318
319impl AstNode for HexStringLiteral {
320    const RULE: Rule = Rule::hex_string;
321
322    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
323        Ok(HexStringLiteral {
324            value: pair.as_str()[2..].to_string(),
325            span: pair.as_span().into(),
326        })
327    }
328
329    fn span(&self) -> &Span {
330        &self.span
331    }
332}
333
334impl AstNode for PartyDef {
335    const RULE: Rule = Rule::party_def;
336
337    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
338        let span = pair.as_span().into();
339        let mut inner = pair.into_inner();
340        let docstring = take_docstring(&mut inner);
341        let identifier = Identifier::parse(inner.next().unwrap())?;
342
343        Ok(PartyDef {
344            name: identifier,
345            docstring,
346            span,
347        })
348    }
349
350    fn span(&self) -> &Span {
351        &self.span
352    }
353}
354
355impl AstNode for LocalsAssign {
356    const RULE: Rule = Rule::locals_assign;
357
358    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
359        let span = pair.as_span().into();
360        let mut inner = pair.into_inner();
361
362        let name = Identifier::parse(inner.next().unwrap())?;
363        let value = DataExpr::parse(inner.next().unwrap())?;
364
365        Ok(LocalsAssign { name, value, span })
366    }
367
368    fn span(&self) -> &Span {
369        &self.span
370    }
371}
372
373impl AstNode for LocalsBlock {
374    const RULE: Rule = Rule::locals_block;
375
376    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
377        let span = pair.as_span().into();
378        let inner = pair.into_inner();
379
380        let assigns = inner
381            .map(|x| LocalsAssign::parse(x))
382            .collect::<Result<Vec<_>, _>>()?;
383
384        Ok(LocalsBlock { assigns, span })
385    }
386
387    fn span(&self) -> &Span {
388        &self.span
389    }
390}
391
392impl AstNode for ReferenceBlock {
393    const RULE: Rule = Rule::reference_block;
394
395    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
396        let span = pair.as_span().into();
397        let mut inner = pair.into_inner();
398
399        let name = inner.next().unwrap().as_str().to_string();
400
401        let pair = inner.next().unwrap();
402        let r#ref = match pair.as_rule() {
403            Rule::input_block_ref => {
404                let pair = pair.into_inner().next().unwrap();
405                DataExpr::parse(pair)?
406            }
407            x => unreachable!("Unexpected rule in ref_input_block: {:?}", x),
408        };
409
410        let datum_is = inner
411            .next()
412            .map(|pair| {
413                let pair = pair.into_inner().next().unwrap();
414                Type::parse(pair)
415            })
416            .transpose()?;
417
418        Ok(ReferenceBlock {
419            name,
420            r#ref,
421            datum_is,
422            span,
423        })
424    }
425
426    fn span(&self) -> &Span {
427        &self.span
428    }
429}
430
431impl AstNode for CollateralBlockField {
432    const RULE: Rule = Rule::collateral_block_field;
433
434    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
435        match pair.as_rule() {
436            Rule::input_block_from => {
437                let pair = pair.into_inner().next().unwrap();
438                let x = CollateralBlockField::From(DataExpr::parse(pair)?);
439                Ok(x)
440            }
441            Rule::input_block_min_amount => {
442                let pair = pair.into_inner().next().unwrap();
443                let x = CollateralBlockField::MinAmount(DataExpr::parse(pair)?);
444                Ok(x)
445            }
446            Rule::input_block_ref => {
447                let pair = pair.into_inner().next().unwrap();
448                let x = CollateralBlockField::Ref(DataExpr::parse(pair)?);
449                Ok(x)
450            }
451            x => unreachable!("Unexpected rule in collateral_block: {:?}", x),
452        }
453    }
454
455    fn span(&self) -> &Span {
456        match self {
457            Self::From(x) => x.span(),
458            Self::MinAmount(x) => x.span(),
459            Self::Ref(x) => x.span(),
460        }
461    }
462}
463
464impl AstNode for CollateralBlock {
465    const RULE: Rule = Rule::collateral_block;
466
467    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
468        let span = pair.as_span().into();
469        let inner = pair.into_inner();
470
471        let fields = inner
472            .map(|x| CollateralBlockField::parse(x))
473            .collect::<Result<Vec<_>, _>>()?;
474
475        Ok(CollateralBlock { fields, span })
476    }
477
478    fn span(&self) -> &Span {
479        &self.span
480    }
481}
482
483impl AstNode for MetadataBlockField {
484    const RULE: Rule = Rule::metadata_block_field;
485
486    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
487        let span = pair.as_span().into();
488        match pair.as_rule() {
489            Rule::metadata_block_field => {
490                let mut inner = pair.into_inner();
491                let key = inner.next().unwrap();
492                let value = inner.next().unwrap();
493                Ok(MetadataBlockField {
494                    key: DataExpr::parse(key)?,
495                    value: DataExpr::parse(value)?,
496                    span,
497                })
498            }
499            x => unreachable!("Unexpected rule in metadata_block: {:?}", x),
500        }
501    }
502
503    fn span(&self) -> &Span {
504        &self.span
505    }
506}
507
508impl AstNode for MetadataBlock {
509    const RULE: Rule = Rule::metadata_block;
510
511    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
512        let span = pair.as_span().into();
513        let inner = pair.into_inner();
514
515        let fields = inner
516            .map(|x| MetadataBlockField::parse(x))
517            .collect::<Result<Vec<_>, _>>()?;
518
519        Ok(MetadataBlock { fields, span })
520    }
521
522    fn span(&self) -> &Span {
523        &self.span
524    }
525}
526
527impl AstNode for InputBlockField {
528    const RULE: Rule = Rule::input_block_field;
529
530    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
531        match pair.as_rule() {
532            Rule::input_block_from => {
533                let pair = pair.into_inner().next().unwrap();
534                let x = InputBlockField::From(DataExpr::parse(pair)?);
535                Ok(x)
536            }
537            Rule::input_block_datum_is => {
538                let pair = pair.into_inner().next().unwrap();
539                let x = InputBlockField::DatumIs(Type::parse(pair)?);
540                Ok(x)
541            }
542            Rule::input_block_min_amount => {
543                let pair = pair.into_inner().next().unwrap();
544                let x = InputBlockField::MinAmount(DataExpr::parse(pair)?);
545                Ok(x)
546            }
547            Rule::input_block_redeemer => {
548                let pair = pair.into_inner().next().unwrap();
549                let x = InputBlockField::Redeemer(DataExpr::parse(pair)?);
550                Ok(x)
551            }
552            Rule::input_block_ref => {
553                let pair = pair.into_inner().next().unwrap();
554                let x = InputBlockField::Ref(DataExpr::parse(pair)?);
555                Ok(x)
556            }
557            x => unreachable!("Unexpected rule in input_block: {:?}", x),
558        }
559    }
560
561    fn span(&self) -> &Span {
562        match self {
563            Self::From(x) => x.span(),
564            Self::DatumIs(x) => x.span(),
565            Self::MinAmount(x) => x.span(),
566            Self::Redeemer(x) => x.span(),
567            Self::Ref(x) => x.span(),
568        }
569    }
570}
571
572impl AstNode for InputBlock {
573    const RULE: Rule = Rule::input_block;
574
575    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
576        let span = pair.as_span().into();
577        let mut inner = pair.into_inner();
578
579        let next = inner.next().unwrap();
580
581        let (many, name) = match next.as_rule() {
582            Rule::input_many => (true, inner.next().unwrap().as_str().to_string()),
583            Rule::identifier => (false, next.as_str().to_string()),
584            _ => unreachable!("Unexpected rule in input_block: {:?}", next.as_rule()),
585        };
586
587        let fields = inner
588            .map(|x| InputBlockField::parse(x))
589            .collect::<Result<Vec<_>, _>>()?;
590
591        Ok(InputBlock {
592            name,
593            many,
594            fields,
595            span,
596        })
597    }
598
599    fn span(&self) -> &Span {
600        &self.span
601    }
602}
603
604impl AstNode for OutputBlockField {
605    const RULE: Rule = Rule::output_block_field;
606
607    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
608        match pair.as_rule() {
609            Rule::output_block_to => {
610                let pair = pair.into_inner().next().unwrap();
611                let x = OutputBlockField::To(Box::new(DataExpr::parse(pair)?));
612                Ok(x)
613            }
614            Rule::output_block_amount => {
615                let pair = pair.into_inner().next().unwrap();
616                let x = OutputBlockField::Amount(DataExpr::parse(pair)?.into());
617                Ok(x)
618            }
619            Rule::output_block_datum => {
620                let pair = pair.into_inner().next().unwrap();
621                let x = OutputBlockField::Datum(DataExpr::parse(pair)?.into());
622                Ok(x)
623            }
624            x => unreachable!("Unexpected rule in output_block_field: {:?}", x),
625        }
626    }
627
628    fn span(&self) -> &Span {
629        match self {
630            Self::To(x) => x.span(),
631            Self::Amount(x) => x.span(),
632            Self::Datum(x) => x.span(),
633        }
634    }
635}
636
637impl AstNode for OutputBlock {
638    const RULE: Rule = Rule::output_block;
639
640    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
641        let span = pair.as_span().into();
642        let mut inner = pair.into_inner();
643
644        let optional = inner
645            .peek()
646            .map_or(false, |first| first.as_rule() == Rule::output_optional);
647
648        if optional {
649            inner.next();
650        }
651
652        let has_name = inner
653            .peek()
654            .map(|x| x.as_rule() == Rule::identifier)
655            .unwrap_or_default();
656
657        let name = if has_name {
658            Some(Identifier::parse(inner.next().unwrap())?)
659        } else {
660            None
661        };
662
663        let fields = inner
664            .map(|x| OutputBlockField::parse(x))
665            .collect::<Result<Vec<_>, _>>()?;
666
667        Ok(OutputBlock {
668            name,
669            optional,
670            fields,
671            span,
672        })
673    }
674
675    fn span(&self) -> &Span {
676        &self.span
677    }
678}
679
680impl AstNode for ValidityBlockField {
681    const RULE: Rule = Rule::validity_block_field;
682
683    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
684        match pair.as_rule() {
685            Rule::validity_since_slot => {
686                let pair = pair.into_inner().next().unwrap();
687                let x = ValidityBlockField::SinceSlot(DataExpr::parse(pair)?.into());
688                Ok(x)
689            }
690            Rule::validity_until_slot => {
691                let pair = pair.into_inner().next().unwrap();
692                let x = ValidityBlockField::UntilSlot(DataExpr::parse(pair)?.into());
693                Ok(x)
694            }
695            x => unreachable!("Unexpected rule in validity_block: {:?}", x),
696        }
697    }
698
699    fn span(&self) -> &Span {
700        match self {
701            Self::UntilSlot(x) => x.span(),
702            Self::SinceSlot(x) => x.span(),
703        }
704    }
705}
706
707impl AstNode for ValidityBlock {
708    const RULE: Rule = Rule::validity_block;
709
710    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
711        let span = pair.as_span().into();
712        let inner = pair.into_inner();
713
714        let fields = inner
715            .map(|x| ValidityBlockField::parse(x))
716            .collect::<Result<Vec<_>, _>>()?;
717
718        Ok(ValidityBlock { fields, span })
719    }
720
721    fn span(&self) -> &Span {
722        &self.span
723    }
724}
725
726impl AstNode for MintBlockField {
727    const RULE: Rule = Rule::mint_block_field;
728
729    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
730        match pair.as_rule() {
731            Rule::mint_block_amount => {
732                let pair = pair.into_inner().next().unwrap();
733                let x = MintBlockField::Amount(DataExpr::parse(pair)?.into());
734                Ok(x)
735            }
736            Rule::mint_block_redeemer => {
737                let pair = pair.into_inner().next().unwrap();
738                let x = MintBlockField::Redeemer(DataExpr::parse(pair)?.into());
739                Ok(x)
740            }
741            x => unreachable!("Unexpected rule in output_block_field: {:?}", x),
742        }
743    }
744
745    fn span(&self) -> &Span {
746        match self {
747            Self::Amount(x) => x.span(),
748            Self::Redeemer(x) => x.span(),
749        }
750    }
751}
752
753impl AstNode for SignersBlock {
754    const RULE: Rule = Rule::signers_block;
755
756    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
757        let span = pair.as_span().into();
758        let inner = pair.into_inner();
759
760        let signers = inner
761            .map(|x| DataExpr::parse(x))
762            .collect::<Result<Vec<_>, _>>()?;
763
764        Ok(SignersBlock { signers, span })
765    }
766
767    fn span(&self) -> &Span {
768        &self.span
769    }
770}
771
772impl AstNode for MintBlock {
773    const RULE: Rule = Rule::mint_block;
774
775    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
776        let span = pair.as_span().into();
777        let inner = pair.into_inner();
778
779        let fields = inner
780            .map(|x| MintBlockField::parse(x))
781            .collect::<Result<Vec<_>, _>>()?;
782
783        Ok(MintBlock { fields, span })
784    }
785
786    fn span(&self) -> &Span {
787        &self.span
788    }
789}
790
791impl AstNode for RecordField {
792    const RULE: Rule = Rule::record_field;
793
794    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
795        let span = pair.as_span().into();
796        let mut inner = pair.into_inner();
797        let identifier = Identifier::parse(inner.next().unwrap())?;
798        let r#type = Type::parse(inner.next().unwrap())?;
799
800        Ok(RecordField {
801            name: identifier,
802            r#type,
803            span,
804        })
805    }
806
807    fn span(&self) -> &Span {
808        &self.span
809    }
810}
811
812impl AstNode for PolicyField {
813    const RULE: Rule = Rule::policy_def_field;
814
815    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
816        match pair.as_rule() {
817            Rule::policy_def_hash => Ok(PolicyField::Hash(DataExpr::parse(
818                pair.into_inner().next().unwrap(),
819            )?)),
820            Rule::policy_def_script => Ok(PolicyField::Script(DataExpr::parse(
821                pair.into_inner().next().unwrap(),
822            )?)),
823            Rule::policy_def_ref => Ok(PolicyField::Ref(DataExpr::parse(
824                pair.into_inner().next().unwrap(),
825            )?)),
826            x => unreachable!("Unexpected rule in policy_field: {:?}", x),
827        }
828    }
829
830    fn span(&self) -> &Span {
831        match self {
832            Self::Hash(x) => x.span(),
833            Self::Script(x) => x.span(),
834            Self::Ref(x) => x.span(),
835        }
836    }
837}
838
839impl AstNode for PolicyConstructor {
840    const RULE: Rule = Rule::policy_def_constructor;
841
842    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
843        let span = pair.as_span().into();
844        let inner = pair.into_inner();
845
846        let fields = inner
847            .map(|x| PolicyField::parse(x))
848            .collect::<Result<Vec<_>, _>>()?;
849
850        Ok(PolicyConstructor { fields, span })
851    }
852
853    fn span(&self) -> &Span {
854        &self.span
855    }
856}
857
858impl AstNode for PolicyValue {
859    const RULE: Rule = Rule::policy_def_value;
860
861    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
862        match pair.as_rule() {
863            Rule::policy_def_constructor => {
864                Ok(PolicyValue::Constructor(PolicyConstructor::parse(pair)?))
865            }
866            Rule::policy_def_assign => Ok(PolicyValue::Assign(HexStringLiteral::parse(
867                pair.into_inner().next().unwrap(),
868            )?)),
869            x => unreachable!("Unexpected rule in policy_value: {:?}", x),
870        }
871    }
872
873    fn span(&self) -> &Span {
874        match self {
875            Self::Constructor(x) => x.span(),
876            Self::Assign(x) => x.span(),
877        }
878    }
879}
880
881impl AstNode for PolicyDef {
882    const RULE: Rule = Rule::policy_def;
883
884    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
885        let span = pair.as_span().into();
886        let mut inner = pair.into_inner();
887        let name = Identifier::parse(inner.next().unwrap())?;
888        let value = PolicyValue::parse(inner.next().unwrap())?;
889
890        Ok(PolicyDef { name, value, span })
891    }
892
893    fn span(&self) -> &Span {
894        &self.span
895    }
896}
897
898impl AstNode for AnyAssetConstructor {
899    const RULE: Rule = Rule::any_asset_constructor;
900
901    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
902        let span = pair.as_span().into();
903        let mut inner = pair.into_inner();
904
905        let policy = DataExpr::parse(inner.next().unwrap())?;
906        let asset_name = DataExpr::parse(inner.next().unwrap())?;
907        let amount = DataExpr::parse(inner.next().unwrap())?;
908
909        Ok(AnyAssetConstructor {
910            policy: Box::new(policy),
911            asset_name: Box::new(asset_name),
912            amount: Box::new(amount),
913            span,
914        })
915    }
916
917    fn span(&self) -> &Span {
918        &self.span
919    }
920}
921
922impl AstNode for ConcatOp {
923    const RULE: Rule = Rule::concat_constructor;
924
925    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
926        let span = pair.as_span().into();
927        let mut inner = pair.into_inner();
928
929        let lhs = DataExpr::parse(inner.next().unwrap())?;
930        let rhs = DataExpr::parse(inner.next().unwrap())?;
931
932        Ok(ConcatOp {
933            lhs: Box::new(lhs),
934            rhs: Box::new(rhs),
935            span,
936        })
937    }
938
939    fn span(&self) -> &Span {
940        &self.span
941    }
942}
943
944impl AstNode for crate::ast::FnCall {
945    const RULE: Rule = Rule::fn_call;
946
947    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
948        let span = pair.as_span().into();
949        let mut inner = pair.into_inner();
950
951        let callee = Identifier::parse(inner.next().unwrap())?;
952
953        let mut args = Vec::new();
954        for arg_pair in inner {
955            args.push(DataExpr::parse(arg_pair)?);
956        }
957
958        Ok(crate::ast::FnCall { callee, args, span })
959    }
960
961    fn span(&self) -> &Span {
962        &self.span
963    }
964}
965
966impl AstNode for LetBinding {
967    const RULE: Rule = Rule::let_binding;
968
969    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
970        let span = pair.as_span().into();
971        let mut inner = pair.into_inner();
972
973        let name = Identifier::parse(inner.next().unwrap())?;
974        let value = DataExpr::parse(inner.next().unwrap())?;
975
976        Ok(LetBinding { name, value, span })
977    }
978
979    fn span(&self) -> &Span {
980        &self.span
981    }
982}
983
984impl AstNode for FnBody {
985    const RULE: Rule = Rule::fn_body;
986
987    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
988        let span = pair.as_span().into();
989        let inner = pair.into_inner();
990
991        let mut let_bindings = Vec::new();
992        let mut result_expr = None;
993
994        for pair in inner {
995            match pair.as_rule() {
996                Rule::let_binding => let_bindings.push(LetBinding::parse(pair)?),
997                Rule::data_expr => result_expr = Some(DataExpr::parse(pair)?),
998                x => unreachable!("Unexpected rule in fn_body: {:?}", x),
999            }
1000        }
1001
1002        Ok(FnBody {
1003            let_bindings,
1004            result: Box::new(result_expr.expect("fn_body must have a result expression")),
1005            span,
1006        })
1007    }
1008
1009    fn span(&self) -> &Span {
1010        &self.span
1011    }
1012}
1013
1014impl AstNode for FnDef {
1015    const RULE: Rule = Rule::fn_def;
1016
1017    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1018        let span = pair.as_span().into();
1019        let mut inner = pair.into_inner();
1020
1021        let name = Identifier::parse(inner.next().unwrap())?;
1022        let parameters = ParameterList::parse(inner.next().unwrap())?;
1023        let return_type = Type::parse(inner.next().unwrap())?;
1024        let body = FnBody::parse(inner.next().unwrap())?;
1025
1026        Ok(FnDef {
1027            name,
1028            parameters,
1029            return_type,
1030            body: Some(body),
1031            builtin: None,
1032            span,
1033            scope: None,
1034        })
1035    }
1036
1037    fn span(&self) -> &Span {
1038        &self.span
1039    }
1040}
1041
1042impl AstNode for RecordConstructorField {
1043    const RULE: Rule = Rule::record_constructor_field;
1044
1045    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1046        let span = pair.as_span().into();
1047        let mut inner = pair.into_inner();
1048
1049        let name = Identifier::parse(inner.next().unwrap())?;
1050        let value = DataExpr::parse(inner.next().unwrap())?;
1051
1052        Ok(RecordConstructorField {
1053            name,
1054            value: Box::new(value),
1055            span,
1056        })
1057    }
1058
1059    fn span(&self) -> &Span {
1060        &self.span
1061    }
1062}
1063
1064impl AstNode for UtxoRef {
1065    const RULE: Rule = Rule::utxo_ref;
1066
1067    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1068        let span = pair.as_span().into();
1069        let raw_ref = pair.as_span().as_str()[2..].to_string();
1070        let (raw_txid, raw_output_ix) = raw_ref.split_once("#").expect("Invalid utxo ref");
1071
1072        Ok(UtxoRef {
1073            txid: hex::decode(raw_txid).expect("Invalid hex txid"),
1074            index: raw_output_ix.parse().expect("Invalid output index"),
1075            span,
1076        })
1077    }
1078
1079    fn span(&self) -> &Span {
1080        &self.span
1081    }
1082}
1083
1084impl AstNode for StructConstructor {
1085    const RULE: Rule = Rule::struct_constructor;
1086
1087    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1088        let span = pair.as_span().into();
1089        let mut inner = pair.into_inner();
1090
1091        let r#type = Identifier::parse(inner.next().unwrap())?;
1092        let case = VariantCaseConstructor::parse(inner.next().unwrap())?;
1093
1094        Ok(StructConstructor {
1095            r#type,
1096            case,
1097            scope: None,
1098            span,
1099        })
1100    }
1101
1102    fn span(&self) -> &Span {
1103        &self.span
1104    }
1105}
1106
1107impl VariantCaseConstructor {
1108    fn implicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1109        let span = pair.as_span().into();
1110        let inner = pair.into_inner();
1111
1112        let mut fields = Vec::new();
1113        let mut spread = None;
1114
1115        for pair in inner {
1116            match pair.as_rule() {
1117                Rule::record_constructor_field => {
1118                    fields.push(RecordConstructorField::parse(pair)?);
1119                }
1120                Rule::spread_expression => {
1121                    spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
1122                }
1123                x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
1124            }
1125        }
1126
1127        Ok(VariantCaseConstructor {
1128            name: Identifier::new("Default"),
1129            fields,
1130            spread: spread.map(Box::new),
1131            scope: None,
1132            span,
1133        })
1134    }
1135
1136    fn explicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1137        let span = pair.as_span().into();
1138        let mut inner = pair.into_inner();
1139
1140        let name = Identifier::parse(inner.next().unwrap())?;
1141
1142        let mut fields = Vec::new();
1143        let mut spread = None;
1144
1145        for pair in inner {
1146            match pair.as_rule() {
1147                Rule::record_constructor_field => {
1148                    fields.push(RecordConstructorField::parse(pair)?);
1149                }
1150                Rule::spread_expression => {
1151                    spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
1152                }
1153                x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
1154            }
1155        }
1156
1157        Ok(VariantCaseConstructor {
1158            name,
1159            fields,
1160            spread: spread.map(Box::new),
1161            scope: None,
1162            span,
1163        })
1164    }
1165}
1166
1167impl AstNode for VariantCaseConstructor {
1168    const RULE: Rule = Rule::variant_case_constructor;
1169
1170    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1171        match pair.as_rule() {
1172            Rule::implicit_variant_case_constructor => Self::implicit_parse(pair),
1173            Rule::explicit_variant_case_constructor => Self::explicit_parse(pair),
1174            x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
1175        }
1176    }
1177
1178    fn span(&self) -> &Span {
1179        &self.span
1180    }
1181}
1182
1183impl AstNode for ListConstructor {
1184    const RULE: Rule = Rule::list_constructor;
1185
1186    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1187        let span = pair.as_span().into();
1188        let inner = pair.into_inner();
1189
1190        let elements = inner.map(DataExpr::parse).collect::<Result<Vec<_>, _>>()?;
1191
1192        Ok(ListConstructor { elements, span })
1193    }
1194
1195    fn span(&self) -> &Span {
1196        &self.span
1197    }
1198}
1199
1200impl AstNode for MapField {
1201    const RULE: Rule = Rule::map_field;
1202
1203    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1204        let span = pair.as_span().into();
1205        let mut inner = pair.into_inner();
1206
1207        let key = DataExpr::parse(inner.next().unwrap())?;
1208        let value = DataExpr::parse(inner.next().unwrap())?;
1209
1210        Ok(MapField { key, value, span })
1211    }
1212
1213    fn span(&self) -> &Span {
1214        &self.span
1215    }
1216}
1217
1218impl AstNode for MapConstructor {
1219    const RULE: Rule = Rule::map_constructor;
1220
1221    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1222        let span = pair.as_span().into();
1223        let inner = pair.into_inner();
1224
1225        let fields = inner.map(MapField::parse).collect::<Result<Vec<_>, _>>()?;
1226
1227        Ok(MapConstructor { fields, span })
1228    }
1229
1230    fn span(&self) -> &Span {
1231        &self.span
1232    }
1233}
1234
1235impl DataExpr {
1236    fn number_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1237        Ok(DataExpr::Number(pair.as_str().parse().unwrap()))
1238    }
1239
1240    fn bool_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1241        Ok(DataExpr::Bool(pair.as_str().parse().unwrap()))
1242    }
1243
1244    fn identifier_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1245        Ok(DataExpr::Identifier(Identifier::parse(pair)?))
1246    }
1247
1248    fn struct_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1249        Ok(DataExpr::StructConstructor(StructConstructor::parse(pair)?))
1250    }
1251
1252    fn list_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1253        Ok(DataExpr::ListConstructor(ListConstructor::parse(pair)?))
1254    }
1255
1256    fn map_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1257        Ok(DataExpr::MapConstructor(MapConstructor::parse(pair)?))
1258    }
1259
1260    fn utxo_ref_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1261        Ok(DataExpr::UtxoRef(UtxoRef::parse(pair)?))
1262    }
1263
1264    fn any_asset_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1265        Ok(DataExpr::AnyAssetConstructor(AnyAssetConstructor::parse(
1266            pair,
1267        )?))
1268    }
1269
1270    fn concat_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1271        Ok(DataExpr::ConcatOp(ConcatOp::parse(pair)?))
1272    }
1273
1274    fn fn_call_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1275        Ok(DataExpr::FnCall(crate::ast::FnCall::parse(pair)?))
1276    }
1277
1278    fn negate_op_parse(pair: Pair<Rule>, right: DataExpr) -> Result<Self, Error> {
1279        Ok(DataExpr::NegateOp(NegateOp {
1280            operand: Box::new(right),
1281            span: pair.as_span().into(),
1282        }))
1283    }
1284
1285    fn property_op_parse(pair: Pair<Rule>, left: DataExpr) -> Result<Self, Error> {
1286        let span: Span = pair.as_span().into();
1287        let mut inner = pair.into_inner();
1288
1289        Ok(DataExpr::PropertyOp(PropertyOp {
1290            operand: Box::new(left),
1291            property: Box::new(DataExpr::Identifier(Identifier::parse(
1292                inner.next().unwrap(),
1293            )?)),
1294            span,
1295            scope: None,
1296        }))
1297    }
1298
1299    fn index_op_parse(pair: Pair<Rule>, left: DataExpr) -> Result<Self, Error> {
1300        let span: Span = pair.as_span().into();
1301        let mut inner = pair.into_inner();
1302
1303        Ok(DataExpr::PropertyOp(PropertyOp {
1304            operand: Box::new(left),
1305            property: Box::new(DataExpr::parse(inner.next().unwrap())?),
1306            span,
1307            scope: None,
1308        }))
1309    }
1310
1311    fn add_op_parse(left: DataExpr, pair: Pair<Rule>, right: DataExpr) -> Result<Self, Error> {
1312        let span = pair.as_span().into();
1313
1314        Ok(DataExpr::AddOp(AddOp {
1315            lhs: Box::new(left),
1316            rhs: Box::new(right),
1317            span,
1318        }))
1319    }
1320
1321    fn sub_op_parse(left: DataExpr, pair: Pair<Rule>, right: DataExpr) -> Result<Self, Error> {
1322        let span = pair.as_span().into();
1323
1324        Ok(DataExpr::SubOp(SubOp {
1325            lhs: Box::new(left),
1326            rhs: Box::new(right),
1327            span,
1328        }))
1329    }
1330}
1331
1332static DATA_EXPR_PRATT_PARSER: LazyLock<PrattParser<Rule>> = LazyLock::new(|| {
1333    PrattParser::new()
1334        .op(Op::infix(Rule::data_add, Assoc::Left) | Op::infix(Rule::data_sub, Assoc::Left))
1335        .op(Op::prefix(Rule::data_negate))
1336        .op(Op::postfix(Rule::data_property) | Op::postfix(Rule::data_index))
1337});
1338
1339impl AstNode for DataExpr {
1340    const RULE: Rule = Rule::data_expr;
1341
1342    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1343        let inner = pair.into_inner();
1344
1345        DATA_EXPR_PRATT_PARSER
1346            .map_primary(|x| match x.as_rule() {
1347                Rule::number => DataExpr::number_parse(x),
1348                Rule::string => Ok(DataExpr::String(StringLiteral::parse(x)?)),
1349                Rule::bool => DataExpr::bool_parse(x),
1350                Rule::hex_string => Ok(DataExpr::HexString(HexStringLiteral::parse(x)?)),
1351                Rule::struct_constructor => DataExpr::struct_constructor_parse(x),
1352                Rule::list_constructor => DataExpr::list_constructor_parse(x),
1353                Rule::map_constructor => DataExpr::map_constructor_parse(x),
1354                Rule::unit => Ok(DataExpr::Unit),
1355                Rule::identifier => DataExpr::identifier_parse(x),
1356                Rule::utxo_ref => DataExpr::utxo_ref_parse(x),
1357                Rule::any_asset_constructor => DataExpr::any_asset_constructor_parse(x),
1358                Rule::concat_constructor => DataExpr::concat_constructor_parse(x),
1359                Rule::fn_call => DataExpr::fn_call_parse(x),
1360                Rule::data_expr => DataExpr::parse(x),
1361                x => unreachable!("unexpected rule as data primary: {:?}", x),
1362            })
1363            .map_prefix(|op, right| match op.as_rule() {
1364                Rule::data_negate => DataExpr::negate_op_parse(op, right?),
1365                x => unreachable!("Unexpected rule as data prefix: {:?}", x),
1366            })
1367            .map_postfix(|left, op| match op.as_rule() {
1368                Rule::data_property => DataExpr::property_op_parse(op, left?),
1369                Rule::data_index => DataExpr::index_op_parse(op, left?),
1370                x => unreachable!("Unexpected rule as data postfix: {:?}", x),
1371            })
1372            .map_infix(|left, op, right| match op.as_rule() {
1373                Rule::data_add => DataExpr::add_op_parse(left?, op, right?),
1374                Rule::data_sub => DataExpr::sub_op_parse(left?, op, right?),
1375                x => unreachable!("Unexpected rule as data infix: {:?}", x),
1376            })
1377            .parse(inner)
1378    }
1379
1380    fn span(&self) -> &Span {
1381        match self {
1382            DataExpr::None => &Span::DUMMY,      // TODO
1383            DataExpr::Unit => &Span::DUMMY,      // TODO
1384            DataExpr::Number(_) => &Span::DUMMY, // TODO
1385            DataExpr::Bool(_) => &Span::DUMMY,   // TODO
1386            DataExpr::String(x) => x.span(),
1387            DataExpr::HexString(x) => x.span(),
1388            DataExpr::StructConstructor(x) => x.span(),
1389            DataExpr::ListConstructor(x) => x.span(),
1390            DataExpr::MapConstructor(x) => x.span(),
1391            DataExpr::AnyAssetConstructor(x) => x.span(),
1392            DataExpr::Identifier(x) => x.span(),
1393            DataExpr::AddOp(x) => &x.span,
1394            DataExpr::SubOp(x) => &x.span,
1395            DataExpr::ConcatOp(x) => &x.span,
1396            DataExpr::NegateOp(x) => &x.span,
1397            DataExpr::PropertyOp(x) => &x.span,
1398            DataExpr::UtxoRef(x) => x.span(),
1399            DataExpr::FnCall(x) => &x.span,
1400        }
1401    }
1402}
1403
1404impl AstNode for Type {
1405    const RULE: Rule = Rule::r#type;
1406
1407    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1408        let inner = pair.into_inner().next().unwrap();
1409
1410        match inner.as_rule() {
1411            Rule::primitive_type => match inner.as_str() {
1412                "Int" => Ok(Type::Int),
1413                "Bool" => Ok(Type::Bool),
1414                "Bytes" => Ok(Type::Bytes),
1415                "Address" => Ok(Type::Address),
1416                "UtxoRef" => Ok(Type::UtxoRef),
1417                "AnyAsset" => Ok(Type::AnyAsset),
1418                _ => unreachable!("Unexpected string in primitive_type: {:?}", inner.as_str()),
1419            },
1420            Rule::list_type => {
1421                let inner = inner.into_inner().next().unwrap();
1422                Ok(Type::List(Box::new(Type::parse(inner)?)))
1423            }
1424            Rule::map_type => {
1425                let mut inner = inner.into_inner();
1426                let key_type = Type::parse(inner.next().unwrap())?;
1427                let value_type = Type::parse(inner.next().unwrap())?;
1428                Ok(Type::Map(Box::new(key_type), Box::new(value_type)))
1429            }
1430            Rule::custom_type => Ok(Type::Custom(Identifier::new(inner.as_str().to_owned()))),
1431            x => unreachable!("Unexpected rule in type: {:?}", x),
1432        }
1433    }
1434
1435    fn span(&self) -> &Span {
1436        &Span::DUMMY // TODO
1437    }
1438}
1439
1440impl TypeDef {
1441    fn parse_variant_format(pair: Pair<Rule>) -> Result<Self, Error> {
1442        let span = pair.as_span().into();
1443        let mut inner = pair.into_inner();
1444
1445        let identifier = Identifier::parse(inner.next().unwrap())?;
1446
1447        let cases = inner
1448            .map(VariantCase::parse)
1449            .collect::<Result<Vec<_>, _>>()?;
1450
1451        Ok(TypeDef {
1452            name: identifier,
1453            cases,
1454            span,
1455        })
1456    }
1457
1458    fn parse_record_format(pair: Pair<Rule>) -> Result<Self, Error> {
1459        let span: Span = pair.as_span().into();
1460        let mut inner = pair.into_inner();
1461
1462        let identifier = Identifier::parse(inner.next().unwrap())?;
1463
1464        let fields = inner
1465            .map(RecordField::parse)
1466            .collect::<Result<Vec<_>, _>>()?;
1467
1468        Ok(TypeDef {
1469            name: identifier.clone(),
1470            cases: vec![VariantCase {
1471                name: Identifier::new("Default"),
1472                fields,
1473                span: span.clone(),
1474            }],
1475            span,
1476        })
1477    }
1478}
1479
1480impl AstNode for TypeDef {
1481    const RULE: Rule = Rule::type_def;
1482
1483    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1484        match pair.as_rule() {
1485            Rule::variant_def => Ok(Self::parse_variant_format(pair)?),
1486            Rule::record_def => Ok(Self::parse_record_format(pair)?),
1487            x => unreachable!("Unexpected rule in type_def: {:?}", x),
1488        }
1489    }
1490
1491    fn span(&self) -> &Span {
1492        &self.span
1493    }
1494}
1495
1496impl AstNode for AliasDef {
1497    const RULE: Rule = Rule::alias_def;
1498
1499    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1500        let span: Span = pair.as_span().into();
1501        let mut inner = pair.into_inner();
1502
1503        let identifier = Identifier::parse(inner.next().unwrap())?;
1504        let r#type = Type::parse(inner.next().unwrap())?;
1505
1506        Ok(AliasDef {
1507            name: identifier,
1508            alias_type: r#type,
1509            span,
1510        })
1511    }
1512
1513    fn span(&self) -> &Span {
1514        &self.span
1515    }
1516}
1517
1518impl VariantCase {
1519    fn struct_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
1520        let span = pair.as_span().into();
1521        let mut inner = pair.into_inner();
1522
1523        let identifier = Identifier::parse(inner.next().unwrap())?;
1524
1525        let fields = inner
1526            .map(RecordField::parse)
1527            .collect::<Result<Vec<_>, _>>()?;
1528
1529        Ok(Self {
1530            name: identifier,
1531            fields,
1532            span,
1533        })
1534    }
1535
1536    fn unit_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
1537        let span = pair.as_span().into();
1538        let mut inner = pair.into_inner();
1539
1540        let identifier = Identifier::parse(inner.next().unwrap())?;
1541
1542        Ok(Self {
1543            name: identifier,
1544            fields: vec![],
1545            span,
1546        })
1547    }
1548}
1549
1550impl AstNode for VariantCase {
1551    const RULE: Rule = Rule::variant_case;
1552
1553    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1554        let case = match pair.as_rule() {
1555            Rule::variant_case_struct => Self::struct_case_parse(pair),
1556            Rule::variant_case_tuple => todo!("parse variant case tuple"),
1557            Rule::variant_case_unit => Self::unit_case_parse(pair),
1558            x => unreachable!("Unexpected rule in datum_variant: {:?}", x),
1559        }?;
1560
1561        Ok(case)
1562    }
1563
1564    fn span(&self) -> &Span {
1565        &self.span
1566    }
1567}
1568
1569impl AstNode for AssetDef {
1570    const RULE: Rule = Rule::asset_def;
1571
1572    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1573        let span = pair.as_span().into();
1574        let mut inner = pair.into_inner();
1575
1576        let identifier = Identifier::parse(inner.next().unwrap())?;
1577        let policy = DataExpr::parse(inner.next().unwrap())?;
1578        let asset_name = DataExpr::parse(inner.next().unwrap())?;
1579
1580        Ok(AssetDef {
1581            name: identifier,
1582            policy,
1583            asset_name,
1584            span,
1585        })
1586    }
1587
1588    fn span(&self) -> &Span {
1589        &self.span
1590    }
1591}
1592
1593impl AstNode for ChainSpecificBlock {
1594    const RULE: Rule = Rule::chain_specific_block;
1595
1596    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1597        let mut inner = pair.into_inner();
1598
1599        let block = inner.next().unwrap();
1600
1601        match block.as_rule() {
1602            Rule::cardano_block => {
1603                let block = crate::cardano::CardanoBlock::parse(block)?;
1604                Ok(ChainSpecificBlock::Cardano(block))
1605            }
1606            x => unreachable!("Unexpected rule in chain_specific_block: {:?}", x),
1607        }
1608    }
1609
1610    fn span(&self) -> &Span {
1611        match self {
1612            Self::Cardano(x) => x.span(),
1613        }
1614    }
1615}
1616
1617/// Parses a Tx3 source string into a Program AST.
1618///
1619/// # Arguments
1620///
1621/// * `input` - String containing Tx3 source code
1622///
1623/// # Returns
1624///
1625/// * `Result<Program, Error>` - The parsed Program AST or an error
1626///
1627/// # Errors
1628///
1629/// Returns an error if:
1630/// - The input string is not valid Tx3 syntax
1631/// - The AST construction fails
1632///
1633/// # Example
1634///
1635/// ```
1636/// use tx3_lang::parsing::parse_string;
1637/// let program = parse_string("tx swap() {}").unwrap();
1638/// ```
1639pub fn parse_string(input: &str) -> Result<Program, Error> {
1640    let pairs = Tx3Grammar::parse(Rule::program, input)?;
1641    Program::parse(pairs.into_iter().next().unwrap())
1642}
1643
1644#[cfg(test)]
1645pub fn parse_well_known_example(example: &str) -> Program {
1646    let manifest_dir = env!("CARGO_MANIFEST_DIR");
1647    let test_file = format!("{}/../../examples/{}.tx3", manifest_dir, example);
1648    let input = std::fs::read_to_string(&test_file).unwrap();
1649    parse_string(&input).unwrap()
1650}
1651
1652#[cfg(test)]
1653mod tests {
1654    use super::*;
1655    use crate::ast;
1656    use assert_json_diff::assert_json_eq;
1657    use paste::paste;
1658    use pest::Parser;
1659
1660    #[test]
1661    fn smoke_test_parse_string() {
1662        let _ = parse_string("tx swap() {}").unwrap();
1663    }
1664
1665    macro_rules! input_to_ast_check {
1666        ($ast:ty, $name:expr, $input:expr, $expected:expr) => {
1667            paste::paste! {
1668                #[test]
1669                fn [<test_parse_ $ast:snake _ $name>]() {
1670                    let pairs = super::Tx3Grammar::parse(<$ast>::RULE, $input).unwrap();
1671                    let single_match = pairs.into_iter().next().unwrap();
1672                    let result = <$ast>::parse(single_match).unwrap();
1673
1674                    assert_eq!(result, $expected);
1675                }
1676            }
1677        };
1678    }
1679
1680    input_to_ast_check!(
1681        ConcatOp,
1682        "basic",
1683        r#"concat("hello", "world")"#,
1684        ast::ConcatOp {
1685            lhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
1686                value: "hello".to_string(),
1687                span: ast::Span::DUMMY,
1688            })),
1689            rhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
1690                value: "world".to_string(),
1691                span: ast::Span::DUMMY,
1692            })),
1693            span: ast::Span::DUMMY,
1694        }
1695    );
1696    input_to_ast_check!(Type, "int", "Int", Type::Int);
1697
1698    input_to_ast_check!(Type, "bool", "Bool", Type::Bool);
1699
1700    input_to_ast_check!(Type, "bytes", "Bytes", Type::Bytes);
1701
1702    input_to_ast_check!(Type, "address", "Address", Type::Address);
1703
1704    input_to_ast_check!(Type, "utxo_ref", "UtxoRef", Type::UtxoRef);
1705
1706    input_to_ast_check!(Type, "any_asset", "AnyAsset", Type::AnyAsset);
1707
1708    input_to_ast_check!(Type, "list", "List<Int>", Type::List(Box::new(Type::Int)));
1709
1710    input_to_ast_check!(
1711        Type,
1712        "identifier",
1713        "MyType",
1714        Type::Custom(Identifier::new("MyType".to_string()))
1715    );
1716
1717    input_to_ast_check!(
1718        Type,
1719        "other_type",
1720        "List<Bytes>",
1721        Type::List(Box::new(Type::Bytes))
1722    );
1723
1724    input_to_ast_check!(
1725        Type,
1726        "within_list",
1727        "List<List<Int>>",
1728        Type::List(Box::new(Type::List(Box::new(Type::Int))))
1729    );
1730
1731    input_to_ast_check!(
1732        TypeDef,
1733        "type_def_record",
1734        "type MyRecord {
1735            field1: Int,
1736            field2: Bytes,
1737        }",
1738        TypeDef {
1739            name: Identifier::new("MyRecord"),
1740            cases: vec![VariantCase {
1741                name: Identifier::new("Default"),
1742                fields: vec![
1743                    RecordField::new("field1", Type::Int),
1744                    RecordField::new("field2", Type::Bytes)
1745                ],
1746                span: Span::DUMMY,
1747            }],
1748            span: Span::DUMMY,
1749        }
1750    );
1751
1752    input_to_ast_check!(
1753        TypeDef,
1754        "type_def_variant",
1755        "type MyVariant {
1756            Case1 {
1757                field1: Int,
1758                field2: Bytes,
1759            },
1760            Case2,
1761        }",
1762        TypeDef {
1763            name: Identifier::new("MyVariant"),
1764            cases: vec![
1765                VariantCase {
1766                    name: Identifier::new("Case1"),
1767                    fields: vec![
1768                        RecordField::new("field1", Type::Int),
1769                        RecordField::new("field2", Type::Bytes)
1770                    ],
1771                    span: Span::DUMMY,
1772                },
1773                VariantCase {
1774                    name: Identifier::new("Case2"),
1775                    fields: vec![],
1776                    span: Span::DUMMY,
1777                },
1778            ],
1779            span: Span::DUMMY,
1780        }
1781    );
1782
1783    input_to_ast_check!(
1784        AliasDef,
1785        "type_def_alias",
1786        "type MyAlias = Bytes;",
1787        AliasDef {
1788            name: Identifier::new("MyAlias"),
1789            alias_type: Type::Bytes,
1790            span: Span::DUMMY,
1791        }
1792    );
1793
1794    input_to_ast_check!(
1795        AliasDef,
1796        "type_alias_custom_type",
1797        "type UserAlias = UserType;",
1798        AliasDef {
1799            name: Identifier::new("UserAlias"),
1800            alias_type: Type::Custom(Identifier::new("UserType")),
1801            span: Span::DUMMY,
1802        }
1803    );
1804
1805    input_to_ast_check!(
1806        AliasDef,
1807        "type_alias_list",
1808        "type StringList = List<Bytes>;",
1809        AliasDef {
1810            name: Identifier::new("StringList"),
1811            alias_type: Type::List(Box::new(Type::Bytes)),
1812            span: Span::DUMMY,
1813        }
1814    );
1815
1816    input_to_ast_check!(
1817        AliasDef,
1818        "type_alias_map",
1819        "type StringIntMap = Map<Bytes, Int>;",
1820        AliasDef {
1821            name: Identifier::new("StringIntMap"),
1822            alias_type: Type::Map(Box::new(Type::Bytes), Box::new(Type::Int)),
1823            span: Span::DUMMY,
1824        }
1825    );
1826
1827    input_to_ast_check!(
1828        AliasDef,
1829        "type_alias_complex_nested",
1830        "type ComplexType = List<Map<Bytes, Int>>;",
1831        AliasDef {
1832            name: Identifier::new("ComplexType"),
1833            alias_type: Type::List(Box::new(Type::Map(
1834                Box::new(Type::Bytes),
1835                Box::new(Type::Int)
1836            ))),
1837            span: Span::DUMMY,
1838        }
1839    );
1840
1841    input_to_ast_check!(
1842        AliasDef,
1843        "type_alias_all_primitives",
1844        "type MyInt = Int;",
1845        AliasDef {
1846            name: Identifier::new("MyInt"),
1847            alias_type: Type::Int,
1848            span: Span::DUMMY,
1849        }
1850    );
1851
1852    input_to_ast_check!(
1853        AliasDef,
1854        "type_alias_bool",
1855        "type MyBool = Bool;",
1856        AliasDef {
1857            name: Identifier::new("MyBool"),
1858            alias_type: Type::Bool,
1859            span: Span::DUMMY,
1860        }
1861    );
1862
1863    input_to_ast_check!(
1864        AliasDef,
1865        "type_alias_address",
1866        "type MyAddress = Address;",
1867        AliasDef {
1868            name: Identifier::new("MyAddress"),
1869            alias_type: Type::Address,
1870            span: Span::DUMMY,
1871        }
1872    );
1873
1874    input_to_ast_check!(
1875        AliasDef,
1876        "type_alias_utxo_ref",
1877        "type MyUtxoRef = UtxoRef;",
1878        AliasDef {
1879            name: Identifier::new("MyUtxoRef"),
1880            alias_type: Type::UtxoRef,
1881            span: Span::DUMMY,
1882        }
1883    );
1884
1885    input_to_ast_check!(
1886        AliasDef,
1887        "type_alias_any_asset",
1888        "type MyAsset = AnyAsset;",
1889        AliasDef {
1890            name: Identifier::new("MyAsset"),
1891            alias_type: Type::AnyAsset,
1892            span: Span::DUMMY,
1893        }
1894    );
1895
1896    input_to_ast_check!(
1897        StringLiteral,
1898        "literal_string",
1899        "\"Hello, world!\"",
1900        StringLiteral::new("Hello, world!".to_string())
1901    );
1902
1903    input_to_ast_check!(
1904        HexStringLiteral,
1905        "hex_string",
1906        "0xAFAFAF",
1907        HexStringLiteral::new("AFAFAF".to_string())
1908    );
1909
1910    input_to_ast_check!(
1911        StringLiteral,
1912        "literal_string_address",
1913        "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1914        StringLiteral::new("addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string())
1915    );
1916
1917    input_to_ast_check!(
1918        ListConstructor,
1919        "empty_list",
1920        "[]",
1921        ListConstructor {
1922            elements: vec![],
1923            span: Span::DUMMY,
1924        }
1925    );
1926
1927    input_to_ast_check!(
1928        ListConstructor,
1929        "trailing_comma",
1930        "[1, 2,]",
1931        ListConstructor {
1932            elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
1933            span: Span::DUMMY,
1934        }
1935    );
1936
1937    input_to_ast_check!(
1938        ListConstructor,
1939        "int_list",
1940        "[1, 2]",
1941        ListConstructor {
1942            elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
1943            span: Span::DUMMY,
1944        }
1945    );
1946
1947    input_to_ast_check!(
1948        ListConstructor,
1949        "string_list",
1950        "[\"Hello\", \"World\"]",
1951        ListConstructor {
1952            elements: vec![
1953                DataExpr::String(StringLiteral::new("Hello".to_string())),
1954                DataExpr::String(StringLiteral::new("World".to_string()))
1955            ],
1956            span: Span::DUMMY,
1957        }
1958    );
1959
1960    input_to_ast_check!(
1961        ListConstructor,
1962        "mixed_list",
1963        "[1, \"Hello\", true]",
1964        ListConstructor {
1965            elements: vec![
1966                DataExpr::Number(1),
1967                DataExpr::String(StringLiteral::new("Hello".to_string())),
1968                DataExpr::Bool(true)
1969            ],
1970            span: Span::DUMMY,
1971        }
1972    );
1973
1974    input_to_ast_check!(
1975        ListConstructor,
1976        "list_within_list",
1977        "[[1, 2], [3, 4]]",
1978        ListConstructor {
1979            elements: vec![
1980                DataExpr::ListConstructor(ListConstructor {
1981                    elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
1982                    span: Span::DUMMY,
1983                }),
1984                DataExpr::ListConstructor(ListConstructor {
1985                    elements: vec![DataExpr::Number(3), DataExpr::Number(4),],
1986                    span: Span::DUMMY,
1987                }),
1988            ],
1989            span: Span::DUMMY,
1990        }
1991    );
1992
1993    input_to_ast_check!(DataExpr, "literal_bool_true", "true", DataExpr::Bool(true));
1994
1995    input_to_ast_check!(
1996        DataExpr,
1997        "literal_bool_false",
1998        "false",
1999        DataExpr::Bool(false)
2000    );
2001
2002    input_to_ast_check!(DataExpr, "unit_value", "())", DataExpr::Unit);
2003
2004    input_to_ast_check!(DataExpr, "number_value", "123", DataExpr::Number(123));
2005
2006    input_to_ast_check!(
2007        PolicyDef,
2008        "policy_def_assign",
2009        "policy MyPolicy = 0xAFAFAF;",
2010        PolicyDef {
2011            name: Identifier::new("MyPolicy"),
2012            value: PolicyValue::Assign(HexStringLiteral::new("AFAFAF".to_string())),
2013            span: Span::DUMMY,
2014        }
2015    );
2016
2017    input_to_ast_check!(
2018        PolicyDef,
2019        "policy_def_constructor",
2020        "policy MyPolicy {
2021            hash: 0x1234567890,
2022            script: 0x1234567890,
2023            ref: 0x1234567890,
2024        };",
2025        PolicyDef {
2026            name: Identifier::new("MyPolicy"),
2027            value: PolicyValue::Constructor(PolicyConstructor {
2028                fields: vec![
2029                    PolicyField::Hash(DataExpr::HexString(HexStringLiteral::new(
2030                        "1234567890".to_string()
2031                    ))),
2032                    PolicyField::Script(DataExpr::HexString(HexStringLiteral::new(
2033                        "1234567890".to_string()
2034                    ))),
2035                    PolicyField::Ref(DataExpr::HexString(HexStringLiteral::new(
2036                        "1234567890".to_string()
2037                    ))),
2038                ],
2039                span: Span::DUMMY,
2040            }),
2041            span: Span::DUMMY,
2042        }
2043    );
2044
2045    input_to_ast_check!(
2046        AssetDef,
2047        "hex_hex",
2048        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.0xef7a1ceb;",
2049        AssetDef {
2050            name: Identifier::new("MyToken"),
2051            policy: DataExpr::HexString(HexStringLiteral::new(
2052                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
2053            )),
2054            asset_name: DataExpr::HexString(HexStringLiteral::new("ef7a1ceb".to_string())),
2055            span: Span::DUMMY,
2056        }
2057    );
2058
2059    input_to_ast_check!(
2060        AssetDef,
2061        "hex_string",
2062        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.\"MY TOKEN\";",
2063        AssetDef {
2064            name: Identifier::new("MyToken"),
2065            policy: DataExpr::HexString(HexStringLiteral::new(
2066                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
2067            )),
2068            asset_name: DataExpr::String(StringLiteral::new("MY TOKEN".to_string())),
2069            span: Span::DUMMY,
2070        }
2071    );
2072
2073    input_to_ast_check!(
2074        DataExpr,
2075        "type_and_literal",
2076        "MyToken(15)",
2077        DataExpr::FnCall(crate::ast::FnCall {
2078            callee: Identifier::new("MyToken"),
2079            args: vec![DataExpr::Number(15)],
2080            span: Span::DUMMY,
2081        })
2082    );
2083
2084    input_to_ast_check!(
2085        AnyAssetConstructor,
2086        "any_asset_constructor",
2087        "AnyAsset(0x1234567890, \"MyToken\", 15)",
2088        AnyAssetConstructor {
2089            policy: Box::new(DataExpr::HexString(HexStringLiteral::new(
2090                "1234567890".to_string()
2091            ))),
2092            asset_name: Box::new(DataExpr::String(StringLiteral::new("MyToken".to_string()))),
2093            amount: Box::new(DataExpr::Number(15)),
2094            span: Span::DUMMY,
2095        }
2096    );
2097
2098    input_to_ast_check!(
2099        AnyAssetConstructor,
2100        "any_asset_identifiers",
2101        "AnyAsset(my_policy, my_token, my_amount)",
2102        AnyAssetConstructor {
2103            policy: Box::new(DataExpr::Identifier(Identifier::new("my_policy"))),
2104            asset_name: Box::new(DataExpr::Identifier(Identifier::new("my_token"))),
2105            amount: Box::new(DataExpr::Identifier(Identifier::new("my_amount"))),
2106            span: Span::DUMMY,
2107        }
2108    );
2109
2110    input_to_ast_check!(
2111        AnyAssetConstructor,
2112        "any_asset_property_access",
2113        "AnyAsset(input1.policy, input1.asset_name, input1.amount)",
2114        AnyAssetConstructor {
2115            policy: Box::new(DataExpr::PropertyOp(PropertyOp {
2116                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2117                property: Box::new(DataExpr::Identifier(Identifier::new("policy"))),
2118                span: Span::DUMMY,
2119                scope: None,
2120            })),
2121            asset_name: Box::new(DataExpr::PropertyOp(PropertyOp {
2122                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2123                property: Box::new(DataExpr::Identifier(Identifier::new("asset_name"))),
2124                span: Span::DUMMY,
2125                scope: None,
2126            })),
2127            amount: Box::new(DataExpr::PropertyOp(PropertyOp {
2128                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2129                property: Box::new(DataExpr::Identifier(Identifier::new("amount"))),
2130                span: Span::DUMMY,
2131                scope: None,
2132            })),
2133            span: Span::DUMMY,
2134        }
2135    );
2136
2137    input_to_ast_check!(DataExpr, "literal", "5", DataExpr::Number(5));
2138
2139    input_to_ast_check!(
2140        DataExpr,
2141        "add_op",
2142        "5 + var1",
2143        DataExpr::AddOp(AddOp {
2144            lhs: Box::new(DataExpr::Number(5)),
2145            rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2146            span: Span::DUMMY,
2147        })
2148    );
2149
2150    input_to_ast_check!(
2151        DataExpr,
2152        "concat_op",
2153        r#"concat("hello", "world")"#,
2154        DataExpr::ConcatOp(ConcatOp {
2155            lhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
2156                value: "hello".to_string(),
2157                span: ast::Span::DUMMY,
2158            })),
2159            rhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
2160                value: "world".to_string(),
2161                span: ast::Span::DUMMY,
2162            })),
2163            span: ast::Span::DUMMY,
2164        })
2165    );
2166
2167    input_to_ast_check!(
2168        DataExpr,
2169        "property_access",
2170        "subject.property",
2171        DataExpr::PropertyOp(PropertyOp {
2172            operand: Box::new(DataExpr::Identifier(Identifier::new("subject"))),
2173            property: Box::new(DataExpr::Identifier(Identifier::new("property"))),
2174            span: Span::DUMMY,
2175            scope: None,
2176        })
2177    );
2178
2179    input_to_ast_check!(
2180        DataExpr,
2181        "multiple_properties",
2182        "subject.property.subproperty",
2183        DataExpr::PropertyOp(PropertyOp {
2184            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2185                operand: Box::new(DataExpr::Identifier(Identifier::new("subject"))),
2186                property: Box::new(DataExpr::Identifier(Identifier::new("property"))),
2187                span: Span::DUMMY,
2188                scope: None,
2189            })),
2190            property: Box::new(DataExpr::Identifier(Identifier::new("subproperty"))),
2191            span: Span::DUMMY,
2192            scope: None,
2193        })
2194    );
2195
2196    input_to_ast_check!(DataExpr, "empty_parentheses", "()", DataExpr::Unit);
2197
2198    input_to_ast_check!(DataExpr, "nested_parentheses", "((()))", DataExpr::Unit);
2199
2200    input_to_ast_check!(
2201        DataExpr,
2202        "nested_arithmetic_expression",
2203        "(1 + ((6 - 3) + 4))",
2204        DataExpr::AddOp(AddOp {
2205            lhs: Box::new(DataExpr::Number(1)),
2206            rhs: Box::new(DataExpr::AddOp(AddOp {
2207                lhs: Box::new(DataExpr::SubOp(SubOp {
2208                    lhs: Box::new(DataExpr::Number(6)),
2209                    rhs: Box::new(DataExpr::Number(3)),
2210                    span: Span::DUMMY,
2211                })),
2212                rhs: Box::new(DataExpr::Number(4)),
2213                span: Span::DUMMY,
2214            })),
2215            span: Span::DUMMY,
2216        })
2217    );
2218
2219    input_to_ast_check!(
2220        DataExpr,
2221        "negate_op",
2222        "!a",
2223        DataExpr::NegateOp(NegateOp {
2224            operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2225            span: Span::DUMMY,
2226        })
2227    );
2228
2229    input_to_ast_check!(
2230        DataExpr,
2231        "negate_precedence",
2232        "!a.b",
2233        DataExpr::NegateOp(NegateOp {
2234            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2235                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2236                property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2237                span: Span::DUMMY,
2238                scope: None,
2239            })),
2240            span: Span::DUMMY,
2241        })
2242    );
2243
2244    input_to_ast_check!(
2245        DataExpr,
2246        "negate_override_precedence",
2247        "(!a).b",
2248        DataExpr::PropertyOp(PropertyOp {
2249            operand: Box::new(DataExpr::NegateOp(NegateOp {
2250                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2251                span: Span::DUMMY,
2252            })),
2253            property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2254            span: Span::DUMMY,
2255            scope: None,
2256        })
2257    );
2258
2259    input_to_ast_check!(
2260        DataExpr,
2261        "overly_complex",
2262        "(1 + 5) - ((a.b.c - 3) + !d.f)",
2263        DataExpr::SubOp(SubOp {
2264            lhs: Box::new(DataExpr::AddOp(AddOp {
2265                lhs: Box::new(DataExpr::Number(1)),
2266                rhs: Box::new(DataExpr::Number(5)),
2267                span: Span::DUMMY,
2268            })),
2269            rhs: Box::new(DataExpr::AddOp(AddOp {
2270                lhs: Box::new(DataExpr::SubOp(SubOp {
2271                    lhs: Box::new(DataExpr::PropertyOp(PropertyOp {
2272                        operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2273                            operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2274                            property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2275                            span: Span::DUMMY,
2276                            scope: None,
2277                        })),
2278                        property: Box::new(DataExpr::Identifier(Identifier::new("c"))),
2279                        span: Span::DUMMY,
2280                        scope: None,
2281                    })),
2282                    rhs: Box::new(DataExpr::Number(3)),
2283                    span: Span::DUMMY,
2284                })),
2285                rhs: Box::new(DataExpr::NegateOp(NegateOp {
2286                    operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2287                        operand: Box::new(DataExpr::Identifier(Identifier::new("d"))),
2288                        property: Box::new(DataExpr::Identifier(Identifier::new("f"))),
2289                        span: Span::DUMMY,
2290                        scope: None,
2291                    })),
2292                    span: Span::DUMMY,
2293                })),
2294
2295                span: Span::DUMMY,
2296            })),
2297            span: Span::DUMMY,
2298        })
2299    );
2300
2301    input_to_ast_check!(
2302        DataExpr,
2303        "min_utxo_basic",
2304        "min_utxo(output1)",
2305        DataExpr::FnCall(crate::ast::FnCall {
2306            callee: Identifier::new("min_utxo"),
2307            args: vec![DataExpr::Identifier(Identifier::new("output1"))],
2308            span: Span::DUMMY,
2309        })
2310    );
2311
2312    input_to_ast_check!(
2313        DataExpr,
2314        "min_utxo_in_expression",
2315        "Ada(100) + min_utxo(my_output)",
2316        DataExpr::AddOp(AddOp {
2317            lhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2318                callee: Identifier::new("Ada"),
2319                args: vec![DataExpr::Number(100)],
2320                span: Span::DUMMY,
2321            })),
2322            rhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2323                callee: Identifier::new("min_utxo"),
2324                args: vec![DataExpr::Identifier(Identifier::new("my_output"))],
2325                span: Span::DUMMY,
2326            })),
2327            span: Span::DUMMY,
2328        })
2329    );
2330
2331    input_to_ast_check!(
2332        DataExpr,
2333        "tip_slot_basic",
2334        "tip_slot()",
2335        DataExpr::FnCall(crate::ast::FnCall {
2336            callee: Identifier::new("tip_slot"),
2337            args: vec![],
2338            span: Span::DUMMY,
2339        })
2340    );
2341
2342    input_to_ast_check!(
2343        DataExpr,
2344        "tip_slot_in_expression",
2345        "1000 + tip_slot()",
2346        DataExpr::AddOp(AddOp {
2347            lhs: Box::new(DataExpr::Number(1000)),
2348            rhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2349                callee: Identifier::new("tip_slot"),
2350                args: vec![],
2351                span: Span::DUMMY,
2352            })),
2353            span: Span::DUMMY,
2354        })
2355    );
2356
2357    input_to_ast_check!(
2358        StructConstructor,
2359        "struct_constructor_record",
2360        "MyRecord {
2361            field1: 10,
2362            field2: abc,
2363        }",
2364        StructConstructor {
2365            r#type: Identifier::new("MyRecord"),
2366            case: VariantCaseConstructor {
2367                name: Identifier::new("Default"),
2368                fields: vec![
2369                    RecordConstructorField {
2370                        name: Identifier::new("field1"),
2371                        value: Box::new(DataExpr::Number(10)),
2372                        span: Span::DUMMY,
2373                    },
2374                    RecordConstructorField {
2375                        name: Identifier::new("field2"),
2376                        value: Box::new(DataExpr::Identifier(Identifier::new("abc"))),
2377                        span: Span::DUMMY,
2378                    },
2379                ],
2380                spread: None,
2381                scope: None,
2382                span: Span::DUMMY,
2383            },
2384            scope: None,
2385            span: Span::DUMMY,
2386        }
2387    );
2388
2389    input_to_ast_check!(
2390        StructConstructor,
2391        "struct_constructor_variant",
2392        "ShipCommand::MoveShip {
2393            delta_x: delta_x,
2394            delta_y: delta_y,
2395        }",
2396        StructConstructor {
2397            r#type: Identifier::new("ShipCommand"),
2398            case: VariantCaseConstructor {
2399                name: Identifier::new("MoveShip"),
2400                fields: vec![
2401                    RecordConstructorField {
2402                        name: Identifier::new("delta_x"),
2403                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
2404                        span: Span::DUMMY,
2405                    },
2406                    RecordConstructorField {
2407                        name: Identifier::new("delta_y"),
2408                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
2409                        span: Span::DUMMY,
2410                    },
2411                ],
2412                spread: None,
2413                scope: None,
2414                span: Span::DUMMY,
2415            },
2416            scope: None,
2417            span: Span::DUMMY,
2418        }
2419    );
2420
2421    input_to_ast_check!(
2422        StructConstructor,
2423        "struct_constructor_variant_with_spread",
2424        "ShipCommand::MoveShip {
2425            delta_x: delta_x,
2426            delta_y: delta_y,
2427            ...abc
2428        }",
2429        StructConstructor {
2430            r#type: Identifier::new("ShipCommand"),
2431            case: VariantCaseConstructor {
2432                name: Identifier::new("MoveShip"),
2433                fields: vec![
2434                    RecordConstructorField {
2435                        name: Identifier::new("delta_x"),
2436                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
2437                        span: Span::DUMMY,
2438                    },
2439                    RecordConstructorField {
2440                        name: Identifier::new("delta_y"),
2441                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
2442                        span: Span::DUMMY,
2443                    },
2444                ],
2445                spread: Some(Box::new(DataExpr::Identifier(Identifier::new(
2446                    "abc".to_string()
2447                )))),
2448                scope: None,
2449                span: Span::DUMMY,
2450            },
2451            scope: None,
2452            span: Span::DUMMY,
2453        }
2454    );
2455
2456    input_to_ast_check!(
2457        LocalsBlock,
2458        "basic",
2459        "locals {
2460            a: 10,
2461        }",
2462        LocalsBlock {
2463            assigns: vec![LocalsAssign {
2464                name: Identifier::new("a"),
2465                value: DataExpr::Number(10),
2466                span: Span::DUMMY,
2467            },],
2468            span: Span::DUMMY,
2469        }
2470    );
2471
2472    input_to_ast_check!(
2473        LocalsBlock,
2474        "multiple",
2475        "locals {
2476            a: 10,
2477            b: 20,
2478        }",
2479        LocalsBlock {
2480            assigns: vec![
2481                LocalsAssign {
2482                    name: Identifier::new("a"),
2483                    value: DataExpr::Number(10),
2484                    span: Span::DUMMY,
2485                },
2486                LocalsAssign {
2487                    name: Identifier::new("b"),
2488                    value: DataExpr::Number(20),
2489                    span: Span::DUMMY,
2490                },
2491            ],
2492            span: Span::DUMMY,
2493        }
2494    );
2495
2496    input_to_ast_check!(
2497        LocalsBlock,
2498        "complex_expression",
2499        "locals {
2500            a: (10 + 20) - 8,
2501            b: a.b.c + (5 - d),
2502        }",
2503        LocalsBlock {
2504            assigns: vec![
2505                LocalsAssign {
2506                    name: Identifier::new("a"),
2507                    value: DataExpr::SubOp(SubOp {
2508                        lhs: Box::new(DataExpr::AddOp(AddOp {
2509                            lhs: Box::new(DataExpr::Number(10)),
2510                            rhs: Box::new(DataExpr::Number(20)),
2511                            span: Span::DUMMY,
2512                        })),
2513                        rhs: Box::new(DataExpr::Number(8)),
2514                        span: Span::DUMMY,
2515                    }),
2516                    span: Span::DUMMY,
2517                },
2518                LocalsAssign {
2519                    name: Identifier::new("b"),
2520                    value: DataExpr::AddOp(AddOp {
2521                        lhs: Box::new(DataExpr::PropertyOp(PropertyOp {
2522                            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2523                                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2524                                property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2525                                span: Span::DUMMY,
2526                                scope: None,
2527                            })),
2528                            property: Box::new(DataExpr::Identifier(Identifier::new("c"))),
2529                            span: Span::DUMMY,
2530                            scope: None,
2531                        })),
2532                        rhs: Box::new(DataExpr::SubOp(SubOp {
2533                            lhs: Box::new(DataExpr::Number(5)),
2534                            rhs: Box::new(DataExpr::Identifier(Identifier::new("d"))),
2535                            span: Span::DUMMY,
2536                        })),
2537                        span: Span::DUMMY,
2538                    }),
2539                    span: Span::DUMMY,
2540                },
2541            ],
2542            span: Span::DUMMY,
2543        }
2544    );
2545
2546    input_to_ast_check!(
2547        InputBlock,
2548        "single",
2549        r#"input source {}"#,
2550        InputBlock {
2551            many: false,
2552            name: "source".to_string(),
2553            fields: vec![],
2554            span: Span::DUMMY,
2555        }
2556    );
2557
2558    input_to_ast_check!(
2559        InputBlock,
2560        "multiple",
2561        r#"input* source {}"#,
2562        InputBlock {
2563            many: true,
2564            name: "source".to_string(),
2565            fields: vec![],
2566            span: Span::DUMMY,
2567        }
2568    );
2569
2570    input_to_ast_check!(
2571        OutputBlock,
2572        "output_block_anonymous",
2573        r#"output {
2574            to: my_party,
2575            amount: Ada(100),
2576        }"#,
2577        OutputBlock {
2578            name: None,
2579            optional: false,
2580            fields: vec![
2581                OutputBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
2582                    "my_party".to_string(),
2583                )))),
2584                OutputBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
2585                    callee: Identifier::new("Ada"),
2586                    args: vec![DataExpr::Number(100)],
2587                    span: Span::DUMMY,
2588                }))),
2589            ],
2590            span: Span::DUMMY,
2591        }
2592    );
2593
2594    input_to_ast_check!(
2595        ChainSpecificBlock,
2596        "chain_specific_block_cardano",
2597        "cardano::vote_delegation_certificate {
2598            drep: 0x1234567890,
2599            stake: 0x1234567890,
2600        }",
2601        ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::VoteDelegationCertificate(
2602            crate::cardano::VoteDelegationCertificate {
2603                drep: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
2604                stake: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
2605                span: Span::DUMMY,
2606            },
2607        ))
2608    );
2609
2610    input_to_ast_check!(
2611        ChainSpecificBlock,
2612        "chain_specific_block_cardano_treasury",
2613        "cardano::treasury_donation {
2614           coin: 20,
2615        }",
2616        ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::TreasuryDonation(
2617            crate::cardano::TreasuryDonationBlock {
2618                coin: DataExpr::Number(20),
2619                span: Span::DUMMY,
2620            },
2621        ))
2622    );
2623
2624    input_to_ast_check!(
2625        EnvDef,
2626        "basic",
2627        "env {
2628            field_a: Int,
2629            field_b: Bytes,
2630        }",
2631        EnvDef {
2632            fields: vec![
2633                EnvField {
2634                    name: "field_a".to_string(),
2635                    r#type: Type::Int,
2636                    docstring: None,
2637                    span: Span::DUMMY,
2638                },
2639                EnvField {
2640                    name: "field_b".to_string(),
2641                    r#type: Type::Bytes,
2642                    docstring: None,
2643                    span: Span::DUMMY,
2644                },
2645            ],
2646            span: Span::DUMMY,
2647        }
2648    );
2649
2650    input_to_ast_check!(
2651        TxDef,
2652        "empty",
2653        "tx my_tx() {}",
2654        TxDef {
2655            name: Identifier::new("my_tx"),
2656            docstring: None,
2657            parameters: ParameterList {
2658                parameters: vec![],
2659                span: Span::DUMMY,
2660            },
2661            locals: None,
2662            references: vec![],
2663            inputs: vec![],
2664            outputs: vec![],
2665            validity: None,
2666            mints: vec![],
2667            burns: vec![],
2668            signers: None,
2669            adhoc: vec![],
2670            collateral: vec![],
2671            metadata: None,
2672            scope: None,
2673            span: Span::DUMMY,
2674        }
2675    );
2676
2677    input_to_ast_check!(
2678        TxDef,
2679        "with_parameters",
2680        "tx my_tx(a: Int, b: Bytes) {}",
2681        TxDef {
2682            name: Identifier::new("my_tx"),
2683            docstring: None,
2684            parameters: ParameterList {
2685                parameters: vec![
2686                    ParamDef {
2687                        name: Identifier::new("a"),
2688                        r#type: Type::Int,
2689                        docstring: None,
2690                    },
2691                    ParamDef {
2692                        name: Identifier::new("b"),
2693                        r#type: Type::Bytes,
2694                        docstring: None,
2695                    },
2696                ],
2697                span: Span::DUMMY,
2698            },
2699            locals: None,
2700            references: vec![],
2701            inputs: vec![],
2702            outputs: vec![],
2703            validity: None,
2704            mints: vec![],
2705            burns: vec![],
2706            signers: None,
2707            adhoc: vec![],
2708            collateral: vec![],
2709            metadata: None,
2710            scope: None,
2711            span: Span::DUMMY,
2712        }
2713    );
2714
2715    input_to_ast_check!(
2716        Program,
2717        "basic",
2718        "party Abc; tx my_tx() {}",
2719        Program {
2720            parties: vec![PartyDef {
2721                name: Identifier::new("Abc"),
2722                docstring: None,
2723                span: Span::DUMMY,
2724            }],
2725            types: vec![],
2726            aliases: vec![],
2727            txs: vec![TxDef {
2728                name: Identifier::new("my_tx"),
2729                docstring: None,
2730                parameters: ParameterList {
2731                    parameters: vec![],
2732                    span: Span::DUMMY,
2733                },
2734                locals: None,
2735                references: vec![],
2736                inputs: vec![],
2737                outputs: vec![],
2738                validity: None,
2739                mints: vec![],
2740                burns: vec![],
2741                signers: None,
2742                adhoc: vec![],
2743                collateral: vec![],
2744                metadata: None,
2745                scope: None,
2746                span: Span::DUMMY,
2747            }],
2748            env: None,
2749            assets: vec![],
2750            policies: vec![],
2751            functions: vec![],
2752            span: Span::DUMMY,
2753            scope: None,
2754        }
2755    );
2756
2757    input_to_ast_check!(
2758        DataExpr,
2759        "array_index_literal",
2760        "my_list[0]",
2761        DataExpr::PropertyOp(PropertyOp {
2762            operand: Box::new(DataExpr::Identifier(Identifier::new("my_list"))),
2763            property: Box::new(DataExpr::Number(0)),
2764            span: Span::DUMMY,
2765            scope: None,
2766        })
2767    );
2768
2769    input_to_ast_check!(
2770        DataExpr,
2771        "array_index_variable",
2772        "my_list[index]",
2773        DataExpr::PropertyOp(PropertyOp {
2774            operand: Box::new(DataExpr::Identifier(Identifier::new("my_list"))),
2775            property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
2776            span: Span::DUMMY,
2777            scope: None,
2778        })
2779    );
2780
2781    input_to_ast_check!(
2782        DataExpr,
2783        "nested_array_index",
2784        "matrix[row][col]",
2785        DataExpr::PropertyOp(PropertyOp {
2786            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2787                operand: Box::new(DataExpr::Identifier(Identifier::new("matrix"))),
2788                property: Box::new(DataExpr::Identifier(Identifier::new("row"))),
2789                span: Span::DUMMY,
2790                scope: None,
2791            })),
2792            property: Box::new(DataExpr::Identifier(Identifier::new("col"))),
2793            span: Span::DUMMY,
2794            scope: None,
2795        })
2796    );
2797
2798    input_to_ast_check!(
2799        DataExpr,
2800        "array_index_with_property_access",
2801        "items[index].name",
2802        DataExpr::PropertyOp(PropertyOp {
2803            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2804                operand: Box::new(DataExpr::Identifier(Identifier::new("items"))),
2805                property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
2806                span: Span::DUMMY,
2807                scope: None,
2808            })),
2809            property: Box::new(DataExpr::Identifier(Identifier::new("name"))),
2810            span: Span::DUMMY,
2811            scope: None,
2812        })
2813    );
2814
2815    input_to_ast_check!(
2816        DataExpr,
2817        "property_access_then_array_index",
2818        "object.list[0]",
2819        DataExpr::PropertyOp(PropertyOp {
2820            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2821                operand: Box::new(DataExpr::Identifier(Identifier::new("object"))),
2822                property: Box::new(DataExpr::Identifier(Identifier::new("list"))),
2823                span: Span::DUMMY,
2824                scope: None,
2825            })),
2826            property: Box::new(DataExpr::Number(0)),
2827            span: Span::DUMMY,
2828            scope: None,
2829        })
2830    );
2831
2832    input_to_ast_check!(
2833        DataExpr,
2834        "array_index_with_function_call",
2835        "values[min_utxo(output)]",
2836        DataExpr::PropertyOp(PropertyOp {
2837            operand: Box::new(DataExpr::Identifier(Identifier::new("values"))),
2838            property: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2839                callee: Identifier::new("min_utxo"),
2840                args: vec![DataExpr::Identifier(Identifier::new("output"))],
2841                span: Span::DUMMY,
2842            })),
2843            span: Span::DUMMY,
2844            scope: None,
2845        })
2846    );
2847
2848    input_to_ast_check!(
2849        DataExpr,
2850        "mixed_property_and_index_access",
2851        "container.items[index].metadata[\"key\"]",
2852        DataExpr::PropertyOp(PropertyOp {
2853            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2854                operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2855                    operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2856                        operand: Box::new(DataExpr::Identifier(Identifier::new("container"))),
2857                        property: Box::new(DataExpr::Identifier(Identifier::new("items"))),
2858                        span: Span::DUMMY,
2859                        scope: None,
2860                    })),
2861                    property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
2862                    span: Span::DUMMY,
2863                    scope: None,
2864                })),
2865                property: Box::new(DataExpr::Identifier(Identifier::new("metadata"))),
2866                span: Span::DUMMY,
2867                scope: None,
2868            })),
2869            property: Box::new(DataExpr::String(StringLiteral::new("key".to_string()))),
2870            span: Span::DUMMY,
2871            scope: None,
2872        })
2873    );
2874
2875    input_to_ast_check!(
2876        ReferenceBlock,
2877        "with_datum_type",
2878        "reference oracle_data { ref: oracle_utxo, datum_is: OracleDatum, }",
2879        ReferenceBlock {
2880            name: "oracle_data".to_string(),
2881            r#ref: DataExpr::Identifier(Identifier::new("oracle_utxo")),
2882            datum_is: Some(Type::Custom(Identifier::new("OracleDatum"))),
2883            span: Span::DUMMY,
2884        }
2885    );
2886
2887    input_to_ast_check!(
2888        MapConstructor,
2889        "empty",
2890        "{}",
2891        MapConstructor {
2892            fields: vec![],
2893            span: Span::DUMMY,
2894        }
2895    );
2896
2897    input_to_ast_check!(
2898        PartyDef,
2899        "with_docstring",
2900        "/// the protocol treasury\nparty Treasury;",
2901        PartyDef {
2902            name: Identifier::new("Treasury"),
2903            docstring: Some("the protocol treasury".to_string()),
2904            span: Span::DUMMY,
2905        }
2906    );
2907
2908    input_to_ast_check!(
2909        EnvField,
2910        "with_docstring",
2911        "/// network magic\nnetwork: Int",
2912        EnvField {
2913            name: "network".to_string(),
2914            r#type: Type::Int,
2915            docstring: Some("network magic".to_string()),
2916            span: Span::DUMMY,
2917        }
2918    );
2919
2920    input_to_ast_check!(
2921        TxDef,
2922        "with_multiline_docstring",
2923        "/// transfer funds from a to b\n/// across two lines\ntx transfer() {}",
2924        TxDef {
2925            name: Identifier::new("transfer"),
2926            docstring: Some("transfer funds from a to b\nacross two lines".to_string()),
2927            parameters: ParameterList {
2928                parameters: vec![],
2929                span: Span::DUMMY,
2930            },
2931            locals: None,
2932            references: vec![],
2933            inputs: vec![],
2934            outputs: vec![],
2935            validity: None,
2936            mints: vec![],
2937            burns: vec![],
2938            signers: None,
2939            adhoc: vec![],
2940            collateral: vec![],
2941            metadata: None,
2942            scope: None,
2943            span: Span::DUMMY,
2944        }
2945    );
2946
2947    input_to_ast_check!(
2948        ParameterList,
2949        "with_param_docstrings",
2950        "(/// amount in lovelace\nquantity: Int, target: Address)",
2951        ParameterList {
2952            parameters: vec![
2953                ParamDef {
2954                    name: Identifier::new("quantity"),
2955                    r#type: Type::Int,
2956                    docstring: Some("amount in lovelace".to_string()),
2957                },
2958                ParamDef {
2959                    name: Identifier::new("target"),
2960                    r#type: Type::Address,
2961                    docstring: None,
2962                },
2963            ],
2964            span: Span::DUMMY,
2965        }
2966    );
2967
2968    #[test]
2969    fn test_spans_are_respected() {
2970        let program = parse_well_known_example("spans");
2971        assert_eq!(program.span, Span::new(0, 759));
2972
2973        assert_eq!(program.parties[0].span, Span::new(27, 41));
2974
2975        assert_eq!(program.types[0].span, Span::new(43, 77));
2976    }
2977
2978    fn make_snapshot_if_missing(example: &str, program: &Program) {
2979        let manifest_dir = env!("CARGO_MANIFEST_DIR");
2980        let path = format!("{}/../../examples/{}.ast", manifest_dir, example);
2981
2982        if !std::fs::exists(&path).unwrap() {
2983            let ast = serde_json::to_string_pretty(program).unwrap();
2984            std::fs::write(&path, ast).unwrap();
2985        }
2986    }
2987
2988    fn test_parsing_example(example: &str) {
2989        let program = parse_well_known_example(example);
2990        make_snapshot_if_missing(example, &program);
2991
2992        let manifest_dir = env!("CARGO_MANIFEST_DIR");
2993        let ast_file = format!("{}/../../examples/{}.ast", manifest_dir, example);
2994        let ast = std::fs::read_to_string(ast_file).unwrap();
2995
2996        let expected: Program = serde_json::from_str(&ast).unwrap();
2997
2998        assert_json_eq!(program, expected);
2999    }
3000
3001    #[macro_export]
3002    macro_rules! test_parsing {
3003        ($name:ident) => {
3004            paste! {
3005                #[test]
3006                fn [<test_example_ $name>]() {
3007                    test_parsing_example(stringify!($name));
3008                }
3009            }
3010        };
3011    }
3012
3013    test_parsing!(lang_tour);
3014
3015    test_parsing!(transfer);
3016
3017    test_parsing!(swap);
3018
3019    test_parsing!(asteria);
3020
3021    test_parsing!(vesting);
3022
3023    test_parsing!(faucet);
3024
3025    test_parsing!(disordered);
3026
3027    test_parsing!(input_datum);
3028
3029    test_parsing!(withdrawal);
3030
3031    test_parsing!(env_vars);
3032
3033    test_parsing!(local_vars);
3034
3035    test_parsing!(cardano_witness);
3036
3037    test_parsing!(reference_script);
3038
3039    test_parsing!(map);
3040
3041    test_parsing!(burn);
3042
3043    test_parsing!(donation);
3044
3045    test_parsing!(list_concat);
3046
3047    test_parsing!(buidler_fest_2026);
3048
3049    test_parsing!(functions);
3050
3051    test_parsing!(nested_functions);
3052}