1use miette::SourceOffset;
6use pest::{iterators::Pair, Parser};
7use pest_derive::Parser;
8
9use crate::ast::*;
10
11#[derive(Parser)]
12#[grammar = "tx3.pest"]
13pub(crate) struct Tx3Grammar;
14
15#[derive(Debug, thiserror::Error, miette::Diagnostic)]
16#[error("Parsing error: {message}")]
17#[diagnostic(code(tx3::parsing))]
18pub struct Error {
19 pub message: String,
20
21 #[source_code]
22 pub src: String,
23
24 #[label]
25 pub span: Span,
26}
27
28impl From<pest::error::Error<Rule>> for Error {
29 fn from(error: pest::error::Error<Rule>) -> Self {
30 match &error.variant {
31 pest::error::ErrorVariant::ParsingError { positives, .. } => Error {
32 message: format!("expected {:?}", positives),
33 src: error.line().to_string(),
34 span: error.location.into(),
35 },
36 pest::error::ErrorVariant::CustomError { message } => Error {
37 message: message.clone(),
38 src: error.line().to_string(),
39 span: error.location.into(),
40 },
41 }
42 }
43}
44
45impl From<pest::error::InputLocation> for Span {
46 fn from(value: pest::error::InputLocation) -> Self {
47 match value {
48 pest::error::InputLocation::Pos(pos) => Self::new(pos, pos),
49 pest::error::InputLocation::Span((start, end)) => Self::new(start, end),
50 }
51 }
52}
53
54impl From<pest::Span<'_>> for Span {
55 fn from(span: pest::Span<'_>) -> Self {
56 Self::new(span.start(), span.end())
57 }
58}
59
60impl From<Span> for miette::SourceSpan {
61 fn from(span: Span) -> Self {
62 miette::SourceSpan::new(SourceOffset::from(span.start), span.end - span.start)
63 }
64}
65
66pub trait AstNode: Sized {
67 const RULE: Rule;
68
69 fn parse(pair: Pair<Rule>) -> Result<Self, Error>;
70
71 fn span(&self) -> &Span;
72}
73
74impl AstNode for Program {
75 const RULE: Rule = Rule::program;
76
77 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
78 let span = pair.as_span().into();
79 let inner = pair.into_inner();
80
81 let mut program = Self {
82 txs: Vec::new(),
83 assets: Vec::new(),
84 types: Vec::new(),
85 parties: Vec::new(),
86 policies: Vec::new(),
87 scope: None,
88 span,
89 };
90
91 for pair in inner {
92 match pair.as_rule() {
93 Rule::tx_def => program.txs.push(TxDef::parse(pair)?),
94 Rule::asset_def => program.assets.push(AssetDef::parse(pair)?),
95 Rule::record_def => program.types.push(TypeDef::parse(pair)?),
96 Rule::variant_def => program.types.push(TypeDef::parse(pair)?),
97 Rule::party_def => program.parties.push(PartyDef::parse(pair)?),
98 Rule::policy_def => program.policies.push(PolicyDef::parse(pair)?),
99 Rule::EOI => break,
100 x => unreachable!("Unexpected rule in program: {:?}", x),
101 }
102 }
103
104 Ok(program)
105 }
106
107 fn span(&self) -> &Span {
108 &self.span
109 }
110}
111
112impl AstNode for ParameterList {
113 const RULE: Rule = Rule::parameter_list;
114
115 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
116 let span = pair.as_span().into();
117 let inner = pair.into_inner();
118
119 let mut parameters = Vec::new();
120
121 for param in inner {
122 let mut inner = param.into_inner();
123 let name = inner.next().unwrap().as_str().to_string();
124 let r#type = Type::parse(inner.next().unwrap())?;
125
126 parameters.push(ParamDef { name, r#type });
127 }
128
129 Ok(ParameterList { parameters, span })
130 }
131
132 fn span(&self) -> &Span {
133 &self.span
134 }
135}
136
137impl AstNode for TxDef {
138 const RULE: Rule = Rule::tx_def;
139
140 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
141 let span = pair.as_span().into();
142 let mut inner = pair.into_inner();
143
144 let name = inner.next().unwrap().as_str().to_string();
145 let parameters = ParameterList::parse(inner.next().unwrap())?;
146
147 let mut inputs = Vec::new();
148 let mut outputs = Vec::new();
149 let mut burn = None;
150 let mut mint = None;
151 let mut adhoc = Vec::new();
152
153 for item in inner {
154 match item.as_rule() {
155 Rule::input_block => inputs.push(InputBlock::parse(item)?),
156 Rule::output_block => outputs.push(OutputBlock::parse(item)?),
157 Rule::burn_block => burn = Some(BurnBlock::parse(item)?),
158 Rule::mint_block => mint = Some(MintBlock::parse(item)?),
159 Rule::chain_specific_block => adhoc.push(ChainSpecificBlock::parse(item)?),
160 x => unreachable!("Unexpected rule in tx_def: {:?}", x),
161 }
162 }
163
164 Ok(TxDef {
165 name,
166 parameters,
167 inputs,
168 outputs,
169 burn,
170 mint,
171 adhoc,
172 scope: None,
173 span,
174 })
175 }
176
177 fn span(&self) -> &Span {
178 &self.span
179 }
180}
181
182impl AstNode for Identifier {
183 const RULE: Rule = Rule::identifier;
184
185 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
186 Ok(Identifier {
187 value: pair.as_str().to_string(),
188 symbol: None,
189 span: pair.as_span().into(),
190 })
191 }
192
193 fn span(&self) -> &Span {
194 &self.span
195 }
196}
197
198impl AstNode for StringLiteral {
199 const RULE: Rule = Rule::string;
200
201 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
202 Ok(StringLiteral {
203 value: pair.as_str()[1..pair.as_str().len() - 1].to_string(),
204 span: pair.as_span().into(),
205 })
206 }
207
208 fn span(&self) -> &Span {
209 &self.span
210 }
211}
212
213impl AstNode for HexStringLiteral {
214 const RULE: Rule = Rule::hex_string;
215
216 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
217 Ok(HexStringLiteral {
218 value: pair.as_str()[2..].to_string(),
219 span: pair.as_span().into(),
220 })
221 }
222
223 fn span(&self) -> &Span {
224 &self.span
225 }
226}
227
228impl AstNode for PartyDef {
229 const RULE: Rule = Rule::party_def;
230
231 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
232 let span = pair.as_span().into();
233 let mut inner = pair.into_inner();
234 let identifier = inner.next().unwrap().as_str().to_string();
235
236 Ok(PartyDef {
237 name: identifier,
238 span,
239 })
240 }
241
242 fn span(&self) -> &Span {
243 &self.span
244 }
245}
246
247impl AstNode for InputBlockField {
248 const RULE: Rule = Rule::input_block_field;
249
250 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
251 match pair.as_rule() {
252 Rule::input_block_from => {
253 let pair = pair.into_inner().next().unwrap();
254 let x = InputBlockField::From(AddressExpr::parse(pair)?);
255 Ok(x)
256 }
257 Rule::input_block_datum_is => {
258 let pair = pair.into_inner().next().unwrap();
259 let x = InputBlockField::DatumIs(Type::parse(pair)?);
260 Ok(x)
261 }
262 Rule::input_block_min_amount => {
263 let pair = pair.into_inner().next().unwrap();
264 let x = InputBlockField::MinAmount(AssetExpr::parse(pair)?);
265 Ok(x)
266 }
267 Rule::input_block_redeemer => {
268 let pair = pair.into_inner().next().unwrap();
269 let x = InputBlockField::Redeemer(DataExpr::parse(pair)?);
270 Ok(x)
271 }
272 Rule::input_block_ref => {
273 let pair = pair.into_inner().next().unwrap();
274 let x = InputBlockField::Ref(DataExpr::parse(pair)?);
275 Ok(x)
276 }
277 x => unreachable!("Unexpected rule in input_block: {:?}", x),
278 }
279 }
280
281 fn span(&self) -> &Span {
282 match self {
283 Self::From(x) => x.span(),
284 Self::DatumIs(x) => x.span(),
285 Self::MinAmount(x) => x.span(),
286 Self::Redeemer(x) => x.span(),
287 Self::Ref(x) => x.span(),
288 }
289 }
290}
291
292impl AstNode for InputBlock {
293 const RULE: Rule = Rule::input_block;
294
295 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
296 let span = pair.as_span().into();
297 let mut inner = pair.into_inner();
298
299 let name = inner.next().unwrap().as_str().to_string();
300
301 let fields = inner
302 .map(|x| InputBlockField::parse(x))
303 .collect::<Result<Vec<_>, _>>()?;
304
305 Ok(InputBlock {
306 name,
307 is_many: false,
308 fields,
309 span,
310 })
311 }
312
313 fn span(&self) -> &Span {
314 &self.span
315 }
316}
317
318impl AstNode for OutputBlockField {
319 const RULE: Rule = Rule::output_block_field;
320
321 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
322 match pair.as_rule() {
323 Rule::output_block_to => {
324 let pair = pair.into_inner().next().unwrap();
325 let x = OutputBlockField::To(Box::new(AddressExpr::parse(pair)?));
326 Ok(x)
327 }
328 Rule::output_block_amount => {
329 let pair = pair.into_inner().next().unwrap();
330 let x = OutputBlockField::Amount(AssetExpr::parse(pair)?.into());
331 Ok(x)
332 }
333 Rule::output_block_datum => {
334 let pair = pair.into_inner().next().unwrap();
335 let x = OutputBlockField::Datum(DataExpr::parse(pair)?.into());
336 Ok(x)
337 }
338 x => unreachable!("Unexpected rule in output_block_field: {:?}", x),
339 }
340 }
341
342 fn span(&self) -> &Span {
343 match self {
344 Self::To(x) => x.span(),
345 Self::Amount(x) => x.span(),
346 Self::Datum(x) => x.span(),
347 }
348 }
349}
350
351impl AstNode for OutputBlock {
352 const RULE: Rule = Rule::output_block;
353
354 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
355 let span = pair.as_span().into();
356 let mut inner = pair.into_inner();
357
358 let has_name = inner
359 .peek()
360 .map(|x| x.as_rule() == Rule::identifier)
361 .unwrap_or_default();
362
363 let name = has_name.then(|| inner.next().unwrap().as_str().to_string());
364
365 let fields = inner
366 .map(|x| OutputBlockField::parse(x))
367 .collect::<Result<Vec<_>, _>>()?;
368
369 Ok(OutputBlock { name, fields, span })
370 }
371
372 fn span(&self) -> &Span {
373 &self.span
374 }
375}
376
377impl AstNode for MintBlockField {
378 const RULE: Rule = Rule::mint_block_field;
379
380 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
381 match pair.as_rule() {
382 Rule::mint_block_amount => {
383 let pair = pair.into_inner().next().unwrap();
384 let x = MintBlockField::Amount(AssetExpr::parse(pair)?.into());
385 Ok(x)
386 }
387 Rule::mint_block_redeemer => {
388 let pair = pair.into_inner().next().unwrap();
389 let x = MintBlockField::Redeemer(DataExpr::parse(pair)?.into());
390 Ok(x)
391 }
392 x => unreachable!("Unexpected rule in output_block_field: {:?}", x),
393 }
394 }
395
396 fn span(&self) -> &Span {
397 match self {
398 Self::Amount(x) => x.span(),
399 Self::Redeemer(x) => x.span(),
400 }
401 }
402}
403
404impl AstNode for MintBlock {
405 const RULE: Rule = Rule::mint_block;
406
407 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
408 let span = pair.as_span().into();
409 let inner = pair.into_inner();
410
411 let fields = inner
412 .map(|x| MintBlockField::parse(x))
413 .collect::<Result<Vec<_>, _>>()?;
414
415 Ok(MintBlock { fields, span })
416 }
417
418 fn span(&self) -> &Span {
419 &self.span
420 }
421}
422
423impl AstNode for BurnBlock {
424 const RULE: Rule = Rule::burn_block;
425
426 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
427 let span = pair.as_span().into();
428 let inner = pair.into_inner();
429
430 let fields = inner
431 .map(|x| MintBlockField::parse(x))
432 .collect::<Result<Vec<_>, _>>()?;
433
434 Ok(BurnBlock { fields, span })
435 }
436
437 fn span(&self) -> &Span {
438 &self.span
439 }
440}
441
442impl AstNode for RecordField {
443 const RULE: Rule = Rule::record_field;
444
445 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
446 let span = pair.as_span().into();
447 let mut inner = pair.into_inner();
448 let identifier = inner.next().unwrap().as_str().to_string();
449 let r#type = Type::parse(inner.next().unwrap())?;
450
451 Ok(RecordField {
452 name: identifier,
453 r#type,
454 span,
455 })
456 }
457
458 fn span(&self) -> &Span {
459 &self.span
460 }
461}
462
463impl AstNode for PolicyField {
464 const RULE: Rule = Rule::policy_def_field;
465
466 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
467 match pair.as_rule() {
468 Rule::policy_def_hash => Ok(PolicyField::Hash(DataExpr::parse(
469 pair.into_inner().next().unwrap(),
470 )?)),
471 Rule::policy_def_script => Ok(PolicyField::Script(DataExpr::parse(
472 pair.into_inner().next().unwrap(),
473 )?)),
474 Rule::policy_def_ref => Ok(PolicyField::Ref(DataExpr::parse(
475 pair.into_inner().next().unwrap(),
476 )?)),
477 x => unreachable!("Unexpected rule in policy_field: {:?}", x),
478 }
479 }
480
481 fn span(&self) -> &Span {
482 match self {
483 Self::Hash(x) => x.span(),
484 Self::Script(x) => x.span(),
485 Self::Ref(x) => x.span(),
486 }
487 }
488}
489
490impl AstNode for PolicyConstructor {
491 const RULE: Rule = Rule::policy_def_constructor;
492
493 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
494 let span = pair.as_span().into();
495 let inner = pair.into_inner();
496
497 let fields = inner
498 .map(|x| PolicyField::parse(x))
499 .collect::<Result<Vec<_>, _>>()?;
500
501 Ok(PolicyConstructor { fields, span })
502 }
503
504 fn span(&self) -> &Span {
505 &self.span
506 }
507}
508
509impl AstNode for PolicyValue {
510 const RULE: Rule = Rule::policy_def_value;
511
512 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
513 match pair.as_rule() {
514 Rule::policy_def_constructor => {
515 Ok(PolicyValue::Constructor(PolicyConstructor::parse(pair)?))
516 }
517 Rule::policy_def_assign => Ok(PolicyValue::Assign(HexStringLiteral::parse(
518 pair.into_inner().next().unwrap(),
519 )?)),
520 x => unreachable!("Unexpected rule in policy_value: {:?}", x),
521 }
522 }
523
524 fn span(&self) -> &Span {
525 match self {
526 Self::Constructor(x) => x.span(),
527 Self::Assign(x) => x.span(),
528 }
529 }
530}
531
532impl AstNode for PolicyDef {
533 const RULE: Rule = Rule::policy_def;
534
535 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
536 let span = pair.as_span().into();
537 let mut inner = pair.into_inner();
538 let name = inner.next().unwrap().as_str().to_string();
539 let value = PolicyValue::parse(inner.next().unwrap())?;
540
541 Ok(PolicyDef { name, value, span })
542 }
543
544 fn span(&self) -> &Span {
545 &self.span
546 }
547}
548
549impl AstNode for AddressExpr {
550 const RULE: Rule = Rule::address_expr;
551
552 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
553 let mut inner = pair.into_inner();
554
555 let value = inner.next().unwrap();
556
557 match value.as_rule() {
558 Rule::string => Ok(AddressExpr::String(StringLiteral::parse(value)?)),
559 Rule::hex_string => Ok(AddressExpr::HexString(HexStringLiteral::parse(value)?)),
560 Rule::identifier => Ok(AddressExpr::Identifier(Identifier::parse(value)?)),
561 x => unreachable!("Unexpected rule in address_expr: {:?}", x),
562 }
563 }
564
565 fn span(&self) -> &Span {
566 match self {
567 Self::String(x) => x.span(),
568 Self::HexString(x) => x.span(),
569 Self::Identifier(x) => x.span(),
570 }
571 }
572}
573
574impl AstNode for StaticAssetConstructor {
575 const RULE: Rule = Rule::static_asset_constructor;
576
577 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
578 let span = pair.as_span().into();
579 let mut inner = pair.into_inner();
580
581 let r#type = Identifier::parse(inner.next().unwrap())?;
582 let amount = DataExpr::parse(inner.next().unwrap())?;
583
584 Ok(StaticAssetConstructor {
585 r#type,
586 amount: Box::new(amount),
587 span,
588 })
589 }
590
591 fn span(&self) -> &Span {
592 &self.span
593 }
594}
595
596impl AstNode for AnyAssetConstructor {
597 const RULE: Rule = Rule::any_asset_constructor;
598
599 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
600 let span = pair.as_span().into();
601 let mut inner = pair.into_inner();
602
603 let policy = DataExpr::parse(inner.next().unwrap())?;
604 let asset_name = DataExpr::parse(inner.next().unwrap())?;
605 let amount = DataExpr::parse(inner.next().unwrap())?;
606
607 Ok(AnyAssetConstructor {
608 policy: Box::new(policy),
609 asset_name: Box::new(asset_name),
610 amount: Box::new(amount),
611 span,
612 })
613 }
614
615 fn span(&self) -> &Span {
616 &self.span
617 }
618}
619
620impl AssetExpr {
621 fn identifier_parse(pair: Pair<Rule>) -> Result<Self, Error> {
622 Ok(AssetExpr::Identifier(Identifier::parse(pair)?))
623 }
624
625 fn static_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
626 Ok(AssetExpr::StaticConstructor(StaticAssetConstructor::parse(
627 pair,
628 )?))
629 }
630
631 fn any_constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
632 Ok(AssetExpr::AnyConstructor(AnyAssetConstructor::parse(pair)?))
633 }
634
635 fn property_access_parse(pair: Pair<Rule>) -> Result<Self, Error> {
636 Ok(AssetExpr::PropertyAccess(PropertyAccess::parse(pair)?))
637 }
638
639 fn term_parse(pair: Pair<Rule>) -> Result<Self, Error> {
640 match pair.as_rule() {
641 Rule::static_asset_constructor => AssetExpr::static_constructor_parse(pair),
642 Rule::any_asset_constructor => AssetExpr::any_constructor_parse(pair),
643 Rule::property_access => AssetExpr::property_access_parse(pair),
644 Rule::identifier => AssetExpr::identifier_parse(pair),
645 x => unreachable!("Unexpected rule in asset_expr: {:?}", x),
646 }
647 }
648}
649
650impl AstNode for AssetExpr {
651 const RULE: Rule = Rule::asset_expr;
652
653 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
654 let mut inner = pair.into_inner();
655
656 let mut final_expr = Self::term_parse(inner.next().unwrap())?;
657
658 while let Some(term) = inner.next() {
659 let span = term.as_span().into();
660 let operator = BinaryOperator::parse(term)?;
661 let next_expr = Self::term_parse(inner.next().unwrap())?;
662
663 final_expr = AssetExpr::BinaryOp(AssetBinaryOp {
664 operator,
665 left: Box::new(final_expr),
666 right: Box::new(next_expr),
667 span,
668 });
669 }
670
671 Ok(final_expr)
672 }
673
674 fn span(&self) -> &Span {
675 match self {
676 AssetExpr::StaticConstructor(x) => x.span(),
677 AssetExpr::AnyConstructor(x) => x.span(),
678 AssetExpr::BinaryOp(x) => &x.span,
679 AssetExpr::PropertyAccess(x) => x.span(),
680 AssetExpr::Identifier(x) => x.span(),
681 }
682 }
683}
684
685impl AstNode for PropertyAccess {
686 const RULE: Rule = Rule::property_access;
687
688 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
689 let span = pair.as_span().into();
690 let mut inner = pair.into_inner();
691
692 let object = Identifier::parse(inner.next().unwrap())?;
693
694 let mut identifiers = Vec::new();
695 identifiers.push(Identifier::parse(inner.next().unwrap())?);
696
697 for identifier in inner {
698 identifiers.push(Identifier::parse(identifier)?);
699 }
700
701 Ok(PropertyAccess {
702 object,
703 path: identifiers,
704 scope: None,
705 span,
706 })
707 }
708
709 fn span(&self) -> &Span {
710 &self.span
711 }
712}
713
714impl AstNode for RecordConstructorField {
715 const RULE: Rule = Rule::record_constructor_field;
716
717 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
718 let span = pair.as_span().into();
719 let mut inner = pair.into_inner();
720
721 let name = Identifier::parse(inner.next().unwrap())?;
722 let value = DataExpr::parse(inner.next().unwrap())?;
723
724 Ok(RecordConstructorField {
725 name,
726 value: Box::new(value),
727 span,
728 })
729 }
730
731 fn span(&self) -> &Span {
732 &self.span
733 }
734}
735
736impl AstNode for DatumConstructor {
737 const RULE: Rule = Rule::datum_constructor;
738
739 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
740 let span = pair.as_span().into();
741 let mut inner = pair.into_inner();
742
743 let r#type = Identifier::parse(inner.next().unwrap())?;
744 let case = VariantCaseConstructor::parse(inner.next().unwrap())?;
745
746 Ok(DatumConstructor {
747 r#type,
748 case,
749 scope: None,
750 span,
751 })
752 }
753
754 fn span(&self) -> &Span {
755 &self.span
756 }
757}
758
759impl VariantCaseConstructor {
760 fn implicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
761 let span = pair.as_span().into();
762 let inner = pair.into_inner();
763
764 let mut fields = Vec::new();
765 let mut spread = None;
766
767 for pair in inner {
768 match pair.as_rule() {
769 Rule::record_constructor_field => {
770 fields.push(RecordConstructorField::parse(pair)?);
771 }
772 Rule::spread_expression => {
773 spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
774 }
775 x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
776 }
777 }
778
779 Ok(VariantCaseConstructor {
780 name: Identifier::new("Default"),
781 fields,
782 spread: spread.map(Box::new),
783 scope: None,
784 span,
785 })
786 }
787
788 fn explicit_parse(pair: Pair<Rule>) -> Result<Self, Error> {
789 let span = pair.as_span().into();
790 let mut inner = pair.into_inner();
791
792 let name = Identifier::parse(inner.next().unwrap())?;
793
794 let mut fields = Vec::new();
795 let mut spread = None;
796
797 for pair in inner {
798 match pair.as_rule() {
799 Rule::record_constructor_field => {
800 fields.push(RecordConstructorField::parse(pair)?);
801 }
802 Rule::spread_expression => {
803 spread = Some(DataExpr::parse(pair.into_inner().next().unwrap())?);
804 }
805 x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
806 }
807 }
808
809 Ok(VariantCaseConstructor {
810 name,
811 fields,
812 spread: spread.map(Box::new),
813 scope: None,
814 span,
815 })
816 }
817}
818
819impl AstNode for VariantCaseConstructor {
820 const RULE: Rule = Rule::variant_case_constructor;
821
822 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
823 match pair.as_rule() {
824 Rule::implicit_variant_case_constructor => Self::implicit_parse(pair),
825 Rule::explicit_variant_case_constructor => Self::explicit_parse(pair),
826 x => unreachable!("Unexpected rule in datum_constructor: {:?}", x),
827 }
828 }
829
830 fn span(&self) -> &Span {
831 &self.span
832 }
833}
834
835impl DataExpr {
836 fn number_parse(pair: Pair<Rule>) -> Result<Self, Error> {
837 Ok(DataExpr::Number(pair.as_str().parse().unwrap()))
838 }
839
840 fn bool_parse(pair: Pair<Rule>) -> Result<Self, Error> {
841 Ok(DataExpr::Bool(pair.as_str().parse().unwrap()))
842 }
843
844 fn identifier_parse(pair: Pair<Rule>) -> Result<Self, Error> {
845 Ok(DataExpr::Identifier(Identifier::parse(pair)?))
846 }
847
848 fn property_access_parse(pair: Pair<Rule>) -> Result<Self, Error> {
849 Ok(DataExpr::PropertyAccess(PropertyAccess::parse(pair)?))
850 }
851
852 fn constructor_parse(pair: Pair<Rule>) -> Result<Self, Error> {
853 Ok(DataExpr::Constructor(DatumConstructor::parse(pair)?))
854 }
855
856 fn term_parse(pair: Pair<Rule>) -> Result<Self, Error> {
857 match pair.as_rule() {
858 Rule::number => DataExpr::number_parse(pair),
859 Rule::string => Ok(DataExpr::String(StringLiteral::parse(pair)?)),
860 Rule::bool => DataExpr::bool_parse(pair),
861 Rule::hex_string => Ok(DataExpr::HexString(HexStringLiteral::parse(pair)?)),
862 Rule::datum_constructor => DataExpr::constructor_parse(pair),
863 Rule::unit => Ok(DataExpr::Unit),
864 Rule::identifier => DataExpr::identifier_parse(pair),
865 Rule::property_access => DataExpr::property_access_parse(pair),
866 x => unreachable!("Unexpected rule in data_expr: {:?}", x),
867 }
868 }
869}
870
871impl AstNode for DataExpr {
872 const RULE: Rule = Rule::data_expr;
873
874 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
875 let mut inner = pair.into_inner();
876
877 let mut final_expr = Self::term_parse(inner.next().unwrap())?;
878
879 while let Some(term) = inner.next() {
880 let span = term.as_span().into();
881 let operator = BinaryOperator::parse(term)?;
882 let next_expr = Self::term_parse(inner.next().unwrap())?;
883
884 final_expr = DataExpr::BinaryOp(DataBinaryOp {
885 operator,
886 left: Box::new(final_expr),
887 right: Box::new(next_expr),
888 span,
889 });
890 }
891
892 Ok(final_expr)
893 }
894
895 fn span(&self) -> &Span {
896 match self {
897 DataExpr::None => &Span::DUMMY, DataExpr::Unit => &Span::DUMMY, DataExpr::Number(_) => &Span::DUMMY, DataExpr::Bool(_) => &Span::DUMMY, DataExpr::String(x) => x.span(),
902 DataExpr::HexString(x) => x.span(),
903 DataExpr::Constructor(x) => x.span(),
904 DataExpr::Identifier(x) => x.span(),
905 DataExpr::PropertyAccess(x) => x.span(),
906 DataExpr::BinaryOp(x) => &x.span,
907 }
908 }
909}
910
911impl AstNode for BinaryOperator {
912 const RULE: Rule = Rule::binary_operator;
913
914 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
915 match pair.as_str() {
916 "+" => Ok(BinaryOperator::Add),
917 "-" => Ok(BinaryOperator::Subtract),
918 x => unreachable!("Unexpected string in binary_operator: {:?}", x),
919 }
920 }
921
922 fn span(&self) -> &Span {
923 &Span::DUMMY }
925}
926
927impl AstNode for Type {
928 const RULE: Rule = Rule::r#type;
929
930 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
931 match pair.as_str() {
932 "Int" => Ok(Type::Int),
933 "Bool" => Ok(Type::Bool),
934 "Bytes" => Ok(Type::Bytes),
935 "Address" => Ok(Type::Address),
936 "UtxoRef" => Ok(Type::UtxoRef),
937 "AnyAsset" => Ok(Type::AnyAsset),
938 t => Ok(Type::Custom(Identifier::new(t.to_string()))),
939 }
940 }
941
942 fn span(&self) -> &Span {
943 &Span::DUMMY }
945}
946
947impl TypeDef {
948 fn parse_variant_format(pair: Pair<Rule>) -> Result<Self, Error> {
949 let span = pair.as_span().into();
950 let mut inner = pair.into_inner();
951
952 let identifier = inner.next().unwrap().as_str().to_string();
953
954 let cases = inner
955 .map(VariantCase::parse)
956 .collect::<Result<Vec<_>, _>>()?;
957
958 Ok(TypeDef {
959 name: identifier,
960 cases,
961 span,
962 })
963 }
964
965 fn parse_record_format(pair: Pair<Rule>) -> Result<Self, Error> {
966 let span: Span = pair.as_span().into();
967 let mut inner = pair.into_inner();
968
969 let identifier = inner.next().unwrap().as_str().to_string();
970
971 let fields = inner
972 .map(RecordField::parse)
973 .collect::<Result<Vec<_>, _>>()?;
974
975 Ok(TypeDef {
976 name: identifier.clone(),
977 cases: vec![VariantCase {
978 name: "Default".to_string(),
979 fields,
980 span: span.clone(),
981 }],
982 span,
983 })
984 }
985}
986
987impl AstNode for TypeDef {
988 const RULE: Rule = Rule::type_def;
989
990 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
991 match pair.as_rule() {
992 Rule::variant_def => Ok(Self::parse_variant_format(pair)?),
993 Rule::record_def => Ok(Self::parse_record_format(pair)?),
994 x => unreachable!("Unexpected rule in type_def: {:?}", x),
995 }
996 }
997
998 fn span(&self) -> &Span {
999 &self.span
1000 }
1001}
1002
1003impl VariantCase {
1004 fn struct_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
1005 let span = pair.as_span().into();
1006 let mut inner = pair.into_inner();
1007
1008 let identifier = inner.next().unwrap().as_str().to_string();
1009
1010 let fields = inner
1011 .map(RecordField::parse)
1012 .collect::<Result<Vec<_>, _>>()?;
1013
1014 Ok(Self {
1015 name: identifier,
1016 fields,
1017 span,
1018 })
1019 }
1020
1021 fn unit_case_parse(pair: pest::iterators::Pair<Rule>) -> Result<Self, Error> {
1022 let span = pair.as_span().into();
1023 let mut inner = pair.into_inner();
1024
1025 let identifier = inner.next().unwrap().as_str().to_string();
1026
1027 Ok(Self {
1028 name: identifier,
1029 fields: vec![],
1030 span,
1031 })
1032 }
1033}
1034
1035impl AstNode for VariantCase {
1036 const RULE: Rule = Rule::variant_case;
1037
1038 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1039 let case = match pair.as_rule() {
1040 Rule::variant_case_struct => Self::struct_case_parse(pair),
1041 Rule::variant_case_tuple => todo!("parse variant case tuple"),
1042 Rule::variant_case_unit => Self::unit_case_parse(pair),
1043 x => unreachable!("Unexpected rule in datum_variant: {:?}", x),
1044 }?;
1045
1046 Ok(case)
1047 }
1048
1049 fn span(&self) -> &Span {
1050 &self.span
1051 }
1052}
1053
1054impl AstNode for AssetDef {
1055 const RULE: Rule = Rule::asset_def;
1056
1057 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1058 let span = pair.as_span().into();
1059 let mut inner = pair.into_inner();
1060
1061 let identifier = inner.next().unwrap().as_str().to_string();
1062 let policy = HexStringLiteral::parse(inner.next().unwrap())?;
1063 let asset_name = inner.next().unwrap().as_str().to_string();
1064
1065 Ok(AssetDef {
1066 name: identifier,
1067 policy: Some(policy),
1068 asset_name: Some(asset_name),
1069 span,
1070 })
1071 }
1072
1073 fn span(&self) -> &Span {
1074 &self.span
1075 }
1076}
1077
1078impl AstNode for ChainSpecificBlock {
1079 const RULE: Rule = Rule::chain_specific_block;
1080
1081 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
1082 let mut inner = pair.into_inner();
1083
1084 let block = inner.next().unwrap();
1085
1086 match block.as_rule() {
1087 Rule::cardano_block => {
1088 let block = crate::cardano::CardanoBlock::parse(block)?;
1089 Ok(ChainSpecificBlock::Cardano(block))
1090 }
1091 x => unreachable!("Unexpected rule in chain_specific_block: {:?}", x),
1092 }
1093 }
1094
1095 fn span(&self) -> &Span {
1096 match self {
1097 Self::Cardano(x) => x.span(),
1098 }
1099 }
1100}
1101
1102pub fn parse_string(input: &str) -> Result<Program, Error> {
1125 let pairs = Tx3Grammar::parse(Rule::program, input)?;
1126 Program::parse(pairs.into_iter().next().unwrap())
1127}
1128
1129#[cfg(test)]
1130pub fn parse_well_known_example(example: &str) -> Program {
1131 let manifest_dir = env!("CARGO_MANIFEST_DIR");
1132 let test_file = format!("{}/../../examples/{}.tx3", manifest_dir, example);
1133 let input = std::fs::read_to_string(&test_file).unwrap();
1134 parse_string(&input).unwrap()
1135}
1136
1137#[cfg(test)]
1138mod tests {
1139 use super::*;
1140 use assert_json_diff::assert_json_eq;
1141 use paste::paste;
1142 use pest::Parser;
1143
1144 #[test]
1145 fn smoke_test_parse_string() {
1146 let _ = parse_string("tx swap() {}").unwrap();
1147 }
1148
1149 macro_rules! input_to_ast_check {
1150 ($ast:ty, $name:expr, $input:expr, $expected:expr) => {
1151 paste::paste! {
1152 #[test]
1153 fn [<test_parse_ $ast:snake _ $name>]() {
1154 let pairs = super::Tx3Grammar::parse(<$ast>::RULE, $input).unwrap();
1155 let single_match = pairs.into_iter().next().unwrap();
1156 let result = <$ast>::parse(single_match).unwrap();
1157
1158 assert_eq!(result, $expected);
1159 }
1160 }
1161 };
1162 }
1163
1164 input_to_ast_check!(BinaryOperator, "plus", "+", BinaryOperator::Add);
1165
1166 input_to_ast_check!(Type, "int", "Int", Type::Int);
1167
1168 input_to_ast_check!(Type, "bool", "Bool", Type::Bool);
1169
1170 input_to_ast_check!(Type, "bytes", "Bytes", Type::Bytes);
1171
1172 input_to_ast_check!(Type, "address", "Address", Type::Address);
1173
1174 input_to_ast_check!(Type, "utxo_ref", "UtxoRef", Type::UtxoRef);
1175
1176 input_to_ast_check!(Type, "any_asset", "AnyAsset", Type::AnyAsset);
1177
1178 input_to_ast_check!(
1179 TypeDef,
1180 "type_def_record",
1181 "type MyRecord {
1182 field1: Int,
1183 field2: Bytes,
1184 }",
1185 TypeDef {
1186 name: "MyRecord".to_string(),
1187 cases: vec![VariantCase {
1188 name: "Default".to_string(),
1189 fields: vec![
1190 RecordField::new("field1", Type::Int),
1191 RecordField::new("field2", Type::Bytes)
1192 ],
1193 span: Span::DUMMY,
1194 }],
1195 span: Span::DUMMY,
1196 }
1197 );
1198
1199 input_to_ast_check!(
1200 TypeDef,
1201 "type_def_variant",
1202 "type MyVariant {
1203 Case1 {
1204 field1: Int,
1205 field2: Bytes,
1206 },
1207 Case2,
1208 }",
1209 TypeDef {
1210 name: "MyVariant".to_string(),
1211 cases: vec![
1212 VariantCase {
1213 name: "Case1".to_string(),
1214 fields: vec![
1215 RecordField::new("field1", Type::Int),
1216 RecordField::new("field2", Type::Bytes)
1217 ],
1218 span: Span::DUMMY,
1219 },
1220 VariantCase {
1221 name: "Case2".to_string(),
1222 fields: vec![],
1223 span: Span::DUMMY,
1224 },
1225 ],
1226 span: Span::DUMMY,
1227 }
1228 );
1229
1230 input_to_ast_check!(
1231 StringLiteral,
1232 "literal_string",
1233 "\"Hello, world!\"",
1234 StringLiteral::new("Hello, world!".to_string())
1235 );
1236
1237 input_to_ast_check!(
1238 HexStringLiteral,
1239 "hex_string",
1240 "0xAFAFAF",
1241 HexStringLiteral::new("AFAFAF".to_string())
1242 );
1243
1244 input_to_ast_check!(
1245 StringLiteral,
1246 "literal_string_address",
1247 "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1248 StringLiteral::new("addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string())
1249 );
1250
1251 input_to_ast_check!(
1252 DataExpr,
1253 "identifier",
1254 "my_party",
1255 DataExpr::Identifier(Identifier::new("my_party".to_string()))
1256 );
1257
1258 input_to_ast_check!(DataExpr, "literal_bool_true", "true", DataExpr::Bool(true));
1259
1260 input_to_ast_check!(
1261 DataExpr,
1262 "literal_bool_false",
1263 "false",
1264 DataExpr::Bool(false)
1265 );
1266
1267 input_to_ast_check!(DataExpr, "unit_value", "())", DataExpr::Unit);
1268
1269 input_to_ast_check!(
1270 PropertyAccess,
1271 "single_property",
1272 "subject.property",
1273 PropertyAccess::new("subject", &["property"])
1274 );
1275
1276 input_to_ast_check!(
1277 PropertyAccess,
1278 "multiple_properties",
1279 "subject.property.subproperty",
1280 PropertyAccess::new("subject", &["property", "subproperty"])
1281 );
1282
1283 input_to_ast_check!(
1284 PolicyDef,
1285 "policy_def_assign",
1286 "policy MyPolicy = 0xAFAFAF;",
1287 PolicyDef {
1288 name: "MyPolicy".to_string(),
1289 value: PolicyValue::Assign(HexStringLiteral::new("AFAFAF".to_string())),
1290 span: Span::DUMMY,
1291 }
1292 );
1293
1294 input_to_ast_check!(
1295 PolicyDef,
1296 "policy_def_constructor",
1297 "policy MyPolicy {
1298 hash: 0x1234567890,
1299 script: 0x1234567890,
1300 ref: 0x1234567890,
1301 };",
1302 PolicyDef {
1303 name: "MyPolicy".to_string(),
1304 value: PolicyValue::Constructor(PolicyConstructor {
1305 fields: vec![
1306 PolicyField::Hash(DataExpr::HexString(HexStringLiteral::new(
1307 "1234567890".to_string()
1308 ))),
1309 PolicyField::Script(DataExpr::HexString(HexStringLiteral::new(
1310 "1234567890".to_string()
1311 ))),
1312 PolicyField::Ref(DataExpr::HexString(HexStringLiteral::new(
1313 "1234567890".to_string()
1314 ))),
1315 ],
1316 span: Span::DUMMY,
1317 }),
1318 span: Span::DUMMY,
1319 }
1320 );
1321
1322 input_to_ast_check!(
1323 AssetDef,
1324 "hex_hex",
1325 "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.0xef7a1ceb;",
1326 AssetDef {
1327 name: "MyToken".to_string(),
1328 policy: Some(HexStringLiteral::new(
1329 "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
1330 )),
1331 asset_name: Some("0xef7a1ceb".to_string()),
1332 span: Span::DUMMY,
1333 }
1334 );
1335
1336 input_to_ast_check!(
1337 AssetDef,
1338 "hex_ascii",
1339 "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.MYTOKEN;",
1340 AssetDef {
1341 name: "MyToken".to_string(),
1342 policy: Some(HexStringLiteral::new(
1343 "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
1344 )),
1345 asset_name: Some("MYTOKEN".to_string()),
1346 span: Span::DUMMY,
1347 }
1348 );
1349
1350 input_to_ast_check!(
1351 StaticAssetConstructor,
1352 "type_and_literal",
1353 "MyToken(15)",
1354 StaticAssetConstructor {
1355 r#type: Identifier::new("MyToken"),
1356 amount: Box::new(DataExpr::Number(15)),
1357 span: Span::DUMMY,
1358 }
1359 );
1360
1361 input_to_ast_check!(
1362 AnyAssetConstructor,
1363 "any_asset_constructor",
1364 "AnyAsset(0x1234567890, \"MyToken\", 15)",
1365 AnyAssetConstructor {
1366 policy: Box::new(DataExpr::HexString(HexStringLiteral::new(
1367 "1234567890".to_string()
1368 ))),
1369 asset_name: Box::new(DataExpr::String(StringLiteral::new("MyToken".to_string()))),
1370 amount: Box::new(DataExpr::Number(15)),
1371 span: Span::DUMMY,
1372 }
1373 );
1374
1375 input_to_ast_check!(
1376 AnyAssetConstructor,
1377 "any_asset_identifiers",
1378 "AnyAsset(my_policy, my_token, my_amount)",
1379 AnyAssetConstructor {
1380 policy: Box::new(DataExpr::Identifier(Identifier::new("my_policy"))),
1381 asset_name: Box::new(DataExpr::Identifier(Identifier::new("my_token"))),
1382 amount: Box::new(DataExpr::Identifier(Identifier::new("my_amount"))),
1383 span: Span::DUMMY,
1384 }
1385 );
1386
1387 input_to_ast_check!(
1388 AnyAssetConstructor,
1389 "any_asset_property_access",
1390 "AnyAsset(input1.policy, input1.asset_name, input1.amount)",
1391 AnyAssetConstructor {
1392 policy: Box::new(DataExpr::PropertyAccess(PropertyAccess::new(
1393 "input1",
1394 &["policy"],
1395 ))),
1396 asset_name: Box::new(DataExpr::PropertyAccess(PropertyAccess::new(
1397 "input1",
1398 &["asset_name"],
1399 ))),
1400 amount: Box::new(DataExpr::PropertyAccess(PropertyAccess::new(
1401 "input1",
1402 &["amount"],
1403 ))),
1404 span: Span::DUMMY,
1405 }
1406 );
1407
1408 input_to_ast_check!(
1409 DataExpr,
1410 "addition",
1411 "5 + var1",
1412 DataExpr::BinaryOp(DataBinaryOp {
1413 operator: BinaryOperator::Add,
1414 left: Box::new(DataExpr::Number(5)),
1415 right: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
1416 span: Span::DUMMY,
1417 })
1418 );
1419
1420 input_to_ast_check!(
1421 AddressExpr,
1422 "address_string",
1423 "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1424 AddressExpr::String(StringLiteral::new(
1425 "addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string()
1426 ))
1427 );
1428
1429 input_to_ast_check!(
1430 AddressExpr,
1431 "address_hex_string",
1432 "0x1234567890abcdef",
1433 AddressExpr::HexString(HexStringLiteral::new("1234567890abcdef".to_string()))
1434 );
1435
1436 input_to_ast_check!(
1437 AddressExpr,
1438 "address_identifier",
1439 "my_address",
1440 AddressExpr::Identifier(Identifier::new("my_address"))
1441 );
1442
1443 input_to_ast_check!(
1444 DatumConstructor,
1445 "datum_constructor_record",
1446 "MyRecord {
1447 field1: 10,
1448 field2: abc,
1449 }",
1450 DatumConstructor {
1451 r#type: Identifier::new("MyRecord"),
1452 case: VariantCaseConstructor {
1453 name: Identifier::new("Default"),
1454 fields: vec![
1455 RecordConstructorField {
1456 name: Identifier::new("field1"),
1457 value: Box::new(DataExpr::Number(10)),
1458 span: Span::DUMMY,
1459 },
1460 RecordConstructorField {
1461 name: Identifier::new("field2"),
1462 value: Box::new(DataExpr::Identifier(Identifier::new("abc"))),
1463 span: Span::DUMMY,
1464 },
1465 ],
1466 spread: None,
1467 scope: None,
1468 span: Span::DUMMY,
1469 },
1470 scope: None,
1471 span: Span::DUMMY,
1472 }
1473 );
1474
1475 input_to_ast_check!(
1476 DatumConstructor,
1477 "datum_constructor_variant",
1478 "ShipCommand::MoveShip {
1479 delta_x: delta_x,
1480 delta_y: delta_y,
1481 }",
1482 DatumConstructor {
1483 r#type: Identifier::new("ShipCommand"),
1484 case: VariantCaseConstructor {
1485 name: Identifier::new("MoveShip"),
1486 fields: vec![
1487 RecordConstructorField {
1488 name: Identifier::new("delta_x"),
1489 value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
1490 span: Span::DUMMY,
1491 },
1492 RecordConstructorField {
1493 name: Identifier::new("delta_y"),
1494 value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
1495 span: Span::DUMMY,
1496 },
1497 ],
1498 spread: None,
1499 scope: None,
1500 span: Span::DUMMY,
1501 },
1502 scope: None,
1503 span: Span::DUMMY,
1504 }
1505 );
1506
1507 input_to_ast_check!(
1508 DatumConstructor,
1509 "datum_constructor_variant_with_spread",
1510 "ShipCommand::MoveShip {
1511 delta_x: delta_x,
1512 delta_y: delta_y,
1513 ...abc
1514 }",
1515 DatumConstructor {
1516 r#type: Identifier::new("ShipCommand"),
1517 case: VariantCaseConstructor {
1518 name: Identifier::new("MoveShip"),
1519 fields: vec![
1520 RecordConstructorField {
1521 name: Identifier::new("delta_x"),
1522 value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
1523 span: Span::DUMMY,
1524 },
1525 RecordConstructorField {
1526 name: Identifier::new("delta_y"),
1527 value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
1528 span: Span::DUMMY,
1529 },
1530 ],
1531 spread: Some(Box::new(DataExpr::Identifier(Identifier::new(
1532 "abc".to_string()
1533 )))),
1534 scope: None,
1535 span: Span::DUMMY,
1536 },
1537 scope: None,
1538 span: Span::DUMMY,
1539 }
1540 );
1541
1542 input_to_ast_check!(
1543 OutputBlock,
1544 "output_block_anonymous",
1545 r#"output {
1546 to: my_party,
1547 amount: Ada(100),
1548 }"#,
1549 OutputBlock {
1550 name: None,
1551 fields: vec![
1552 OutputBlockField::To(Box::new(AddressExpr::Identifier(Identifier::new(
1553 "my_party".to_string(),
1554 )))),
1555 OutputBlockField::Amount(Box::new(AssetExpr::StaticConstructor(
1556 StaticAssetConstructor {
1557 r#type: Identifier::new("Ada"),
1558 amount: Box::new(DataExpr::Number(100)),
1559 span: Span::DUMMY,
1560 },
1561 ))),
1562 ],
1563 span: Span::DUMMY,
1564 }
1565 );
1566
1567 input_to_ast_check!(
1568 ChainSpecificBlock,
1569 "chain_specific_block_cardano",
1570 "cardano::vote_delegation_certificate {
1571 drep: 0x1234567890,
1572 stake: 0x1234567890,
1573 }",
1574 ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::VoteDelegationCertificate(
1575 crate::cardano::VoteDelegationCertificate {
1576 drep: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
1577 stake: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
1578 span: Span::DUMMY,
1579 },
1580 ))
1581 );
1582
1583 #[test]
1584 fn test_spans_are_respected() {
1585 let program = parse_well_known_example("lang_tour");
1586 assert_eq!(program.span, Span::new(0, 904));
1587
1588 assert_eq!(program.parties[0].span, Span::new(0, 14));
1589
1590 assert_eq!(program.types[0].span, Span::new(16, 88));
1591 }
1592
1593 fn make_snapshot_if_missing(example: &str, program: &Program) {
1594 let manifest_dir = env!("CARGO_MANIFEST_DIR");
1595 let path = format!("{}/../../examples/{}.ast", manifest_dir, example);
1596
1597 if !std::fs::exists(&path).unwrap() {
1598 let ast = serde_json::to_string_pretty(program).unwrap();
1599 std::fs::write(&path, ast).unwrap();
1600 }
1601 }
1602
1603 fn test_parsing_example(example: &str) {
1604 let program = parse_well_known_example(example);
1605
1606 make_snapshot_if_missing(example, &program);
1607
1608 let manifest_dir = env!("CARGO_MANIFEST_DIR");
1609 let ast_file = format!("{}/../../examples/{}.ast", manifest_dir, example);
1610 let ast = std::fs::read_to_string(ast_file).unwrap();
1611
1612 let expected: Program = serde_json::from_str(&ast).unwrap();
1613
1614 assert_json_eq!(program, expected);
1615 }
1616
1617 #[macro_export]
1618 macro_rules! test_parsing {
1619 ($name:ident) => {
1620 paste! {
1621 #[test]
1622 fn [<test_example_ $name>]() {
1623 test_parsing_example(stringify!($name));
1624 }
1625 }
1626 };
1627 }
1628
1629 test_parsing!(lang_tour);
1630
1631 test_parsing!(transfer);
1632
1633 test_parsing!(swap);
1634
1635 test_parsing!(asteria);
1636
1637 test_parsing!(vesting);
1638
1639 test_parsing!(faucet);
1640}