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