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