1use std::{collections::HashMap, rc::Rc};
7use thiserror::Error;
8
9use crate::ast::*;
10
11#[derive(Debug, thiserror::Error, miette::Diagnostic)]
12#[error("not in scope: {name}")]
13#[diagnostic(code(tx3::not_in_scope))]
14pub struct NotInScopeError {
15 pub name: String,
16
17 #[source_code]
18 src: Option<String>,
19
20 #[label]
21 span: Span,
22}
23
24#[derive(Debug, thiserror::Error, miette::Diagnostic)]
25#[error("invalid symbol, expected {expected}, got {got}")]
26#[diagnostic(code(tx3::invalid_symbol))]
27pub struct InvalidSymbolError {
28 pub expected: &'static str,
29 pub got: String,
30
31 #[source_code]
32 src: Option<String>,
33
34 #[label]
35 span: Span,
36}
37
38#[derive(Error, Debug, miette::Diagnostic)]
39pub enum Error {
40 #[error("duplicate definition: {0}")]
41 #[diagnostic(code(tx3::duplicate_definition))]
42 DuplicateDefinition(String),
43
44 #[error(transparent)]
45 #[diagnostic(transparent)]
46 NotInScope(#[from] NotInScopeError),
47
48 #[error("needs parent scope")]
49 #[diagnostic(code(tx3::needs_parent_scope))]
50 NeedsParentScope,
51
52 #[error(transparent)]
53 #[diagnostic(transparent)]
54 InvalidSymbol(#[from] InvalidSymbolError),
55}
56
57impl Error {
58 pub fn span(&self) -> &Span {
59 match self {
60 Self::NotInScope(x) => &x.span,
61 Self::InvalidSymbol(x) => &x.span,
62 _ => &Span::DUMMY,
63 }
64 }
65
66 pub fn src(&self) -> Option<&str> {
67 match self {
68 Self::NotInScope(x) => x.src.as_deref(),
69 _ => None,
70 }
71 }
72
73 pub fn not_in_scope(name: String, ast: &impl crate::parsing::AstNode) -> Self {
74 Self::NotInScope(NotInScopeError {
75 name,
76 src: None,
77 span: ast.span().clone(),
78 })
79 }
80
81 pub fn invalid_symbol(
82 expected: &'static str,
83 got: &Symbol,
84 ast: &impl crate::parsing::AstNode,
85 ) -> Self {
86 Self::InvalidSymbol(InvalidSymbolError {
87 expected,
88 got: format!("{:?}", got),
89 src: None,
90 span: ast.span().clone(),
91 })
92 }
93}
94
95#[derive(Debug, Default)]
96pub struct AnalyzeReport {
97 pub errors: Vec<Error>,
98}
99
100impl AnalyzeReport {
101 pub fn is_empty(&self) -> bool {
102 self.errors.is_empty()
103 }
104
105 pub fn ok(self) -> Result<(), Self> {
106 if self.is_empty() {
107 Ok(())
108 } else {
109 Err(self)
110 }
111 }
112}
113
114impl std::error::Error for AnalyzeReport {}
115
116impl std::fmt::Display for AnalyzeReport {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 write!(f, "AnalyzeReport {{ errors: {:?} }}", self.errors)
119 }
120}
121
122impl std::ops::Add for Error {
123 type Output = AnalyzeReport;
124
125 fn add(self, other: Self) -> Self::Output {
126 Self::Output {
127 errors: vec![self, other],
128 }
129 }
130}
131
132impl From<Error> for AnalyzeReport {
133 fn from(error: Error) -> Self {
134 Self {
135 errors: vec![error],
136 }
137 }
138}
139
140impl From<Vec<Error>> for AnalyzeReport {
141 fn from(errors: Vec<Error>) -> Self {
142 Self { errors }
143 }
144}
145
146impl std::ops::Add for AnalyzeReport {
147 type Output = AnalyzeReport;
148
149 fn add(self, other: Self) -> Self::Output {
150 [self, other].into_iter().collect()
151 }
152}
153
154impl FromIterator<Error> for AnalyzeReport {
155 fn from_iter<T: IntoIterator<Item = Error>>(iter: T) -> Self {
156 Self {
157 errors: iter.into_iter().collect(),
158 }
159 }
160}
161
162impl FromIterator<AnalyzeReport> for AnalyzeReport {
163 fn from_iter<T: IntoIterator<Item = AnalyzeReport>>(iter: T) -> Self {
164 Self {
165 errors: iter.into_iter().flat_map(|r| r.errors).collect(),
166 }
167 }
168}
169
170macro_rules! bail_report {
171 ($($args:expr),*) => {
172 { return AnalyzeReport::from(vec![$($args),*]); }
173 };
174}
175
176impl Scope {
177 pub fn new(parent: Option<Rc<Scope>>) -> Self {
178 Self {
179 symbols: HashMap::new(),
180 parent,
181 }
182 }
183
184 pub fn track_type_def(&mut self, type_: &TypeDef) {
185 self.symbols
186 .insert(type_.name.clone(), Symbol::TypeDef(Box::new(type_.clone())));
187 }
188
189 pub fn track_variant_case(&mut self, case: &VariantCase) {
190 self.symbols.insert(
191 case.name.clone(),
192 Symbol::VariantCase(Box::new(case.clone())),
193 );
194 }
195
196 pub fn track_record_field(&mut self, field: &RecordField) {
197 self.symbols.insert(
198 field.name.clone(),
199 Symbol::RecordField(Box::new(field.clone())),
200 );
201 }
202
203 pub fn track_party_def(&mut self, party: &PartyDef) {
204 self.symbols.insert(
205 party.name.clone(),
206 Symbol::PartyDef(Box::new(party.clone())),
207 );
208 }
209
210 pub fn track_policy_def(&mut self, policy: &PolicyDef) {
211 self.symbols.insert(
212 policy.name.clone(),
213 Symbol::PolicyDef(Box::new(policy.clone())),
214 );
215 }
216
217 pub fn track_asset_def(&mut self, asset: &AssetDef) {
218 self.symbols.insert(
219 asset.name.clone(),
220 Symbol::AssetDef(Box::new(asset.clone())),
221 );
222 }
223
224 pub fn track_param_var(&mut self, param: &str, ty: Type) {
225 self.symbols.insert(
226 param.to_string(),
227 Symbol::ParamVar(param.to_string(), Box::new(ty)),
228 );
229 }
230
231 pub fn track_input(&mut self, name: &str, ty: Type) {
232 self.symbols.insert(
233 name.to_string(),
234 Symbol::Input(name.to_string(), Box::new(ty)),
235 );
236 }
237
238 pub fn track_record_fields_for_type(&mut self, r#type: &Type) {
239 let schema = resolve_type_schema(r#type);
240
241 for (name, r#type) in schema {
242 self.track_record_field(&RecordField {
243 name,
244 r#type,
245 span: Span::DUMMY,
246 });
247 }
248 }
249
250 pub fn resolve(&self, name: &str) -> Option<Symbol> {
251 if let Some(symbol) = self.symbols.get(name) {
252 Some(symbol.clone())
253 } else if let Some(parent) = &self.parent {
254 parent.resolve(name)
255 } else {
256 None
257 }
258 }
259}
260
261fn resolve_type_schema(ty: &Type) -> Vec<(String, Type)> {
262 match ty {
263 Type::AnyAsset => {
264 vec![
265 ("amount".to_string(), Type::Int),
266 ("policy".to_string(), Type::Bytes),
267 ("asset_name".to_string(), Type::Bytes),
268 ]
269 }
270 Type::UtxoRef => {
271 vec![
272 ("tx_hash".to_string(), Type::Bytes),
273 ("output_index".to_string(), Type::Int),
274 ]
275 }
276 Type::Custom(identifier) => {
277 let def = identifier.symbol.as_ref().and_then(|s| s.as_type_def());
278
279 match def {
280 Some(ty) if ty.cases.len() == 1 => ty.cases[0]
281 .fields
282 .iter()
283 .map(|f| (f.name.clone(), f.r#type.clone()))
284 .collect(),
285 _ => vec![],
286 }
287 }
288 _ => vec![],
289 }
290}
291
292pub trait Analyzable {
297 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport;
305
306 fn is_resolved(&self) -> bool;
308}
309
310impl<T: Analyzable> Analyzable for Option<T> {
311 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
312 if let Some(item) = self {
313 item.analyze(parent)
314 } else {
315 AnalyzeReport::default()
316 }
317 }
318
319 fn is_resolved(&self) -> bool {
320 self.as_ref().map_or(true, |x| x.is_resolved())
321 }
322}
323
324impl<T: Analyzable> Analyzable for Box<T> {
325 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
326 self.as_mut().analyze(parent)
327 }
328
329 fn is_resolved(&self) -> bool {
330 self.as_ref().is_resolved()
331 }
332}
333
334impl<T: Analyzable> Analyzable for Vec<T> {
335 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
336 self.iter_mut()
337 .map(|item| item.analyze(parent.clone()))
338 .collect()
339 }
340
341 fn is_resolved(&self) -> bool {
342 self.iter().all(|x| x.is_resolved())
343 }
344}
345
346impl Analyzable for PolicyField {
347 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
348 match self {
349 PolicyField::Hash(x) => x.analyze(parent),
350 PolicyField::Script(x) => x.analyze(parent),
351 PolicyField::Ref(x) => x.analyze(parent),
352 }
353 }
354
355 fn is_resolved(&self) -> bool {
356 match self {
357 PolicyField::Hash(x) => x.is_resolved(),
358 PolicyField::Script(x) => x.is_resolved(),
359 PolicyField::Ref(x) => x.is_resolved(),
360 }
361 }
362}
363impl Analyzable for PolicyConstructor {
364 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
365 self.fields.analyze(parent)
366 }
367
368 fn is_resolved(&self) -> bool {
369 self.fields.is_resolved()
370 }
371}
372
373impl Analyzable for PolicyDef {
374 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
375 match &mut self.value {
376 PolicyValue::Constructor(x) => x.analyze(parent),
377 PolicyValue::Assign(_) => AnalyzeReport::default(),
378 }
379 }
380
381 fn is_resolved(&self) -> bool {
382 match &self.value {
383 PolicyValue::Constructor(x) => x.is_resolved(),
384 PolicyValue::Assign(_) => true,
385 }
386 }
387}
388
389impl Analyzable for DataBinaryOp {
390 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
391 let left = self.left.analyze(parent.clone());
392 let right = self.right.analyze(parent.clone());
393
394 left + right
395 }
396
397 fn is_resolved(&self) -> bool {
398 self.left.is_resolved() && self.right.is_resolved()
399 }
400}
401
402impl Analyzable for RecordConstructorField {
403 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
404 let name = self.name.analyze(parent.clone());
405 let value = self.value.analyze(parent.clone());
406
407 name + value
408 }
409
410 fn is_resolved(&self) -> bool {
411 self.name.is_resolved() && self.value.is_resolved()
412 }
413}
414
415impl Analyzable for VariantCaseConstructor {
416 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
417 let name = self.name.analyze(parent.clone());
418
419 let mut scope = Scope::new(parent);
420
421 let case = match &self.name.symbol {
422 Some(Symbol::VariantCase(x)) => x,
423 Some(x) => bail_report!(Error::invalid_symbol("VariantCase", x, &self.name)),
424 None => bail_report!(Error::not_in_scope(self.name.value.clone(), &self.name)),
425 };
426
427 for field in case.fields.iter() {
428 scope.track_record_field(field);
429 }
430
431 self.scope = Some(Rc::new(scope));
432
433 let fields = self.fields.analyze(self.scope.clone());
434
435 let spread = self.spread.analyze(self.scope.clone());
436
437 name + fields + spread
438 }
439
440 fn is_resolved(&self) -> bool {
441 self.name.is_resolved() && self.fields.is_resolved() && self.spread.is_resolved()
442 }
443}
444
445impl Analyzable for StructConstructor {
446 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
447 let r#type = self.r#type.analyze(parent.clone());
448
449 let mut scope = Scope::new(parent);
450
451 let type_def = match &self.r#type.symbol {
452 Some(Symbol::TypeDef(x)) => x,
453 Some(x) => bail_report!(Error::invalid_symbol("TypeDef", x, &self.r#type)),
454 _ => unreachable!(),
455 };
456
457 for case in type_def.cases.iter() {
458 scope.track_variant_case(case);
459 }
460
461 self.scope = Some(Rc::new(scope));
462
463 let case = self.case.analyze(self.scope.clone());
464
465 r#type + case
466 }
467
468 fn is_resolved(&self) -> bool {
469 self.r#type.is_resolved() && self.case.is_resolved()
470 }
471}
472
473impl Analyzable for ListConstructor {
474 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
475 self.elements.analyze(parent)
476 }
477
478 fn is_resolved(&self) -> bool {
479 self.elements.is_resolved()
480 }
481}
482
483impl Analyzable for DataExpr {
484 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
485 match self {
486 DataExpr::StructConstructor(x) => x.analyze(parent),
487 DataExpr::ListConstructor(x) => x.analyze(parent),
488 DataExpr::Identifier(x) => x.analyze(parent),
489 DataExpr::PropertyAccess(x) => x.analyze(parent),
490 DataExpr::BinaryOp(x) => x.analyze(parent),
491 _ => AnalyzeReport::default(),
492 }
493 }
494
495 fn is_resolved(&self) -> bool {
496 match self {
497 DataExpr::StructConstructor(x) => x.is_resolved(),
498 DataExpr::ListConstructor(x) => x.is_resolved(),
499 DataExpr::Identifier(x) => x.is_resolved(),
500 DataExpr::PropertyAccess(x) => x.is_resolved(),
501 DataExpr::BinaryOp(x) => x.is_resolved(),
502 _ => true,
503 }
504 }
505}
506
507impl Analyzable for AssetBinaryOp {
508 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
509 let left = self.left.analyze(parent.clone());
510 let right = self.right.analyze(parent.clone());
511
512 left + right
513 }
514
515 fn is_resolved(&self) -> bool {
516 self.left.is_resolved() && self.right.is_resolved()
517 }
518}
519
520impl Analyzable for StaticAssetConstructor {
521 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
522 let amount = self.amount.analyze(parent.clone());
523 let r#type = self.r#type.analyze(parent.clone());
524
525 amount + r#type
526 }
527
528 fn is_resolved(&self) -> bool {
529 self.amount.is_resolved() && self.r#type.is_resolved()
530 }
531}
532
533impl Analyzable for AnyAssetConstructor {
534 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
535 let policy = self.policy.analyze(parent.clone());
536 let asset_name = self.asset_name.analyze(parent.clone());
537 let amount = self.amount.analyze(parent.clone());
538
539 policy + asset_name + amount
540 }
541
542 fn is_resolved(&self) -> bool {
543 self.policy.is_resolved() && self.asset_name.is_resolved() && self.amount.is_resolved()
544 }
545}
546
547impl Analyzable for PropertyAccess {
548 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
549 let object = self.object.analyze(parent.clone());
550
551 let mut scope = Scope::new(parent);
552
553 if let Some(ty) = self.object.symbol.as_ref().and_then(|s| s.target_type()) {
554 scope.track_record_fields_for_type(&ty);
555 }
556
557 self.scope = Some(Rc::new(scope));
558
559 let path = self.path.analyze(self.scope.clone());
560
561 object + path
562 }
563
564 fn is_resolved(&self) -> bool {
565 self.object.is_resolved() && self.path.is_resolved()
566 }
567}
568
569impl Analyzable for AssetExpr {
570 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
571 match self {
572 AssetExpr::Identifier(x) => x.analyze(parent),
573 AssetExpr::StaticConstructor(x) => x.analyze(parent),
574 AssetExpr::AnyConstructor(x) => x.analyze(parent),
575 AssetExpr::BinaryOp(x) => x.analyze(parent),
576 AssetExpr::PropertyAccess(x) => x.analyze(parent),
577 }
578 }
579
580 fn is_resolved(&self) -> bool {
581 match self {
582 AssetExpr::Identifier(x) => x.is_resolved(),
583 AssetExpr::StaticConstructor(x) => x.is_resolved(),
584 AssetExpr::AnyConstructor(x) => x.is_resolved(),
585 AssetExpr::BinaryOp(x) => x.is_resolved(),
586 AssetExpr::PropertyAccess(x) => x.is_resolved(),
587 }
588 }
589}
590
591impl Analyzable for AddressExpr {
592 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
593 match self {
594 AddressExpr::Identifier(x) => x.analyze(parent),
595 _ => AnalyzeReport::default(),
596 }
597 }
598
599 fn is_resolved(&self) -> bool {
600 match self {
601 AddressExpr::Identifier(x) => x.is_resolved(),
602 _ => true,
603 }
604 }
605}
606impl Analyzable for Identifier {
607 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
608 let symbol = parent.and_then(|p| p.resolve(&self.value));
609
610 if symbol.is_none() {
611 bail_report!(Error::not_in_scope(self.value.clone(), self));
612 }
613
614 self.symbol = symbol;
615
616 AnalyzeReport::default()
617 }
618
619 fn is_resolved(&self) -> bool {
620 self.symbol.is_some()
621 }
622}
623
624impl Analyzable for Type {
625 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
626 match self {
627 Type::Custom(x) => x.analyze(parent),
628 Type::List(x) => x.analyze(parent),
629 _ => AnalyzeReport::default(),
630 }
631 }
632
633 fn is_resolved(&self) -> bool {
634 match self {
635 Type::Custom(x) => x.is_resolved(),
636 Type::List(x) => x.is_resolved(),
637 _ => true,
638 }
639 }
640}
641
642impl Analyzable for InputBlockField {
643 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
644 match self {
645 InputBlockField::From(x) => x.analyze(parent),
646 InputBlockField::DatumIs(x) => x.analyze(parent),
647 InputBlockField::MinAmount(x) => x.analyze(parent),
648 InputBlockField::Redeemer(x) => x.analyze(parent),
649 InputBlockField::Ref(x) => x.analyze(parent),
650 }
651 }
652
653 fn is_resolved(&self) -> bool {
654 match self {
655 InputBlockField::From(x) => x.is_resolved(),
656 InputBlockField::DatumIs(x) => x.is_resolved(),
657 InputBlockField::MinAmount(x) => x.is_resolved(),
658 InputBlockField::Redeemer(x) => x.is_resolved(),
659 InputBlockField::Ref(x) => x.is_resolved(),
660 }
661 }
662}
663
664impl Analyzable for InputBlock {
665 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
666 self.fields.analyze(parent)
667 }
668
669 fn is_resolved(&self) -> bool {
670 self.fields.is_resolved()
671 }
672}
673
674impl Analyzable for OutputBlockField {
675 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
676 match self {
677 OutputBlockField::To(x) => x.analyze(parent),
678 OutputBlockField::Amount(x) => x.analyze(parent),
679 OutputBlockField::Datum(x) => x.analyze(parent),
680 }
681 }
682
683 fn is_resolved(&self) -> bool {
684 match self {
685 OutputBlockField::To(x) => x.is_resolved(),
686 OutputBlockField::Amount(x) => x.is_resolved(),
687 OutputBlockField::Datum(x) => x.is_resolved(),
688 }
689 }
690}
691
692impl Analyzable for OutputBlock {
693 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
694 self.fields.analyze(parent)
695 }
696
697 fn is_resolved(&self) -> bool {
698 self.fields.is_resolved()
699 }
700}
701
702impl Analyzable for RecordField {
703 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
704 self.r#type.analyze(parent)
705 }
706
707 fn is_resolved(&self) -> bool {
708 self.r#type.is_resolved()
709 }
710}
711
712impl Analyzable for VariantCase {
713 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
714 self.fields.analyze(parent)
715 }
716
717 fn is_resolved(&self) -> bool {
718 self.fields.is_resolved()
719 }
720}
721
722impl Analyzable for TypeDef {
723 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
724 self.cases.analyze(parent)
725 }
726
727 fn is_resolved(&self) -> bool {
728 self.cases.is_resolved()
729 }
730}
731
732impl Analyzable for MintBlockField {
733 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
734 match self {
735 MintBlockField::Amount(x) => x.analyze(parent),
736 MintBlockField::Redeemer(x) => x.analyze(parent),
737 }
738 }
739
740 fn is_resolved(&self) -> bool {
741 match self {
742 MintBlockField::Amount(x) => x.is_resolved(),
743 MintBlockField::Redeemer(x) => x.is_resolved(),
744 }
745 }
746}
747
748impl Analyzable for MintBlock {
749 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
750 self.fields.analyze(parent)
751 }
752
753 fn is_resolved(&self) -> bool {
754 self.fields.is_resolved()
755 }
756}
757
758impl Analyzable for ChainSpecificBlock {
759 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
760 match self {
761 ChainSpecificBlock::Cardano(x) => x.analyze(parent),
762 }
763 }
764
765 fn is_resolved(&self) -> bool {
766 match self {
767 ChainSpecificBlock::Cardano(x) => x.is_resolved(),
768 }
769 }
770}
771
772impl Analyzable for TxDef {
773 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
774 let params = self
777 .parameters
778 .parameters
779 .iter_mut()
780 .map(|param| param.r#type.analyze(parent.clone()))
781 .collect::<AnalyzeReport>();
782
783 let input_types = self
784 .inputs
785 .iter_mut()
786 .flat_map(|input| input.fields.iter_mut())
787 .map(|field| match field {
788 InputBlockField::DatumIs(x) => x.analyze(parent.clone()),
789 _ => AnalyzeReport::default(),
790 })
791 .collect::<AnalyzeReport>();
792
793 let mut scope = Scope::new(parent.clone());
796
797 scope.symbols.insert("fees".to_string(), Symbol::Fees);
798
799 for param in self.parameters.parameters.iter() {
800 scope.track_param_var(¶m.name, param.r#type.clone());
801 }
802
803 for input in self.inputs.iter() {
804 scope.track_input(
805 &input.name,
806 input.datum_is().cloned().unwrap_or(Type::Undefined),
807 );
808 }
809
810 self.scope = Some(Rc::new(scope));
813
814 let inputs = self.inputs.analyze(self.scope.clone());
815
816 let outputs = self.outputs.analyze(self.scope.clone());
817
818 let mint = self.mint.analyze(self.scope.clone());
819
820 let adhoc = self.adhoc.analyze(self.scope.clone());
821
822 params + input_types + inputs + outputs + mint + adhoc
823 }
824
825 fn is_resolved(&self) -> bool {
826 self.inputs.is_resolved()
827 && self.outputs.is_resolved()
828 && self.mint.is_resolved()
829 && self.adhoc.is_resolved()
830 }
831}
832
833static ADA: std::sync::LazyLock<AssetDef> = std::sync::LazyLock::new(|| AssetDef {
834 name: "Ada".to_string(),
835 policy: None,
836 asset_name: None,
837 span: Span::DUMMY,
838});
839
840impl Analyzable for Program {
841 fn analyze(&mut self, parent: Option<Rc<Scope>>) -> AnalyzeReport {
842 let mut scope = Scope::new(parent);
843
844 for party in self.parties.iter() {
845 scope.track_party_def(party);
846 }
847
848 for policy in self.policies.iter() {
849 scope.track_policy_def(policy);
850 }
851
852 scope.track_asset_def(&ADA);
853
854 for asset in self.assets.iter() {
855 scope.track_asset_def(asset);
856 }
857
858 for type_def in self.types.iter() {
859 scope.track_type_def(type_def);
860 }
861
862 self.scope = Some(Rc::new(scope));
863
864 let policies = self.policies.analyze(self.scope.clone());
868
869 let types = self.types.analyze(self.scope.clone());
873
874 let txs = self.txs.analyze(self.scope.clone());
875
876 policies + types + txs
877 }
878
879 fn is_resolved(&self) -> bool {
880 self.policies.is_resolved() && self.types.is_resolved() && self.txs.is_resolved()
881 }
882}
883
884pub fn analyze(ast: &mut Program) -> AnalyzeReport {
898 ast.analyze(None)
899}