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