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 .is_some_and(|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![Type::Tuple(vec![Type::Int, Type::Int]), Type::Bytes,])
1809 );
1810
1811 input_to_ast_check!(
1812 TypeDef,
1813 "type_def_record",
1814 "type MyRecord {
1815 field1: Int,
1816 field2: Bytes,
1817 }",
1818 TypeDef {
1819 name: Identifier::new("MyRecord"),
1820 cases: vec![VariantCase {
1821 name: Identifier::new("Default"),
1822 fields: vec![
1823 RecordField::new("field1", Type::Int),
1824 RecordField::new("field2", Type::Bytes)
1825 ],
1826 span: Span::DUMMY,
1827 }],
1828 span: Span::DUMMY,
1829 }
1830 );
1831
1832 input_to_ast_check!(
1833 TypeDef,
1834 "type_def_variant",
1835 "type MyVariant {
1836 Case1 {
1837 field1: Int,
1838 field2: Bytes,
1839 },
1840 Case2,
1841 }",
1842 TypeDef {
1843 name: Identifier::new("MyVariant"),
1844 cases: vec![
1845 VariantCase {
1846 name: Identifier::new("Case1"),
1847 fields: vec![
1848 RecordField::new("field1", Type::Int),
1849 RecordField::new("field2", Type::Bytes)
1850 ],
1851 span: Span::DUMMY,
1852 },
1853 VariantCase {
1854 name: Identifier::new("Case2"),
1855 fields: vec![],
1856 span: Span::DUMMY,
1857 },
1858 ],
1859 span: Span::DUMMY,
1860 }
1861 );
1862
1863 input_to_ast_check!(
1864 AliasDef,
1865 "type_def_alias",
1866 "type MyAlias = Bytes;",
1867 AliasDef {
1868 name: Identifier::new("MyAlias"),
1869 alias_type: Type::Bytes,
1870 span: Span::DUMMY,
1871 }
1872 );
1873
1874 input_to_ast_check!(
1875 AliasDef,
1876 "type_alias_custom_type",
1877 "type UserAlias = UserType;",
1878 AliasDef {
1879 name: Identifier::new("UserAlias"),
1880 alias_type: Type::Custom(Identifier::new("UserType")),
1881 span: Span::DUMMY,
1882 }
1883 );
1884
1885 input_to_ast_check!(
1886 AliasDef,
1887 "type_alias_list",
1888 "type StringList = List<Bytes>;",
1889 AliasDef {
1890 name: Identifier::new("StringList"),
1891 alias_type: Type::List(Box::new(Type::Bytes)),
1892 span: Span::DUMMY,
1893 }
1894 );
1895
1896 input_to_ast_check!(
1897 AliasDef,
1898 "type_alias_map",
1899 "type StringIntMap = Map<Bytes, Int>;",
1900 AliasDef {
1901 name: Identifier::new("StringIntMap"),
1902 alias_type: Type::Map(Box::new(Type::Bytes), Box::new(Type::Int)),
1903 span: Span::DUMMY,
1904 }
1905 );
1906
1907 input_to_ast_check!(
1908 AliasDef,
1909 "type_alias_complex_nested",
1910 "type ComplexType = List<Map<Bytes, Int>>;",
1911 AliasDef {
1912 name: Identifier::new("ComplexType"),
1913 alias_type: Type::List(Box::new(Type::Map(
1914 Box::new(Type::Bytes),
1915 Box::new(Type::Int)
1916 ))),
1917 span: Span::DUMMY,
1918 }
1919 );
1920
1921 input_to_ast_check!(
1922 AliasDef,
1923 "type_alias_all_primitives",
1924 "type MyInt = Int;",
1925 AliasDef {
1926 name: Identifier::new("MyInt"),
1927 alias_type: Type::Int,
1928 span: Span::DUMMY,
1929 }
1930 );
1931
1932 input_to_ast_check!(
1933 AliasDef,
1934 "type_alias_bool",
1935 "type MyBool = Bool;",
1936 AliasDef {
1937 name: Identifier::new("MyBool"),
1938 alias_type: Type::Bool,
1939 span: Span::DUMMY,
1940 }
1941 );
1942
1943 input_to_ast_check!(
1944 AliasDef,
1945 "type_alias_address",
1946 "type MyAddress = Address;",
1947 AliasDef {
1948 name: Identifier::new("MyAddress"),
1949 alias_type: Type::Address,
1950 span: Span::DUMMY,
1951 }
1952 );
1953
1954 input_to_ast_check!(
1955 AliasDef,
1956 "type_alias_utxo_ref",
1957 "type MyUtxoRef = UtxoRef;",
1958 AliasDef {
1959 name: Identifier::new("MyUtxoRef"),
1960 alias_type: Type::UtxoRef,
1961 span: Span::DUMMY,
1962 }
1963 );
1964
1965 input_to_ast_check!(
1966 AliasDef,
1967 "type_alias_any_asset",
1968 "type MyAsset = AnyAsset;",
1969 AliasDef {
1970 name: Identifier::new("MyAsset"),
1971 alias_type: Type::AnyAsset,
1972 span: Span::DUMMY,
1973 }
1974 );
1975
1976 input_to_ast_check!(
1977 StringLiteral,
1978 "literal_string",
1979 "\"Hello, world!\"",
1980 StringLiteral::new("Hello, world!".to_string())
1981 );
1982
1983 input_to_ast_check!(
1984 HexStringLiteral,
1985 "hex_string",
1986 "0xAFAFAF",
1987 HexStringLiteral::new("AFAFAF".to_string())
1988 );
1989
1990 input_to_ast_check!(
1991 StringLiteral,
1992 "literal_string_address",
1993 "\"addr1qx234567890abcdefghijklmnopqrstuvwxyz\"",
1994 StringLiteral::new("addr1qx234567890abcdefghijklmnopqrstuvwxyz".to_string())
1995 );
1996
1997 input_to_ast_check!(
1998 ListConstructor,
1999 "empty_list",
2000 "[]",
2001 ListConstructor {
2002 elements: vec![],
2003 span: Span::DUMMY,
2004 }
2005 );
2006
2007 input_to_ast_check!(
2008 ListConstructor,
2009 "trailing_comma",
2010 "[1, 2,]",
2011 ListConstructor {
2012 elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2013 span: Span::DUMMY,
2014 }
2015 );
2016
2017 input_to_ast_check!(
2018 ListConstructor,
2019 "int_list",
2020 "[1, 2]",
2021 ListConstructor {
2022 elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2023 span: Span::DUMMY,
2024 }
2025 );
2026
2027 input_to_ast_check!(
2028 ListConstructor,
2029 "string_list",
2030 "[\"Hello\", \"World\"]",
2031 ListConstructor {
2032 elements: vec![
2033 DataExpr::String(StringLiteral::new("Hello".to_string())),
2034 DataExpr::String(StringLiteral::new("World".to_string()))
2035 ],
2036 span: Span::DUMMY,
2037 }
2038 );
2039
2040 input_to_ast_check!(
2041 ListConstructor,
2042 "mixed_list",
2043 "[1, \"Hello\", true]",
2044 ListConstructor {
2045 elements: vec![
2046 DataExpr::Number(1),
2047 DataExpr::String(StringLiteral::new("Hello".to_string())),
2048 DataExpr::Bool(true)
2049 ],
2050 span: Span::DUMMY,
2051 }
2052 );
2053
2054 input_to_ast_check!(
2055 ListConstructor,
2056 "list_within_list",
2057 "[[1, 2], [3, 4]]",
2058 ListConstructor {
2059 elements: vec![
2060 DataExpr::ListConstructor(ListConstructor {
2061 elements: vec![DataExpr::Number(1), DataExpr::Number(2),],
2062 span: Span::DUMMY,
2063 }),
2064 DataExpr::ListConstructor(ListConstructor {
2065 elements: vec![DataExpr::Number(3), DataExpr::Number(4),],
2066 span: Span::DUMMY,
2067 }),
2068 ],
2069 span: Span::DUMMY,
2070 }
2071 );
2072
2073 input_to_ast_check!(
2075 DataExpr,
2076 "tuple_literal",
2077 "(1, 0xFF, true)",
2078 DataExpr::TupleConstructor(TupleConstructor {
2079 elements: vec![
2080 DataExpr::Number(1),
2081 DataExpr::HexString(HexStringLiteral::new("FF".to_string())),
2082 DataExpr::Bool(true),
2083 ],
2084 span: Span::DUMMY,
2085 })
2086 );
2087
2088 input_to_ast_check!(
2090 DataExpr,
2091 "tuple_literal_trailing_comma",
2092 "(1, 2,)",
2093 DataExpr::TupleConstructor(TupleConstructor {
2094 elements: vec![DataExpr::Number(1), DataExpr::Number(2)],
2095 span: Span::DUMMY,
2096 })
2097 );
2098
2099 input_to_ast_check!(DataExpr, "grouping_not_tuple", "(42)", DataExpr::Number(42));
2101
2102 input_to_ast_check!(
2105 DataExpr,
2106 "tuple_index_access",
2107 "my_tuple[0]",
2108 DataExpr::PropertyOp(PropertyOp {
2109 operand: Box::new(DataExpr::Identifier(Identifier::new("my_tuple"))),
2110 property: Box::new(DataExpr::Number(0)),
2111 span: Span::DUMMY,
2112 scope: None,
2113 })
2114 );
2115
2116 input_to_ast_check!(DataExpr, "literal_bool_true", "true", DataExpr::Bool(true));
2117
2118 input_to_ast_check!(
2119 DataExpr,
2120 "literal_bool_false",
2121 "false",
2122 DataExpr::Bool(false)
2123 );
2124
2125 input_to_ast_check!(DataExpr, "unit_value", "())", DataExpr::Unit);
2126
2127 input_to_ast_check!(DataExpr, "number_value", "123", DataExpr::Number(123));
2128
2129 input_to_ast_check!(
2130 PolicyDef,
2131 "policy_def_assign",
2132 "policy MyPolicy = 0xAFAFAF;",
2133 PolicyDef {
2134 name: Identifier::new("MyPolicy"),
2135 value: PolicyValue::Assign(HexStringLiteral::new("AFAFAF".to_string())),
2136 span: Span::DUMMY,
2137 }
2138 );
2139
2140 input_to_ast_check!(
2141 PolicyDef,
2142 "policy_def_constructor",
2143 "policy MyPolicy {
2144 hash: 0x1234567890,
2145 script: 0x1234567890,
2146 ref: 0x1234567890,
2147 };",
2148 PolicyDef {
2149 name: Identifier::new("MyPolicy"),
2150 value: PolicyValue::Constructor(PolicyConstructor {
2151 fields: vec![
2152 PolicyField::Hash(DataExpr::HexString(HexStringLiteral::new(
2153 "1234567890".to_string()
2154 ))),
2155 PolicyField::Script(DataExpr::HexString(HexStringLiteral::new(
2156 "1234567890".to_string()
2157 ))),
2158 PolicyField::Ref(DataExpr::HexString(HexStringLiteral::new(
2159 "1234567890".to_string()
2160 ))),
2161 ],
2162 span: Span::DUMMY,
2163 }),
2164 span: Span::DUMMY,
2165 }
2166 );
2167
2168 input_to_ast_check!(
2169 AssetDef,
2170 "hex_hex",
2171 "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.0xef7a1ceb;",
2172 AssetDef {
2173 name: Identifier::new("MyToken"),
2174 policy: DataExpr::HexString(HexStringLiteral::new(
2175 "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
2176 )),
2177 asset_name: DataExpr::HexString(HexStringLiteral::new("ef7a1ceb".to_string())),
2178 span: Span::DUMMY,
2179 }
2180 );
2181
2182 input_to_ast_check!(
2183 AssetDef,
2184 "hex_string",
2185 "asset MyToken = 0xef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe.\"MY TOKEN\";",
2186 AssetDef {
2187 name: Identifier::new("MyToken"),
2188 policy: DataExpr::HexString(HexStringLiteral::new(
2189 "ef7a1cebb2dc7de884ddf82f8fcbc91fe9750dcd8c12ec7643a99bbe".to_string()
2190 )),
2191 asset_name: DataExpr::String(StringLiteral::new("MY TOKEN".to_string())),
2192 span: Span::DUMMY,
2193 }
2194 );
2195
2196 input_to_ast_check!(
2197 DataExpr,
2198 "type_and_literal",
2199 "MyToken(15)",
2200 DataExpr::FnCall(crate::ast::FnCall {
2201 callee: Identifier::new("MyToken"),
2202 args: vec![DataExpr::Number(15)],
2203 span: Span::DUMMY,
2204 })
2205 );
2206
2207 input_to_ast_check!(
2208 AnyAssetConstructor,
2209 "any_asset_constructor",
2210 "AnyAsset(0x1234567890, \"MyToken\", 15)",
2211 AnyAssetConstructor {
2212 policy: Box::new(DataExpr::HexString(HexStringLiteral::new(
2213 "1234567890".to_string()
2214 ))),
2215 asset_name: Box::new(DataExpr::String(StringLiteral::new("MyToken".to_string()))),
2216 amount: Box::new(DataExpr::Number(15)),
2217 span: Span::DUMMY,
2218 }
2219 );
2220
2221 input_to_ast_check!(
2222 AnyAssetConstructor,
2223 "any_asset_identifiers",
2224 "AnyAsset(my_policy, my_token, my_amount)",
2225 AnyAssetConstructor {
2226 policy: Box::new(DataExpr::Identifier(Identifier::new("my_policy"))),
2227 asset_name: Box::new(DataExpr::Identifier(Identifier::new("my_token"))),
2228 amount: Box::new(DataExpr::Identifier(Identifier::new("my_amount"))),
2229 span: Span::DUMMY,
2230 }
2231 );
2232
2233 input_to_ast_check!(
2234 AnyAssetConstructor,
2235 "any_asset_property_access",
2236 "AnyAsset(input1.policy, input1.asset_name, input1.amount)",
2237 AnyAssetConstructor {
2238 policy: Box::new(DataExpr::PropertyOp(PropertyOp {
2239 operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2240 property: Box::new(DataExpr::Identifier(Identifier::new("policy"))),
2241 span: Span::DUMMY,
2242 scope: None,
2243 })),
2244 asset_name: Box::new(DataExpr::PropertyOp(PropertyOp {
2245 operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2246 property: Box::new(DataExpr::Identifier(Identifier::new("asset_name"))),
2247 span: Span::DUMMY,
2248 scope: None,
2249 })),
2250 amount: Box::new(DataExpr::PropertyOp(PropertyOp {
2251 operand: Box::new(DataExpr::Identifier(Identifier::new("input1"))),
2252 property: Box::new(DataExpr::Identifier(Identifier::new("amount"))),
2253 span: Span::DUMMY,
2254 scope: None,
2255 })),
2256 span: Span::DUMMY,
2257 }
2258 );
2259
2260 input_to_ast_check!(DataExpr, "literal", "5", DataExpr::Number(5));
2261
2262 input_to_ast_check!(
2263 DataExpr,
2264 "add_op",
2265 "5 + var1",
2266 DataExpr::AddOp(AddOp {
2267 lhs: Box::new(DataExpr::Number(5)),
2268 rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2269 span: Span::DUMMY,
2270 })
2271 );
2272
2273 input_to_ast_check!(
2274 DataExpr,
2275 "mul_op",
2276 "5 * var1",
2277 DataExpr::MulOp(MulOp {
2278 lhs: Box::new(DataExpr::Number(5)),
2279 rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2280 span: Span::DUMMY,
2281 })
2282 );
2283
2284 input_to_ast_check!(
2286 DataExpr,
2287 "mul_binds_tighter_than_add",
2288 "2 + 3 * 4",
2289 DataExpr::AddOp(AddOp {
2290 lhs: Box::new(DataExpr::Number(2)),
2291 rhs: Box::new(DataExpr::MulOp(MulOp {
2292 lhs: Box::new(DataExpr::Number(3)),
2293 rhs: Box::new(DataExpr::Number(4)),
2294 span: Span::DUMMY,
2295 })),
2296 span: Span::DUMMY,
2297 })
2298 );
2299
2300 input_to_ast_check!(
2302 DataExpr,
2303 "mul_left_binds_tighter_than_add",
2304 "3 * 4 + 2",
2305 DataExpr::AddOp(AddOp {
2306 lhs: Box::new(DataExpr::MulOp(MulOp {
2307 lhs: Box::new(DataExpr::Number(3)),
2308 rhs: Box::new(DataExpr::Number(4)),
2309 span: Span::DUMMY,
2310 })),
2311 rhs: Box::new(DataExpr::Number(2)),
2312 span: Span::DUMMY,
2313 })
2314 );
2315
2316 input_to_ast_check!(
2317 DataExpr,
2318 "div_op",
2319 "10 / var1",
2320 DataExpr::DivOp(DivOp {
2321 lhs: Box::new(DataExpr::Number(10)),
2322 rhs: Box::new(DataExpr::Identifier(Identifier::new("var1"))),
2323 span: Span::DUMMY,
2324 })
2325 );
2326
2327 input_to_ast_check!(
2329 DataExpr,
2330 "div_binds_tighter_than_add",
2331 "2 + 8 / 4",
2332 DataExpr::AddOp(AddOp {
2333 lhs: Box::new(DataExpr::Number(2)),
2334 rhs: Box::new(DataExpr::DivOp(DivOp {
2335 lhs: Box::new(DataExpr::Number(8)),
2336 rhs: Box::new(DataExpr::Number(4)),
2337 span: Span::DUMMY,
2338 })),
2339 span: Span::DUMMY,
2340 })
2341 );
2342
2343 input_to_ast_check!(
2346 DataExpr,
2347 "div_binds_like_mul",
2348 "8 / 4 * 2",
2349 DataExpr::MulOp(MulOp {
2350 lhs: Box::new(DataExpr::DivOp(DivOp {
2351 lhs: Box::new(DataExpr::Number(8)),
2352 rhs: Box::new(DataExpr::Number(4)),
2353 span: Span::DUMMY,
2354 })),
2355 rhs: Box::new(DataExpr::Number(2)),
2356 span: Span::DUMMY,
2357 })
2358 );
2359
2360 input_to_ast_check!(
2361 DataExpr,
2362 "concat_op",
2363 r#"concat("hello", "world")"#,
2364 DataExpr::ConcatOp(ConcatOp {
2365 lhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
2366 value: "hello".to_string(),
2367 span: ast::Span::DUMMY,
2368 })),
2369 rhs: Box::new(ast::DataExpr::String(ast::StringLiteral {
2370 value: "world".to_string(),
2371 span: ast::Span::DUMMY,
2372 })),
2373 span: ast::Span::DUMMY,
2374 })
2375 );
2376
2377 input_to_ast_check!(
2378 DataExpr,
2379 "property_access",
2380 "subject.property",
2381 DataExpr::PropertyOp(PropertyOp {
2382 operand: Box::new(DataExpr::Identifier(Identifier::new("subject"))),
2383 property: Box::new(DataExpr::Identifier(Identifier::new("property"))),
2384 span: Span::DUMMY,
2385 scope: None,
2386 })
2387 );
2388
2389 input_to_ast_check!(
2390 DataExpr,
2391 "multiple_properties",
2392 "subject.property.subproperty",
2393 DataExpr::PropertyOp(PropertyOp {
2394 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2395 operand: Box::new(DataExpr::Identifier(Identifier::new("subject"))),
2396 property: Box::new(DataExpr::Identifier(Identifier::new("property"))),
2397 span: Span::DUMMY,
2398 scope: None,
2399 })),
2400 property: Box::new(DataExpr::Identifier(Identifier::new("subproperty"))),
2401 span: Span::DUMMY,
2402 scope: None,
2403 })
2404 );
2405
2406 input_to_ast_check!(DataExpr, "empty_parentheses", "()", DataExpr::Unit);
2407
2408 input_to_ast_check!(DataExpr, "nested_parentheses", "((()))", DataExpr::Unit);
2409
2410 input_to_ast_check!(
2411 DataExpr,
2412 "nested_arithmetic_expression",
2413 "(1 + ((6 - 3) + 4))",
2414 DataExpr::AddOp(AddOp {
2415 lhs: Box::new(DataExpr::Number(1)),
2416 rhs: Box::new(DataExpr::AddOp(AddOp {
2417 lhs: Box::new(DataExpr::SubOp(SubOp {
2418 lhs: Box::new(DataExpr::Number(6)),
2419 rhs: Box::new(DataExpr::Number(3)),
2420 span: Span::DUMMY,
2421 })),
2422 rhs: Box::new(DataExpr::Number(4)),
2423 span: Span::DUMMY,
2424 })),
2425 span: Span::DUMMY,
2426 })
2427 );
2428
2429 input_to_ast_check!(
2430 DataExpr,
2431 "negate_op",
2432 "!a",
2433 DataExpr::NegateOp(NegateOp {
2434 operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2435 span: Span::DUMMY,
2436 })
2437 );
2438
2439 input_to_ast_check!(
2440 DataExpr,
2441 "negate_precedence",
2442 "!a.b",
2443 DataExpr::NegateOp(NegateOp {
2444 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2445 operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2446 property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2447 span: Span::DUMMY,
2448 scope: None,
2449 })),
2450 span: Span::DUMMY,
2451 })
2452 );
2453
2454 input_to_ast_check!(
2455 DataExpr,
2456 "negate_override_precedence",
2457 "(!a).b",
2458 DataExpr::PropertyOp(PropertyOp {
2459 operand: Box::new(DataExpr::NegateOp(NegateOp {
2460 operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2461 span: Span::DUMMY,
2462 })),
2463 property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2464 span: Span::DUMMY,
2465 scope: None,
2466 })
2467 );
2468
2469 input_to_ast_check!(
2470 DataExpr,
2471 "overly_complex",
2472 "(1 + 5) - ((a.b.c - 3) + !d.f)",
2473 DataExpr::SubOp(SubOp {
2474 lhs: Box::new(DataExpr::AddOp(AddOp {
2475 lhs: Box::new(DataExpr::Number(1)),
2476 rhs: Box::new(DataExpr::Number(5)),
2477 span: Span::DUMMY,
2478 })),
2479 rhs: Box::new(DataExpr::AddOp(AddOp {
2480 lhs: Box::new(DataExpr::SubOp(SubOp {
2481 lhs: Box::new(DataExpr::PropertyOp(PropertyOp {
2482 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2483 operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2484 property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2485 span: Span::DUMMY,
2486 scope: None,
2487 })),
2488 property: Box::new(DataExpr::Identifier(Identifier::new("c"))),
2489 span: Span::DUMMY,
2490 scope: None,
2491 })),
2492 rhs: Box::new(DataExpr::Number(3)),
2493 span: Span::DUMMY,
2494 })),
2495 rhs: Box::new(DataExpr::NegateOp(NegateOp {
2496 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2497 operand: Box::new(DataExpr::Identifier(Identifier::new("d"))),
2498 property: Box::new(DataExpr::Identifier(Identifier::new("f"))),
2499 span: Span::DUMMY,
2500 scope: None,
2501 })),
2502 span: Span::DUMMY,
2503 })),
2504
2505 span: Span::DUMMY,
2506 })),
2507 span: Span::DUMMY,
2508 })
2509 );
2510
2511 input_to_ast_check!(
2512 DataExpr,
2513 "min_utxo_basic",
2514 "min_utxo(output1)",
2515 DataExpr::FnCall(crate::ast::FnCall {
2516 callee: Identifier::new("min_utxo"),
2517 args: vec![DataExpr::Identifier(Identifier::new("output1"))],
2518 span: Span::DUMMY,
2519 })
2520 );
2521
2522 input_to_ast_check!(
2523 DataExpr,
2524 "min_utxo_in_expression",
2525 "Ada(100) + min_utxo(my_output)",
2526 DataExpr::AddOp(AddOp {
2527 lhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2528 callee: Identifier::new("Ada"),
2529 args: vec![DataExpr::Number(100)],
2530 span: Span::DUMMY,
2531 })),
2532 rhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2533 callee: Identifier::new("min_utxo"),
2534 args: vec![DataExpr::Identifier(Identifier::new("my_output"))],
2535 span: Span::DUMMY,
2536 })),
2537 span: Span::DUMMY,
2538 })
2539 );
2540
2541 input_to_ast_check!(
2542 DataExpr,
2543 "tip_slot_basic",
2544 "tip_slot()",
2545 DataExpr::FnCall(crate::ast::FnCall {
2546 callee: Identifier::new("tip_slot"),
2547 args: vec![],
2548 span: Span::DUMMY,
2549 })
2550 );
2551
2552 input_to_ast_check!(
2553 DataExpr,
2554 "tip_slot_in_expression",
2555 "1000 + tip_slot()",
2556 DataExpr::AddOp(AddOp {
2557 lhs: Box::new(DataExpr::Number(1000)),
2558 rhs: Box::new(DataExpr::FnCall(crate::ast::FnCall {
2559 callee: Identifier::new("tip_slot"),
2560 args: vec![],
2561 span: Span::DUMMY,
2562 })),
2563 span: Span::DUMMY,
2564 })
2565 );
2566
2567 input_to_ast_check!(
2568 StructConstructor,
2569 "struct_constructor_record",
2570 "MyRecord {
2571 field1: 10,
2572 field2: abc,
2573 }",
2574 StructConstructor {
2575 r#type: Identifier::new("MyRecord"),
2576 case: VariantCaseConstructor {
2577 name: Identifier::new("Default"),
2578 fields: vec![
2579 RecordConstructorField {
2580 name: Identifier::new("field1"),
2581 value: Box::new(DataExpr::Number(10)),
2582 span: Span::DUMMY,
2583 },
2584 RecordConstructorField {
2585 name: Identifier::new("field2"),
2586 value: Box::new(DataExpr::Identifier(Identifier::new("abc"))),
2587 span: Span::DUMMY,
2588 },
2589 ],
2590 spread: None,
2591 scope: None,
2592 span: Span::DUMMY,
2593 },
2594 scope: None,
2595 span: Span::DUMMY,
2596 }
2597 );
2598
2599 input_to_ast_check!(
2600 StructConstructor,
2601 "struct_constructor_variant",
2602 "ShipCommand::MoveShip {
2603 delta_x: delta_x,
2604 delta_y: delta_y,
2605 }",
2606 StructConstructor {
2607 r#type: Identifier::new("ShipCommand"),
2608 case: VariantCaseConstructor {
2609 name: Identifier::new("MoveShip"),
2610 fields: vec![
2611 RecordConstructorField {
2612 name: Identifier::new("delta_x"),
2613 value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
2614 span: Span::DUMMY,
2615 },
2616 RecordConstructorField {
2617 name: Identifier::new("delta_y"),
2618 value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
2619 span: Span::DUMMY,
2620 },
2621 ],
2622 spread: None,
2623 scope: None,
2624 span: Span::DUMMY,
2625 },
2626 scope: None,
2627 span: Span::DUMMY,
2628 }
2629 );
2630
2631 input_to_ast_check!(
2632 StructConstructor,
2633 "struct_constructor_variant_with_spread",
2634 "ShipCommand::MoveShip {
2635 delta_x: delta_x,
2636 delta_y: delta_y,
2637 ...abc
2638 }",
2639 StructConstructor {
2640 r#type: Identifier::new("ShipCommand"),
2641 case: VariantCaseConstructor {
2642 name: Identifier::new("MoveShip"),
2643 fields: vec![
2644 RecordConstructorField {
2645 name: Identifier::new("delta_x"),
2646 value: Box::new(DataExpr::Identifier(Identifier::new("delta_x"))),
2647 span: Span::DUMMY,
2648 },
2649 RecordConstructorField {
2650 name: Identifier::new("delta_y"),
2651 value: Box::new(DataExpr::Identifier(Identifier::new("delta_y"))),
2652 span: Span::DUMMY,
2653 },
2654 ],
2655 spread: Some(Box::new(DataExpr::Identifier(Identifier::new(
2656 "abc".to_string()
2657 )))),
2658 scope: None,
2659 span: Span::DUMMY,
2660 },
2661 scope: None,
2662 span: Span::DUMMY,
2663 }
2664 );
2665
2666 input_to_ast_check!(
2667 LocalsBlock,
2668 "basic",
2669 "locals {
2670 a: 10,
2671 }",
2672 LocalsBlock {
2673 assigns: vec![LocalsAssign {
2674 name: Identifier::new("a"),
2675 value: DataExpr::Number(10),
2676 span: Span::DUMMY,
2677 },],
2678 span: Span::DUMMY,
2679 }
2680 );
2681
2682 input_to_ast_check!(
2683 LocalsBlock,
2684 "multiple",
2685 "locals {
2686 a: 10,
2687 b: 20,
2688 }",
2689 LocalsBlock {
2690 assigns: vec![
2691 LocalsAssign {
2692 name: Identifier::new("a"),
2693 value: DataExpr::Number(10),
2694 span: Span::DUMMY,
2695 },
2696 LocalsAssign {
2697 name: Identifier::new("b"),
2698 value: DataExpr::Number(20),
2699 span: Span::DUMMY,
2700 },
2701 ],
2702 span: Span::DUMMY,
2703 }
2704 );
2705
2706 input_to_ast_check!(
2707 LocalsBlock,
2708 "complex_expression",
2709 "locals {
2710 a: (10 + 20) - 8,
2711 b: a.b.c + (5 - d),
2712 }",
2713 LocalsBlock {
2714 assigns: vec![
2715 LocalsAssign {
2716 name: Identifier::new("a"),
2717 value: DataExpr::SubOp(SubOp {
2718 lhs: Box::new(DataExpr::AddOp(AddOp {
2719 lhs: Box::new(DataExpr::Number(10)),
2720 rhs: Box::new(DataExpr::Number(20)),
2721 span: Span::DUMMY,
2722 })),
2723 rhs: Box::new(DataExpr::Number(8)),
2724 span: Span::DUMMY,
2725 }),
2726 span: Span::DUMMY,
2727 },
2728 LocalsAssign {
2729 name: Identifier::new("b"),
2730 value: DataExpr::AddOp(AddOp {
2731 lhs: Box::new(DataExpr::PropertyOp(PropertyOp {
2732 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2733 operand: Box::new(DataExpr::Identifier(Identifier::new("a"))),
2734 property: Box::new(DataExpr::Identifier(Identifier::new("b"))),
2735 span: Span::DUMMY,
2736 scope: None,
2737 })),
2738 property: Box::new(DataExpr::Identifier(Identifier::new("c"))),
2739 span: Span::DUMMY,
2740 scope: None,
2741 })),
2742 rhs: Box::new(DataExpr::SubOp(SubOp {
2743 lhs: Box::new(DataExpr::Number(5)),
2744 rhs: Box::new(DataExpr::Identifier(Identifier::new("d"))),
2745 span: Span::DUMMY,
2746 })),
2747 span: Span::DUMMY,
2748 }),
2749 span: Span::DUMMY,
2750 },
2751 ],
2752 span: Span::DUMMY,
2753 }
2754 );
2755
2756 input_to_ast_check!(
2757 InputBlock,
2758 "single",
2759 r#"input source {}"#,
2760 InputBlock {
2761 many: false,
2762 name: "source".to_string(),
2763 fields: vec![],
2764 span: Span::DUMMY,
2765 }
2766 );
2767
2768 input_to_ast_check!(
2769 InputBlock,
2770 "multiple",
2771 r#"input* source {}"#,
2772 InputBlock {
2773 many: true,
2774 name: "source".to_string(),
2775 fields: vec![],
2776 span: Span::DUMMY,
2777 }
2778 );
2779
2780 input_to_ast_check!(
2781 OutputBlock,
2782 "output_block_anonymous",
2783 r#"output {
2784 to: my_party,
2785 amount: Ada(100),
2786 }"#,
2787 OutputBlock {
2788 name: None,
2789 optional: false,
2790 fields: vec![
2791 OutputBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
2792 "my_party".to_string(),
2793 )))),
2794 OutputBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
2795 callee: Identifier::new("Ada"),
2796 args: vec![DataExpr::Number(100)],
2797 span: Span::DUMMY,
2798 }))),
2799 ],
2800 span: Span::DUMMY,
2801 }
2802 );
2803
2804 input_to_ast_check!(
2805 ChainSpecificBlock,
2806 "chain_specific_block_cardano",
2807 "cardano::vote_delegation_certificate {
2808 drep: 0x1234567890,
2809 stake: 0x1234567890,
2810 }",
2811 ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::VoteDelegationCertificate(
2812 crate::cardano::VoteDelegationCertificate {
2813 drep: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
2814 stake: DataExpr::HexString(HexStringLiteral::new("1234567890".to_string())),
2815 span: Span::DUMMY,
2816 },
2817 ))
2818 );
2819
2820 input_to_ast_check!(
2821 ChainSpecificBlock,
2822 "chain_specific_block_cardano_treasury",
2823 "cardano::treasury_donation {
2824 coin: 20,
2825 }",
2826 ChainSpecificBlock::Cardano(crate::cardano::CardanoBlock::TreasuryDonation(
2827 crate::cardano::TreasuryDonationBlock {
2828 coin: DataExpr::Number(20),
2829 span: Span::DUMMY,
2830 },
2831 ))
2832 );
2833
2834 input_to_ast_check!(
2835 EnvDef,
2836 "basic",
2837 "env {
2838 field_a: Int,
2839 field_b: Bytes,
2840 }",
2841 EnvDef {
2842 fields: vec![
2843 EnvField {
2844 name: "field_a".to_string(),
2845 r#type: Type::Int,
2846 docstring: None,
2847 span: Span::DUMMY,
2848 },
2849 EnvField {
2850 name: "field_b".to_string(),
2851 r#type: Type::Bytes,
2852 docstring: None,
2853 span: Span::DUMMY,
2854 },
2855 ],
2856 span: Span::DUMMY,
2857 }
2858 );
2859
2860 input_to_ast_check!(
2861 TxDef,
2862 "empty",
2863 "tx my_tx() {}",
2864 TxDef {
2865 name: Identifier::new("my_tx"),
2866 docstring: None,
2867 parameters: ParameterList {
2868 parameters: vec![],
2869 span: Span::DUMMY,
2870 },
2871 locals: None,
2872 references: vec![],
2873 inputs: vec![],
2874 outputs: vec![],
2875 validity: None,
2876 mints: vec![],
2877 burns: vec![],
2878 signers: None,
2879 adhoc: vec![],
2880 collateral: vec![],
2881 metadata: None,
2882 scope: None,
2883 span: Span::DUMMY,
2884 }
2885 );
2886
2887 input_to_ast_check!(
2888 TxDef,
2889 "with_parameters",
2890 "tx my_tx(a: Int, b: Bytes) {}",
2891 TxDef {
2892 name: Identifier::new("my_tx"),
2893 docstring: None,
2894 parameters: ParameterList {
2895 parameters: vec![
2896 ParamDef {
2897 name: Identifier::new("a"),
2898 r#type: Type::Int,
2899 docstring: None,
2900 },
2901 ParamDef {
2902 name: Identifier::new("b"),
2903 r#type: Type::Bytes,
2904 docstring: None,
2905 },
2906 ],
2907 span: Span::DUMMY,
2908 },
2909 locals: None,
2910 references: vec![],
2911 inputs: vec![],
2912 outputs: vec![],
2913 validity: None,
2914 mints: vec![],
2915 burns: vec![],
2916 signers: None,
2917 adhoc: vec![],
2918 collateral: vec![],
2919 metadata: None,
2920 scope: None,
2921 span: Span::DUMMY,
2922 }
2923 );
2924
2925 input_to_ast_check!(
2926 Program,
2927 "basic",
2928 "party Abc; tx my_tx() {}",
2929 Program {
2930 parties: vec![PartyDef {
2931 name: Identifier::new("Abc"),
2932 docstring: None,
2933 span: Span::DUMMY,
2934 }],
2935 types: vec![],
2936 aliases: vec![],
2937 txs: vec![TxDef {
2938 name: Identifier::new("my_tx"),
2939 docstring: None,
2940 parameters: ParameterList {
2941 parameters: vec![],
2942 span: Span::DUMMY,
2943 },
2944 locals: None,
2945 references: vec![],
2946 inputs: vec![],
2947 outputs: vec![],
2948 validity: None,
2949 mints: vec![],
2950 burns: vec![],
2951 signers: None,
2952 adhoc: vec![],
2953 collateral: vec![],
2954 metadata: None,
2955 scope: None,
2956 span: Span::DUMMY,
2957 }],
2958 env: None,
2959 assets: vec![],
2960 policies: vec![],
2961 functions: vec![],
2962 span: Span::DUMMY,
2963 scope: None,
2964 }
2965 );
2966
2967 input_to_ast_check!(
2968 DataExpr,
2969 "array_index_literal",
2970 "my_list[0]",
2971 DataExpr::PropertyOp(PropertyOp {
2972 operand: Box::new(DataExpr::Identifier(Identifier::new("my_list"))),
2973 property: Box::new(DataExpr::Number(0)),
2974 span: Span::DUMMY,
2975 scope: None,
2976 })
2977 );
2978
2979 input_to_ast_check!(
2980 DataExpr,
2981 "array_index_variable",
2982 "my_list[index]",
2983 DataExpr::PropertyOp(PropertyOp {
2984 operand: Box::new(DataExpr::Identifier(Identifier::new("my_list"))),
2985 property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
2986 span: Span::DUMMY,
2987 scope: None,
2988 })
2989 );
2990
2991 input_to_ast_check!(
2992 DataExpr,
2993 "nested_array_index",
2994 "matrix[row][col]",
2995 DataExpr::PropertyOp(PropertyOp {
2996 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
2997 operand: Box::new(DataExpr::Identifier(Identifier::new("matrix"))),
2998 property: Box::new(DataExpr::Identifier(Identifier::new("row"))),
2999 span: Span::DUMMY,
3000 scope: None,
3001 })),
3002 property: Box::new(DataExpr::Identifier(Identifier::new("col"))),
3003 span: Span::DUMMY,
3004 scope: None,
3005 })
3006 );
3007
3008 input_to_ast_check!(
3009 DataExpr,
3010 "array_index_with_property_access",
3011 "items[index].name",
3012 DataExpr::PropertyOp(PropertyOp {
3013 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3014 operand: Box::new(DataExpr::Identifier(Identifier::new("items"))),
3015 property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
3016 span: Span::DUMMY,
3017 scope: None,
3018 })),
3019 property: Box::new(DataExpr::Identifier(Identifier::new("name"))),
3020 span: Span::DUMMY,
3021 scope: None,
3022 })
3023 );
3024
3025 input_to_ast_check!(
3026 DataExpr,
3027 "property_access_then_array_index",
3028 "object.list[0]",
3029 DataExpr::PropertyOp(PropertyOp {
3030 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3031 operand: Box::new(DataExpr::Identifier(Identifier::new("object"))),
3032 property: Box::new(DataExpr::Identifier(Identifier::new("list"))),
3033 span: Span::DUMMY,
3034 scope: None,
3035 })),
3036 property: Box::new(DataExpr::Number(0)),
3037 span: Span::DUMMY,
3038 scope: None,
3039 })
3040 );
3041
3042 input_to_ast_check!(
3043 DataExpr,
3044 "array_index_with_function_call",
3045 "values[min_utxo(output)]",
3046 DataExpr::PropertyOp(PropertyOp {
3047 operand: Box::new(DataExpr::Identifier(Identifier::new("values"))),
3048 property: Box::new(DataExpr::FnCall(crate::ast::FnCall {
3049 callee: Identifier::new("min_utxo"),
3050 args: vec![DataExpr::Identifier(Identifier::new("output"))],
3051 span: Span::DUMMY,
3052 })),
3053 span: Span::DUMMY,
3054 scope: None,
3055 })
3056 );
3057
3058 input_to_ast_check!(
3059 DataExpr,
3060 "mixed_property_and_index_access",
3061 "container.items[index].metadata[\"key\"]",
3062 DataExpr::PropertyOp(PropertyOp {
3063 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3064 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3065 operand: Box::new(DataExpr::PropertyOp(PropertyOp {
3066 operand: Box::new(DataExpr::Identifier(Identifier::new("container"))),
3067 property: Box::new(DataExpr::Identifier(Identifier::new("items"))),
3068 span: Span::DUMMY,
3069 scope: None,
3070 })),
3071 property: Box::new(DataExpr::Identifier(Identifier::new("index"))),
3072 span: Span::DUMMY,
3073 scope: None,
3074 })),
3075 property: Box::new(DataExpr::Identifier(Identifier::new("metadata"))),
3076 span: Span::DUMMY,
3077 scope: None,
3078 })),
3079 property: Box::new(DataExpr::String(StringLiteral::new("key".to_string()))),
3080 span: Span::DUMMY,
3081 scope: None,
3082 })
3083 );
3084
3085 input_to_ast_check!(
3086 ReferenceBlock,
3087 "with_datum_type",
3088 "reference oracle_data { ref: oracle_utxo, datum_is: OracleDatum, }",
3089 ReferenceBlock {
3090 name: "oracle_data".to_string(),
3091 r#ref: DataExpr::Identifier(Identifier::new("oracle_utxo")),
3092 datum_is: Some(Type::Custom(Identifier::new("OracleDatum"))),
3093 span: Span::DUMMY,
3094 }
3095 );
3096
3097 input_to_ast_check!(
3098 MapConstructor,
3099 "empty",
3100 "{}",
3101 MapConstructor {
3102 fields: vec![],
3103 span: Span::DUMMY,
3104 }
3105 );
3106
3107 input_to_ast_check!(
3108 PartyDef,
3109 "with_docstring",
3110 "/// the protocol treasury\nparty Treasury;",
3111 PartyDef {
3112 name: Identifier::new("Treasury"),
3113 docstring: Some("the protocol treasury".to_string()),
3114 span: Span::DUMMY,
3115 }
3116 );
3117
3118 input_to_ast_check!(
3119 EnvField,
3120 "with_docstring",
3121 "/// network magic\nnetwork: Int",
3122 EnvField {
3123 name: "network".to_string(),
3124 r#type: Type::Int,
3125 docstring: Some("network magic".to_string()),
3126 span: Span::DUMMY,
3127 }
3128 );
3129
3130 input_to_ast_check!(
3131 TxDef,
3132 "with_multiline_docstring",
3133 "/// transfer funds from a to b\n/// across two lines\ntx transfer() {}",
3134 TxDef {
3135 name: Identifier::new("transfer"),
3136 docstring: Some("transfer funds from a to b\nacross two lines".to_string()),
3137 parameters: ParameterList {
3138 parameters: vec![],
3139 span: Span::DUMMY,
3140 },
3141 locals: None,
3142 references: vec![],
3143 inputs: vec![],
3144 outputs: vec![],
3145 validity: None,
3146 mints: vec![],
3147 burns: vec![],
3148 signers: None,
3149 adhoc: vec![],
3150 collateral: vec![],
3151 metadata: None,
3152 scope: None,
3153 span: Span::DUMMY,
3154 }
3155 );
3156
3157 input_to_ast_check!(
3158 ParameterList,
3159 "with_param_docstrings",
3160 "(/// amount in lovelace\nquantity: Int, target: Address)",
3161 ParameterList {
3162 parameters: vec![
3163 ParamDef {
3164 name: Identifier::new("quantity"),
3165 r#type: Type::Int,
3166 docstring: Some("amount in lovelace".to_string()),
3167 },
3168 ParamDef {
3169 name: Identifier::new("target"),
3170 r#type: Type::Address,
3171 docstring: None,
3172 },
3173 ],
3174 span: Span::DUMMY,
3175 }
3176 );
3177
3178 #[test]
3179 fn test_spans_are_respected() {
3180 let program = parse_well_known_example("spans");
3181 assert_eq!(program.span, Span::new(0, 759));
3182
3183 assert_eq!(program.parties[0].span, Span::new(27, 41));
3184
3185 assert_eq!(program.types[0].span, Span::new(43, 77));
3186 }
3187
3188 fn make_snapshot_if_missing(example: &str, program: &Program) {
3189 let manifest_dir = env!("CARGO_MANIFEST_DIR");
3190 let path = format!("{}/../../examples/{}.ast", manifest_dir, example);
3191
3192 if !std::fs::exists(&path).unwrap() {
3193 let ast = serde_json::to_string_pretty(program).unwrap();
3194 std::fs::write(&path, ast).unwrap();
3195 }
3196 }
3197
3198 fn test_parsing_example(example: &str) {
3199 let program = parse_well_known_example(example);
3200 make_snapshot_if_missing(example, &program);
3201
3202 let manifest_dir = env!("CARGO_MANIFEST_DIR");
3203 let ast_file = format!("{}/../../examples/{}.ast", manifest_dir, example);
3204 let ast = std::fs::read_to_string(ast_file).unwrap();
3205
3206 let expected: Program = serde_json::from_str(&ast).unwrap();
3207
3208 assert_json_eq!(program, expected);
3209 }
3210
3211 #[macro_export]
3212 macro_rules! test_parsing {
3213 ($name:ident) => {
3214 paste! {
3215 #[test]
3216 fn [<test_example_ $name>]() {
3217 test_parsing_example(stringify!($name));
3218 }
3219 }
3220 };
3221 }
3222
3223 test_parsing!(lang_tour);
3224
3225 test_parsing!(tuples);
3226
3227 test_parsing!(transfer);
3228
3229 test_parsing!(swap);
3230
3231 test_parsing!(asteria);
3232
3233 test_parsing!(vesting);
3234
3235 test_parsing!(faucet);
3236
3237 test_parsing!(disordered);
3238
3239 test_parsing!(input_datum);
3240
3241 test_parsing!(withdrawal);
3242
3243 test_parsing!(env_vars);
3244
3245 test_parsing!(local_vars);
3246
3247 test_parsing!(cardano_witness);
3248
3249 test_parsing!(reference_script);
3250
3251 test_parsing!(map);
3252
3253 test_parsing!(burn);
3254
3255 test_parsing!(donation);
3256
3257 test_parsing!(list_concat);
3258
3259 test_parsing!(buidler_fest_2026);
3260
3261 test_parsing!(functions);
3262
3263 test_parsing!(nested_functions);
3264}