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