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) => Ok(("to".to_string(), x.into_lower(ctx)?)),
715 CardanoPublishBlockField::Amount(x) => Ok(("amount".to_string(), x.into_lower(ctx)?)),
716 CardanoPublishBlockField::Datum(x) => Ok(("datum".to_string(), x.into_lower(ctx)?)),
717 CardanoPublishBlockField::Version(x) => Ok(("version".to_string(), x.into_lower(ctx)?)),
718 CardanoPublishBlockField::Script(x) => Ok(("script".to_string(), x.into_lower(ctx)?)),
719 }
720 }
721}
722
723impl IntoLower for CardanoPublishBlock {
724 type Output = ir::AdHocDirective;
725
726 fn into_lower(
727 &self,
728 ctx: &crate::lowering::Context,
729 ) -> Result<Self::Output, crate::lowering::Error> {
730 let data = self
731 .fields
732 .iter()
733 .map(|x| x.into_lower(ctx))
734 .collect::<Result<_, _>>()?;
735
736 Ok(ir::AdHocDirective {
737 name: "cardano_publish".to_string(),
738 data,
739 })
740 }
741}
742
743#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
744pub enum CardanoBlock {
745 VoteDelegationCertificate(VoteDelegationCertificate),
746 StakeDelegationCertificate(StakeDelegationCertificate),
747 Withdrawal(WithdrawalBlock),
748 PlutusWitness(PlutusWitnessBlock),
749 NativeWitness(NativeWitnessBlock),
750 TreasuryDonation(TreasuryDonationBlock),
751 Publish(CardanoPublishBlock),
752}
753
754impl AstNode for CardanoBlock {
755 const RULE: Rule = Rule::cardano_block;
756
757 fn parse(pair: Pair<Rule>) -> Result<Self, Error> {
758 let mut inner = pair.into_inner();
759 let item = inner.next().unwrap();
760
761 match item.as_rule() {
762 Rule::cardano_vote_delegation_certificate => Ok(
763 CardanoBlock::VoteDelegationCertificate(VoteDelegationCertificate::parse(item)?),
764 ),
765 Rule::cardano_stake_delegation_certificate => Ok(
766 CardanoBlock::StakeDelegationCertificate(StakeDelegationCertificate::parse(item)?),
767 ),
768 Rule::cardano_withdrawal_block => {
769 Ok(CardanoBlock::Withdrawal(WithdrawalBlock::parse(item)?))
770 }
771 Rule::cardano_plutus_witness_block => Ok(CardanoBlock::PlutusWitness(
772 PlutusWitnessBlock::parse(item)?,
773 )),
774 Rule::cardano_native_witness_block => Ok(CardanoBlock::NativeWitness(
775 NativeWitnessBlock::parse(item)?,
776 )),
777 Rule::cardano_treasury_donation_block => Ok(CardanoBlock::TreasuryDonation(
778 TreasuryDonationBlock::parse(item)?,
779 )),
780 Rule::cardano_publish_block => {
781 Ok(CardanoBlock::Publish(CardanoPublishBlock::parse(item)?))
782 }
783 x => unreachable!("Unexpected rule in cardano_block: {:?}", x),
784 }
785 }
786
787 fn span(&self) -> &Span {
788 match self {
789 CardanoBlock::VoteDelegationCertificate(x) => x.span(),
790 CardanoBlock::StakeDelegationCertificate(x) => x.span(),
791 CardanoBlock::Withdrawal(x) => x.span(),
792 CardanoBlock::PlutusWitness(x) => x.span(),
793 CardanoBlock::NativeWitness(x) => x.span(),
794 CardanoBlock::TreasuryDonation(x) => x.span(),
795 CardanoBlock::Publish(x) => x.span(),
796 }
797 }
798}
799
800impl Analyzable for CardanoBlock {
801 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
802 match self {
803 CardanoBlock::VoteDelegationCertificate(x) => x.analyze(parent),
804 CardanoBlock::StakeDelegationCertificate(x) => x.analyze(parent),
805 CardanoBlock::Withdrawal(x) => x.analyze(parent),
806 CardanoBlock::PlutusWitness(x) => x.analyze(parent),
807 CardanoBlock::NativeWitness(x) => x.analyze(parent),
808 CardanoBlock::TreasuryDonation(x) => x.analyze(parent),
809 CardanoBlock::Publish(x) => x.analyze(parent),
810 }
811 }
812
813 fn is_resolved(&self) -> bool {
814 match self {
815 CardanoBlock::VoteDelegationCertificate(x) => x.is_resolved(),
816 CardanoBlock::StakeDelegationCertificate(x) => x.is_resolved(),
817 CardanoBlock::Withdrawal(x) => x.is_resolved(),
818 CardanoBlock::PlutusWitness(x) => x.is_resolved(),
819 CardanoBlock::NativeWitness(x) => x.is_resolved(),
820 Self::TreasuryDonation(x) => x.is_resolved(),
821 CardanoBlock::Publish(x) => x.is_resolved(),
822 }
823 }
824}
825
826impl IntoLower for CardanoBlock {
827 type Output = ir::AdHocDirective;
828
829 fn into_lower(
830 &self,
831 ctx: &crate::lowering::Context,
832 ) -> Result<<CardanoBlock as IntoLower>::Output, crate::lowering::Error> {
833 match self {
834 CardanoBlock::VoteDelegationCertificate(x) => x.into_lower(ctx),
835 CardanoBlock::StakeDelegationCertificate(x) => x.into_lower(ctx),
836 CardanoBlock::Withdrawal(x) => x.into_lower(ctx),
837 CardanoBlock::PlutusWitness(x) => x.into_lower(ctx),
838 CardanoBlock::NativeWitness(x) => x.into_lower(ctx),
839 CardanoBlock::TreasuryDonation(x) => x.into_lower(ctx),
840 CardanoBlock::Publish(x) => x.into_lower(ctx),
841 }
842 }
843}
844
845#[cfg(test)]
846mod tests {
847 use super::*;
848 use crate::{
849 analyzing::analyze,
850 ast::{self, *},
851 };
852 use pest::Parser;
853
854 macro_rules! input_to_ast_check {
855 ($ast:ty, $name:expr, $input:expr, $expected:expr) => {
856 paste::paste! {
857 #[test]
858 fn [<test_parse_ $ast:snake _ $name>]() {
859 let pairs = crate::parsing::Tx3Grammar::parse(<$ast>::RULE, $input).unwrap();
860 let single_match = pairs.into_iter().next().unwrap();
861 let result = <$ast>::parse(single_match).unwrap();
862
863 assert_eq!(result, $expected);
864 }
865 }
866 };
867 }
868
869 input_to_ast_check!(
870 PlutusWitnessBlock,
871 "basic",
872 "plutus_witness {
873 version: 3,
874 script: 0xABCDEF,
875 }",
876 PlutusWitnessBlock {
877 fields: vec![
878 PlutusWitnessField::Version(DataExpr::Number(3), Span::DUMMY),
879 PlutusWitnessField::Script(
880 DataExpr::HexString(HexStringLiteral::new("ABCDEF".to_string())),
881 Span::DUMMY
882 )
883 ],
884 span: Span::DUMMY,
885 }
886 );
887
888 input_to_ast_check!(
889 NativeWitnessBlock,
890 "basic",
891 "native_witness {
892 script: 0xABCDEF,
893 }",
894 NativeWitnessBlock {
895 fields: vec![NativeWitnessField::Script(
896 DataExpr::HexString(HexStringLiteral::new("ABCDEF".to_string())),
897 Span::DUMMY
898 )],
899 span: Span::DUMMY,
900 }
901 );
902
903 input_to_ast_check!(
904 TreasuryDonationBlock,
905 "basic",
906 "treasury_donation {
907 coin: 2020,
908 }",
909 TreasuryDonationBlock {
910 coin: DataExpr::Number(2020),
911 span: Span::DUMMY,
912 }
913 );
914
915 input_to_ast_check!(
916 CardanoPublishBlock,
917 "basic",
918 "publish {
919 to: Receiver,
920 amount: Ada(quantity),
921 version: 3,
922 script: 0xABCDEF,
923 }",
924 CardanoPublishBlock {
925 name: None,
926 fields: vec![
927 CardanoPublishBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
928 "Receiver"
929 )))),
930 CardanoPublishBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
931 callee: Identifier::new("Ada"),
932 args: vec![DataExpr::Identifier(Identifier::new("quantity"))],
933 span: Span::DUMMY,
934 }))),
935 CardanoPublishBlockField::Version(Box::new(DataExpr::Number(3))),
936 CardanoPublishBlockField::Script(Box::new(DataExpr::HexString(
937 HexStringLiteral::new("ABCDEF".to_string())
938 ))),
939 ],
940 span: Span::DUMMY,
941 }
942 );
943
944 input_to_ast_check!(
945 CardanoPublishBlock,
946 "basic_with_name",
947 "publish test_publish {
948 to: Receiver,
949 amount: Ada(quantity),
950 version: 3,
951 script: 0xABCDEF,
952 }",
953 CardanoPublishBlock {
954 name: Some(Identifier::new("test_publish")),
955 fields: vec![
956 CardanoPublishBlockField::To(Box::new(DataExpr::Identifier(Identifier::new(
957 "Receiver"
958 )))),
959 CardanoPublishBlockField::Amount(Box::new(DataExpr::FnCall(crate::ast::FnCall {
960 callee: Identifier::new("Ada"),
961 args: vec![DataExpr::Identifier(Identifier::new("quantity"))],
962 span: Span::DUMMY,
963 }))),
964 CardanoPublishBlockField::Version(Box::new(DataExpr::Number(3))),
965 CardanoPublishBlockField::Script(Box::new(DataExpr::HexString(
966 HexStringLiteral::new("ABCDEF".to_string())
967 ))),
968 ],
969 span: Span::DUMMY,
970 }
971 );
972
973 #[test]
974 fn test_treasury_donation_type() {
975 let mut ast = crate::parsing::parse_string(
976 r#"
977 tx test(quantity: Int) {
978 cardano::treasury_donation {
979 coin: quantity,
980 }
981 }
982 "#,
983 )
984 .unwrap();
985
986 let result = analyze(&mut ast);
987 assert!(result.errors.is_empty());
988 }
989
990 #[test]
991 fn test_treasury_donation_type_not_ok() {
992 let mut ast = crate::parsing::parse_string(
993 r#"
994 tx test(quantity: Bytes) {
995 cardano::treasury_donation {
996 coin: quantity,
997 }
998 }
999 "#,
1000 )
1001 .unwrap();
1002
1003 let result = analyze(&mut ast);
1004 assert!(!result.errors.is_empty());
1005 }
1006
1007 #[test]
1008 fn test_publish_type_ok() {
1009 let mut ast = crate::parsing::parse_string(
1010 r#"
1011 party Receiver;
1012
1013 tx test(quantity: Int) {
1014 cardano::publish {
1015 to: Receiver,
1016 amount: Ada(quantity),
1017 version: 3,
1018 script: 0xABCDEF,
1019 }
1020 }
1021 "#,
1022 )
1023 .unwrap();
1024
1025 let result = analyze(&mut ast);
1026 assert!(result.errors.is_empty());
1027 }
1028
1029 #[test]
1030 fn test_publish_type_with_name_ok() {
1031 let mut ast = crate::parsing::parse_string(
1032 r#"
1033 party Receiver;
1034
1035 tx test(quantity: Int) {
1036 cardano::publish deploy {
1037 to: Receiver,
1038 amount: Ada(quantity),
1039 version: 3,
1040 script: 0xABCDEF,
1041 }
1042 }
1043 "#,
1044 )
1045 .unwrap();
1046
1047 let result = analyze(&mut ast);
1048 assert!(result.errors.is_empty());
1049 }
1050}