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