Skip to main content

tx3_lang/
parsing.rs

1//! Parses the Tx3 language.
2//!
3//! This module takes a string and parses it into Tx3 AST.
4
5use std::sync::LazyLock;
6
7use miette::SourceOffset;
8use pest::{
9    iterators::Pair,
10    pratt_parser::{Assoc, Op, PrattParser},
11    Parser,
12};
13use pest_derive::Parser;
14
15use crate::ast::*;
16
17#[derive(Parser)]
18#[grammar = "tx3.pest"]
19pub(crate) struct Tx3Grammar;
20
21#[derive(Debug, thiserror::Error, miette::Diagnostic)]
22#[error("Parsing error: {message}")]
23#[diagnostic(code(tx3::parsing))]
24pub struct Error {
25    pub message: String,
26
27    #[source_code]
28    pub src: String,
29
30    #[label]
31    pub span: Span,
32}
33
34impl From<pest::error::Error<Rule>> for Error {
35    fn from(error: pest::error::Error<Rule>) -> Self {
36        match &error.variant {
37            pest::error::ErrorVariant::ParsingError { positives, .. } => Error {
38                message: format!("expected {positives:?}"),
39                src: error.line().to_string(),
40                span: error.location.into(),
41            },
42            pest::error::ErrorVariant::CustomError { message } => Error {
43                message: message.clone(),
44                src: error.line().to_string(),
45                span: error.location.into(),
46            },
47        }
48    }
49}
50
51impl From<pest::error::InputLocation> for Span {
52    fn from(value: pest::error::InputLocation) -> Self {
53        match value {
54            pest::error::InputLocation::Pos(pos) => Self::new(pos, pos),
55            pest::error::InputLocation::Span((start, end)) => Self::new(start, end),
56        }
57    }
58}
59
60impl From<pest::Span<'_>> for Span {
61    fn from(span: pest::Span<'_>) -> Self {
62        Self::new(span.start(), span.end())
63    }
64}
65
66impl From<Span> for miette::SourceSpan {
67    fn from(span: Span) -> Self {
68        miette::SourceSpan::new(SourceOffset::from(span.start), span.end - span.start)
69    }
70}
71
72pub trait AstNode: Sized {
73    const RULE: Rule;
74
75    fn parse(pair: Pair<Rule>) -> Result<Self, Error>;
76
77    fn span(&self) -> &Span;
78}
79
80/// Pulls an optional leading `Rule::docstring` pair from a `Pairs` iterator and
81/// normalizes it: each line's leading `///` (and at most one space after it) is
82/// stripped, lines are joined with `\n`, and trailing whitespace is trimmed.
83fn take_docstring(inner: &mut pest::iterators::Pairs<Rule>) -> Option<String> {
84    let next = inner.peek()?;
85    if next.as_rule() != Rule::docstring {
86        return None;
87    }
88    let pair = inner.next().unwrap();
89
90    let mut lines = Vec::new();
91    for line in pair.into_inner() {
92        debug_assert_eq!(line.as_rule(), Rule::doc_line);
93        let raw = line.as_str();
94        let trimmed = raw.strip_prefix("///").unwrap_or(raw);
95        let trimmed = trimmed.strip_prefix(' ').unwrap_or(trimmed);
96        lines.push(trimmed.trim_end().to_string());
97    }
98
99    let joined = lines.join("\n");
100    if joined.trim().is_empty() {
101        None
102    } else {
103        Some(joined)
104    }
105}
106
107impl AstNode for Program {
108    const RULE: Rule = Rule::program;
109
110    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
111        let span = pair.as_span().into();
112        let inner = pair.into_inner();
113
114        let mut program = Self {
115            env: None,
116            txs: Vec::new(),
117            assets: Vec::new(),
118            types: Vec::new(),
119            aliases: Vec::new(),
120            parties: Vec::new(),
121            policies: Vec::new(),
122            functions: Vec::new(),
123            scope: None,
124            span,
125        };
126
127        for pair in inner {
128            match pair.as_rule() {
129                Rule::env_def => program.env = Some(EnvDef::parse(pair)?),
130                Rule::tx_def => program.txs.push(TxDef::parse(pair)?),
131                Rule::asset_def => program.assets.push(AssetDef::parse(pair)?),
132                Rule::record_def => program.types.push(TypeDef::parse(pair)?),
133                Rule::variant_def => program.types.push(TypeDef::parse(pair)?),
134                Rule::alias_def => program.aliases.push(AliasDef::parse(pair)?),
135                Rule::party_def => program.parties.push(PartyDef::parse(pair)?),
136                Rule::policy_def => program.policies.push(PolicyDef::parse(pair)?),
137                Rule::fn_def => program.functions.push(FnDef::parse(pair)?),
138                Rule::EOI => break,
139                x => unreachable!("Unexpected rule in program: {:?}", x),
140            }
141        }
142
143        Ok(program)
144    }
145
146    fn span(&self) -> &Span {
147        &self.span
148    }
149}
150
151impl AstNode for EnvField {
152    const RULE: Rule = Rule::env_field;
153
154    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
155        let span = pair.as_span().into();
156        let mut inner = pair.into_inner();
157        let docstring = take_docstring(&mut inner);
158        let identifier = inner.next().unwrap().as_str().to_string();
159        let r#type = Type::parse(inner.next().unwrap())?;
160
161        Ok(EnvField {
162            name: identifier,
163            r#type,
164            docstring,
165            span,
166        })
167    }
168
169    fn span(&self) -> &Span {
170        &self.span
171    }
172}
173
174impl AstNode for EnvDef {
175    const RULE: Rule = Rule::env_def;
176
177    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
178        let span = pair.as_span().into();
179        let inner = pair.into_inner();
180
181        let fields = inner
182            .map(|x| EnvField::parse(x))
183            .collect::<Result<Vec<_>, _>>()?;
184
185        Ok(EnvDef { fields, span })
186    }
187
188    fn span(&self) -> &Span {
189        &self.span
190    }
191}
192
193impl AstNode for ParameterList {
194    const RULE: Rule = Rule::parameter_list;
195
196    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
197        let span = pair.as_span().into();
198        let inner = pair.into_inner();
199
200        let mut parameters = Vec::new();
201
202        for param in inner {
203            let mut inner = param.into_inner();
204            let docstring = take_docstring(&mut inner);
205            let name = Identifier::parse(inner.next().unwrap())?;
206            let r#type = Type::parse(inner.next().unwrap())?;
207
208            parameters.push(ParamDef {
209                name,
210                r#type,
211                docstring,
212            });
213        }
214
215        Ok(ParameterList { parameters, span })
216    }
217
218    fn span(&self) -> &Span {
219        &self.span
220    }
221}
222
223impl AstNode for TxDef {
224    const RULE: Rule = Rule::tx_def;
225
226    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
227        let span = pair.as_span().into();
228        let mut inner = pair.into_inner();
229
230        let docstring = take_docstring(&mut inner);
231        let name = Identifier::parse(inner.next().unwrap())?;
232        let parameters = ParameterList::parse(inner.next().unwrap())?;
233
234        let mut locals = None;
235        let mut references = Vec::new();
236        let mut inputs = Vec::new();
237        let mut outputs = Vec::new();
238        let mut validity = None;
239        let mut burns = Vec::new();
240        let mut mints = Vec::new();
241        let mut adhoc = Vec::new();
242        let mut collateral = Vec::new();
243        let mut signers = None;
244        let mut metadata = None;
245
246        for item in inner {
247            match item.as_rule() {
248                Rule::locals_block => locals = Some(LocalsBlock::parse(item)?),
249                Rule::reference_block => references.push(ReferenceBlock::parse(item)?),
250                Rule::input_block => inputs.push(InputBlock::parse(item)?),
251                Rule::output_block => outputs.push(OutputBlock::parse(item)?),
252                Rule::validity_block => validity = Some(ValidityBlock::parse(item)?),
253                Rule::mint_block => mints.push(MintBlock::parse(item)?),
254                Rule::burn_block => burns.push(MintBlock::parse(item)?),
255                Rule::chain_specific_block => adhoc.push(ChainSpecificBlock::parse(item)?),
256                Rule::collateral_block => collateral.push(CollateralBlock::parse(item)?),
257                Rule::signers_block => signers = Some(SignersBlock::parse(item)?),
258                Rule::metadata_block => metadata = Some(MetadataBlock::parse(item)?),
259                x => unreachable!("Unexpected rule in tx_def: {:?}", x),
260            }
261        }
262
263        Ok(TxDef {
264            name,
265            docstring,
266            parameters,
267            locals,
268            references,
269            inputs,
270            outputs,
271            validity,
272            mints,
273            burns,
274            signers,
275            adhoc,
276            scope: None,
277            span,
278            collateral,
279            metadata,
280        })
281    }
282
283    fn span(&self) -> &Span {
284        &self.span
285    }
286}
287
288impl AstNode for Identifier {
289    const RULE: Rule = Rule::identifier;
290
291    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
292        Ok(Identifier {
293            value: pair.as_str().to_string(),
294            symbol: None,
295            span: pair.as_span().into(),
296        })
297    }
298
299    fn span(&self) -> &Span {
300        &self.span
301    }
302}
303
304impl AstNode for StringLiteral {
305    const RULE: Rule = Rule::string;
306
307    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
308        Ok(StringLiteral {
309            value: pair.as_str()[1..pair.as_str().len() - 1].to_string(),
310            span: pair.as_span().into(),
311        })
312    }
313
314    fn span(&self) -> &Span {
315        &self.span
316    }
317}
318
319impl AstNode for HexStringLiteral {
320    const RULE: Rule = Rule::hex_string;
321
322    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
323        Ok(HexStringLiteral {
324            value: pair.as_str()[2..].to_string(),
325            span: pair.as_span().into(),
326        })
327    }
328
329    fn span(&self) -> &Span {
330        &self.span
331    }
332}
333
334impl AstNode for PartyDef {
335    const RULE: Rule = Rule::party_def;
336
337    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
338        let span = pair.as_span().into();
339        let mut inner = pair.into_inner();
340        let docstring = take_docstring(&mut inner);
341        let identifier = Identifier::parse(inner.next().unwrap())?;
342
343        Ok(PartyDef {
344            name: identifier,
345            docstring,
346            span,
347        })
348    }
349
350    fn span(&self) -> &Span {
351        &self.span
352    }
353}
354
355impl AstNode for LocalsAssign {
356    const RULE: Rule = Rule::locals_assign;
357
358    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
359        let span = pair.as_span().into();
360        let mut inner = pair.into_inner();
361
362        let name = Identifier::parse(inner.next().unwrap())?;
363        let value = DataExpr::parse(inner.next().unwrap())?;
364
365        Ok(LocalsAssign { name, value, span })
366    }
367
368    fn span(&self) -> &Span {
369        &self.span
370    }
371}
372
373impl AstNode for LocalsBlock {
374    const RULE: Rule = Rule::locals_block;
375
376    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
377        let span = pair.as_span().into();
378        let inner = pair.into_inner();
379
380        let assigns = inner
381            .map(|x| LocalsAssign::parse(x))
382            .collect::<Result<Vec<_>, _>>()?;
383
384        Ok(LocalsBlock { assigns, span })
385    }
386
387    fn span(&self) -> &Span {
388        &self.span
389    }
390}
391
392impl AstNode for ReferenceBlock {
393    const RULE: Rule = Rule::reference_block;
394
395    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
396        let span = pair.as_span().into();
397        let mut inner = pair.into_inner();
398
399        let name = inner.next().unwrap().as_str().to_string();
400
401        let pair = inner.next().unwrap();
402        let r#ref = match pair.as_rule() {
403            Rule::input_block_ref => {
404                let pair = pair.into_inner().next().unwrap();
405                DataExpr::parse(pair)?
406            }
407            x => unreachable!("Unexpected rule in ref_input_block: {:?}", x),
408        };
409
410        let datum_is = inner
411            .next()
412            .map(|pair| {
413                let pair = pair.into_inner().next().unwrap();
414                Type::parse(pair)
415            })
416            .transpose()?;
417
418        Ok(ReferenceBlock {
419            name,
420            r#ref,
421            datum_is,
422            span,
423        })
424    }
425
426    fn span(&self) -> &Span {
427        &self.span
428    }
429}
430
431impl AstNode for CollateralBlockField {
432    const RULE: Rule = Rule::collateral_block_field;
433
434    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
435        match pair.as_rule() {
436            Rule::input_block_from => {
437                let pair = pair.into_inner().next().unwrap();
438                let x = CollateralBlockField::From(DataExpr::parse(pair)?);
439                Ok(x)
440            }
441            Rule::input_block_min_amount => {
442                let pair = pair.into_inner().next().unwrap();
443                let x = CollateralBlockField::MinAmount(DataExpr::parse(pair)?);
444                Ok(x)
445            }
446            Rule::input_block_ref => {
447                let pair = pair.into_inner().next().unwrap();
448                let x = CollateralBlockField::Ref(DataExpr::parse(pair)?);
449                Ok(x)
450            }
451            x => unreachable!("Unexpected rule in collateral_block: {:?}", x),
452        }
453    }
454
455    fn span(&self) -> &Span {
456        match self {
457            Self::From(x) => x.span(),
458            Self::MinAmount(x) => x.span(),
459            Self::Ref(x) => x.span(),
460        }
461    }
462}
463
464impl AstNode for CollateralBlock {
465    const RULE: Rule = Rule::collateral_block;
466
467    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
468        let span = pair.as_span().into();
469        let inner = pair.into_inner();
470
471        let fields = inner
472            .map(|x| CollateralBlockField::parse(x))
473            .collect::<Result<Vec<_>, _>>()?;
474
475        Ok(CollateralBlock { fields, span })
476    }
477
478    fn span(&self) -> &Span {
479        &self.span
480    }
481}
482
483impl AstNode for MetadataBlockField {
484    const RULE: Rule = Rule::metadata_block_field;
485
486    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
487        let span = pair.as_span().into();
488        match pair.as_rule() {
489            Rule::metadata_block_field => {
490                let mut inner = pair.into_inner();
491                let key = inner.next().unwrap();
492                let value = inner.next().unwrap();
493                Ok(MetadataBlockField {
494                    key: DataExpr::parse(key)?,
495                    value: DataExpr::parse(value)?,
496                    span,
497                })
498            }
499            x => unreachable!("Unexpected rule in metadata_block: {:?}", x),
500        }
501    }
502
503    fn span(&self) -> &Span {
504        &self.span
505    }
506}
507
508impl AstNode for MetadataBlock {
509    const RULE: Rule = Rule::metadata_block;
510
511    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
512        let span = pair.as_span().into();
513        let inner = pair.into_inner();
514
515        let fields = inner
516            .map(|x| MetadataBlockField::parse(x))
517            .collect::<Result<Vec<_>, _>>()?;
518
519        Ok(MetadataBlock { fields, span })
520    }
521
522    fn span(&self) -> &Span {
523        &self.span
524    }
525}
526
527impl AstNode for InputBlockField {
528    const RULE: Rule = Rule::input_block_field;
529
530    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
531        match pair.as_rule() {
532            Rule::input_block_from => {
533                let pair = pair.into_inner().next().unwrap();
534                let x = InputBlockField::From(DataExpr::parse(pair)?);
535                Ok(x)
536            }
537            Rule::input_block_datum_is => {
538                let pair = pair.into_inner().next().unwrap();
539                let x = InputBlockField::DatumIs(Type::parse(pair)?);
540                Ok(x)
541            }
542            Rule::input_block_min_amount => {
543                let pair = pair.into_inner().next().unwrap();
544                let x = InputBlockField::MinAmount(DataExpr::parse(pair)?);
545                Ok(x)
546            }
547            Rule::input_block_redeemer => {
548                let pair = pair.into_inner().next().unwrap();
549                let x = InputBlockField::Redeemer(DataExpr::parse(pair)?);
550                Ok(x)
551            }
552            Rule::input_block_ref => {
553                let pair = pair.into_inner().next().unwrap();
554                let x = InputBlockField::Ref(DataExpr::parse(pair)?);
555                Ok(x)
556            }
557            x => unreachable!("Unexpected rule in input_block: {:?}", x),
558        }
559    }
560
561    fn span(&self) -> &Span {
562        match self {
563            Self::From(x) => x.span(),
564            Self::DatumIs(x) => x.span(),
565            Self::MinAmount(x) => x.span(),
566            Self::Redeemer(x) => x.span(),
567            Self::Ref(x) => x.span(),
568        }
569    }
570}
571
572impl AstNode for InputBlock {
573    const RULE: Rule = Rule::input_block;
574
575    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
576        let span = pair.as_span().into();
577        let mut inner = pair.into_inner();
578
579        let next = inner.next().unwrap();
580
581        let (many, name) = match next.as_rule() {
582            Rule::input_many => (true, inner.next().unwrap().as_str().to_string()),
583            Rule::identifier => (false, next.as_str().to_string()),
584            _ => unreachable!("Unexpected rule in input_block: {:?}", next.as_rule()),
585        };
586
587        let fields = inner
588            .map(|x| InputBlockField::parse(x))
589            .collect::<Result<Vec<_>, _>>()?;
590
591        Ok(InputBlock {
592            name,
593            many,
594            fields,
595            span,
596        })
597    }
598
599    fn span(&self) -> &Span {
600        &self.span
601    }
602}
603
604impl AstNode for OutputBlockField {
605    const RULE: Rule = Rule::output_block_field;
606
607    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
608        match pair.as_rule() {
609            Rule::output_block_to => {
610                let pair = pair.into_inner().next().unwrap();
611                let x = OutputBlockField::To(Box::new(DataExpr::parse(pair)?));
612                Ok(x)
613            }
614            Rule::output_block_amount => {
615                let pair = pair.into_inner().next().unwrap();
616                let x = OutputBlockField::Amount(DataExpr::parse(pair)?.into());
617                Ok(x)
618            }
619            Rule::output_block_datum => {
620                let pair = pair.into_inner().next().unwrap();
621                let x = OutputBlockField::Datum(DataExpr::parse(pair)?.into());
622                Ok(x)
623            }
624            x => unreachable!("Unexpected rule in output_block_field: {:?}", x),
625        }
626    }
627
628    fn span(&self) -> &Span {
629        match self {
630            Self::To(x) => x.span(),
631            Self::Amount(x) => x.span(),
632            Self::Datum(x) => x.span(),
633        }
634    }
635}
636
637impl AstNode for OutputBlock {
638    const RULE: Rule = Rule::output_block;
639
640    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
641        let span = pair.as_span().into();
642        let mut inner = pair.into_inner();
643
644        let optional = inner
645            .peek()
646            .map_or(false, |first| first.as_rule() == Rule::output_optional);
647
648        if optional {
649            inner.next();
650        }
651
652        let has_name = inner
653            .peek()
654            .map(|x| x.as_rule() == Rule::identifier)
655            .unwrap_or_default();
656
657        let name = if has_name {
658            Some(Identifier::parse(inner.next().unwrap())?)
659        } else {
660            None
661        };
662
663        let fields = inner
664            .map(|x| OutputBlockField::parse(x))
665            .collect::<Result<Vec<_>, _>>()?;
666
667        Ok(OutputBlock {
668            name,
669            optional,
670            fields,
671            span,
672        })
673    }
674
675    fn span(&self) -> &Span {
676        &self.span
677    }
678}
679
680impl AstNode for ValidityBlockField {
681    const RULE: Rule = Rule::validity_block_field;
682
683    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
684        match pair.as_rule() {
685            Rule::validity_since_slot => {
686                let pair = pair.into_inner().next().unwrap();
687                let x = ValidityBlockField::SinceSlot(DataExpr::parse(pair)?.into());
688                Ok(x)
689            }
690            Rule::validity_until_slot => {
691                let pair = pair.into_inner().next().unwrap();
692                let x = ValidityBlockField::UntilSlot(DataExpr::parse(pair)?.into());
693                Ok(x)
694            }
695            x => unreachable!("Unexpected rule in validity_block: {:?}", x),
696        }
697    }
698
699    fn span(&self) -> &Span {
700        match self {
701            Self::UntilSlot(x) => x.span(),
702            Self::SinceSlot(x) => x.span(),
703        }
704    }
705}
706
707impl AstNode for ValidityBlock {
708    const RULE: Rule = Rule::validity_block;
709
710    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
711        let span = pair.as_span().into();
712        let inner = pair.into_inner();
713
714        let fields = inner
715            .map(|x| ValidityBlockField::parse(x))
716            .collect::<Result<Vec<_>, _>>()?;
717
718        Ok(ValidityBlock { fields, span })
719    }
720
721    fn span(&self) -> &Span {
722        &self.span
723    }
724}
725
726impl AstNode for MintBlockField {
727    const RULE: Rule = Rule::mint_block_field;
728
729    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
730        match pair.as_rule() {
731            Rule::mint_block_amount => {
732                let pair = pair.into_inner().next().unwrap();
733                let x = MintBlockField::Amount(DataExpr::parse(pair)?.into());
734                Ok(x)
735            }
736            Rule::mint_block_redeemer => {
737                let pair = pair.into_inner().next().unwrap();
738                let x = MintBlockField::Redeemer(DataExpr::parse(pair)?.into());
739                Ok(x)
740            }
741            x => unreachable!("Unexpected rule in output_block_field: {:?}", x),
742        }
743    }
744
745    fn span(&self) -> &Span {
746        match self {
747            Self::Amount(x) => x.span(),
748            Self::Redeemer(x) => x.span(),
749        }
750    }
751}
752
753impl AstNode for SignersBlock {
754    const RULE: Rule = Rule::signers_block;
755
756    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
757        let span = pair.as_span().into();
758        let inner = pair.into_inner();
759
760        let signers = inner
761            .map(|x| DataExpr::parse(x))
762            .collect::<Result<Vec<_>, _>>()?;
763
764        Ok(SignersBlock { signers, span })
765    }
766
767    fn span(&self) -> &Span {
768        &self.span
769    }
770}
771
772impl AstNode for MintBlock {
773    const RULE: Rule = Rule::mint_block;
774
775    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
776        let span = pair.as_span().into();
777        let inner = pair.into_inner();
778
779        let fields = inner
780            .map(|x| MintBlockField::parse(x))
781            .collect::<Result<Vec<_>, _>>()?;
782
783        Ok(MintBlock { fields, span })
784    }
785
786    fn span(&self) -> &Span {
787        &self.span
788    }
789}
790
791impl AstNode for RecordField {
792    const RULE: Rule = Rule::record_field;
793
794    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
795        let span = pair.as_span().into();
796        let mut inner = pair.into_inner();
797        let identifier = Identifier::parse(inner.next().unwrap())?;
798        let r#type = Type::parse(inner.next().unwrap())?;
799
800        Ok(RecordField {
801            name: identifier,
802            r#type,
803            span,
804        })
805    }
806
807    fn span(&self) -> &Span {
808        &self.span
809    }
810}
811
812impl AstNode for PolicyField {
813    const RULE: Rule = Rule::policy_def_field;
814
815    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
816        match pair.as_rule() {
817            Rule::policy_def_hash => Ok(PolicyField::Hash(DataExpr::parse(
818                pair.into_inner().next().unwrap(),
819            )?)),
820            Rule::policy_def_script => Ok(PolicyField::Script(DataExpr::parse(
821                pair.into_inner().next().unwrap(),
822            )?)),
823            Rule::policy_def_ref => Ok(PolicyField::Ref(DataExpr::parse(
824                pair.into_inner().next().unwrap(),
825            )?)),
826            x => unreachable!("Unexpected rule in policy_field: {:?}", x),
827        }
828    }
829
830    fn span(&self) -> &Span {
831        match self {
832            Self::Hash(x) => x.span(),
833            Self::Script(x) => x.span(),
834            Self::Ref(x) => x.span(),
835        }
836    }
837}
838
839impl AstNode for PolicyConstructor {
840    const RULE: Rule = Rule::policy_def_constructor;
841
842    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
843        let span = pair.as_span().into();
844        let inner = pair.into_inner();
845
846        let fields = inner
847            .map(|x| PolicyField::parse(x))
848            .collect::<Result<Vec<_>, _>>()?;
849
850        Ok(PolicyConstructor { fields, span })
851    }
852
853    fn span(&self) -> &Span {
854        &self.span
855    }
856}
857
858impl AstNode for PolicyValue {
859    const RULE: Rule = Rule::policy_def_value;
860
861    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
862        match pair.as_rule() {
863            Rule::policy_def_constructor => {
864                Ok(PolicyValue::Constructor(PolicyConstructor::parse(pair)?))
865            }
866            Rule::policy_def_assign => Ok(PolicyValue::Assign(HexStringLiteral::parse(
867                pair.into_inner().next().unwrap(),
868            )?)),
869            x => unreachable!("Unexpected rule in policy_value: {:?}", x),
870        }
871    }
872
873    fn span(&self) -> &Span {
874        match self {
875            Self::Constructor(x) => x.span(),
876            Self::Assign(x) => x.span(),
877        }
878    }
879}
880
881impl AstNode for PolicyDef {
882    const RULE: Rule = Rule::policy_def;
883
884    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
885        let span = pair.as_span().into();
886        let mut inner = pair.into_inner();
887        let name = Identifier::parse(inner.next().unwrap())?;
888        let value = PolicyValue::parse(inner.next().unwrap())?;
889
890        Ok(PolicyDef { name, value, span })
891    }
892
893    fn span(&self) -> &Span {
894        &self.span
895    }
896}
897
898impl AstNode for AnyAssetConstructor {
899    const RULE: Rule = Rule::any_asset_constructor;
900
901    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
902        let span = pair.as_span().into();
903        let mut inner = pair.into_inner();
904
905        let policy = DataExpr::parse(inner.next().unwrap())?;
906        let asset_name = DataExpr::parse(inner.next().unwrap())?;
907        let amount = DataExpr::parse(inner.next().unwrap())?;
908
909        Ok(AnyAssetConstructor {
910            policy: Box::new(policy),
911            asset_name: Box::new(asset_name),
912            amount: Box::new(amount),
913            span,
914        })
915    }
916
917    fn span(&self) -> &Span {
918        &self.span
919    }
920}
921
922impl AstNode for ConcatOp {
923    const RULE: Rule = Rule::concat_constructor;
924
925    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
926        let span = pair.as_span().into();
927        let mut inner = pair.into_inner();
928
929        let lhs = DataExpr::parse(inner.next().unwrap())?;
930        let rhs = DataExpr::parse(inner.next().unwrap())?;
931
932        Ok(ConcatOp {
933            lhs: Box::new(lhs),
934            rhs: Box::new(rhs),
935            span,
936        })
937    }
938
939    fn span(&self) -> &Span {
940        &self.span
941    }
942}
943
944impl AstNode for crate::ast::FnCall {
945    const RULE: Rule = Rule::fn_call;
946
947    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
948        let span = pair.as_span().into();
949        let mut inner = pair.into_inner();
950
951        let callee = Identifier::parse(inner.next().unwrap())?;
952
953        let mut args = Vec::new();
954        for arg_pair in inner {
955            args.push(DataExpr::parse(arg_pair)?);
956        }
957
958        Ok(crate::ast::FnCall { callee, args, span })
959    }
960
961    fn span(&self) -> &Span {
962        &self.span
963    }
964}
965
966impl AstNode for LetBinding {
967    const RULE: Rule = Rule::let_binding;
968
969    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
970        let span = pair.as_span().into();
971        let mut inner = pair.into_inner();
972
973        let name = Identifier::parse(inner.next().unwrap())?;
974        let value = DataExpr::parse(inner.next().unwrap())?;
975
976        Ok(LetBinding { name, value, span })
977    }
978
979    fn span(&self) -> &Span {
980        &self.span
981    }
982}
983
984impl AstNode for FnBody {
985    const RULE: Rule = Rule::fn_body;
986
987    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
988        let span = pair.as_span().into();
989        let inner = pair.into_inner();
990
991        let mut let_bindings = Vec::new();
992        let mut result_expr = None;
993
994        for pair in inner {
995            match pair.as_rule() {
996                Rule::let_binding => let_bindings.push(LetBinding::parse(pair)?),
997                Rule::data_expr => result_expr = Some(DataExpr::parse(pair)?),
998                x => unreachable!("Unexpected rule in fn_body: {:?}", x),
999            }
1000        }
1001
1002        Ok(FnBody {
1003            let_bindings,
1004            result: Box::new(result_expr.expect("fn_body must have a result expression")),
1005            span,
1006        })
1007    }
1008
1009    fn span(&self) -> &Span {
1010        &self.span
1011    }
1012}
1013
1014impl AstNode for FnDef {
1015    const RULE: Rule = Rule::fn_def;
1016
1017    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1018        let span = pair.as_span().into();
1019        let mut inner = pair.into_inner();
1020
1021        let name = Identifier::parse(inner.next().unwrap())?;
1022        let parameters = ParameterList::parse(inner.next().unwrap())?;
1023        let return_type = Type::parse(inner.next().unwrap())?;
1024        let body = FnBody::parse(inner.next().unwrap())?;
1025
1026        Ok(FnDef {
1027            name,
1028            parameters,
1029            return_type,
1030            body: Some(body),
1031            builtin: None,
1032            span,
1033            scope: None,
1034        })
1035    }
1036
1037    fn span(&self) -> &Span {
1038        &self.span
1039    }
1040}
1041
1042impl AstNode for RecordConstructorField {
1043    const RULE: Rule = Rule::record_constructor_field;
1044
1045    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1046        let span = pair.as_span().into();
1047        let mut inner = pair.into_inner();
1048
1049        let name = Identifier::parse(inner.next().unwrap())?;
1050        let value = DataExpr::parse(inner.next().unwrap())?;
1051
1052        Ok(RecordConstructorField {
1053            name,
1054            value: Box::new(value),
1055            span,
1056        })
1057    }
1058
1059    fn span(&self) -> &Span {
1060        &self.span
1061    }
1062}
1063
1064impl AstNode for UtxoRef {
1065    const RULE: Rule = Rule::utxo_ref;
1066
1067    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1068        let span = pair.as_span().into();
1069        let raw_ref = pair.as_span().as_str()[2..].to_string();
1070        let (raw_txid, raw_output_ix) = raw_ref.split_once("#").expect("Invalid utxo ref");
1071
1072        Ok(UtxoRef {
1073            txid: hex::decode(raw_txid).expect("Invalid hex txid"),
1074            index: raw_output_ix.parse().expect("Invalid output index"),
1075            span,
1076        })
1077    }
1078
1079    fn span(&self) -> &Span {
1080        &self.span
1081    }
1082}
1083
1084impl AstNode for StructConstructor {
1085    const RULE: Rule = Rule::struct_constructor;
1086
1087    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1088        let span = pair.as_span().into();
1089        let mut inner = pair.into_inner();
1090
1091        let r#type = Identifier::parse(inner.next().unwrap())?;
1092        let case = VariantCaseConstructor::parse(inner.next().unwrap())?;
1093
1094        Ok(StructConstructor {
1095            r#type,
1096            case,
1097            scope: None,
1098            span,
1099        })
1100    }
1101
1102    fn span(&self) -> &Span {
1103        &self.span
1104    }
1105}
1106
1107impl VariantCaseConstructor {
1108    fn implicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1109        let span = pair.as_span().into();
1110        let inner = pair.into_inner();
1111
1112        let mut fields = Vec::new();
1113        let mut spread = None;
1114
1115        for pair in inner {
1116            match pair.as_rule() {
1117                Rule::record_constructor_field => {
1118                    fields.push(RecordConstructorField::parse(pair)?);
1119                }
1120                Rule::spread_expression => {
1121                    spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
1122                }
1123                x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
1124            }
1125        }
1126
1127        Ok(VariantCaseConstructor {
1128            name: Identifier::new("Default"),
1129            fields,
1130            spread: spread.map(Box::new),
1131            scope: None,
1132            span,
1133        })
1134    }
1135
1136    fn explicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
1137        let span = pair.as_span().into();
1138        let mut inner = pair.into_inner();
1139
1140        let name = Identifier::parse(inner.next().unwrap())?;
1141
1142        let mut fields = Vec::new();
1143        let mut spread = None;
1144
1145        for pair in inner {
1146            match pair.as_rule() {
1147                Rule::record_constructor_field => {
1148                    fields.push(RecordConstructorField::parse(pair)?);
1149                }
1150                Rule::spread_expression => {
1151                    spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
1152                }
1153                x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
1154            }
1155        }
1156
1157        Ok(VariantCaseConstructor {
1158            name,
1159            fields,
1160            spread: spread.map(Box::new),
1161            scope: None,
1162            span,
1163        })
1164    }
1165}
1166
1167impl AstNode for VariantCaseConstructor {
1168    const RULE: Rule = Rule::variant_case_constructor;
1169
1170    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1171        match pair.as_rule() {
1172            Rule::implicit_variant_case_constructor => Self::implicit_parse(pair),
1173            Rule::explicit_variant_case_constructor => Self::explicit_parse(pair),
1174            x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
1175        }
1176    }
1177
1178    fn span(&self) -> &Span {
1179        &self.span
1180    }
1181}
1182
1183impl AstNode for ListConstructor {
1184    const RULE: Rule = Rule::list_constructor;
1185
1186    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1187        let span = pair.as_span().into();
1188        let inner = pair.into_inner();
1189
1190        let elements = inner.map(DataExpr::parse).collect::<Result<Vec<_>, _>>()?;
1191
1192        Ok(ListConstructor { elements, span })
1193    }
1194
1195    fn span(&self) -> &Span {
1196        &self.span
1197    }
1198}
1199
1200impl AstNode for MapField {
1201    const RULE: Rule = Rule::map_field;
1202
1203    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1204        let span = pair.as_span().into();
1205        let mut inner = pair.into_inner();
1206
1207        let key = DataExpr::parse(inner.next().unwrap())?;
1208        let value = DataExpr::parse(inner.next().unwrap())?;
1209
1210        Ok(MapField { key, value, span })
1211    }
1212
1213    fn span(&self) -> &Span {
1214        &self.span
1215    }
1216}
1217
1218impl AstNode for MapConstructor {
1219    const RULE: Rule = Rule::map_constructor;
1220
1221    fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1222        let span = pair.as_span().into();
1223        let inner = pair.into_inner();
1224
1225        let fields = inner.map(MapField::parse).collect::<Result<Vec<_>, _>>()?;
1226
1227        Ok(MapConstructor { fields, span })
1228    }
1229
1230    fn span(&self) -> &Span {
1231        &self.span
1232    }
1233}
1234
1235impl 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![
1809            Type::Tuple(vec![Type::Int, Type::Int]),
1810            Type::Bytes,
1811        ])
1812    );
1813
1814    input_to_ast_check!(
1815        TypeDef,
1816        "type_def_record",
1817        "type MyRecord {
1818            field1: Int,
1819            field2: Bytes,
1820        }",
1821        TypeDef {
1822            name: Identifier::new("MyRecord"),
1823            cases: vec![VariantCase {
1824                name: Identifier::new("Default"),
1825                fields: vec![
1826                    RecordField::new("field1", Type::Int),
1827                    RecordField::new("field2", Type::Bytes)
1828                ],
1829                span: Span::DUMMY,
1830            }],
1831            span: Span::DUMMY,
1832        }
1833    );
1834
1835    input_to_ast_check!(
1836        TypeDef,
1837        "type_def_variant",
1838        "type MyVariant {
1839            Case1 {
1840                field1: Int,
1841                field2: Bytes,
1842            },
1843            Case2,
1844        }",
1845        TypeDef {
1846            name: Identifier::new("MyVariant"),
1847            cases: vec![
1848                VariantCase {
1849                    name: Identifier::new("Case1"),
1850                    fields: vec![
1851                        RecordField::new("field1", Type::Int),
1852                        RecordField::new("field2", Type::Bytes)
1853                    ],
1854                    span: Span::DUMMY,
1855                },
1856                VariantCase {
1857                    name: Identifier::new("Case2"),
1858                    fields: vec![],
1859                    span: Span::DUMMY,
1860                },
1861            ],
1862            span: Span::DUMMY,
1863        }
1864    );
1865
1866    input_to_ast_check!(
1867        AliasDef,
1868        "type_def_alias",
1869        "type MyAlias = Bytes;",
1870        AliasDef {
1871            name: Identifier::new("MyAlias"),
1872            alias_type: Type::Bytes,
1873            span: Span::DUMMY,
1874        }
1875    );
1876
1877    input_to_ast_check!(
1878        AliasDef,
1879        "type_alias_custom_type",
1880        "type UserAlias = UserType;",
1881        AliasDef {
1882            name: Identifier::new("UserAlias"),
1883            alias_type: Type::Custom(Identifier::new("UserType")),
1884            span: Span::DUMMY,
1885        }
1886    );
1887
1888    input_to_ast_check!(
1889        AliasDef,
1890        "type_alias_list",
1891        "type StringList = List<Bytes>;",
1892        AliasDef {
1893            name: Identifier::new("StringList"),
1894            alias_type: Type::List(Box::new(Type::Bytes)),
1895            span: Span::DUMMY,
1896        }
1897    );
1898
1899    input_to_ast_check!(
1900        AliasDef,
1901        "type_alias_map",
1902        "type StringIntMap = Map<Bytes, Int>;",
1903        AliasDef {
1904            name: Identifier::new("StringIntMap"),
1905            alias_type: Type::Map(Box::new(Type::Bytes), Box::new(Type::Int)),
1906            span: Span::DUMMY,
1907        }
1908    );
1909
1910    input_to_ast_check!(
1911        AliasDef,
1912        "type_alias_complex_nested",
1913        "type ComplexType = List<Map<Bytes, Int>>;",
1914        AliasDef {
1915            name: Identifier::new("ComplexType"),
1916            alias_type: Type::List(Box::new(Type::Map(
1917                Box::new(Type::Bytes),
1918                Box::new(Type::Int)
1919            ))),
1920            span: Span::DUMMY,
1921        }
1922    );
1923
1924    input_to_ast_check!(
1925        AliasDef,
1926        "type_alias_all_primitives",
1927        "type MyInt = Int;",
1928        AliasDef {
1929            name: Identifier::new("MyInt"),
1930            alias_type: Type::Int,
1931            span: Span::DUMMY,
1932        }
1933    );
1934
1935    input_to_ast_check!(
1936        AliasDef,
1937        "type_alias_bool",
1938        "type MyBool = Bool;",
1939        AliasDef {
1940            name: Identifier::new("MyBool"),
1941            alias_type: Type::Bool,
1942            span: Span::DUMMY,
1943        }
1944    );
1945
1946    input_to_ast_check!(
1947        AliasDef,
1948        "type_alias_address",
1949        "type MyAddress = Address;",
1950        AliasDef {
1951            name: Identifier::new("MyAddress"),
1952            alias_type: Type::Address,
1953            span: Span::DUMMY,
1954        }
1955    );
1956
1957    input_to_ast_check!(
1958        AliasDef,
1959        "type_alias_utxo_ref",
1960        "type MyUtxoRef = UtxoRef;",
1961        AliasDef {
1962            name: Identifier::new("MyUtxoRef"),
1963            alias_type: Type::UtxoRef,
1964            span: Span::DUMMY,
1965        }
1966    );
1967
1968    input_to_ast_check!(
1969        AliasDef,
1970        "type_alias_any_asset",
1971        "type MyAsset = AnyAsset;",
1972        AliasDef {
1973            name: Identifier::new("MyAsset"),
1974            alias_type: Type::AnyAsset,
1975            span: Span::DUMMY,
1976        }
1977    );
1978
1979    input_to_ast_check!(
1980        StringLiteral,
1981        "literal_string",
1982        "\"Hello, world!\"",
1983        StringLiteral::new("Hello, world!".to_string())
1984    );
1985
1986    input_to_ast_check!(
1987        HexStringLiteral,
1988        "hex_string",
1989        "0xAFAFAF",
1990        HexStringLiteral::new("AFAFAF".to_string())
1991    );
1992
1993    input_to_ast_check!(
1994        StringLiteral,
1995        "literal_string_address",
1996        "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1997        StringLiteral::new("addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string())
1998    );
1999
2000    input_to_ast_check!(
2001        ListConstructor,
2002        "empty_list",
2003        "[]",
2004        ListConstructor {
2005            elements: vec![],
2006            span: Span::DUMMY,
2007        }
2008    );
2009
2010    input_to_ast_check!(
2011        ListConstructor,
2012        "trailing_comma",
2013        "[1, 2,]",
2014        ListConstructor {
2015            elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2016            span: Span::DUMMY,
2017        }
2018    );
2019
2020    input_to_ast_check!(
2021        ListConstructor,
2022        "int_list",
2023        "[1, 2]",
2024        ListConstructor {
2025            elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2026            span: Span::DUMMY,
2027        }
2028    );
2029
2030    input_to_ast_check!(
2031        ListConstructor,
2032        "string_list",
2033        "[\"Hello\", \"World\"]",
2034        ListConstructor {
2035            elements: vec![
2036                DataExpr::String(StringLiteral::new("Hello".to_string())),
2037                DataExpr::String(StringLiteral::new("World".to_string()))
2038            ],
2039            span: Span::DUMMY,
2040        }
2041    );
2042
2043    input_to_ast_check!(
2044        ListConstructor,
2045        "mixed_list",
2046        "[1, \"Hello\", true]",
2047        ListConstructor {
2048            elements: vec![
2049                DataExpr::Number(1),
2050                DataExpr::String(StringLiteral::new("Hello".to_string())),
2051                DataExpr::Bool(true)
2052            ],
2053            span: Span::DUMMY,
2054        }
2055    );
2056
2057    input_to_ast_check!(
2058        ListConstructor,
2059        "list_within_list",
2060        "[[1, 2], [3, 4]]",
2061        ListConstructor {
2062            elements: vec![
2063                DataExpr::ListConstructor(ListConstructor {
2064                    elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2065                    span: Span::DUMMY,
2066                }),
2067                DataExpr::ListConstructor(ListConstructor {
2068                    elements: vec![DataExpr::Number(3), DataExpr::Number(4),],
2069                    span: Span::DUMMY,
2070                }),
2071            ],
2072            span: Span::DUMMY,
2073        }
2074    );
2075
2076    // A two-or-more element parenthesized list is a tuple literal.
2077    input_to_ast_check!(
2078        DataExpr,
2079        "tuple_literal",
2080        "(1, 0xFF, true)",
2081        DataExpr::TupleConstructor(TupleConstructor {
2082            elements: vec![
2083                DataExpr::Number(1),
2084                DataExpr::HexString(HexStringLiteral::new("FF".to_string())),
2085                DataExpr::Bool(true),
2086            ],
2087            span: Span::DUMMY,
2088        })
2089    );
2090
2091    // Trailing comma is permitted in a tuple literal.
2092    input_to_ast_check!(
2093        DataExpr,
2094        "tuple_literal_trailing_comma",
2095        "(1, 2,)",
2096        DataExpr::TupleConstructor(TupleConstructor {
2097            elements: vec![DataExpr::Number(1), DataExpr::Number(2)],
2098            span: Span::DUMMY,
2099        })
2100    );
2101
2102    // A single parenthesized expression is grouping, NOT a one-tuple.
2103    input_to_ast_check!(DataExpr, "grouping_not_tuple", "(42)", DataExpr::Number(42));
2104
2105    // Positional tuple access uses bracket indexing with a literal index; it
2106    // lowers through the same PropertyOp path as struct/list access.
2107    input_to_ast_check!(
2108        DataExpr,
2109        "tuple_index_access",
2110        "my_tuple[0]",
2111        DataExpr::PropertyOp(PropertyOp {
2112            operand: Box::new(DataExpr::Identifier(Identifier::new("my_tuple"))),
2113            property: Box::new(DataExpr::Number(0)),
2114            span: Span::DUMMY,
2115            scope: None,
2116        })
2117    );
2118
2119    input_to_ast_check!(DataExpr, "literal_bool_true", "true", DataExpr::Bool(true));
2120
2121    input_to_ast_check!(
2122        DataExpr,
2123        "literal_bool_false",
2124        "false",
2125        DataExpr::Bool(false)
2126    );
2127
2128    input_to_ast_check!(DataExpr, "unit_value", "())", DataExpr::Unit);
2129
2130    input_to_ast_check!(DataExpr, "number_value", "123", DataExpr::Number(123));
2131
2132    input_to_ast_check!(
2133        PolicyDef,
2134        "policy_def_assign",
2135        "policy MyPolicy = 0xAFAFAF;",
2136        PolicyDef {
2137            name: Identifier::new("MyPolicy"),
2138            value: PolicyValue::Assign(HexStringLiteral::new("AFAFAF".to_string())),
2139            span: Span::DUMMY,
2140        }
2141    );
2142
2143    input_to_ast_check!(
2144        PolicyDef,
2145        "policy_def_constructor",
2146        "policy MyPolicy {
2147            hash: 0x1234567890,
2148            script: 0x1234567890,
2149            ref: 0x1234567890,
2150        };",
2151        PolicyDef {
2152            name: Identifier::new("MyPolicy"),
2153            value: PolicyValue::Constructor(PolicyConstructor {
2154                fields: vec![
2155                    PolicyField::Hash(DataExpr::HexString(HexStringLiteral::new(
2156                        "1234567890".to_string()
2157                    ))),
2158                    PolicyField::Script(DataExpr::HexString(HexStringLiteral::new(
2159                        "1234567890".to_string()
2160                    ))),
2161                    PolicyField::Ref(DataExpr::HexString(HexStringLiteral::new(
2162                        "1234567890".to_string()
2163                    ))),
2164                ],
2165                span: Span::DUMMY,
2166            }),
2167            span: Span::DUMMY,
2168        }
2169    );
2170
2171    input_to_ast_check!(
2172        AssetDef,
2173        "hex_hex",
2174        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.0xef7a1ceb;",
2175        AssetDef {
2176            name: Identifier::new("MyToken"),
2177            policy: DataExpr::HexString(HexStringLiteral::new(
2178                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
2179            )),
2180            asset_name: DataExpr::HexString(HexStringLiteral::new("ef7a1ceb".to_string())),
2181            span: Span::DUMMY,
2182        }
2183    );
2184
2185    input_to_ast_check!(
2186        AssetDef,
2187        "hex_string",
2188        "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.\"MY TOKEN\";",
2189        AssetDef {
2190            name: Identifier::new("MyToken"),
2191            policy: DataExpr::HexString(HexStringLiteral::new(
2192                "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
2193            )),
2194            asset_name: DataExpr::String(StringLiteral::new("MY TOKEN".to_string())),
2195            span: Span::DUMMY,
2196        }
2197    );
2198
2199    input_to_ast_check!(
2200        DataExpr,
2201        "type_and_literal",
2202        "MyToken(15)",
2203        DataExpr::FnCall(crate::ast::FnCall {
2204            callee: Identifier::new("MyToken"),
2205            args: vec![DataExpr::Number(15)],
2206            span: Span::DUMMY,
2207        })
2208    );
2209
2210    input_to_ast_check!(
2211        AnyAssetConstructor,
2212        "any_asset_constructor",
2213        "AnyAsset(0x1234567890, \"MyToken\", 15)",
2214        AnyAssetConstructor {
2215            policy: Box::new(DataExpr::HexString(HexStringLiteral::new(
2216                "1234567890".to_string()
2217            ))),
2218            asset_name: Box::new(DataExpr::String(StringLiteral::new("MyToken".to_string()))),
2219            amount: Box::new(DataExpr::Number(15)),
2220            span: Span::DUMMY,
2221        }
2222    );
2223
2224    input_to_ast_check!(
2225        AnyAssetConstructor,
2226        "any_asset_identifiers",
2227        "AnyAsset(my_policy, my_token, my_amount)",
2228        AnyAssetConstructor {
2229            policy: Box::new(DataExpr::Identifier(Identifier::new("my_policy"))),
2230            asset_name: Box::new(DataExpr::Identifier(Identifier::new("my_token"))),
2231            amount: Box::new(DataExpr::Identifier(Identifier::new("my_amount"))),
2232            span: Span::DUMMY,
2233        }
2234    );
2235
2236    input_to_ast_check!(
2237        AnyAssetConstructor,
2238        "any_asset_property_access",
2239        "AnyAsset(input1.policy, input1.asset_name, input1.amount)",
2240        AnyAssetConstructor {
2241            policy: Box::new(DataExpr::PropertyOp(PropertyOp {
2242                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2243                property: Box::new(DataExpr::Identifier(Identifier::new("policy"))),
2244                span: Span::DUMMY,
2245                scope: None,
2246            })),
2247            asset_name: Box::new(DataExpr::PropertyOp(PropertyOp {
2248                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2249                property: Box::new(DataExpr::Identifier(Identifier::new("asset_name"))),
2250                span: Span::DUMMY,
2251                scope: None,
2252            })),
2253            amount: Box::new(DataExpr::PropertyOp(PropertyOp {
2254                operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2255                property: Box::new(DataExpr::Identifier(Identifier::new("amount"))),
2256                span: Span::DUMMY,
2257                scope: None,
2258            })),
2259            span: Span::DUMMY,
2260        }
2261    );
2262
2263    input_to_ast_check!(DataExpr, "literal", "5", DataExpr::Number(5));
2264
2265    input_to_ast_check!(
2266        DataExpr,
2267        "add_op",
2268        "5 + var1",
2269        DataExpr::AddOp(AddOp {
2270            lhs: Box::new(DataExpr::Number(5)),
2271            rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2272            span: Span::DUMMY,
2273        })
2274    );
2275
2276    input_to_ast_check!(
2277        DataExpr,
2278        "mul_op",
2279        "5 * var1",
2280        DataExpr::MulOp(MulOp {
2281            lhs: Box::new(DataExpr::Number(5)),
2282            rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2283            span: Span::DUMMY,
2284        })
2285    );
2286
2287    // `*` binds tighter than `+`, so `2 + 3 * 4` parses as `2 + (3 * 4)`.
2288    input_to_ast_check!(
2289        DataExpr,
2290        "mul_binds_tighter_than_add",
2291        "2 + 3 * 4",
2292        DataExpr::AddOp(AddOp {
2293            lhs: Box::new(DataExpr::Number(2)),
2294            rhs: Box::new(DataExpr::MulOp(MulOp {
2295                lhs: Box::new(DataExpr::Number(3)),
2296                rhs: Box::new(DataExpr::Number(4)),
2297                span: Span::DUMMY,
2298            })),
2299            span: Span::DUMMY,
2300        })
2301    );
2302
2303    // ... and on the left, `3 * 4 + 2` parses as `(3 * 4) + 2`.
2304    input_to_ast_check!(
2305        DataExpr,
2306        "mul_left_binds_tighter_than_add",
2307        "3 * 4 + 2",
2308        DataExpr::AddOp(AddOp {
2309            lhs: Box::new(DataExpr::MulOp(MulOp {
2310                lhs: Box::new(DataExpr::Number(3)),
2311                rhs: Box::new(DataExpr::Number(4)),
2312                span: Span::DUMMY,
2313            })),
2314            rhs: Box::new(DataExpr::Number(2)),
2315            span: Span::DUMMY,
2316        })
2317    );
2318
2319    input_to_ast_check!(
2320        DataExpr,
2321        "div_op",
2322        "10 / var1",
2323        DataExpr::DivOp(DivOp {
2324            lhs: Box::new(DataExpr::Number(10)),
2325            rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2326            span: Span::DUMMY,
2327        })
2328    );
2329
2330    // `/` binds tighter than `+`, so `2 + 8 / 4` parses as `2 + (8 / 4)`.
2331    input_to_ast_check!(
2332        DataExpr,
2333        "div_binds_tighter_than_add",
2334        "2 + 8 / 4",
2335        DataExpr::AddOp(AddOp {
2336            lhs: Box::new(DataExpr::Number(2)),
2337            rhs: Box::new(DataExpr::DivOp(DivOp {
2338                lhs: Box::new(DataExpr::Number(8)),
2339                rhs: Box::new(DataExpr::Number(4)),
2340                span: Span::DUMMY,
2341            })),
2342            span: Span::DUMMY,
2343        })
2344    );
2345
2346    // `*` and `/` share precedence and are left-associative, so `8 / 4 * 2`
2347    // parses as `(8 / 4) * 2`.
2348    input_to_ast_check!(
2349        DataExpr,
2350        "div_binds_like_mul",
2351        "8 / 4 * 2",
2352        DataExpr::MulOp(MulOp {
2353            lhs: Box::new(DataExpr::DivOp(DivOp {
2354                lhs: Box::new(DataExpr::Number(8)),
2355                rhs: Box::new(DataExpr::Number(4)),
2356                span: Span::DUMMY,
2357            })),
2358            rhs: Box::new(DataExpr::Number(2)),
2359            span: Span::DUMMY,
2360        })
2361    );
2362
2363    input_to_ast_check!(
2364        DataExpr,
2365        "concat_op",
2366        r#"concat("hello", "world")"#,
2367        DataExpr::ConcatOp(ConcatOp {
2368            lhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
2369                value: "hello".to_string(),
2370                span: ast::Span::DUMMY,
2371            })),
2372            rhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
2373                value: "world".to_string(),
2374                span: ast::Span::DUMMY,
2375            })),
2376            span: ast::Span::DUMMY,
2377        })
2378    );
2379
2380    input_to_ast_check!(
2381        DataExpr,
2382        "property_access",
2383        "subject.property",
2384        DataExpr::PropertyOp(PropertyOp {
2385            operand: Box::new(DataExpr::Identifier(Identifier::new("subject"))),
2386            property: Box::new(DataExpr::Identifier(Identifier::new("property"))),
2387            span: Span::DUMMY,
2388            scope: None,
2389        })
2390    );
2391
2392    input_to_ast_check!(
2393        DataExpr,
2394        "multiple_properties",
2395        "subject.property.subproperty",
2396        DataExpr::PropertyOp(PropertyOp {
2397            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2398                operand: Box::new(DataExpr::Identifier(Identifier::new("subject"))),
2399                property: Box::new(DataExpr::Identifier(Identifier::new("property"))),
2400                span: Span::DUMMY,
2401                scope: None,
2402            })),
2403            property: Box::new(DataExpr::Identifier(Identifier::new("subproperty"))),
2404            span: Span::DUMMY,
2405            scope: None,
2406        })
2407    );
2408
2409    input_to_ast_check!(DataExpr, "empty_parentheses", "()", DataExpr::Unit);
2410
2411    input_to_ast_check!(DataExpr, "nested_parentheses", "((()))", DataExpr::Unit);
2412
2413    input_to_ast_check!(
2414        DataExpr,
2415        "nested_arithmetic_expression",
2416        "(1 + ((6 - 3) + 4))",
2417        DataExpr::AddOp(AddOp {
2418            lhs: Box::new(DataExpr::Number(1)),
2419            rhs: Box::new(DataExpr::AddOp(AddOp {
2420                lhs: Box::new(DataExpr::SubOp(SubOp {
2421                    lhs: Box::new(DataExpr::Number(6)),
2422                    rhs: Box::new(DataExpr::Number(3)),
2423                    span: Span::DUMMY,
2424                })),
2425                rhs: Box::new(DataExpr::Number(4)),
2426                span: Span::DUMMY,
2427            })),
2428            span: Span::DUMMY,
2429        })
2430    );
2431
2432    input_to_ast_check!(
2433        DataExpr,
2434        "negate_op",
2435        "!a",
2436        DataExpr::NegateOp(NegateOp {
2437            operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2438            span: Span::DUMMY,
2439        })
2440    );
2441
2442    input_to_ast_check!(
2443        DataExpr,
2444        "negate_precedence",
2445        "!a.b",
2446        DataExpr::NegateOp(NegateOp {
2447            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2448                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2449                property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2450                span: Span::DUMMY,
2451                scope: None,
2452            })),
2453            span: Span::DUMMY,
2454        })
2455    );
2456
2457    input_to_ast_check!(
2458        DataExpr,
2459        "negate_override_precedence",
2460        "(!a).b",
2461        DataExpr::PropertyOp(PropertyOp {
2462            operand: Box::new(DataExpr::NegateOp(NegateOp {
2463                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2464                span: Span::DUMMY,
2465            })),
2466            property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2467            span: Span::DUMMY,
2468            scope: None,
2469        })
2470    );
2471
2472    input_to_ast_check!(
2473        DataExpr,
2474        "overly_complex",
2475        "(1 + 5) - ((a.b.c - 3) + !d.f)",
2476        DataExpr::SubOp(SubOp {
2477            lhs: Box::new(DataExpr::AddOp(AddOp {
2478                lhs: Box::new(DataExpr::Number(1)),
2479                rhs: Box::new(DataExpr::Number(5)),
2480                span: Span::DUMMY,
2481            })),
2482            rhs: Box::new(DataExpr::AddOp(AddOp {
2483                lhs: Box::new(DataExpr::SubOp(SubOp {
2484                    lhs: Box::new(DataExpr::PropertyOp(PropertyOp {
2485                        operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2486                            operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2487                            property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2488                            span: Span::DUMMY,
2489                            scope: None,
2490                        })),
2491                        property: Box::new(DataExpr::Identifier(Identifier::new("c"))),
2492                        span: Span::DUMMY,
2493                        scope: None,
2494                    })),
2495                    rhs: Box::new(DataExpr::Number(3)),
2496                    span: Span::DUMMY,
2497                })),
2498                rhs: Box::new(DataExpr::NegateOp(NegateOp {
2499                    operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2500                        operand: Box::new(DataExpr::Identifier(Identifier::new("d"))),
2501                        property: Box::new(DataExpr::Identifier(Identifier::new("f"))),
2502                        span: Span::DUMMY,
2503                        scope: None,
2504                    })),
2505                    span: Span::DUMMY,
2506                })),
2507
2508                span: Span::DUMMY,
2509            })),
2510            span: Span::DUMMY,
2511        })
2512    );
2513
2514    input_to_ast_check!(
2515        DataExpr,
2516        "min_utxo_basic",
2517        "min_utxo(output1)",
2518        DataExpr::FnCall(crate::ast::FnCall {
2519            callee: Identifier::new("min_utxo"),
2520            args: vec![DataExpr::Identifier(Identifier::new("output1"))],
2521            span: Span::DUMMY,
2522        })
2523    );
2524
2525    input_to_ast_check!(
2526        DataExpr,
2527        "min_utxo_in_expression",
2528        "Ada(100) + min_utxo(my_output)",
2529        DataExpr::AddOp(AddOp {
2530            lhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2531                callee: Identifier::new("Ada"),
2532                args: vec![DataExpr::Number(100)],
2533                span: Span::DUMMY,
2534            })),
2535            rhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2536                callee: Identifier::new("min_utxo"),
2537                args: vec![DataExpr::Identifier(Identifier::new("my_output"))],
2538                span: Span::DUMMY,
2539            })),
2540            span: Span::DUMMY,
2541        })
2542    );
2543
2544    input_to_ast_check!(
2545        DataExpr,
2546        "tip_slot_basic",
2547        "tip_slot()",
2548        DataExpr::FnCall(crate::ast::FnCall {
2549            callee: Identifier::new("tip_slot"),
2550            args: vec![],
2551            span: Span::DUMMY,
2552        })
2553    );
2554
2555    input_to_ast_check!(
2556        DataExpr,
2557        "tip_slot_in_expression",
2558        "1000 + tip_slot()",
2559        DataExpr::AddOp(AddOp {
2560            lhs: Box::new(DataExpr::Number(1000)),
2561            rhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2562                callee: Identifier::new("tip_slot"),
2563                args: vec![],
2564                span: Span::DUMMY,
2565            })),
2566            span: Span::DUMMY,
2567        })
2568    );
2569
2570    input_to_ast_check!(
2571        StructConstructor,
2572        "struct_constructor_record",
2573        "MyRecord {
2574            field1: 10,
2575            field2: abc,
2576        }",
2577        StructConstructor {
2578            r#type: Identifier::new("MyRecord"),
2579            case: VariantCaseConstructor {
2580                name: Identifier::new("Default"),
2581                fields: vec![
2582                    RecordConstructorField {
2583                        name: Identifier::new("field1"),
2584                        value: Box::new(DataExpr::Number(10)),
2585                        span: Span::DUMMY,
2586                    },
2587                    RecordConstructorField {
2588                        name: Identifier::new("field2"),
2589                        value: Box::new(DataExpr::Identifier(Identifier::new("abc"))),
2590                        span: Span::DUMMY,
2591                    },
2592                ],
2593                spread: None,
2594                scope: None,
2595                span: Span::DUMMY,
2596            },
2597            scope: None,
2598            span: Span::DUMMY,
2599        }
2600    );
2601
2602    input_to_ast_check!(
2603        StructConstructor,
2604        "struct_constructor_variant",
2605        "ShipCommand::MoveShip {
2606            delta_x: delta_x,
2607            delta_y: delta_y,
2608        }",
2609        StructConstructor {
2610            r#type: Identifier::new("ShipCommand"),
2611            case: VariantCaseConstructor {
2612                name: Identifier::new("MoveShip"),
2613                fields: vec![
2614                    RecordConstructorField {
2615                        name: Identifier::new("delta_x"),
2616                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
2617                        span: Span::DUMMY,
2618                    },
2619                    RecordConstructorField {
2620                        name: Identifier::new("delta_y"),
2621                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
2622                        span: Span::DUMMY,
2623                    },
2624                ],
2625                spread: None,
2626                scope: None,
2627                span: Span::DUMMY,
2628            },
2629            scope: None,
2630            span: Span::DUMMY,
2631        }
2632    );
2633
2634    input_to_ast_check!(
2635        StructConstructor,
2636        "struct_constructor_variant_with_spread",
2637        "ShipCommand::MoveShip {
2638            delta_x: delta_x,
2639            delta_y: delta_y,
2640            ...abc
2641        }",
2642        StructConstructor {
2643            r#type: Identifier::new("ShipCommand"),
2644            case: VariantCaseConstructor {
2645                name: Identifier::new("MoveShip"),
2646                fields: vec![
2647                    RecordConstructorField {
2648                        name: Identifier::new("delta_x"),
2649                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
2650                        span: Span::DUMMY,
2651                    },
2652                    RecordConstructorField {
2653                        name: Identifier::new("delta_y"),
2654                        value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
2655                        span: Span::DUMMY,
2656                    },
2657                ],
2658                spread: Some(Box::new(DataExpr::Identifier(Identifier::new(
2659                    "abc".to_string()
2660                )))),
2661                scope: None,
2662                span: Span::DUMMY,
2663            },
2664            scope: None,
2665            span: Span::DUMMY,
2666        }
2667    );
2668
2669    input_to_ast_check!(
2670        LocalsBlock,
2671        "basic",
2672        "locals {
2673            a: 10,
2674        }",
2675        LocalsBlock {
2676            assigns: vec![LocalsAssign {
2677                name: Identifier::new("a"),
2678                value: DataExpr::Number(10),
2679                span: Span::DUMMY,
2680            },],
2681            span: Span::DUMMY,
2682        }
2683    );
2684
2685    input_to_ast_check!(
2686        LocalsBlock,
2687        "multiple",
2688        "locals {
2689            a: 10,
2690            b: 20,
2691        }",
2692        LocalsBlock {
2693            assigns: vec![
2694                LocalsAssign {
2695                    name: Identifier::new("a"),
2696                    value: DataExpr::Number(10),
2697                    span: Span::DUMMY,
2698                },
2699                LocalsAssign {
2700                    name: Identifier::new("b"),
2701                    value: DataExpr::Number(20),
2702                    span: Span::DUMMY,
2703                },
2704            ],
2705            span: Span::DUMMY,
2706        }
2707    );
2708
2709    input_to_ast_check!(
2710        LocalsBlock,
2711        "complex_expression",
2712        "locals {
2713            a: (10 + 20) - 8,
2714            b: a.b.c + (5 - d),
2715        }",
2716        LocalsBlock {
2717            assigns: vec![
2718                LocalsAssign {
2719                    name: Identifier::new("a"),
2720                    value: DataExpr::SubOp(SubOp {
2721                        lhs: Box::new(DataExpr::AddOp(AddOp {
2722                            lhs: Box::new(DataExpr::Number(10)),
2723                            rhs: Box::new(DataExpr::Number(20)),
2724                            span: Span::DUMMY,
2725                        })),
2726                        rhs: Box::new(DataExpr::Number(8)),
2727                        span: Span::DUMMY,
2728                    }),
2729                    span: Span::DUMMY,
2730                },
2731                LocalsAssign {
2732                    name: Identifier::new("b"),
2733                    value: DataExpr::AddOp(AddOp {
2734                        lhs: Box::new(DataExpr::PropertyOp(PropertyOp {
2735                            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2736                                operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2737                                property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2738                                span: Span::DUMMY,
2739                                scope: None,
2740                            })),
2741                            property: Box::new(DataExpr::Identifier(Identifier::new("c"))),
2742                            span: Span::DUMMY,
2743                            scope: None,
2744                        })),
2745                        rhs: Box::new(DataExpr::SubOp(SubOp {
2746                            lhs: Box::new(DataExpr::Number(5)),
2747                            rhs: Box::new(DataExpr::Identifier(Identifier::new("d"))),
2748                            span: Span::DUMMY,
2749                        })),
2750                        span: Span::DUMMY,
2751                    }),
2752                    span: Span::DUMMY,
2753                },
2754            ],
2755            span: Span::DUMMY,
2756        }
2757    );
2758
2759    input_to_ast_check!(
2760        InputBlock,
2761        "single",
2762        r#"input source {}"#,
2763        InputBlock {
2764            many: false,
2765            name: "source".to_string(),
2766            fields: vec![],
2767            span: Span::DUMMY,
2768        }
2769    );
2770
2771    input_to_ast_check!(
2772        InputBlock,
2773        "multiple",
2774        r#"input* source {}"#,
2775        InputBlock {
2776            many: true,
2777            name: "source".to_string(),
2778            fields: vec![],
2779            span: Span::DUMMY,
2780        }
2781    );
2782
2783    input_to_ast_check!(
2784        OutputBlock,
2785        "output_block_anonymous",
2786        r#"output {
2787            to: my_party,
2788            amount: Ada(100),
2789        }"#,
2790        OutputBlock {
2791            name: None,
2792            optional: false,
2793            fields: vec![
2794                OutputBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
2795                    "my_party".to_string(),
2796                )))),
2797                OutputBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
2798                    callee: Identifier::new("Ada"),
2799                    args: vec![DataExpr::Number(100)],
2800                    span: Span::DUMMY,
2801                }))),
2802            ],
2803            span: Span::DUMMY,
2804        }
2805    );
2806
2807    input_to_ast_check!(
2808        ChainSpecificBlock,
2809        "chain_specific_block_cardano",
2810        "cardano::vote_delegation_certificate {
2811            drep: 0x1234567890,
2812            stake: 0x1234567890,
2813        }",
2814        ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::VoteDelegationCertificate(
2815            crate::cardano::VoteDelegationCertificate {
2816                drep: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
2817                stake: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
2818                span: Span::DUMMY,
2819            },
2820        ))
2821    );
2822
2823    input_to_ast_check!(
2824        ChainSpecificBlock,
2825        "chain_specific_block_cardano_treasury",
2826        "cardano::treasury_donation {
2827           coin: 20,
2828        }",
2829        ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::TreasuryDonation(
2830            crate::cardano::TreasuryDonationBlock {
2831                coin: DataExpr::Number(20),
2832                span: Span::DUMMY,
2833            },
2834        ))
2835    );
2836
2837    input_to_ast_check!(
2838        EnvDef,
2839        "basic",
2840        "env {
2841            field_a: Int,
2842            field_b: Bytes,
2843        }",
2844        EnvDef {
2845            fields: vec![
2846                EnvField {
2847                    name: "field_a".to_string(),
2848                    r#type: Type::Int,
2849                    docstring: None,
2850                    span: Span::DUMMY,
2851                },
2852                EnvField {
2853                    name: "field_b".to_string(),
2854                    r#type: Type::Bytes,
2855                    docstring: None,
2856                    span: Span::DUMMY,
2857                },
2858            ],
2859            span: Span::DUMMY,
2860        }
2861    );
2862
2863    input_to_ast_check!(
2864        TxDef,
2865        "empty",
2866        "tx my_tx() {}",
2867        TxDef {
2868            name: Identifier::new("my_tx"),
2869            docstring: None,
2870            parameters: ParameterList {
2871                parameters: vec![],
2872                span: Span::DUMMY,
2873            },
2874            locals: None,
2875            references: vec![],
2876            inputs: vec![],
2877            outputs: vec![],
2878            validity: None,
2879            mints: vec![],
2880            burns: vec![],
2881            signers: None,
2882            adhoc: vec![],
2883            collateral: vec![],
2884            metadata: None,
2885            scope: None,
2886            span: Span::DUMMY,
2887        }
2888    );
2889
2890    input_to_ast_check!(
2891        TxDef,
2892        "with_parameters",
2893        "tx my_tx(a: Int, b: Bytes) {}",
2894        TxDef {
2895            name: Identifier::new("my_tx"),
2896            docstring: None,
2897            parameters: ParameterList {
2898                parameters: vec![
2899                    ParamDef {
2900                        name: Identifier::new("a"),
2901                        r#type: Type::Int,
2902                        docstring: None,
2903                    },
2904                    ParamDef {
2905                        name: Identifier::new("b"),
2906                        r#type: Type::Bytes,
2907                        docstring: None,
2908                    },
2909                ],
2910                span: Span::DUMMY,
2911            },
2912            locals: None,
2913            references: vec![],
2914            inputs: vec![],
2915            outputs: vec![],
2916            validity: None,
2917            mints: vec![],
2918            burns: vec![],
2919            signers: None,
2920            adhoc: vec![],
2921            collateral: vec![],
2922            metadata: None,
2923            scope: None,
2924            span: Span::DUMMY,
2925        }
2926    );
2927
2928    input_to_ast_check!(
2929        Program,
2930        "basic",
2931        "party Abc; tx my_tx() {}",
2932        Program {
2933            parties: vec![PartyDef {
2934                name: Identifier::new("Abc"),
2935                docstring: None,
2936                span: Span::DUMMY,
2937            }],
2938            types: vec![],
2939            aliases: vec![],
2940            txs: vec![TxDef {
2941                name: Identifier::new("my_tx"),
2942                docstring: None,
2943                parameters: ParameterList {
2944                    parameters: vec![],
2945                    span: Span::DUMMY,
2946                },
2947                locals: None,
2948                references: vec![],
2949                inputs: vec![],
2950                outputs: vec![],
2951                validity: None,
2952                mints: vec![],
2953                burns: vec![],
2954                signers: None,
2955                adhoc: vec![],
2956                collateral: vec![],
2957                metadata: None,
2958                scope: None,
2959                span: Span::DUMMY,
2960            }],
2961            env: None,
2962            assets: vec![],
2963            policies: vec![],
2964            functions: vec![],
2965            span: Span::DUMMY,
2966            scope: None,
2967        }
2968    );
2969
2970    input_to_ast_check!(
2971        DataExpr,
2972        "array_index_literal",
2973        "my_list[0]",
2974        DataExpr::PropertyOp(PropertyOp {
2975            operand: Box::new(DataExpr::Identifier(Identifier::new("my_list"))),
2976            property: Box::new(DataExpr::Number(0)),
2977            span: Span::DUMMY,
2978            scope: None,
2979        })
2980    );
2981
2982    input_to_ast_check!(
2983        DataExpr,
2984        "array_index_variable",
2985        "my_list[index]",
2986        DataExpr::PropertyOp(PropertyOp {
2987            operand: Box::new(DataExpr::Identifier(Identifier::new("my_list"))),
2988            property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
2989            span: Span::DUMMY,
2990            scope: None,
2991        })
2992    );
2993
2994    input_to_ast_check!(
2995        DataExpr,
2996        "nested_array_index",
2997        "matrix[row][col]",
2998        DataExpr::PropertyOp(PropertyOp {
2999            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3000                operand: Box::new(DataExpr::Identifier(Identifier::new("matrix"))),
3001                property: Box::new(DataExpr::Identifier(Identifier::new("row"))),
3002                span: Span::DUMMY,
3003                scope: None,
3004            })),
3005            property: Box::new(DataExpr::Identifier(Identifier::new("col"))),
3006            span: Span::DUMMY,
3007            scope: None,
3008        })
3009    );
3010
3011    input_to_ast_check!(
3012        DataExpr,
3013        "array_index_with_property_access",
3014        "items[index].name",
3015        DataExpr::PropertyOp(PropertyOp {
3016            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3017                operand: Box::new(DataExpr::Identifier(Identifier::new("items"))),
3018                property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
3019                span: Span::DUMMY,
3020                scope: None,
3021            })),
3022            property: Box::new(DataExpr::Identifier(Identifier::new("name"))),
3023            span: Span::DUMMY,
3024            scope: None,
3025        })
3026    );
3027
3028    input_to_ast_check!(
3029        DataExpr,
3030        "property_access_then_array_index",
3031        "object.list[0]",
3032        DataExpr::PropertyOp(PropertyOp {
3033            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3034                operand: Box::new(DataExpr::Identifier(Identifier::new("object"))),
3035                property: Box::new(DataExpr::Identifier(Identifier::new("list"))),
3036                span: Span::DUMMY,
3037                scope: None,
3038            })),
3039            property: Box::new(DataExpr::Number(0)),
3040            span: Span::DUMMY,
3041            scope: None,
3042        })
3043    );
3044
3045    input_to_ast_check!(
3046        DataExpr,
3047        "array_index_with_function_call",
3048        "values[min_utxo(output)]",
3049        DataExpr::PropertyOp(PropertyOp {
3050            operand: Box::new(DataExpr::Identifier(Identifier::new("values"))),
3051            property: Box::new(DataExpr::FnCall(crate::ast::FnCall {
3052                callee: Identifier::new("min_utxo"),
3053                args: vec![DataExpr::Identifier(Identifier::new("output"))],
3054                span: Span::DUMMY,
3055            })),
3056            span: Span::DUMMY,
3057            scope: None,
3058        })
3059    );
3060
3061    input_to_ast_check!(
3062        DataExpr,
3063        "mixed_property_and_index_access",
3064        "container.items[index].metadata[\"key\"]",
3065        DataExpr::PropertyOp(PropertyOp {
3066            operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3067                operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3068                    operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3069                        operand: Box::new(DataExpr::Identifier(Identifier::new("container"))),
3070                        property: Box::new(DataExpr::Identifier(Identifier::new("items"))),
3071                        span: Span::DUMMY,
3072                        scope: None,
3073                    })),
3074                    property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
3075                    span: Span::DUMMY,
3076                    scope: None,
3077                })),
3078                property: Box::new(DataExpr::Identifier(Identifier::new("metadata"))),
3079                span: Span::DUMMY,
3080                scope: None,
3081            })),
3082            property: Box::new(DataExpr::String(StringLiteral::new("key".to_string()))),
3083            span: Span::DUMMY,
3084            scope: None,
3085        })
3086    );
3087
3088    input_to_ast_check!(
3089        ReferenceBlock,
3090        "with_datum_type",
3091        "reference oracle_data { ref: oracle_utxo, datum_is: OracleDatum, }",
3092        ReferenceBlock {
3093            name: "oracle_data".to_string(),
3094            r#ref: DataExpr::Identifier(Identifier::new("oracle_utxo")),
3095            datum_is: Some(Type::Custom(Identifier::new("OracleDatum"))),
3096            span: Span::DUMMY,
3097        }
3098    );
3099
3100    input_to_ast_check!(
3101        MapConstructor,
3102        "empty",
3103        "{}",
3104        MapConstructor {
3105            fields: vec![],
3106            span: Span::DUMMY,
3107        }
3108    );
3109
3110    input_to_ast_check!(
3111        PartyDef,
3112        "with_docstring",
3113        "/// the protocol treasury\nparty Treasury;",
3114        PartyDef {
3115            name: Identifier::new("Treasury"),
3116            docstring: Some("the protocol treasury".to_string()),
3117            span: Span::DUMMY,
3118        }
3119    );
3120
3121    input_to_ast_check!(
3122        EnvField,
3123        "with_docstring",
3124        "/// network magic\nnetwork: Int",
3125        EnvField {
3126            name: "network".to_string(),
3127            r#type: Type::Int,
3128            docstring: Some("network magic".to_string()),
3129            span: Span::DUMMY,
3130        }
3131    );
3132
3133    input_to_ast_check!(
3134        TxDef,
3135        "with_multiline_docstring",
3136        "/// transfer funds from a to b\n/// across two lines\ntx transfer() {}",
3137        TxDef {
3138            name: Identifier::new("transfer"),
3139            docstring: Some("transfer funds from a to b\nacross two lines".to_string()),
3140            parameters: ParameterList {
3141                parameters: vec![],
3142                span: Span::DUMMY,
3143            },
3144            locals: None,
3145            references: vec![],
3146            inputs: vec![],
3147            outputs: vec![],
3148            validity: None,
3149            mints: vec![],
3150            burns: vec![],
3151            signers: None,
3152            adhoc: vec![],
3153            collateral: vec![],
3154            metadata: None,
3155            scope: None,
3156            span: Span::DUMMY,
3157        }
3158    );
3159
3160    input_to_ast_check!(
3161        ParameterList,
3162        "with_param_docstrings",
3163        "(/// amount in lovelace\nquantity: Int, target: Address)",
3164        ParameterList {
3165            parameters: vec![
3166                ParamDef {
3167                    name: Identifier::new("quantity"),
3168                    r#type: Type::Int,
3169                    docstring: Some("amount in lovelace".to_string()),
3170                },
3171                ParamDef {
3172                    name: Identifier::new("target"),
3173                    r#type: Type::Address,
3174                    docstring: None,
3175                },
3176            ],
3177            span: Span::DUMMY,
3178        }
3179    );
3180
3181    #[test]
3182    fn test_spans_are_respected() {
3183        let program = parse_well_known_example("spans");
3184        assert_eq!(program.span, Span::new(0, 759));
3185
3186        assert_eq!(program.parties[0].span, Span::new(27, 41));
3187
3188        assert_eq!(program.types[0].span, Span::new(43, 77));
3189    }
3190
3191    fn make_snapshot_if_missing(example: &str, program: &Program) {
3192        let manifest_dir = env!("CARGO_MANIFEST_DIR");
3193        let path = format!("{}/../../examples/{}.ast", manifest_dir, example);
3194
3195        if !std::fs::exists(&path).unwrap() {
3196            let ast = serde_json::to_string_pretty(program).unwrap();
3197            std::fs::write(&path, ast).unwrap();
3198        }
3199    }
3200
3201    fn test_parsing_example(example: &str) {
3202        let program = parse_well_known_example(example);
3203        make_snapshot_if_missing(example, &program);
3204
3205        let manifest_dir = env!("CARGO_MANIFEST_DIR");
3206        let ast_file = format!("{}/../../examples/{}.ast", manifest_dir, example);
3207        let ast = std::fs::read_to_string(ast_file).unwrap();
3208
3209        let expected: Program = serde_json::from_str(&ast).unwrap();
3210
3211        assert_json_eq!(program, expected);
3212    }
3213
3214    #[macro_export]
3215    macro_rules! test_parsing {
3216        ($name:ident) => {
3217            paste! {
3218                #[test]
3219                fn [<test_example_ $name>]() {
3220                    test_parsing_example(stringify!($name));
3221                }
3222            }
3223        };
3224    }
3225
3226    test_parsing!(lang_tour);
3227
3228    test_parsing!(tuples);
3229
3230    test_parsing!(transfer);
3231
3232    test_parsing!(swap);
3233
3234    test_parsing!(asteria);
3235
3236    test_parsing!(vesting);
3237
3238    test_parsing!(faucet);
3239
3240    test_parsing!(disordered);
3241
3242    test_parsing!(input_datum);
3243
3244    test_parsing!(withdrawal);
3245
3246    test_parsing!(env_vars);
3247
3248    test_parsing!(local_vars);
3249
3250    test_parsing!(cardano_witness);
3251
3252    test_parsing!(reference_script);
3253
3254    test_parsing!(map);
3255
3256    test_parsing!(burn);
3257
3258    test_parsing!(donation);
3259
3260    test_parsing!(list_concat);
3261
3262    test_parsing!(buidler_fest_2026);
3263
3264    test_parsing!(functions);
3265
3266    test_parsing!(nested_functions);
3267}