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