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