Skip to main content

tx3_lang/
parsing.rs

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