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            .is_some_and(|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 AstNode for TupleConstructor {
1236    const RULE: Rule = Rule::tuple_constructor;
1237
1238    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1239        let span = pair.as_span().into();
1240        let inner = pair.into_inner();
1241
1242        let elements = inner.map(DataExpr::parse).collect::<Result<Vec<_>, _>>()?;
1243
1244        Ok(TupleConstructor { elements, span })
1245    }
1246
1247    fn span(&self) -> &Span {
1248        &self.span
1249    }
1250}
1251
1252impl DataExpr {
1253    fn number_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1254        Ok(DataExpr::Number(pair.as_str().parse().unwrap()))
1255    }
1256
1257    fn bool_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1258        Ok(DataExpr::Bool(pair.as_str().parse().unwrap()))
1259    }
1260
1261    fn identifier_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1262        Ok(DataExpr::Identifier(Identifier::parse(pair)?))
1263    }
1264
1265    fn struct_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1266        Ok(DataExpr::StructConstructor(StructConstructor::parse(pair)?))
1267    }
1268
1269    fn list_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1270        Ok(DataExpr::ListConstructor(ListConstructor::parse(pair)?))
1271    }
1272
1273    fn map_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1274        Ok(DataExpr::MapConstructor(MapConstructor::parse(pair)?))
1275    }
1276
1277    fn tuple_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1278        Ok(DataExpr::TupleConstructor(TupleConstructor::parse(pair)?))
1279    }
1280
1281    fn utxo_ref_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1282        Ok(DataExpr::UtxoRef(UtxoRef::parse(pair)?))
1283    }
1284
1285    fn any_asset_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1286        Ok(DataExpr::AnyAssetConstructor(AnyAssetConstructor::parse(
1287            pair,
1288        )?))
1289    }
1290
1291    fn concat_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1292        Ok(DataExpr::ConcatOp(ConcatOp::parse(pair)?))
1293    }
1294
1295    fn fn_call_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1296        Ok(DataExpr::FnCall(crate::ast::FnCall::parse(pair)?))
1297    }
1298
1299    fn negate_op_parse(pair: Pair<Rule>, right: DataExpr) -> Result<Self, Error> {
1300        Ok(DataExpr::NegateOp(NegateOp {
1301            operand: Box::new(right),
1302            span: pair.as_span().into(),
1303        }))
1304    }
1305
1306    fn property_op_parse(pair: Pair<Rule>, left: DataExpr) -> Result<Self, Error> {
1307        let span: Span = pair.as_span().into();
1308        let mut inner = pair.into_inner();
1309
1310        Ok(DataExpr::PropertyOp(PropertyOp {
1311            operand: Box::new(left),
1312            property: Box::new(DataExpr::Identifier(Identifier::parse(
1313                inner.next().unwrap(),
1314            )?)),
1315            span,
1316            scope: None,
1317        }))
1318    }
1319
1320    fn index_op_parse(pair: Pair<Rule>, left: DataExpr) -> Result<Self, Error> {
1321        let span: Span = pair.as_span().into();
1322        let mut inner = pair.into_inner();
1323
1324        Ok(DataExpr::PropertyOp(PropertyOp {
1325            operand: Box::new(left),
1326            property: Box::new(DataExpr::parse(inner.next().unwrap())?),
1327            span,
1328            scope: None,
1329        }))
1330    }
1331
1332    fn add_op_parse(left: DataExpr, pair: Pair<Rule>, right: DataExpr) -> Result<Self, Error> {
1333        let span = pair.as_span().into();
1334
1335        Ok(DataExpr::AddOp(AddOp {
1336            lhs: Box::new(left),
1337            rhs: Box::new(right),
1338            span,
1339        }))
1340    }
1341
1342    fn sub_op_parse(left: DataExpr, pair: Pair<Rule>, right: DataExpr) -> Result<Self, Error> {
1343        let span = pair.as_span().into();
1344
1345        Ok(DataExpr::SubOp(SubOp {
1346            lhs: Box::new(left),
1347            rhs: Box::new(right),
1348            span,
1349        }))
1350    }
1351
1352    fn mul_op_parse(left: DataExpr, pair: Pair<Rule>, right: DataExpr) -> Result<Self, Error> {
1353        let span = pair.as_span().into();
1354
1355        Ok(DataExpr::MulOp(MulOp {
1356            lhs: Box::new(left),
1357            rhs: Box::new(right),
1358            span,
1359        }))
1360    }
1361
1362    fn div_op_parse(left: DataExpr, pair: Pair<Rule>, right: DataExpr) -> Result<Self, Error> {
1363        let span = pair.as_span().into();
1364
1365        Ok(DataExpr::DivOp(DivOp {
1366            lhs: Box::new(left),
1367            rhs: Box::new(right),
1368            span,
1369        }))
1370    }
1371}
1372
1373static DATA_EXPR_PRATT_PARSER: LazyLock<PrattParser<Rule>> = LazyLock::new(|| {
1374    PrattParser::new()
1375        .op(Op::infix(Rule::data_add, Assoc::Left) | Op::infix(Rule::data_sub, Assoc::Left))
1376        .op(Op::infix(Rule::data_mul, Assoc::Left) | Op::infix(Rule::data_div, Assoc::Left))
1377        .op(Op::prefix(Rule::data_negate))
1378        .op(Op::postfix(Rule::data_property) | Op::postfix(Rule::data_index))
1379});
1380
1381impl AstNode for DataExpr {
1382    const RULE: Rule = Rule::data_expr;
1383
1384    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1385        let inner = pair.into_inner();
1386
1387        DATA_EXPR_PRATT_PARSER
1388            .map_primary(|x| match x.as_rule() {
1389                Rule::number => DataExpr::number_parse(x),
1390                Rule::string => Ok(DataExpr::String(StringLiteral::parse(x)?)),
1391                Rule::bool => DataExpr::bool_parse(x),
1392                Rule::hex_string => Ok(DataExpr::HexString(HexStringLiteral::parse(x)?)),
1393                Rule::struct_constructor => DataExpr::struct_constructor_parse(x),
1394                Rule::list_constructor => DataExpr::list_constructor_parse(x),
1395                Rule::map_constructor => DataExpr::map_constructor_parse(x),
1396                Rule::tuple_constructor => DataExpr::tuple_constructor_parse(x),
1397                Rule::unit => Ok(DataExpr::Unit),
1398                Rule::identifier => DataExpr::identifier_parse(x),
1399                Rule::utxo_ref => DataExpr::utxo_ref_parse(x),
1400                Rule::any_asset_constructor => DataExpr::any_asset_constructor_parse(x),
1401                Rule::concat_constructor => DataExpr::concat_constructor_parse(x),
1402                Rule::fn_call => DataExpr::fn_call_parse(x),
1403                Rule::data_expr => DataExpr::parse(x),
1404                x => unreachable!("unexpected rule as data primary: {:?}", x),
1405            })
1406            .map_prefix(|op, right| match op.as_rule() {
1407                Rule::data_negate => DataExpr::negate_op_parse(op, right?),
1408                x => unreachable!("Unexpected rule as data prefix: {:?}", x),
1409            })
1410            .map_postfix(|left, op| match op.as_rule() {
1411                Rule::data_property => DataExpr::property_op_parse(op, left?),
1412                Rule::data_index => DataExpr::index_op_parse(op, left?),
1413                x => unreachable!("Unexpected rule as data postfix: {:?}", x),
1414            })
1415            .map_infix(|left, op, right| match op.as_rule() {
1416                Rule::data_add => DataExpr::add_op_parse(left?, op, right?),
1417                Rule::data_sub => DataExpr::sub_op_parse(left?, op, right?),
1418                Rule::data_mul => DataExpr::mul_op_parse(left?, op, right?),
1419                Rule::data_div => DataExpr::div_op_parse(left?, op, right?),
1420                x => unreachable!("Unexpected rule as data infix: {:?}", x),
1421            })
1422            .parse(inner)
1423    }
1424
1425    fn span(&self) -> &Span {
1426        match self {
1427            DataExpr::None => &Span::DUMMY,      // TODO
1428            DataExpr::Unit => &Span::DUMMY,      // TODO
1429            DataExpr::Number(_) => &Span::DUMMY, // TODO
1430            DataExpr::Bool(_) => &Span::DUMMY,   // TODO
1431            DataExpr::String(x) => x.span(),
1432            DataExpr::HexString(x) => x.span(),
1433            DataExpr::StructConstructor(x) => x.span(),
1434            DataExpr::ListConstructor(x) => x.span(),
1435            DataExpr::MapConstructor(x) => x.span(),
1436            DataExpr::TupleConstructor(x) => x.span(),
1437            DataExpr::AnyAssetConstructor(x) => x.span(),
1438            DataExpr::Identifier(x) => x.span(),
1439            DataExpr::AddOp(x) => &x.span,
1440            DataExpr::SubOp(x) => &x.span,
1441            DataExpr::MulOp(x) => &x.span,
1442            DataExpr::DivOp(x) => &x.span,
1443            DataExpr::ConcatOp(x) => &x.span,
1444            DataExpr::NegateOp(x) => &x.span,
1445            DataExpr::PropertyOp(x) => &x.span,
1446            DataExpr::UtxoRef(x) => x.span(),
1447            DataExpr::FnCall(x) => &x.span,
1448        }
1449    }
1450}
1451
1452impl AstNode for Type {
1453    const RULE: Rule = Rule::r#type;
1454
1455    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1456        let inner = pair.into_inner().next().unwrap();
1457
1458        match inner.as_rule() {
1459            Rule::primitive_type => match inner.as_str() {
1460                "Int" => Ok(Type::Int),
1461                "Bool" => Ok(Type::Bool),
1462                "Bytes" => Ok(Type::Bytes),
1463                "Address" => Ok(Type::Address),
1464                "UtxoRef" => Ok(Type::UtxoRef),
1465                "AnyAsset" => Ok(Type::AnyAsset),
1466                _ => unreachable!("Unexpected string in primitive_type: {:?}", inner.as_str()),
1467            },
1468            Rule::list_type => {
1469                let inner = inner.into_inner().next().unwrap();
1470                Ok(Type::List(Box::new(Type::parse(inner)?)))
1471            }
1472            Rule::map_type => {
1473                let mut inner = inner.into_inner();
1474                let key_type = Type::parse(inner.next().unwrap())?;
1475                let value_type = Type::parse(inner.next().unwrap())?;
1476                Ok(Type::Map(Box::new(key_type), Box::new(value_type)))
1477            }
1478            Rule::tuple_type => {
1479                let elements = inner
1480                    .into_inner()
1481                    .map(Type::parse)
1482                    .collect::<Result<Vec<_>, _>>()?;
1483                Ok(Type::Tuple(elements))
1484            }
1485            Rule::custom_type => Ok(Type::Custom(Identifier::new(inner.as_str().to_owned()))),
1486            x => unreachable!("Unexpected rule in type: {:?}", x),
1487        }
1488    }
1489
1490    fn span(&self) -> &Span {
1491        &Span::DUMMY // TODO
1492    }
1493}
1494
1495impl TypeDef {
1496    fn parse_variant_format(pair: Pair<Rule>) -> Result<Self, Error> {
1497        let span = pair.as_span().into();
1498        let mut inner = pair.into_inner();
1499
1500        let identifier = Identifier::parse(inner.next().unwrap())?;
1501
1502        let cases = inner
1503            .map(VariantCase::parse)
1504            .collect::<Result<Vec<_>, _>>()?;
1505
1506        Ok(TypeDef {
1507            name: identifier,
1508            cases,
1509            span,
1510        })
1511    }
1512
1513    fn parse_record_format(pair: Pair<Rule>) -> Result<Self, Error> {
1514        let span: Span = pair.as_span().into();
1515        let mut inner = pair.into_inner();
1516
1517        let identifier = Identifier::parse(inner.next().unwrap())?;
1518
1519        let fields = inner
1520            .map(RecordField::parse)
1521            .collect::<Result<Vec<_>, _>>()?;
1522
1523        Ok(TypeDef {
1524            name: identifier.clone(),
1525            cases: vec![VariantCase {
1526                name: Identifier::new("Default"),
1527                fields,
1528                span: span.clone(),
1529            }],
1530            span,
1531        })
1532    }
1533}
1534
1535impl AstNode for TypeDef {
1536    const RULE: Rule = Rule::type_def;
1537
1538    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1539        match pair.as_rule() {
1540            Rule::variant_def => Ok(Self::parse_variant_format(pair)?),
1541            Rule::record_def => Ok(Self::parse_record_format(pair)?),
1542            x => unreachable!("Unexpected rule in type_def: {:?}", x),
1543        }
1544    }
1545
1546    fn span(&self) -> &Span {
1547        &self.span
1548    }
1549}
1550
1551impl AstNode for AliasDef {
1552    const RULE: Rule = Rule::alias_def;
1553
1554    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1555        let span: Span = pair.as_span().into();
1556        let mut inner = pair.into_inner();
1557
1558        let identifier = Identifier::parse(inner.next().unwrap())?;
1559        let r#type = Type::parse(inner.next().unwrap())?;
1560
1561        Ok(AliasDef {
1562            name: identifier,
1563            alias_type: r#type,
1564            span,
1565        })
1566    }
1567
1568    fn span(&self) -> &Span {
1569        &self.span
1570    }
1571}
1572
1573impl VariantCase {
1574    fn struct_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
1575        let span = pair.as_span().into();
1576        let mut inner = pair.into_inner();
1577
1578        let identifier = Identifier::parse(inner.next().unwrap())?;
1579
1580        let fields = inner
1581            .map(RecordField::parse)
1582            .collect::<Result<Vec<_>, _>>()?;
1583
1584        Ok(Self {
1585            name: identifier,
1586            fields,
1587            span,
1588        })
1589    }
1590
1591    fn unit_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
1592        let span = pair.as_span().into();
1593        let mut inner = pair.into_inner();
1594
1595        let identifier = Identifier::parse(inner.next().unwrap())?;
1596
1597        Ok(Self {
1598            name: identifier,
1599            fields: vec![],
1600            span,
1601        })
1602    }
1603}
1604
1605impl AstNode for VariantCase {
1606    const RULE: Rule = Rule::variant_case;
1607
1608    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1609        let case = match pair.as_rule() {
1610            Rule::variant_case_struct => Self::struct_case_parse(pair),
1611            Rule::variant_case_tuple => todo!("parse variant case tuple"),
1612            Rule::variant_case_unit => Self::unit_case_parse(pair),
1613            x => unreachable!("Unexpected rule in datum_variant: {:?}", x),
1614        }?;
1615
1616        Ok(case)
1617    }
1618
1619    fn span(&self) -> &Span {
1620        &self.span
1621    }
1622}
1623
1624impl AstNode for AssetDef {
1625    const RULE: Rule = Rule::asset_def;
1626
1627    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1628        let span = pair.as_span().into();
1629        let mut inner = pair.into_inner();
1630
1631        let identifier = Identifier::parse(inner.next().unwrap())?;
1632        let policy = DataExpr::parse(inner.next().unwrap())?;
1633        let asset_name = DataExpr::parse(inner.next().unwrap())?;
1634
1635        Ok(AssetDef {
1636            name: identifier,
1637            policy,
1638            asset_name,
1639            span,
1640        })
1641    }
1642
1643    fn span(&self) -> &Span {
1644        &self.span
1645    }
1646}
1647
1648impl AstNode for ChainSpecificBlock {
1649    const RULE: Rule = Rule::chain_specific_block;
1650
1651    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1652        let mut inner = pair.into_inner();
1653
1654        let block = inner.next().unwrap();
1655
1656        match block.as_rule() {
1657            Rule::cardano_block => {
1658                let block = crate::cardano::CardanoBlock::parse(block)?;
1659                Ok(ChainSpecificBlock::Cardano(block))
1660            }
1661            x => unreachable!("Unexpected rule in chain_specific_block: {:?}", x),
1662        }
1663    }
1664
1665    fn span(&self) -> &Span {
1666        match self {
1667            Self::Cardano(x) => x.span(),
1668        }
1669    }
1670}
1671
1672/// Parses a Tx3 source string into a Program AST.
1673///
1674/// # Arguments
1675///
1676/// * `input` - String containing Tx3 source code
1677///
1678/// # Returns
1679///
1680/// * `Result<Program, Error>` - The parsed Program AST or an error
1681///
1682/// # Errors
1683///
1684/// Returns an error if:
1685/// - The input string is not valid Tx3 syntax
1686/// - The AST construction fails
1687///
1688/// # Example
1689///
1690/// ```
1691/// use tx3_lang::parsing::parse_string;
1692/// let program = parse_string("tx swap() {}").unwrap();
1693/// ```
1694pub fn parse_string(input: &str) -> Result<Program, Error> {
1695    let pairs = Tx3Grammar::parse(Rule::program, input)?;
1696    Program::parse(pairs.into_iter().next().unwrap())
1697}
1698
1699#[cfg(test)]
1700pub fn parse_well_known_example(example: &str) -> Program {
1701    let manifest_dir = env!("CARGO_MANIFEST_DIR");
1702    let test_file = format!("{}/../../examples/{}.tx3", manifest_dir, example);
1703    let input = std::fs::read_to_string(&test_file).unwrap();
1704    parse_string(&input).unwrap()
1705}
1706
1707#[cfg(test)]
1708mod tests {
1709    use super::*;
1710    use crate::ast;
1711    use assert_json_diff::assert_json_eq;
1712    use paste::paste;
1713    use pest::Parser;
1714
1715    #[test]
1716    fn smoke_test_parse_string() {
1717        let _ = parse_string("tx swap() {}").unwrap();
1718    }
1719
1720    macro_rules! input_to_ast_check {
1721        ($ast:ty, $name:expr, $input:expr, $expected:expr) => {
1722            paste::paste! {
1723                #[test]
1724                fn [<test_parse_ $ast:snake _ $name>]() {
1725                    let pairs = super::Tx3Grammar::parse(<$ast>::RULE, $input).unwrap();
1726                    let single_match = pairs.into_iter().next().unwrap();
1727                    let result = <$ast>::parse(single_match).unwrap();
1728
1729                    assert_eq!(result, $expected);
1730                }
1731            }
1732        };
1733    }
1734
1735    input_to_ast_check!(
1736        ConcatOp,
1737        "basic",
1738        r#"concat("hello", "world")"#,
1739        ast::ConcatOp {
1740            lhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
1741                value: "hello".to_string(),
1742                span: ast::Span::DUMMY,
1743            })),
1744            rhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
1745                value: "world".to_string(),
1746                span: ast::Span::DUMMY,
1747            })),
1748            span: ast::Span::DUMMY,
1749        }
1750    );
1751    input_to_ast_check!(Type, "int", "Int", Type::Int);
1752
1753    input_to_ast_check!(Type, "bool", "Bool", Type::Bool);
1754
1755    input_to_ast_check!(Type, "bytes", "Bytes", Type::Bytes);
1756
1757    input_to_ast_check!(Type, "address", "Address", Type::Address);
1758
1759    input_to_ast_check!(Type, "utxo_ref", "UtxoRef", Type::UtxoRef);
1760
1761    input_to_ast_check!(Type, "any_asset", "AnyAsset", Type::AnyAsset);
1762
1763    input_to_ast_check!(Type, "list", "List<Int>", Type::List(Box::new(Type::Int)));
1764
1765    input_to_ast_check!(
1766        Type,
1767        "identifier",
1768        "MyType",
1769        Type::Custom(Identifier::new("MyType".to_string()))
1770    );
1771
1772    input_to_ast_check!(
1773        Type,
1774        "other_type",
1775        "List<Bytes>",
1776        Type::List(Box::new(Type::Bytes))
1777    );
1778
1779    input_to_ast_check!(
1780        Type,
1781        "within_list",
1782        "List<List<Int>>",
1783        Type::List(Box::new(Type::List(Box::new(Type::Int))))
1784    );
1785
1786    input_to_ast_check!(
1787        Type,
1788        "tuple",
1789        "Tuple<Int, Bytes>",
1790        Type::Tuple(vec![Type::Int, Type::Bytes])
1791    );
1792
1793    input_to_ast_check!(
1794        Type,
1795        "tuple_three_with_nested",
1796        "Tuple<Int, Bytes, List<Bool>>",
1797        Type::Tuple(vec![
1798            Type::Int,
1799            Type::Bytes,
1800            Type::List(Box::new(Type::Bool)),
1801        ])
1802    );
1803
1804    input_to_ast_check!(
1805        Type,
1806        "tuple_nested",
1807        "Tuple<Tuple<Int, Int>, Bytes>",
1808        Type::Tuple(vec![Type::Tuple(vec![Type::Int, Type::Int]), Type::Bytes,])
1809    );
1810
1811    input_to_ast_check!(
1812        TypeDef,
1813        "type_def_record",
1814        "type MyRecord {
1815            field1: Int,
1816            field2: Bytes,
1817        }",
1818        TypeDef {
1819            name: Identifier::new("MyRecord"),
1820            cases: vec![VariantCase {
1821                name: Identifier::new("Default"),
1822                fields: vec![
1823                    RecordField::new("field1", Type::Int),
1824                    RecordField::new("field2", Type::Bytes)
1825                ],
1826                span: Span::DUMMY,
1827            }],
1828            span: Span::DUMMY,
1829        }
1830    );
1831
1832    input_to_ast_check!(
1833        TypeDef,
1834        "type_def_variant",
1835        "type MyVariant {
1836            Case1 {
1837                field1: Int,
1838                field2: Bytes,
1839            },
1840            Case2,
1841        }",
1842        TypeDef {
1843            name: Identifier::new("MyVariant"),
1844            cases: vec![
1845                VariantCase {
1846                    name: Identifier::new("Case1"),
1847                    fields: vec![
1848                        RecordField::new("field1", Type::Int),
1849                        RecordField::new("field2", Type::Bytes)
1850                    ],
1851                    span: Span::DUMMY,
1852                },
1853                VariantCase {
1854                    name: Identifier::new("Case2"),
1855                    fields: vec![],
1856                    span: Span::DUMMY,
1857                },
1858            ],
1859            span: Span::DUMMY,
1860        }
1861    );
1862
1863    input_to_ast_check!(
1864        AliasDef,
1865        "type_def_alias",
1866        "type MyAlias = Bytes;",
1867        AliasDef {
1868            name: Identifier::new("MyAlias"),
1869            alias_type: Type::Bytes,
1870            span: Span::DUMMY,
1871        }
1872    );
1873
1874    input_to_ast_check!(
1875        AliasDef,
1876        "type_alias_custom_type",
1877        "type UserAlias = UserType;",
1878        AliasDef {
1879            name: Identifier::new("UserAlias"),
1880            alias_type: Type::Custom(Identifier::new("UserType")),
1881            span: Span::DUMMY,
1882        }
1883    );
1884
1885    input_to_ast_check!(
1886        AliasDef,
1887        "type_alias_list",
1888        "type StringList = List<Bytes>;",
1889        AliasDef {
1890            name: Identifier::new("StringList"),
1891            alias_type: Type::List(Box::new(Type::Bytes)),
1892            span: Span::DUMMY,
1893        }
1894    );
1895
1896    input_to_ast_check!(
1897        AliasDef,
1898        "type_alias_map",
1899        "type StringIntMap = Map<Bytes, Int>;",
1900        AliasDef {
1901            name: Identifier::new("StringIntMap"),
1902            alias_type: Type::Map(Box::new(Type::Bytes), Box::new(Type::Int)),
1903            span: Span::DUMMY,
1904        }
1905    );
1906
1907    input_to_ast_check!(
1908        AliasDef,
1909        "type_alias_complex_nested",
1910        "type ComplexType = List<Map<Bytes, Int>>;",
1911        AliasDef {
1912            name: Identifier::new("ComplexType"),
1913            alias_type: Type::List(Box::new(Type::Map(
1914                Box::new(Type::Bytes),
1915                Box::new(Type::Int)
1916            ))),
1917            span: Span::DUMMY,
1918        }
1919    );
1920
1921    input_to_ast_check!(
1922        AliasDef,
1923        "type_alias_all_primitives",
1924        "type MyInt = Int;",
1925        AliasDef {
1926            name: Identifier::new("MyInt"),
1927            alias_type: Type::Int,
1928            span: Span::DUMMY,
1929        }
1930    );
1931
1932    input_to_ast_check!(
1933        AliasDef,
1934        "type_alias_bool",
1935        "type MyBool = Bool;",
1936        AliasDef {
1937            name: Identifier::new("MyBool"),
1938            alias_type: Type::Bool,
1939            span: Span::DUMMY,
1940        }
1941    );
1942
1943    input_to_ast_check!(
1944        AliasDef,
1945        "type_alias_address",
1946        "type MyAddress = Address;",
1947        AliasDef {
1948            name: Identifier::new("MyAddress"),
1949            alias_type: Type::Address,
1950            span: Span::DUMMY,
1951        }
1952    );
1953
1954    input_to_ast_check!(
1955        AliasDef,
1956        "type_alias_utxo_ref",
1957        "type MyUtxoRef = UtxoRef;",
1958        AliasDef {
1959            name: Identifier::new("MyUtxoRef"),
1960            alias_type: Type::UtxoRef,
1961            span: Span::DUMMY,
1962        }
1963    );
1964
1965    input_to_ast_check!(
1966        AliasDef,
1967        "type_alias_any_asset",
1968        "type MyAsset = AnyAsset;",
1969        AliasDef {
1970            name: Identifier::new("MyAsset"),
1971            alias_type: Type::AnyAsset,
1972            span: Span::DUMMY,
1973        }
1974    );
1975
1976    input_to_ast_check!(
1977        StringLiteral,
1978        "literal_string",
1979        "\"Hello, world!\"",
1980        StringLiteral::new("Hello, world!".to_string())
1981    );
1982
1983    input_to_ast_check!(
1984        HexStringLiteral,
1985        "hex_string",
1986        "0xAFAFAF",
1987        HexStringLiteral::new("AFAFAF".to_string())
1988    );
1989
1990    input_to_ast_check!(
1991        StringLiteral,
1992        "literal_string_address",
1993        "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1994        StringLiteral::new("addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string())
1995    );
1996
1997    input_to_ast_check!(
1998        ListConstructor,
1999        "empty_list",
2000        "[]",
2001        ListConstructor {
2002            elements: vec![],
2003            span: Span::DUMMY,
2004        }
2005    );
2006
2007    input_to_ast_check!(
2008        ListConstructor,
2009        "trailing_comma",
2010        "[1, 2,]",
2011        ListConstructor {
2012            elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2013            span: Span::DUMMY,
2014        }
2015    );
2016
2017    input_to_ast_check!(
2018        ListConstructor,
2019        "int_list",
2020        "[1, 2]",
2021        ListConstructor {
2022            elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2023            span: Span::DUMMY,
2024        }
2025    );
2026
2027    input_to_ast_check!(
2028        ListConstructor,
2029        "string_list",
2030        "[\"Hello\", \"World\"]",
2031        ListConstructor {
2032            elements: vec![
2033                DataExpr::String(StringLiteral::new("Hello".to_string())),
2034                DataExpr::String(StringLiteral::new("World".to_string()))
2035            ],
2036            span: Span::DUMMY,
2037        }
2038    );
2039
2040    input_to_ast_check!(
2041        ListConstructor,
2042        "mixed_list",
2043        "[1, \"Hello\", true]",
2044        ListConstructor {
2045            elements: vec![
2046                DataExpr::Number(1),
2047                DataExpr::String(StringLiteral::new("Hello".to_string())),
2048                DataExpr::Bool(true)
2049            ],
2050            span: Span::DUMMY,
2051        }
2052    );
2053
2054    input_to_ast_check!(
2055        ListConstructor,
2056        "list_within_list",
2057        "[[1, 2], [3, 4]]",
2058        ListConstructor {
2059            elements: vec![
2060                DataExpr::ListConstructor(ListConstructor {
2061                    elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2062                    span: Span::DUMMY,
2063                }),
2064                DataExpr::ListConstructor(ListConstructor {
2065                    elements: vec![DataExpr::Number(3), DataExpr::Number(4),],
2066                    span: Span::DUMMY,
2067                }),
2068            ],
2069            span: Span::DUMMY,
2070        }
2071    );
2072
2073    // A two-or-more element parenthesized list is a tuple literal.
2074    input_to_ast_check!(
2075        DataExpr,
2076        "tuple_literal",
2077        "(1, 0xFF, true)",
2078        DataExpr::TupleConstructor(TupleConstructor {
2079            elements: vec![
2080                DataExpr::Number(1),
2081                DataExpr::HexString(HexStringLiteral::new("FF".to_string())),
2082                DataExpr::Bool(true),
2083            ],
2084            span: Span::DUMMY,
2085        })
2086    );
2087
2088    // Trailing comma is permitted in a tuple literal.
2089    input_to_ast_check!(
2090        DataExpr,
2091        "tuple_literal_trailing_comma",
2092        "(1, 2,)",
2093        DataExpr::TupleConstructor(TupleConstructor {
2094            elements: vec![DataExpr::Number(1), DataExpr::Number(2)],
2095            span: Span::DUMMY,
2096        })
2097    );
2098
2099    // A single parenthesized expression is grouping, NOT a one-tuple.
2100    input_to_ast_check!(DataExpr, "grouping_not_tuple", "(42)", DataExpr::Number(42));
2101
2102    // Positional tuple access uses bracket indexing with a literal index; it
2103    // lowers through the same PropertyOp path as struct/list access.
2104    input_to_ast_check!(
2105        DataExpr,
2106        "tuple_index_access",
2107        "my_tuple[0]",
2108        DataExpr::PropertyOp(PropertyOp {
2109            operand: Box::new(DataExpr::Identifier(Identifier::new("my_tuple"))),
2110            property: Box::new(DataExpr::Number(0)),
2111            span: Span::DUMMY,
2112            scope: None,
2113        })
2114    );
2115
2116    input_to_ast_check!(DataExpr, "literal_bool_true", "true", DataExpr::Bool(true));
2117
2118    input_to_ast_check!(
2119        DataExpr,
2120        "literal_bool_false",
2121        "false",
2122        DataExpr::Bool(false)
2123    );
2124
2125    input_to_ast_check!(DataExpr, "unit_value", "())", DataExpr::Unit);
2126
2127    input_to_ast_check!(DataExpr, "number_value", "123", DataExpr::Number(123));
2128
2129    input_to_ast_check!(
2130        PolicyDef,
2131        "policy_def_assign",
2132        "policy MyPolicy = 0xAFAFAF;",
2133        PolicyDef {
2134            name: Identifier::new("MyPolicy"),
2135            value: PolicyValue::Assign(HexStringLiteral::new("AFAFAF".to_string())),
2136            span: Span::DUMMY,
2137        }
2138    );
2139
2140    input_to_ast_check!(
2141        PolicyDef,
2142        "policy_def_constructor",
2143        "policy MyPolicy {
2144            hash: 0x1234567890,
2145            script: 0x1234567890,
2146            ref: 0x1234567890,
2147        };",
2148        PolicyDef {
2149            name: Identifier::new("MyPolicy"),
2150            value: PolicyValue::Constructor(PolicyConstructor {
2151                fields: vec![
2152                    PolicyField::Hash(DataExpr::HexString(HexStringLiteral::new(
2153                        "1234567890".to_string()
2154                    ))),
2155                    PolicyField::Script(DataExpr::HexString(HexStringLiteral::new(
2156                        "1234567890".to_string()
2157                    ))),
2158                    PolicyField::Ref(DataExpr::HexString(HexStringLiteral::new(
2159                        "1234567890".to_string()
2160                    ))),
2161                ],
2162                span: Span::DUMMY,
2163            }),
2164            span: Span::DUMMY,
2165        }
2166    );
2167
2168    input_to_ast_check!(
2169        AssetDef,
2170        "hex_hex",
2171        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.0xef7a1ceb;",
2172        AssetDef {
2173            name: Identifier::new("MyToken"),
2174            policy: DataExpr::HexString(HexStringLiteral::new(
2175                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
2176            )),
2177            asset_name: DataExpr::HexString(HexStringLiteral::new("ef7a1ceb".to_string())),
2178            span: Span::DUMMY,
2179        }
2180    );
2181
2182    input_to_ast_check!(
2183        AssetDef,
2184        "hex_string",
2185        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.\"MY TOKEN\";",
2186        AssetDef {
2187            name: Identifier::new("MyToken"),
2188            policy: DataExpr::HexString(HexStringLiteral::new(
2189                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
2190            )),
2191            asset_name: DataExpr::String(StringLiteral::new("MY TOKEN".to_string())),
2192            span: Span::DUMMY,
2193        }
2194    );
2195
2196    input_to_ast_check!(
2197        DataExpr,
2198        "type_and_literal",
2199        "MyToken(15)",
2200        DataExpr::FnCall(crate::ast::FnCall {
2201            callee: Identifier::new("MyToken"),
2202            args: vec![DataExpr::Number(15)],
2203            span: Span::DUMMY,
2204        })
2205    );
2206
2207    input_to_ast_check!(
2208        AnyAssetConstructor,
2209        "any_asset_constructor",
2210        "AnyAsset(0x1234567890, \"MyToken\", 15)",
2211        AnyAssetConstructor {
2212            policy: Box::new(DataExpr::HexString(HexStringLiteral::new(
2213                "1234567890".to_string()
2214            ))),
2215            asset_name: Box::new(DataExpr::String(StringLiteral::new("MyToken".to_string()))),
2216            amount: Box::new(DataExpr::Number(15)),
2217            span: Span::DUMMY,
2218        }
2219    );
2220
2221    input_to_ast_check!(
2222        AnyAssetConstructor,
2223        "any_asset_identifiers",
2224        "AnyAsset(my_policy, my_token, my_amount)",
2225        AnyAssetConstructor {
2226            policy: Box::new(DataExpr::Identifier(Identifier::new("my_policy"))),
2227            asset_name: Box::new(DataExpr::Identifier(Identifier::new("my_token"))),
2228            amount: Box::new(DataExpr::Identifier(Identifier::new("my_amount"))),
2229            span: Span::DUMMY,
2230        }
2231    );
2232
2233    input_to_ast_check!(
2234        AnyAssetConstructor,
2235        "any_asset_property_access",
2236        "AnyAsset(input1.policy, input1.asset_name, input1.amount)",
2237        AnyAssetConstructor {
2238            policy: Box::new(DataExpr::PropertyOp(PropertyOp {
2239                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2240                property: Box::new(DataExpr::Identifier(Identifier::new("policy"))),
2241                span: Span::DUMMY,
2242                scope: None,
2243            })),
2244            asset_name: Box::new(DataExpr::PropertyOp(PropertyOp {
2245                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2246                property: Box::new(DataExpr::Identifier(Identifier::new("asset_name"))),
2247                span: Span::DUMMY,
2248                scope: None,
2249            })),
2250            amount: Box::new(DataExpr::PropertyOp(PropertyOp {
2251                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2252                property: Box::new(DataExpr::Identifier(Identifier::new("amount"))),
2253                span: Span::DUMMY,
2254                scope: None,
2255            })),
2256            span: Span::DUMMY,
2257        }
2258    );
2259
2260    input_to_ast_check!(DataExpr, "literal", "5", DataExpr::Number(5));
2261
2262    input_to_ast_check!(
2263        DataExpr,
2264        "add_op",
2265        "5 + var1",
2266        DataExpr::AddOp(AddOp {
2267            lhs: Box::new(DataExpr::Number(5)),
2268            rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2269            span: Span::DUMMY,
2270        })
2271    );
2272
2273    input_to_ast_check!(
2274        DataExpr,
2275        "mul_op",
2276        "5 * var1",
2277        DataExpr::MulOp(MulOp {
2278            lhs: Box::new(DataExpr::Number(5)),
2279            rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2280            span: Span::DUMMY,
2281        })
2282    );
2283
2284    // `*` binds tighter than `+`, so `2 + 3 * 4` parses as `2 + (3 * 4)`.
2285    input_to_ast_check!(
2286        DataExpr,
2287        "mul_binds_tighter_than_add",
2288        "2 + 3 * 4",
2289        DataExpr::AddOp(AddOp {
2290            lhs: Box::new(DataExpr::Number(2)),
2291            rhs: Box::new(DataExpr::MulOp(MulOp {
2292                lhs: Box::new(DataExpr::Number(3)),
2293                rhs: Box::new(DataExpr::Number(4)),
2294                span: Span::DUMMY,
2295            })),
2296            span: Span::DUMMY,
2297        })
2298    );
2299
2300    // ... and on the left, `3 * 4 + 2` parses as `(3 * 4) + 2`.
2301    input_to_ast_check!(
2302        DataExpr,
2303        "mul_left_binds_tighter_than_add",
2304        "3 * 4 + 2",
2305        DataExpr::AddOp(AddOp {
2306            lhs: Box::new(DataExpr::MulOp(MulOp {
2307                lhs: Box::new(DataExpr::Number(3)),
2308                rhs: Box::new(DataExpr::Number(4)),
2309                span: Span::DUMMY,
2310            })),
2311            rhs: Box::new(DataExpr::Number(2)),
2312            span: Span::DUMMY,
2313        })
2314    );
2315
2316    input_to_ast_check!(
2317        DataExpr,
2318        "div_op",
2319        "10 / var1",
2320        DataExpr::DivOp(DivOp {
2321            lhs: Box::new(DataExpr::Number(10)),
2322            rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2323            span: Span::DUMMY,
2324        })
2325    );
2326
2327    // `/` binds tighter than `+`, so `2 + 8 / 4` parses as `2 + (8 / 4)`.
2328    input_to_ast_check!(
2329        DataExpr,
2330        "div_binds_tighter_than_add",
2331        "2 + 8 / 4",
2332        DataExpr::AddOp(AddOp {
2333            lhs: Box::new(DataExpr::Number(2)),
2334            rhs: Box::new(DataExpr::DivOp(DivOp {
2335                lhs: Box::new(DataExpr::Number(8)),
2336                rhs: Box::new(DataExpr::Number(4)),
2337                span: Span::DUMMY,
2338            })),
2339            span: Span::DUMMY,
2340        })
2341    );
2342
2343    // `*` and `/` share precedence and are left-associative, so `8 / 4 * 2`
2344    // parses as `(8 / 4) * 2`.
2345    input_to_ast_check!(
2346        DataExpr,
2347        "div_binds_like_mul",
2348        "8 / 4 * 2",
2349        DataExpr::MulOp(MulOp {
2350            lhs: Box::new(DataExpr::DivOp(DivOp {
2351                lhs: Box::new(DataExpr::Number(8)),
2352                rhs: Box::new(DataExpr::Number(4)),
2353                span: Span::DUMMY,
2354            })),
2355            rhs: Box::new(DataExpr::Number(2)),
2356            span: Span::DUMMY,
2357        })
2358    );
2359
2360    input_to_ast_check!(
2361        DataExpr,
2362        "concat_op",
2363        r#"concat("hello", "world")"#,
2364        DataExpr::ConcatOp(ConcatOp {
2365            lhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
2366                value: "hello".to_string(),
2367                span: ast::Span::DUMMY,
2368            })),
2369            rhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
2370                value: "world".to_string(),
2371                span: ast::Span::DUMMY,
2372            })),
2373            span: ast::Span::DUMMY,
2374        })
2375    );
2376
2377    input_to_ast_check!(
2378        DataExpr,
2379        "property_access",
2380        "subject.property",
2381        DataExpr::PropertyOp(PropertyOp {
2382            operand: Box::new(DataExpr::Identifier(Identifier::new("subject"))),
2383            property: Box::new(DataExpr::Identifier(Identifier::new("property"))),
2384            span: Span::DUMMY,
2385            scope: None,
2386        })
2387    );
2388
2389    input_to_ast_check!(
2390        DataExpr,
2391        "multiple_properties",
2392        "subject.property.subproperty",
2393        DataExpr::PropertyOp(PropertyOp {
2394            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2395                operand: Box::new(DataExpr::Identifier(Identifier::new("subject"))),
2396                property: Box::new(DataExpr::Identifier(Identifier::new("property"))),
2397                span: Span::DUMMY,
2398                scope: None,
2399            })),
2400            property: Box::new(DataExpr::Identifier(Identifier::new("subproperty"))),
2401            span: Span::DUMMY,
2402            scope: None,
2403        })
2404    );
2405
2406    input_to_ast_check!(DataExpr, "empty_parentheses", "()", DataExpr::Unit);
2407
2408    input_to_ast_check!(DataExpr, "nested_parentheses", "((()))", DataExpr::Unit);
2409
2410    input_to_ast_check!(
2411        DataExpr,
2412        "nested_arithmetic_expression",
2413        "(1 + ((6 - 3) + 4))",
2414        DataExpr::AddOp(AddOp {
2415            lhs: Box::new(DataExpr::Number(1)),
2416            rhs: Box::new(DataExpr::AddOp(AddOp {
2417                lhs: Box::new(DataExpr::SubOp(SubOp {
2418                    lhs: Box::new(DataExpr::Number(6)),
2419                    rhs: Box::new(DataExpr::Number(3)),
2420                    span: Span::DUMMY,
2421                })),
2422                rhs: Box::new(DataExpr::Number(4)),
2423                span: Span::DUMMY,
2424            })),
2425            span: Span::DUMMY,
2426        })
2427    );
2428
2429    input_to_ast_check!(
2430        DataExpr,
2431        "negate_op",
2432        "!a",
2433        DataExpr::NegateOp(NegateOp {
2434            operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2435            span: Span::DUMMY,
2436        })
2437    );
2438
2439    input_to_ast_check!(
2440        DataExpr,
2441        "negate_precedence",
2442        "!a.b",
2443        DataExpr::NegateOp(NegateOp {
2444            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2445                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2446                property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2447                span: Span::DUMMY,
2448                scope: None,
2449            })),
2450            span: Span::DUMMY,
2451        })
2452    );
2453
2454    input_to_ast_check!(
2455        DataExpr,
2456        "negate_override_precedence",
2457        "(!a).b",
2458        DataExpr::PropertyOp(PropertyOp {
2459            operand: Box::new(DataExpr::NegateOp(NegateOp {
2460                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2461                span: Span::DUMMY,
2462            })),
2463            property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2464            span: Span::DUMMY,
2465            scope: None,
2466        })
2467    );
2468
2469    input_to_ast_check!(
2470        DataExpr,
2471        "overly_complex",
2472        "(1 + 5) - ((a.b.c - 3) + !d.f)",
2473        DataExpr::SubOp(SubOp {
2474            lhs: Box::new(DataExpr::AddOp(AddOp {
2475                lhs: Box::new(DataExpr::Number(1)),
2476                rhs: Box::new(DataExpr::Number(5)),
2477                span: Span::DUMMY,
2478            })),
2479            rhs: Box::new(DataExpr::AddOp(AddOp {
2480                lhs: Box::new(DataExpr::SubOp(SubOp {
2481                    lhs: Box::new(DataExpr::PropertyOp(PropertyOp {
2482                        operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2483                            operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2484                            property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2485                            span: Span::DUMMY,
2486                            scope: None,
2487                        })),
2488                        property: Box::new(DataExpr::Identifier(Identifier::new("c"))),
2489                        span: Span::DUMMY,
2490                        scope: None,
2491                    })),
2492                    rhs: Box::new(DataExpr::Number(3)),
2493                    span: Span::DUMMY,
2494                })),
2495                rhs: Box::new(DataExpr::NegateOp(NegateOp {
2496                    operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2497                        operand: Box::new(DataExpr::Identifier(Identifier::new("d"))),
2498                        property: Box::new(DataExpr::Identifier(Identifier::new("f"))),
2499                        span: Span::DUMMY,
2500                        scope: None,
2501                    })),
2502                    span: Span::DUMMY,
2503                })),
2504
2505                span: Span::DUMMY,
2506            })),
2507            span: Span::DUMMY,
2508        })
2509    );
2510
2511    input_to_ast_check!(
2512        DataExpr,
2513        "min_utxo_basic",
2514        "min_utxo(output1)",
2515        DataExpr::FnCall(crate::ast::FnCall {
2516            callee: Identifier::new("min_utxo"),
2517            args: vec![DataExpr::Identifier(Identifier::new("output1"))],
2518            span: Span::DUMMY,
2519        })
2520    );
2521
2522    input_to_ast_check!(
2523        DataExpr,
2524        "min_utxo_in_expression",
2525        "Ada(100) + min_utxo(my_output)",
2526        DataExpr::AddOp(AddOp {
2527            lhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2528                callee: Identifier::new("Ada"),
2529                args: vec![DataExpr::Number(100)],
2530                span: Span::DUMMY,
2531            })),
2532            rhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2533                callee: Identifier::new("min_utxo"),
2534                args: vec![DataExpr::Identifier(Identifier::new("my_output"))],
2535                span: Span::DUMMY,
2536            })),
2537            span: Span::DUMMY,
2538        })
2539    );
2540
2541    input_to_ast_check!(
2542        DataExpr,
2543        "tip_slot_basic",
2544        "tip_slot()",
2545        DataExpr::FnCall(crate::ast::FnCall {
2546            callee: Identifier::new("tip_slot"),
2547            args: vec![],
2548            span: Span::DUMMY,
2549        })
2550    );
2551
2552    input_to_ast_check!(
2553        DataExpr,
2554        "tip_slot_in_expression",
2555        "1000 + tip_slot()",
2556        DataExpr::AddOp(AddOp {
2557            lhs: Box::new(DataExpr::Number(1000)),
2558            rhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2559                callee: Identifier::new("tip_slot"),
2560                args: vec![],
2561                span: Span::DUMMY,
2562            })),
2563            span: Span::DUMMY,
2564        })
2565    );
2566
2567    input_to_ast_check!(
2568        StructConstructor,
2569        "struct_constructor_record",
2570        "MyRecord {
2571            field1: 10,
2572            field2: abc,
2573        }",
2574        StructConstructor {
2575            r#type: Identifier::new("MyRecord"),
2576            case: VariantCaseConstructor {
2577                name: Identifier::new("Default"),
2578                fields: vec![
2579                    RecordConstructorField {
2580                        name: Identifier::new("field1"),
2581                        value: Box::new(DataExpr::Number(10)),
2582                        span: Span::DUMMY,
2583                    },
2584                    RecordConstructorField {
2585                        name: Identifier::new("field2"),
2586                        value: Box::new(DataExpr::Identifier(Identifier::new("abc"))),
2587                        span: Span::DUMMY,
2588                    },
2589                ],
2590                spread: None,
2591                scope: None,
2592                span: Span::DUMMY,
2593            },
2594            scope: None,
2595            span: Span::DUMMY,
2596        }
2597    );
2598
2599    input_to_ast_check!(
2600        StructConstructor,
2601        "struct_constructor_variant",
2602        "ShipCommand::MoveShip {
2603            delta_x: delta_x,
2604            delta_y: delta_y,
2605        }",
2606        StructConstructor {
2607            r#type: Identifier::new("ShipCommand"),
2608            case: VariantCaseConstructor {
2609                name: Identifier::new("MoveShip"),
2610                fields: vec![
2611                    RecordConstructorField {
2612                        name: Identifier::new("delta_x"),
2613                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
2614                        span: Span::DUMMY,
2615                    },
2616                    RecordConstructorField {
2617                        name: Identifier::new("delta_y"),
2618                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
2619                        span: Span::DUMMY,
2620                    },
2621                ],
2622                spread: None,
2623                scope: None,
2624                span: Span::DUMMY,
2625            },
2626            scope: None,
2627            span: Span::DUMMY,
2628        }
2629    );
2630
2631    input_to_ast_check!(
2632        StructConstructor,
2633        "struct_constructor_variant_with_spread",
2634        "ShipCommand::MoveShip {
2635            delta_x: delta_x,
2636            delta_y: delta_y,
2637            ...abc
2638        }",
2639        StructConstructor {
2640            r#type: Identifier::new("ShipCommand"),
2641            case: VariantCaseConstructor {
2642                name: Identifier::new("MoveShip"),
2643                fields: vec![
2644                    RecordConstructorField {
2645                        name: Identifier::new("delta_x"),
2646                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
2647                        span: Span::DUMMY,
2648                    },
2649                    RecordConstructorField {
2650                        name: Identifier::new("delta_y"),
2651                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
2652                        span: Span::DUMMY,
2653                    },
2654                ],
2655                spread: Some(Box::new(DataExpr::Identifier(Identifier::new(
2656                    "abc".to_string()
2657                )))),
2658                scope: None,
2659                span: Span::DUMMY,
2660            },
2661            scope: None,
2662            span: Span::DUMMY,
2663        }
2664    );
2665
2666    input_to_ast_check!(
2667        LocalsBlock,
2668        "basic",
2669        "locals {
2670            a: 10,
2671        }",
2672        LocalsBlock {
2673            assigns: vec![LocalsAssign {
2674                name: Identifier::new("a"),
2675                value: DataExpr::Number(10),
2676                span: Span::DUMMY,
2677            },],
2678            span: Span::DUMMY,
2679        }
2680    );
2681
2682    input_to_ast_check!(
2683        LocalsBlock,
2684        "multiple",
2685        "locals {
2686            a: 10,
2687            b: 20,
2688        }",
2689        LocalsBlock {
2690            assigns: vec![
2691                LocalsAssign {
2692                    name: Identifier::new("a"),
2693                    value: DataExpr::Number(10),
2694                    span: Span::DUMMY,
2695                },
2696                LocalsAssign {
2697                    name: Identifier::new("b"),
2698                    value: DataExpr::Number(20),
2699                    span: Span::DUMMY,
2700                },
2701            ],
2702            span: Span::DUMMY,
2703        }
2704    );
2705
2706    input_to_ast_check!(
2707        LocalsBlock,
2708        "complex_expression",
2709        "locals {
2710            a: (10 + 20) - 8,
2711            b: a.b.c + (5 - d),
2712        }",
2713        LocalsBlock {
2714            assigns: vec![
2715                LocalsAssign {
2716                    name: Identifier::new("a"),
2717                    value: DataExpr::SubOp(SubOp {
2718                        lhs: Box::new(DataExpr::AddOp(AddOp {
2719                            lhs: Box::new(DataExpr::Number(10)),
2720                            rhs: Box::new(DataExpr::Number(20)),
2721                            span: Span::DUMMY,
2722                        })),
2723                        rhs: Box::new(DataExpr::Number(8)),
2724                        span: Span::DUMMY,
2725                    }),
2726                    span: Span::DUMMY,
2727                },
2728                LocalsAssign {
2729                    name: Identifier::new("b"),
2730                    value: DataExpr::AddOp(AddOp {
2731                        lhs: Box::new(DataExpr::PropertyOp(PropertyOp {
2732                            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2733                                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2734                                property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2735                                span: Span::DUMMY,
2736                                scope: None,
2737                            })),
2738                            property: Box::new(DataExpr::Identifier(Identifier::new("c"))),
2739                            span: Span::DUMMY,
2740                            scope: None,
2741                        })),
2742                        rhs: Box::new(DataExpr::SubOp(SubOp {
2743                            lhs: Box::new(DataExpr::Number(5)),
2744                            rhs: Box::new(DataExpr::Identifier(Identifier::new("d"))),
2745                            span: Span::DUMMY,
2746                        })),
2747                        span: Span::DUMMY,
2748                    }),
2749                    span: Span::DUMMY,
2750                },
2751            ],
2752            span: Span::DUMMY,
2753        }
2754    );
2755
2756    input_to_ast_check!(
2757        InputBlock,
2758        "single",
2759        r#"input source {}"#,
2760        InputBlock {
2761            many: false,
2762            name: "source".to_string(),
2763            fields: vec![],
2764            span: Span::DUMMY,
2765        }
2766    );
2767
2768    input_to_ast_check!(
2769        InputBlock,
2770        "multiple",
2771        r#"input* source {}"#,
2772        InputBlock {
2773            many: true,
2774            name: "source".to_string(),
2775            fields: vec![],
2776            span: Span::DUMMY,
2777        }
2778    );
2779
2780    input_to_ast_check!(
2781        OutputBlock,
2782        "output_block_anonymous",
2783        r#"output {
2784            to: my_party,
2785            amount: Ada(100),
2786        }"#,
2787        OutputBlock {
2788            name: None,
2789            optional: false,
2790            fields: vec![
2791                OutputBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
2792                    "my_party".to_string(),
2793                )))),
2794                OutputBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
2795                    callee: Identifier::new("Ada"),
2796                    args: vec![DataExpr::Number(100)],
2797                    span: Span::DUMMY,
2798                }))),
2799            ],
2800            span: Span::DUMMY,
2801        }
2802    );
2803
2804    input_to_ast_check!(
2805        ChainSpecificBlock,
2806        "chain_specific_block_cardano",
2807        "cardano::vote_delegation_certificate {
2808            drep: 0x1234567890,
2809            stake: 0x1234567890,
2810        }",
2811        ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::VoteDelegationCertificate(
2812            crate::cardano::VoteDelegationCertificate {
2813                drep: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
2814                stake: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
2815                span: Span::DUMMY,
2816            },
2817        ))
2818    );
2819
2820    input_to_ast_check!(
2821        ChainSpecificBlock,
2822        "chain_specific_block_cardano_treasury",
2823        "cardano::treasury_donation {
2824           coin: 20,
2825        }",
2826        ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::TreasuryDonation(
2827            crate::cardano::TreasuryDonationBlock {
2828                coin: DataExpr::Number(20),
2829                span: Span::DUMMY,
2830            },
2831        ))
2832    );
2833
2834    input_to_ast_check!(
2835        EnvDef,
2836        "basic",
2837        "env {
2838            field_a: Int,
2839            field_b: Bytes,
2840        }",
2841        EnvDef {
2842            fields: vec![
2843                EnvField {
2844                    name: "field_a".to_string(),
2845                    r#type: Type::Int,
2846                    docstring: None,
2847                    span: Span::DUMMY,
2848                },
2849                EnvField {
2850                    name: "field_b".to_string(),
2851                    r#type: Type::Bytes,
2852                    docstring: None,
2853                    span: Span::DUMMY,
2854                },
2855            ],
2856            span: Span::DUMMY,
2857        }
2858    );
2859
2860    input_to_ast_check!(
2861        TxDef,
2862        "empty",
2863        "tx my_tx() {}",
2864        TxDef {
2865            name: Identifier::new("my_tx"),
2866            docstring: None,
2867            parameters: ParameterList {
2868                parameters: vec![],
2869                span: Span::DUMMY,
2870            },
2871            locals: None,
2872            references: vec![],
2873            inputs: vec![],
2874            outputs: vec![],
2875            validity: None,
2876            mints: vec![],
2877            burns: vec![],
2878            signers: None,
2879            adhoc: vec![],
2880            collateral: vec![],
2881            metadata: None,
2882            scope: None,
2883            span: Span::DUMMY,
2884        }
2885    );
2886
2887    input_to_ast_check!(
2888        TxDef,
2889        "with_parameters",
2890        "tx my_tx(a: Int, b: Bytes) {}",
2891        TxDef {
2892            name: Identifier::new("my_tx"),
2893            docstring: None,
2894            parameters: ParameterList {
2895                parameters: vec![
2896                    ParamDef {
2897                        name: Identifier::new("a"),
2898                        r#type: Type::Int,
2899                        docstring: None,
2900                    },
2901                    ParamDef {
2902                        name: Identifier::new("b"),
2903                        r#type: Type::Bytes,
2904                        docstring: None,
2905                    },
2906                ],
2907                span: Span::DUMMY,
2908            },
2909            locals: None,
2910            references: vec![],
2911            inputs: vec![],
2912            outputs: vec![],
2913            validity: None,
2914            mints: vec![],
2915            burns: vec![],
2916            signers: None,
2917            adhoc: vec![],
2918            collateral: vec![],
2919            metadata: None,
2920            scope: None,
2921            span: Span::DUMMY,
2922        }
2923    );
2924
2925    input_to_ast_check!(
2926        Program,
2927        "basic",
2928        "party Abc; tx my_tx() {}",
2929        Program {
2930            parties: vec![PartyDef {
2931                name: Identifier::new("Abc"),
2932                docstring: None,
2933                span: Span::DUMMY,
2934            }],
2935            types: vec![],
2936            aliases: vec![],
2937            txs: vec![TxDef {
2938                name: Identifier::new("my_tx"),
2939                docstring: None,
2940                parameters: ParameterList {
2941                    parameters: vec![],
2942                    span: Span::DUMMY,
2943                },
2944                locals: None,
2945                references: vec![],
2946                inputs: vec![],
2947                outputs: vec![],
2948                validity: None,
2949                mints: vec![],
2950                burns: vec![],
2951                signers: None,
2952                adhoc: vec![],
2953                collateral: vec![],
2954                metadata: None,
2955                scope: None,
2956                span: Span::DUMMY,
2957            }],
2958            env: None,
2959            assets: vec![],
2960            policies: vec![],
2961            functions: vec![],
2962            span: Span::DUMMY,
2963            scope: None,
2964        }
2965    );
2966
2967    input_to_ast_check!(
2968        DataExpr,
2969        "array_index_literal",
2970        "my_list[0]",
2971        DataExpr::PropertyOp(PropertyOp {
2972            operand: Box::new(DataExpr::Identifier(Identifier::new("my_list"))),
2973            property: Box::new(DataExpr::Number(0)),
2974            span: Span::DUMMY,
2975            scope: None,
2976        })
2977    );
2978
2979    input_to_ast_check!(
2980        DataExpr,
2981        "array_index_variable",
2982        "my_list[index]",
2983        DataExpr::PropertyOp(PropertyOp {
2984            operand: Box::new(DataExpr::Identifier(Identifier::new("my_list"))),
2985            property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
2986            span: Span::DUMMY,
2987            scope: None,
2988        })
2989    );
2990
2991    input_to_ast_check!(
2992        DataExpr,
2993        "nested_array_index",
2994        "matrix[row][col]",
2995        DataExpr::PropertyOp(PropertyOp {
2996            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2997                operand: Box::new(DataExpr::Identifier(Identifier::new("matrix"))),
2998                property: Box::new(DataExpr::Identifier(Identifier::new("row"))),
2999                span: Span::DUMMY,
3000                scope: None,
3001            })),
3002            property: Box::new(DataExpr::Identifier(Identifier::new("col"))),
3003            span: Span::DUMMY,
3004            scope: None,
3005        })
3006    );
3007
3008    input_to_ast_check!(
3009        DataExpr,
3010        "array_index_with_property_access",
3011        "items[index].name",
3012        DataExpr::PropertyOp(PropertyOp {
3013            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3014                operand: Box::new(DataExpr::Identifier(Identifier::new("items"))),
3015                property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
3016                span: Span::DUMMY,
3017                scope: None,
3018            })),
3019            property: Box::new(DataExpr::Identifier(Identifier::new("name"))),
3020            span: Span::DUMMY,
3021            scope: None,
3022        })
3023    );
3024
3025    input_to_ast_check!(
3026        DataExpr,
3027        "property_access_then_array_index",
3028        "object.list[0]",
3029        DataExpr::PropertyOp(PropertyOp {
3030            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3031                operand: Box::new(DataExpr::Identifier(Identifier::new("object"))),
3032                property: Box::new(DataExpr::Identifier(Identifier::new("list"))),
3033                span: Span::DUMMY,
3034                scope: None,
3035            })),
3036            property: Box::new(DataExpr::Number(0)),
3037            span: Span::DUMMY,
3038            scope: None,
3039        })
3040    );
3041
3042    input_to_ast_check!(
3043        DataExpr,
3044        "array_index_with_function_call",
3045        "values[min_utxo(output)]",
3046        DataExpr::PropertyOp(PropertyOp {
3047            operand: Box::new(DataExpr::Identifier(Identifier::new("values"))),
3048            property: Box::new(DataExpr::FnCall(crate::ast::FnCall {
3049                callee: Identifier::new("min_utxo"),
3050                args: vec![DataExpr::Identifier(Identifier::new("output"))],
3051                span: Span::DUMMY,
3052            })),
3053            span: Span::DUMMY,
3054            scope: None,
3055        })
3056    );
3057
3058    input_to_ast_check!(
3059        DataExpr,
3060        "mixed_property_and_index_access",
3061        "container.items[index].metadata[\"key\"]",
3062        DataExpr::PropertyOp(PropertyOp {
3063            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3064                operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3065                    operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3066                        operand: Box::new(DataExpr::Identifier(Identifier::new("container"))),
3067                        property: Box::new(DataExpr::Identifier(Identifier::new("items"))),
3068                        span: Span::DUMMY,
3069                        scope: None,
3070                    })),
3071                    property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
3072                    span: Span::DUMMY,
3073                    scope: None,
3074                })),
3075                property: Box::new(DataExpr::Identifier(Identifier::new("metadata"))),
3076                span: Span::DUMMY,
3077                scope: None,
3078            })),
3079            property: Box::new(DataExpr::String(StringLiteral::new("key".to_string()))),
3080            span: Span::DUMMY,
3081            scope: None,
3082        })
3083    );
3084
3085    input_to_ast_check!(
3086        ReferenceBlock,
3087        "with_datum_type",
3088        "reference oracle_data { ref: oracle_utxo, datum_is: OracleDatum, }",
3089        ReferenceBlock {
3090            name: "oracle_data".to_string(),
3091            r#ref: DataExpr::Identifier(Identifier::new("oracle_utxo")),
3092            datum_is: Some(Type::Custom(Identifier::new("OracleDatum"))),
3093            span: Span::DUMMY,
3094        }
3095    );
3096
3097    input_to_ast_check!(
3098        MapConstructor,
3099        "empty",
3100        "{}",
3101        MapConstructor {
3102            fields: vec![],
3103            span: Span::DUMMY,
3104        }
3105    );
3106
3107    input_to_ast_check!(
3108        PartyDef,
3109        "with_docstring",
3110        "/// the protocol treasury\nparty Treasury;",
3111        PartyDef {
3112            name: Identifier::new("Treasury"),
3113            docstring: Some("the protocol treasury".to_string()),
3114            span: Span::DUMMY,
3115        }
3116    );
3117
3118    input_to_ast_check!(
3119        EnvField,
3120        "with_docstring",
3121        "/// network magic\nnetwork: Int",
3122        EnvField {
3123            name: "network".to_string(),
3124            r#type: Type::Int,
3125            docstring: Some("network magic".to_string()),
3126            span: Span::DUMMY,
3127        }
3128    );
3129
3130    input_to_ast_check!(
3131        TxDef,
3132        "with_multiline_docstring",
3133        "/// transfer funds from a to b\n/// across two lines\ntx transfer() {}",
3134        TxDef {
3135            name: Identifier::new("transfer"),
3136            docstring: Some("transfer funds from a to b\nacross two lines".to_string()),
3137            parameters: ParameterList {
3138                parameters: vec![],
3139                span: Span::DUMMY,
3140            },
3141            locals: None,
3142            references: vec![],
3143            inputs: vec![],
3144            outputs: vec![],
3145            validity: None,
3146            mints: vec![],
3147            burns: vec![],
3148            signers: None,
3149            adhoc: vec![],
3150            collateral: vec![],
3151            metadata: None,
3152            scope: None,
3153            span: Span::DUMMY,
3154        }
3155    );
3156
3157    input_to_ast_check!(
3158        ParameterList,
3159        "with_param_docstrings",
3160        "(/// amount in lovelace\nquantity: Int, target: Address)",
3161        ParameterList {
3162            parameters: vec![
3163                ParamDef {
3164                    name: Identifier::new("quantity"),
3165                    r#type: Type::Int,
3166                    docstring: Some("amount in lovelace".to_string()),
3167                },
3168                ParamDef {
3169                    name: Identifier::new("target"),
3170                    r#type: Type::Address,
3171                    docstring: None,
3172                },
3173            ],
3174            span: Span::DUMMY,
3175        }
3176    );
3177
3178    #[test]
3179    fn test_spans_are_respected() {
3180        let program = parse_well_known_example("spans");
3181        assert_eq!(program.span, Span::new(0, 759));
3182
3183        assert_eq!(program.parties[0].span, Span::new(27, 41));
3184
3185        assert_eq!(program.types[0].span, Span::new(43, 77));
3186    }
3187
3188    fn make_snapshot_if_missing(example: &str, program: &Program) {
3189        let manifest_dir = env!("CARGO_MANIFEST_DIR");
3190        let path = format!("{}/../../examples/{}.ast", manifest_dir, example);
3191
3192        if !std::fs::exists(&path).unwrap() {
3193            let ast = serde_json::to_string_pretty(program).unwrap();
3194            std::fs::write(&path, ast).unwrap();
3195        }
3196    }
3197
3198    fn test_parsing_example(example: &str) {
3199        let program = parse_well_known_example(example);
3200        make_snapshot_if_missing(example, &program);
3201
3202        let manifest_dir = env!("CARGO_MANIFEST_DIR");
3203        let ast_file = format!("{}/../../examples/{}.ast", manifest_dir, example);
3204        let ast = std::fs::read_to_string(ast_file).unwrap();
3205
3206        let expected: Program = serde_json::from_str(&ast).unwrap();
3207
3208        assert_json_eq!(program, expected);
3209    }
3210
3211    #[macro_export]
3212    macro_rules! test_parsing {
3213        ($name:ident) => {
3214            paste! {
3215                #[test]
3216                fn [<test_example_ $name>]() {
3217                    test_parsing_example(stringify!($name));
3218                }
3219            }
3220        };
3221    }
3222
3223    test_parsing!(lang_tour);
3224
3225    test_parsing!(tuples);
3226
3227    test_parsing!(transfer);
3228
3229    test_parsing!(swap);
3230
3231    test_parsing!(asteria);
3232
3233    test_parsing!(vesting);
3234
3235    test_parsing!(faucet);
3236
3237    test_parsing!(disordered);
3238
3239    test_parsing!(input_datum);
3240
3241    test_parsing!(withdrawal);
3242
3243    test_parsing!(env_vars);
3244
3245    test_parsing!(local_vars);
3246
3247    test_parsing!(cardano_witness);
3248
3249    test_parsing!(reference_script);
3250
3251    test_parsing!(map);
3252
3253    test_parsing!(burn);
3254
3255    test_parsing!(donation);
3256
3257    test_parsing!(list_concat);
3258
3259    test_parsing!(buidler_fest_2026);
3260
3261    test_parsing!(functions);
3262
3263    test_parsing!(nested_functions);
3264}