1use std::{collections::HashMap, rc::Rc};
2
3use pest::iterators::Pair;
4use serde::{Deserialize, Serialize};
5
6use tx3_tir::model::v1beta0 as ir;
7
8use crate::{
9 analyzing::{Analyzable, AnalyzeReport},
10 ast::{DataExpr, Identifier, Scope, Span, Type},
11 lowering::IntoLower,
12 parsing::{AstNode, Error, Rule},
13};
14
15#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
16pub enum WithdrawalField {
17 From(Box<DataExpr>),
18 Amount(Box<DataExpr>),
19 Redeemer(Box<DataExpr>),
20}
21
22impl WithdrawalField {
23 fn key(&self) -> &str {
24 match self {
25 WithdrawalField::From(_) => "from",
26 WithdrawalField::Amount(_) => "amount",
27 WithdrawalField::Redeemer(_) => "redeemer",
28 }
29 }
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
33pub struct WithdrawalBlock {
34 pub fields: Vec<WithdrawalField>,
35 pub span: Span,
36}
37
38impl WithdrawalBlock {
39 pub(crate) fn find(&self, key: &str) -> Option<&WithdrawalField> {
40 self.fields.iter().find(|x| x.key() == key)
41 }
42}
43
44impl AstNode for WithdrawalField {
45 const RULE: Rule = Rule::cardano_withdrawal_field;
46
47 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
48 match pair.as_rule() {
49 Rule::cardano_withdrawal_from => {
50 let pair = pair.into_inner().next().unwrap();
51 Ok(WithdrawalField::From(DataExpr::parse(pair)?.into()))
52 }
53 Rule::cardano_withdrawal_amount => {
54 let pair = pair.into_inner().next().unwrap();
55 Ok(WithdrawalField::Amount(DataExpr::parse(pair)?.into()))
56 }
57 Rule::cardano_withdrawal_redeemer => {
58 let pair = pair.into_inner().next().unwrap();
59 Ok(WithdrawalField::Redeemer(DataExpr::parse(pair)?.into()))
60 }
61 x => unreachable!("Unexpected rule in cardano_withdrawal_field: {:?}", x),
62 }
63 }
64
65 fn span(&self) -> &Span {
66 match self {
67 Self::From(x) => x.span(),
68 Self::Amount(x) => x.span(),
69 Self::Redeemer(x) => x.span(),
70 }
71 }
72}
73
74impl AstNode for WithdrawalBlock {
75 const RULE: Rule = Rule::cardano_withdrawal_block;
76
77 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
78 let span = pair.as_span().into();
79 let inner = pair.into_inner();
80
81 let fields = inner
82 .map(|x| WithdrawalField::parse(x))
83 .collect::<Result<Vec<_>, _>>()?;
84
85 Ok(WithdrawalBlock { fields, span })
86 }
87
88 fn span(&self) -> &Span {
89 &self.span
90 }
91}
92
93impl Analyzable for WithdrawalField {
94 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
95 match self {
96 WithdrawalField::From(x) => x.analyze(parent),
97 WithdrawalField::Amount(x) => {
98 let amount = x.analyze(parent.clone());
99 let amount_type = AnalyzeReport::expect_data_expr_type(x, &Type::Int);
100 amount + amount_type
101 }
102 WithdrawalField::Redeemer(x) => x.analyze(parent),
103 }
104 }
105
106 fn is_resolved(&self) -> bool {
107 match self {
108 WithdrawalField::From(x) => x.is_resolved(),
109 WithdrawalField::Amount(x) => x.is_resolved(),
110 WithdrawalField::Redeemer(x) => x.is_resolved(),
111 }
112 }
113}
114
115impl Analyzable for WithdrawalBlock {
116 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
117 self.fields.analyze(parent)
118 }
119
120 fn is_resolved(&self) -> bool {
121 self.fields.is_resolved()
122 }
123}
124
125impl IntoLower for WithdrawalField {
126 type Output = ir::Expression;
127
128 fn into_lower(
129 &self,
130 ctx: &crate::lowering::Context,
131 ) -> Result<Self::Output, crate::lowering::Error> {
132 match self {
133 WithdrawalField::From(x) => x.into_lower(ctx),
134 WithdrawalField::Amount(x) => x.into_lower(ctx),
135 WithdrawalField::Redeemer(x) => x.into_lower(ctx),
136 }
137 }
138}
139
140impl IntoLower for WithdrawalBlock {
141 type Output = ir::AdHocDirective;
142
143 fn into_lower(
144 &self,
145 ctx: &crate::lowering::Context,
146 ) -> Result<Self::Output, crate::lowering::Error> {
147 let credential = self
148 .find("from")
149 .ok_or_else(|| {
150 crate::lowering::Error::MissingRequiredField("from".to_string(), "WithdrawalBlock")
151 })?
152 .into_lower(ctx)?;
153
154 let amount = self
155 .find("amount")
156 .ok_or_else(|| {
157 crate::lowering::Error::MissingRequiredField(
158 "amount".to_string(),
159 "WithdrawalBlock",
160 )
161 })?
162 .into_lower(ctx)?;
163
164 let redeemer = self
165 .find("redeemer")
166 .map(|r| r.into_lower(ctx))
167 .transpose()?
168 .unwrap_or(ir::Expression::None);
169
170 Ok(ir::AdHocDirective {
171 name: "withdrawal".to_string(),
172 data: std::collections::HashMap::from([
173 ("credential".to_string(), credential),
174 ("amount".to_string(), amount),
175 ("redeemer".to_string(), redeemer),
176 ]),
177 })
178 }
179}
180
181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
182pub struct VoteDelegationCertificate {
183 pub drep: DataExpr,
184 pub stake: DataExpr,
185 pub span: Span,
186}
187
188impl AstNode for VoteDelegationCertificate {
189 const RULE: Rule = Rule::cardano_vote_delegation_certificate;
190
191 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
192 let span = pair.as_span().into();
193 let mut inner = pair.into_inner();
194
195 Ok(VoteDelegationCertificate {
196 drep: DataExpr::parse(inner.next().unwrap())?,
197 stake: DataExpr::parse(inner.next().unwrap())?,
198 span,
199 })
200 }
201
202 fn span(&self) -> &Span {
203 &self.span
204 }
205}
206
207impl Analyzable for VoteDelegationCertificate {
208 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
209 let drep = self.drep.analyze(parent.clone());
210 let stake = self.stake.analyze(parent.clone());
211
212 drep + stake
213 }
214
215 fn is_resolved(&self) -> bool {
216 self.drep.is_resolved() && self.stake.is_resolved()
217 }
218}
219
220impl IntoLower for VoteDelegationCertificate {
221 type Output = ir::AdHocDirective;
222
223 fn into_lower(
224 &self,
225 ctx: &crate::lowering::Context,
226 ) -> Result<Self::Output, crate::lowering::Error> {
227 Ok(ir::AdHocDirective {
228 name: "vote_delegation_certificate".to_string(),
229 data: HashMap::from([
230 ("drep".to_string(), self.drep.into_lower(ctx)?),
231 ("stake".to_string(), self.stake.into_lower(ctx)?),
232 ]),
233 })
234 }
235}
236
237#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
238pub struct StakeDelegationCertificate {
239 pub pool: DataExpr,
240 pub stake: DataExpr,
241 pub span: Span,
242}
243
244impl AstNode for StakeDelegationCertificate {
245 const RULE: Rule = Rule::cardano_stake_delegation_certificate;
246
247 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
248 let span = pair.as_span().into();
249 let mut inner = pair.into_inner();
250
251 Ok(StakeDelegationCertificate {
252 pool: DataExpr::parse(inner.next().unwrap())?,
253 stake: DataExpr::parse(inner.next().unwrap())?,
254 span,
255 })
256 }
257
258 fn span(&self) -> &Span {
259 &self.span
260 }
261}
262
263impl Analyzable for StakeDelegationCertificate {
264 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
265 let pool = self.pool.analyze(parent.clone());
266 let stake = self.stake.analyze(parent.clone());
267
268 pool + stake
269 }
270
271 fn is_resolved(&self) -> bool {
272 self.pool.is_resolved() && self.stake.is_resolved()
273 }
274}
275
276impl IntoLower for StakeDelegationCertificate {
277 type Output = ir::AdHocDirective;
278
279 fn into_lower(
280 &self,
281 _ctx: &crate::lowering::Context,
282 ) -> Result<Self::Output, crate::lowering::Error> {
283 todo!("StakeDelegationCertificate lowering not implemented")
284 }
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
288pub enum PlutusWitnessField {
289 Version(DataExpr, Span),
290 Script(DataExpr, Span),
291}
292
293impl IntoLower for PlutusWitnessField {
294 type Output = (String, ir::Expression);
295
296 fn into_lower(
297 &self,
298 ctx: &crate::lowering::Context,
299 ) -> Result<Self::Output, crate::lowering::Error> {
300 match self {
301 PlutusWitnessField::Version(x, _) => Ok(("version".to_string(), x.into_lower(ctx)?)),
302 PlutusWitnessField::Script(x, _) => Ok(("script".to_string(), x.into_lower(ctx)?)),
303 }
304 }
305}
306
307impl AstNode for PlutusWitnessField {
308 const RULE: Rule = Rule::cardano_plutus_witness_field;
309
310 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
311 let span = pair.as_span().into();
312
313 match pair.as_rule() {
314 Rule::cardano_plutus_witness_version => {
315 Ok(PlutusWitnessField::Version(DataExpr::parse(pair)?, span))
316 }
317 Rule::cardano_plutus_witness_script => {
318 Ok(PlutusWitnessField::Script(DataExpr::parse(pair)?, span))
319 }
320 x => unreachable!("Unexpected rule in cardano_plutus_witness_field: {:?}", x),
321 }
322 }
323
324 fn span(&self) -> &Span {
325 match self {
326 PlutusWitnessField::Version(_, span) => span,
327 PlutusWitnessField::Script(_, span) => span,
328 }
329 }
330}
331
332impl Analyzable for PlutusWitnessField {
333 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
334 match self {
335 PlutusWitnessField::Version(x, _) => x.analyze(parent),
336 PlutusWitnessField::Script(x, _) => x.analyze(parent),
337 }
338 }
339
340 fn is_resolved(&self) -> bool {
341 match self {
342 PlutusWitnessField::Version(x, _) => x.is_resolved(),
343 PlutusWitnessField::Script(x, _) => x.is_resolved(),
344 }
345 }
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
349pub struct PlutusWitnessBlock {
350 pub fields: Vec<PlutusWitnessField>,
351 pub span: Span,
352}
353
354impl AstNode for PlutusWitnessBlock {
355 const RULE: Rule = Rule::cardano_plutus_witness_block;
356
357 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
358 let span = pair.as_span().into();
359 let inner = pair.into_inner();
360
361 let fields = inner
362 .map(|x| PlutusWitnessField::parse(x))
363 .collect::<Result<Vec<_>, _>>()?;
364
365 Ok(PlutusWitnessBlock { fields, span })
366 }
367
368 fn span(&self) -> &Span {
369 &self.span
370 }
371}
372
373impl Analyzable for PlutusWitnessBlock {
374 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
375 self.fields.analyze(parent)
376 }
377
378 fn is_resolved(&self) -> bool {
379 self.fields.is_resolved()
380 }
381}
382
383impl IntoLower for PlutusWitnessBlock {
384 type Output = ir::AdHocDirective;
385
386 fn into_lower(
387 &self,
388 ctx: &crate::lowering::Context,
389 ) -> Result<Self::Output, crate::lowering::Error> {
390 let data = self
391 .fields
392 .iter()
393 .map(|x| x.into_lower(ctx))
394 .collect::<Result<_, _>>()?;
395
396 Ok(ir::AdHocDirective {
397 name: "plutus_witness".to_string(),
398 data,
399 })
400 }
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
404pub enum NativeWitnessField {
405 Script(DataExpr, Span),
406}
407
408impl IntoLower for NativeWitnessField {
409 type Output = (String, ir::Expression);
410
411 fn into_lower(
412 &self,
413 ctx: &crate::lowering::Context,
414 ) -> Result<Self::Output, crate::lowering::Error> {
415 match self {
416 NativeWitnessField::Script(x, _) => Ok(("script".to_string(), x.into_lower(ctx)?)),
417 }
418 }
419}
420
421impl AstNode for NativeWitnessField {
422 const RULE: Rule = Rule::cardano_native_witness_field;
423
424 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
425 let span = pair.as_span().into();
426
427 match pair.as_rule() {
428 Rule::cardano_native_witness_script => {
429 Ok(NativeWitnessField::Script(DataExpr::parse(pair)?, span))
430 }
431 x => unreachable!("Unexpected rule in cardano_native_witness_field: {:?}", x),
432 }
433 }
434
435 fn span(&self) -> &Span {
436 match self {
437 NativeWitnessField::Script(_, span) => span,
438 }
439 }
440}
441
442impl Analyzable for NativeWitnessField {
443 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
444 match self {
445 NativeWitnessField::Script(x, _) => x.analyze(parent),
446 }
447 }
448
449 fn is_resolved(&self) -> bool {
450 match self {
451 NativeWitnessField::Script(x, _) => x.is_resolved(),
452 }
453 }
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
457pub struct NativeWitnessBlock {
458 pub fields: Vec<NativeWitnessField>,
459 pub span: Span,
460}
461
462impl AstNode for NativeWitnessBlock {
463 const RULE: Rule = Rule::cardano_native_witness_block;
464
465 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
466 let span = pair.as_span().into();
467 let inner = pair.into_inner();
468
469 let fields = inner
470 .map(|x| NativeWitnessField::parse(x))
471 .collect::<Result<Vec<_>, _>>()?;
472
473 Ok(NativeWitnessBlock { fields, span })
474 }
475
476 fn span(&self) -> &Span {
477 &self.span
478 }
479}
480
481impl Analyzable for NativeWitnessBlock {
482 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
483 self.fields.analyze(parent)
484 }
485
486 fn is_resolved(&self) -> bool {
487 self.fields.is_resolved()
488 }
489}
490
491impl IntoLower for NativeWitnessBlock {
492 type Output = ir::AdHocDirective;
493
494 fn into_lower(
495 &self,
496 ctx: &crate::lowering::Context,
497 ) -> Result<Self::Output, crate::lowering::Error> {
498 let data = self
499 .fields
500 .iter()
501 .map(|x| x.into_lower(ctx))
502 .collect::<Result<_, _>>()?;
503
504 Ok(ir::AdHocDirective {
505 name: "native_witness".to_string(),
506 data,
507 })
508 }
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
512pub struct TreasuryDonationBlock {
513 pub coin: DataExpr,
514 pub span: Span,
515}
516
517impl AstNode for TreasuryDonationBlock {
518 const RULE: Rule = Rule::cardano_treasury_donation_block;
519
520 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
521 let span = pair.as_span().into();
522
523 let mut inner = pair.into_inner();
524 let coin = DataExpr::parse(inner.next().unwrap())?;
525
526 Ok(TreasuryDonationBlock { coin, span })
527 }
528
529 fn span(&self) -> &Span {
530 &self.span
531 }
532}
533
534impl Analyzable for TreasuryDonationBlock {
535 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
536 let coin = self.coin.analyze(parent);
537 let coin_type = AnalyzeReport::expect_data_expr_type(&self.coin, &Type::Int);
538
539 coin + coin_type
540 }
541
542 fn is_resolved(&self) -> bool {
543 self.coin.is_resolved()
544 }
545}
546
547impl IntoLower for TreasuryDonationBlock {
548 type Output = ir::AdHocDirective;
549
550 fn into_lower(
551 &self,
552 ctx: &crate::lowering::Context,
553 ) -> Result<Self::Output, crate::lowering::Error> {
554 let coin = self.coin.into_lower(ctx)?;
555
556 Ok(ir::AdHocDirective {
557 name: "treasury_donation".to_string(),
558 data: std::collections::HashMap::from([("coin".to_string(), coin)]),
559 })
560 }
561}
562
563#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
564pub enum CardanoPublishBlockField {
565 To(Box<DataExpr>),
566 Amount(Box<DataExpr>),
567 Datum(Box<DataExpr>),
568 Version(Box<DataExpr>),
569 Script(Box<DataExpr>),
570}
571
572impl CardanoPublishBlockField {
573 fn key(&self) -> &str {
574 match self {
575 CardanoPublishBlockField::To(_) => "to",
576 CardanoPublishBlockField::Amount(_) => "amount",
577 CardanoPublishBlockField::Datum(_) => "datum",
578 CardanoPublishBlockField::Version(_) => "version",
579 CardanoPublishBlockField::Script(_) => "script",
580 }
581 }
582}
583
584#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
585pub struct CardanoPublishBlock {
586 pub name: Option<Identifier>,
587 pub fields: Vec<CardanoPublishBlockField>,
588 pub span: Span,
589}
590
591impl CardanoPublishBlock {
592 pub(crate) fn find(&self, key: &str) -> Option<&CardanoPublishBlockField> {
593 self.fields.iter().find(|x| x.key() == key)
594 }
595}
596
597impl AstNode for CardanoPublishBlockField {
598 const RULE: Rule = Rule::cardano_publish_block_field;
599
600 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
601 match pair.as_rule() {
602 Rule::cardano_publish_block_to => {
603 let pair = pair.into_inner().next().unwrap();
604 Ok(CardanoPublishBlockField::To(DataExpr::parse(pair)?.into()))
605 }
606 Rule::cardano_publish_block_amount => {
607 let pair = pair.into_inner().next().unwrap();
608 Ok(CardanoPublishBlockField::Amount(
609 DataExpr::parse(pair)?.into(),
610 ))
611 }
612 Rule::cardano_publish_block_datum => {
613 let pair = pair.into_inner().next().unwrap();
614 Ok(CardanoPublishBlockField::Datum(
615 DataExpr::parse(pair)?.into(),
616 ))
617 }
618 Rule::cardano_publish_block_version => {
619 let pair = pair.into_inner().next().unwrap();
620 Ok(CardanoPublishBlockField::Version(
621 DataExpr::parse(pair)?.into(),
622 ))
623 }
624 Rule::cardano_publish_block_script => {
625 let pair = pair.into_inner().next().unwrap();
626 Ok(CardanoPublishBlockField::Script(
627 DataExpr::parse(pair)?.into(),
628 ))
629 }
630 x => unreachable!("Unexpected rule in cardano_publish_block_field: {:?}", x),
631 }
632 }
633
634 fn span(&self) -> &Span {
635 match self {
636 Self::To(x) => x.span(),
637 Self::Amount(x) => x.span(),
638 Self::Datum(x) => x.span(),
639 Self::Version(x) => x.span(),
640 Self::Script(x) => x.span(),
641 }
642 }
643}
644
645impl AstNode for CardanoPublishBlock {
646 const RULE: Rule = Rule::cardano_publish_block;
647
648 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
649 let span = pair.as_span().into();
650 let mut inner = pair.into_inner();
651 let has_name = inner
652 .peek()
653 .map(|x| x.as_rule() == Rule::identifier)
654 .unwrap_or_default();
655
656 let name = if has_name {
657 Some(Identifier::parse(inner.next().unwrap())?)
658 } else {
659 None
660 };
661
662 let fields = inner
663 .map(|x| CardanoPublishBlockField::parse(x))
664 .collect::<Result<Vec<_>, _>>()?;
665
666 Ok(CardanoPublishBlock { name, fields, span })
667 }
668
669 fn span(&self) -> &Span {
670 &self.span
671 }
672}
673
674impl Analyzable for CardanoPublishBlockField {
675 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
676 match self {
677 CardanoPublishBlockField::To(x) => x.analyze(parent),
678 CardanoPublishBlockField::Amount(x) => x.analyze(parent),
679 CardanoPublishBlockField::Datum(x) => x.analyze(parent),
680 CardanoPublishBlockField::Version(x) => x.analyze(parent),
681 CardanoPublishBlockField::Script(x) => x.analyze(parent),
682 }
683 }
684
685 fn is_resolved(&self) -> bool {
686 match self {
687 CardanoPublishBlockField::To(x) => x.is_resolved(),
688 CardanoPublishBlockField::Amount(x) => x.is_resolved(),
689 CardanoPublishBlockField::Datum(x) => x.is_resolved(),
690 CardanoPublishBlockField::Version(x) => x.is_resolved(),
691 CardanoPublishBlockField::Script(x) => x.is_resolved(),
692 }
693 }
694}
695
696impl Analyzable for CardanoPublishBlock {
697 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
698 self.fields.analyze(parent)
699 }
700
701 fn is_resolved(&self) -> bool {
702 self.fields.is_resolved()
703 }
704}
705
706impl IntoLower for CardanoPublishBlockField {
707 type Output = (String, ir::Expression);
708
709 fn into_lower(
710 &self,
711 ctx: &crate::lowering::Context,
712 ) -> Result<Self::Output, crate::lowering::Error> {
713 match self {
714 CardanoPublishBlockField::To(x) => {
715 let ctx = ctx.enter_address_expr();
716 Ok(("to".to_string(), x.into_lower(&ctx)?))
717 }
718 CardanoPublishBlockField::Amount(x) => {
719 let ctx = ctx.enter_asset_expr();
720 Ok(("amount".to_string(), x.into_lower(&ctx)?))
721 }
722 CardanoPublishBlockField::Datum(x) => {
723 let ctx = ctx.enter_datum_expr();
724 Ok(("datum".to_string(), x.into_lower(&ctx)?))
725 }
726 CardanoPublishBlockField::Version(x) => Ok(("version".to_string(), x.into_lower(ctx)?)),
727 CardanoPublishBlockField::Script(x) => Ok(("script".to_string(), x.into_lower(ctx)?)),
728 }
729 }
730}
731
732impl IntoLower for CardanoPublishBlock {
733 type Output = ir::AdHocDirective;
734
735 fn into_lower(
736 &self,
737 ctx: &crate::lowering::Context,
738 ) -> Result<Self::Output, crate::lowering::Error> {
739 let data = self
740 .fields
741 .iter()
742 .map(|x| x.into_lower(ctx))
743 .collect::<Result<_, _>>()?;
744
745 Ok(ir::AdHocDirective {
746 name: "cardano_publish".to_string(),
747 data,
748 })
749 }
750}
751
752#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
753pub enum CardanoBlock {
754 VoteDelegationCertificate(VoteDelegationCertificate),
755 StakeDelegationCertificate(StakeDelegationCertificate),
756 Withdrawal(WithdrawalBlock),
757 PlutusWitness(PlutusWitnessBlock),
758 NativeWitness(NativeWitnessBlock),
759 TreasuryDonation(TreasuryDonationBlock),
760 Publish(CardanoPublishBlock),
761}
762
763impl AstNode for CardanoBlock {
764 const RULE: Rule = Rule::cardano_block;
765
766 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
767 let mut inner = pair.into_inner();
768 let item = inner.next().unwrap();
769
770 match item.as_rule() {
771 Rule::cardano_vote_delegation_certificate => Ok(
772 CardanoBlock::VoteDelegationCertificate(VoteDelegationCertificate::parse(item)?),
773 ),
774 Rule::cardano_stake_delegation_certificate => Ok(
775 CardanoBlock::StakeDelegationCertificate(StakeDelegationCertificate::parse(item)?),
776 ),
777 Rule::cardano_withdrawal_block => {
778 Ok(CardanoBlock::Withdrawal(WithdrawalBlock::parse(item)?))
779 }
780 Rule::cardano_plutus_witness_block => Ok(CardanoBlock::PlutusWitness(
781 PlutusWitnessBlock::parse(item)?,
782 )),
783 Rule::cardano_native_witness_block => Ok(CardanoBlock::NativeWitness(
784 NativeWitnessBlock::parse(item)?,
785 )),
786 Rule::cardano_treasury_donation_block => Ok(CardanoBlock::TreasuryDonation(
787 TreasuryDonationBlock::parse(item)?,
788 )),
789 Rule::cardano_publish_block => {
790 Ok(CardanoBlock::Publish(CardanoPublishBlock::parse(item)?))
791 }
792 x => unreachable!("Unexpected rule in cardano_block: {:?}", x),
793 }
794 }
795
796 fn span(&self) -> &Span {
797 match self {
798 CardanoBlock::VoteDelegationCertificate(x) => x.span(),
799 CardanoBlock::StakeDelegationCertificate(x) => x.span(),
800 CardanoBlock::Withdrawal(x) => x.span(),
801 CardanoBlock::PlutusWitness(x) => x.span(),
802 CardanoBlock::NativeWitness(x) => x.span(),
803 CardanoBlock::TreasuryDonation(x) => x.span(),
804 CardanoBlock::Publish(x) => x.span(),
805 }
806 }
807}
808
809impl Analyzable for CardanoBlock {
810 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
811 match self {
812 CardanoBlock::VoteDelegationCertificate(x) => x.analyze(parent),
813 CardanoBlock::StakeDelegationCertificate(x) => x.analyze(parent),
814 CardanoBlock::Withdrawal(x) => x.analyze(parent),
815 CardanoBlock::PlutusWitness(x) => x.analyze(parent),
816 CardanoBlock::NativeWitness(x) => x.analyze(parent),
817 CardanoBlock::TreasuryDonation(x) => x.analyze(parent),
818 CardanoBlock::Publish(x) => x.analyze(parent),
819 }
820 }
821
822 fn is_resolved(&self) -> bool {
823 match self {
824 CardanoBlock::VoteDelegationCertificate(x) => x.is_resolved(),
825 CardanoBlock::StakeDelegationCertificate(x) => x.is_resolved(),
826 CardanoBlock::Withdrawal(x) => x.is_resolved(),
827 CardanoBlock::PlutusWitness(x) => x.is_resolved(),
828 CardanoBlock::NativeWitness(x) => x.is_resolved(),
829 Self::TreasuryDonation(x) => x.is_resolved(),
830 CardanoBlock::Publish(x) => x.is_resolved(),
831 }
832 }
833}
834
835impl IntoLower for CardanoBlock {
836 type Output = ir::AdHocDirective;
837
838 fn into_lower(
839 &self,
840 ctx: &crate::lowering::Context,
841 ) -> Result<<CardanoBlock as IntoLower>::Output, crate::lowering::Error> {
842 match self {
843 CardanoBlock::VoteDelegationCertificate(x) => x.into_lower(ctx),
844 CardanoBlock::StakeDelegationCertificate(x) => x.into_lower(ctx),
845 CardanoBlock::Withdrawal(x) => x.into_lower(ctx),
846 CardanoBlock::PlutusWitness(x) => x.into_lower(ctx),
847 CardanoBlock::NativeWitness(x) => x.into_lower(ctx),
848 CardanoBlock::TreasuryDonation(x) => x.into_lower(ctx),
849 CardanoBlock::Publish(x) => x.into_lower(ctx),
850 }
851 }
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857 use crate::{
858 analyzing::analyze,
859 ast::{self, *},
860 };
861 use pest::Parser;
862
863 macro_rules! input_to_ast_check {
864 ($ast:ty, $name:expr, $input:expr, $expected:expr) => {
865 paste::paste! {
866 #[test]
867 fn [<test_parse_ $ast:snake _ $name>]() {
868 let pairs = crate::parsing::Tx3Grammar::parse(<$ast>::RULE, $input).unwrap();
869 let single_match = pairs.into_iter().next().unwrap();
870 let result = <$ast>::parse(single_match).unwrap();
871
872 assert_eq!(result, $expected);
873 }
874 }
875 };
876 }
877
878 input_to_ast_check!(
879 PlutusWitnessBlock,
880 "basic",
881 "plutus_witness {
882 version: 3,
883 script: 0xABCDEF,
884 }",
885 PlutusWitnessBlock {
886 fields: vec![
887 PlutusWitnessField::Version(DataExpr::Number(3), Span::DUMMY),
888 PlutusWitnessField::Script(
889 DataExpr::HexString(HexStringLiteral::new("ABCDEF".to_string())),
890 Span::DUMMY
891 )
892 ],
893 span: Span::DUMMY,
894 }
895 );
896
897 input_to_ast_check!(
898 NativeWitnessBlock,
899 "basic",
900 "native_witness {
901 script: 0xABCDEF,
902 }",
903 NativeWitnessBlock {
904 fields: vec![NativeWitnessField::Script(
905 DataExpr::HexString(HexStringLiteral::new("ABCDEF".to_string())),
906 Span::DUMMY
907 )],
908 span: Span::DUMMY,
909 }
910 );
911
912 input_to_ast_check!(
913 TreasuryDonationBlock,
914 "basic",
915 "treasury_donation {
916 coin: 2020,
917 }",
918 TreasuryDonationBlock {
919 coin: DataExpr::Number(2020),
920 span: Span::DUMMY,
921 }
922 );
923
924 input_to_ast_check!(
925 CardanoPublishBlock,
926 "basic",
927 "publish {
928 to: Receiver,
929 amount: Ada(quantity),
930 version: 3,
931 script: 0xABCDEF,
932 }",
933 CardanoPublishBlock {
934 name: None,
935 fields: vec![
936 CardanoPublishBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
937 "Receiver"
938 )))),
939 CardanoPublishBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
940 callee: Identifier::new("Ada"),
941 args: vec![DataExpr::Identifier(Identifier::new("quantity"))],
942 span: Span::DUMMY,
943 }))),
944 CardanoPublishBlockField::Version(Box::new(DataExpr::Number(3))),
945 CardanoPublishBlockField::Script(Box::new(DataExpr::HexString(
946 HexStringLiteral::new("ABCDEF".to_string())
947 ))),
948 ],
949 span: Span::DUMMY,
950 }
951 );
952
953 input_to_ast_check!(
954 CardanoPublishBlock,
955 "basic_with_name",
956 "publish test_publish {
957 to: Receiver,
958 amount: Ada(quantity),
959 version: 3,
960 script: 0xABCDEF,
961 }",
962 CardanoPublishBlock {
963 name: Some(Identifier::new("test_publish")),
964 fields: vec![
965 CardanoPublishBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
966 "Receiver"
967 )))),
968 CardanoPublishBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
969 callee: Identifier::new("Ada"),
970 args: vec![DataExpr::Identifier(Identifier::new("quantity"))],
971 span: Span::DUMMY,
972 }))),
973 CardanoPublishBlockField::Version(Box::new(DataExpr::Number(3))),
974 CardanoPublishBlockField::Script(Box::new(DataExpr::HexString(
975 HexStringLiteral::new("ABCDEF".to_string())
976 ))),
977 ],
978 span: Span::DUMMY,
979 }
980 );
981
982 #[test]
983 fn test_treasury_donation_type() {
984 let mut ast = crate::parsing::parse_string(
985 r#"
986 tx test(quantity: Int) {
987 cardano::treasury_donation {
988 coin: quantity,
989 }
990 }
991 "#,
992 )
993 .unwrap();
994
995 let result = analyze(&mut ast);
996 assert!(result.errors.is_empty());
997 }
998
999 #[test]
1000 fn test_treasury_donation_type_not_ok() {
1001 let mut ast = crate::parsing::parse_string(
1002 r#"
1003 tx test(quantity: Bytes) {
1004 cardano::treasury_donation {
1005 coin: quantity,
1006 }
1007 }
1008 "#,
1009 )
1010 .unwrap();
1011
1012 let result = analyze(&mut ast);
1013 assert!(!result.errors.is_empty());
1014 }
1015
1016 #[test]
1017 fn test_publish_type_ok() {
1018 let mut ast = crate::parsing::parse_string(
1019 r#"
1020 party Receiver;
1021
1022 tx test(quantity: Int) {
1023 cardano::publish {
1024 to: Receiver,
1025 amount: Ada(quantity),
1026 version: 3,
1027 script: 0xABCDEF,
1028 }
1029 }
1030 "#,
1031 )
1032 .unwrap();
1033
1034 let result = analyze(&mut ast);
1035 assert!(result.errors.is_empty());
1036 }
1037
1038 #[test]
1039 fn test_publish_type_with_name_ok() {
1040 let mut ast = crate::parsing::parse_string(
1041 r#"
1042 party Receiver;
1043
1044 tx test(quantity: Int) {
1045 cardano::publish deploy {
1046 to: Receiver,
1047 amount: Ada(quantity),
1048 version: 3,
1049 script: 0xABCDEF,
1050 }
1051 }
1052 "#,
1053 )
1054 .unwrap();
1055
1056 let result = analyze(&mut ast);
1057 assert!(result.errors.is_empty());
1058 }
1059}