diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index 120d6366..9148ad40 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -12,6 +12,19 @@ use crate::parsing::AstNode; const METADATA_MAX_SIZE_BYTES: usize = 64; +#[derive(Debug, Clone)] +pub struct Context { + pub target_type: Type, +} + +impl Default for Context { + fn default() -> Self { + Self { + target_type: Type::Undefined, + } + } +} + #[derive(Debug, thiserror::Error, miette::Diagnostic, PartialEq, Eq)] #[error("not in scope: {name}")] #[diagnostic(code(tx3::not_in_scope))] @@ -222,11 +235,13 @@ impl AnalyzeReport { } } - pub fn expect_data_expr_type(expr: &DataExpr, expected: &Type) -> Self { - if expr.target_type().as_ref() != Some(expected) { + pub fn expect_data_expr_type(expr: &DataExpr, expected: &Type, ctx: &mut Context) -> Self { + if expr.target_type(Some(ctx)).as_ref() != Some(expected) { Self::from(Error::invalid_target_type( expected, - expr.target_type().as_ref().unwrap_or(&Type::Undefined), + expr.target_type(Some(ctx)) + .as_ref() + .unwrap_or(&Type::Undefined), expr, )) } else { @@ -425,16 +440,16 @@ pub trait Analyzable { /// /// # Returns /// * `AnalyzeReport` of the analysis. Empty if no errors are found. - fn analyze(&mut self, parent: Option>) -> AnalyzeReport; + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport; /// Returns true if all of the symbols have been resolved . fn is_resolved(&self) -> bool; } impl Analyzable for Option { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { if let Some(item) = self { - item.analyze(parent) + item.analyze(parent, ctx) } else { AnalyzeReport::default() } @@ -446,8 +461,8 @@ impl Analyzable for Option { } impl Analyzable for Box { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.as_mut().analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.as_mut().analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -456,9 +471,9 @@ impl Analyzable for Box { } impl Analyzable for Vec { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { self.iter_mut() - .map(|item| item.analyze(parent.clone())) + .map(|item| item.analyze(parent.clone(), ctx)) .collect() } @@ -468,7 +483,7 @@ impl Analyzable for Vec { } impl Analyzable for PartyDef { - fn analyze(&mut self, _parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, _parent: Option>, _ctx: &mut Context) -> AnalyzeReport { AnalyzeReport::default() } @@ -478,11 +493,11 @@ impl Analyzable for PartyDef { } impl Analyzable for PolicyField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - PolicyField::Hash(x) => x.analyze(parent), - PolicyField::Script(x) => x.analyze(parent), - PolicyField::Ref(x) => x.analyze(parent), + PolicyField::Hash(x) => x.analyze(parent, ctx), + PolicyField::Script(x) => x.analyze(parent, ctx), + PolicyField::Ref(x) => x.analyze(parent, ctx), } } @@ -495,8 +510,8 @@ impl Analyzable for PolicyField { } } impl Analyzable for PolicyConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -505,9 +520,9 @@ impl Analyzable for PolicyConstructor { } impl Analyzable for PolicyDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match &mut self.value { - PolicyValue::Constructor(x) => x.analyze(parent), + PolicyValue::Constructor(x) => x.analyze(parent, ctx), PolicyValue::Assign(_) => AnalyzeReport::default(), } } @@ -521,11 +536,21 @@ impl Analyzable for PolicyDef { } impl Analyzable for AddOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let left = self.lhs.analyze(parent.clone()); - let right = self.rhs.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let left = self.lhs.analyze(parent.clone(), ctx); + let right = self.rhs.analyze(parent.clone(), ctx); - left + right + let left_type = self.lhs.target_type(Some(ctx)); + let right_type = self.rhs.target_type(Some(ctx)); + + let type_check = match (left_type.as_ref(), right_type.as_ref()) { + (Some(l), Some(r)) if l != r => { + AnalyzeReport::from(Error::invalid_target_type(l, r, self.rhs.as_ref())) + } + _ => AnalyzeReport::default(), + }; + + left + right + type_check } fn is_resolved(&self) -> bool { @@ -534,9 +559,9 @@ impl Analyzable for AddOp { } impl Analyzable for ConcatOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let left = self.lhs.analyze(parent.clone()); - let right = self.rhs.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let left = self.lhs.analyze(parent.clone(), ctx); + let right = self.rhs.analyze(parent.clone(), ctx); left + right } @@ -547,11 +572,21 @@ impl Analyzable for ConcatOp { } impl Analyzable for SubOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let left = self.lhs.analyze(parent.clone()); - let right = self.rhs.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let left = self.lhs.analyze(parent.clone(), ctx); + let right = self.rhs.analyze(parent.clone(), ctx); - left + right + let left_type = self.lhs.target_type(Some(ctx)); + let right_type = self.rhs.target_type(Some(ctx)); + + let type_check = match (left_type.as_ref(), right_type.as_ref()) { + (Some(l), Some(r)) if l != r => { + AnalyzeReport::from(Error::invalid_target_type(l, r, self.rhs.as_ref())) + } + _ => AnalyzeReport::default(), + }; + + left + right + type_check } fn is_resolved(&self) -> bool { @@ -560,8 +595,8 @@ impl Analyzable for SubOp { } impl Analyzable for NegateOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.operand.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.operand.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -570,9 +605,9 @@ impl Analyzable for NegateOp { } impl Analyzable for RecordConstructorField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let name = self.name.analyze(parent.clone()); - let value = self.value.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let name = self.name.analyze(parent.clone(), ctx); + let value = self.value.analyze(parent.clone(), ctx); name + value } @@ -583,11 +618,11 @@ impl Analyzable for RecordConstructorField { } impl Analyzable for VariantCaseConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { let name = if self.name.symbol.is_some() { AnalyzeReport::default() } else { - self.name.analyze(parent.clone()) + self.name.analyze(parent.clone(), ctx) }; let mut scope = Scope::new(parent); @@ -604,9 +639,9 @@ impl Analyzable for VariantCaseConstructor { self.scope = Some(Rc::new(scope)); - let fields = self.fields.analyze(self.scope.clone()); + let fields = self.fields.analyze(self.scope.clone(), ctx); - let spread = self.spread.analyze(self.scope.clone()); + let spread = self.spread.analyze(self.scope.clone(), ctx); name + fields + spread } @@ -617,8 +652,8 @@ impl Analyzable for VariantCaseConstructor { } impl Analyzable for StructConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let r#type = self.r#type.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let r#type = self.r#type.analyze(parent.clone(), ctx); let mut scope = Scope::new(parent); @@ -646,7 +681,7 @@ impl Analyzable for StructConstructor { self.scope = Some(Rc::new(scope)); - let case = self.case.analyze(self.scope.clone()); + let case = self.case.analyze(self.scope.clone(), ctx); r#type + case } @@ -657,8 +692,8 @@ impl Analyzable for StructConstructor { } impl Analyzable for ListConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.elements.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.elements.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -667,8 +702,8 @@ impl Analyzable for ListConstructor { } impl Analyzable for MapField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.key.analyze(parent.clone()) + self.value.analyze(parent.clone()) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.key.analyze(parent.clone(), ctx) + self.value.analyze(parent.clone(), ctx) } fn is_resolved(&self) -> bool { @@ -677,8 +712,8 @@ impl Analyzable for MapField { } impl Analyzable for MapConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -687,22 +722,20 @@ impl Analyzable for MapConstructor { } impl Analyzable for DataExpr { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - DataExpr::StructConstructor(x) => x.analyze(parent), - DataExpr::ListConstructor(x) => x.analyze(parent), - DataExpr::MapConstructor(x) => x.analyze(parent), - DataExpr::Identifier(x) => x.analyze(parent), - DataExpr::AddOp(x) => x.analyze(parent), - DataExpr::SubOp(x) => x.analyze(parent), - DataExpr::NegateOp(x) => x.analyze(parent), - DataExpr::PropertyOp(x) => x.analyze(parent), - DataExpr::StaticAssetConstructor(x) => x.analyze(parent), - DataExpr::AnyAssetConstructor(x) => x.analyze(parent), - DataExpr::MinUtxo(x) => x.analyze(parent), - DataExpr::SlotToTime(x) => x.analyze(parent), - DataExpr::TimeToSlot(x) => x.analyze(parent), - DataExpr::ConcatOp(x) => x.analyze(parent), + DataExpr::StructConstructor(x) => x.analyze(parent, ctx), + DataExpr::ListConstructor(x) => x.analyze(parent, ctx), + DataExpr::MapConstructor(x) => x.analyze(parent, ctx), + DataExpr::Identifier(x) => x.analyze(parent, ctx), + DataExpr::AddOp(x) => x.analyze(parent, ctx), + DataExpr::SubOp(x) => x.analyze(parent, ctx), + DataExpr::NegateOp(x) => x.analyze(parent, ctx), + DataExpr::PropertyOp(x) => x.analyze(parent, ctx), + DataExpr::StaticAssetConstructor(x) => x.analyze(parent, ctx), + DataExpr::AnyAssetConstructor(x) => x.analyze(parent, ctx), + DataExpr::MinUtxo(x) => x.analyze(parent, ctx), + DataExpr::ConcatOp(x) => x.analyze(parent, ctx), _ => AnalyzeReport::default(), } } @@ -729,9 +762,9 @@ impl Analyzable for DataExpr { } impl Analyzable for StaticAssetConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let amount = self.amount.analyze(parent.clone()); - let r#type = self.r#type.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let amount = self.amount.analyze(parent.clone(), ctx); + let r#type = self.r#type.analyze(parent.clone(), ctx); amount + r#type } @@ -742,10 +775,10 @@ impl Analyzable for StaticAssetConstructor { } impl Analyzable for AnyAssetConstructor { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let policy = self.policy.analyze(parent.clone()); - let asset_name = self.asset_name.analyze(parent.clone()); - let amount = self.amount.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let policy = self.policy.analyze(parent.clone(), ctx); + let asset_name = self.asset_name.analyze(parent.clone(), ctx); + let amount = self.amount.analyze(parent.clone(), ctx); policy + asset_name + amount } @@ -756,18 +789,18 @@ impl Analyzable for AnyAssetConstructor { } impl Analyzable for PropertyOp { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let object = self.operand.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let object = self.operand.analyze(parent.clone(), ctx); let mut scope = Scope::new(parent); - if let Some(ty) = self.operand.target_type() { + if let Some(ty) = self.operand.target_type(None) { scope.track_record_fields_for_type(&ty); } self.scope = Some(Rc::new(scope)); - let path = self.property.analyze(self.scope.clone()); + let path = self.property.analyze(self.scope.clone(), ctx); object + path } @@ -778,9 +811,9 @@ impl Analyzable for PropertyOp { } impl Analyzable for AddressExpr { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - AddressExpr::Identifier(x) => x.analyze(parent), + AddressExpr::Identifier(x) => x.analyze(parent, ctx), _ => AnalyzeReport::default(), } } @@ -794,12 +827,13 @@ impl Analyzable for AddressExpr { } impl Analyzable for AssetDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let policy = self.policy.analyze(parent.clone()); - let asset_name = self.asset_name.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let policy = self.policy.analyze(parent.clone(), ctx); + let asset_name = self.asset_name.analyze(parent.clone(), ctx); - let policy_type = AnalyzeReport::expect_data_expr_type(&self.policy, &Type::Bytes); - let asset_name_type = AnalyzeReport::expect_data_expr_type(&self.asset_name, &Type::Bytes); + let policy_type = AnalyzeReport::expect_data_expr_type(&self.policy, &Type::Bytes, ctx); + let asset_name_type = + AnalyzeReport::expect_data_expr_type(&self.asset_name, &Type::Bytes, ctx); policy + asset_name + policy_type + asset_name_type } @@ -810,7 +844,7 @@ impl Analyzable for AssetDef { } impl Analyzable for Identifier { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, _ctx: &mut Context) -> AnalyzeReport { let symbol = parent.and_then(|p| p.resolve(&self.value)); if symbol.is_none() { @@ -828,12 +862,12 @@ impl Analyzable for Identifier { } impl Analyzable for Type { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - Type::Custom(x) => x.analyze(parent), - Type::List(x) => x.analyze(parent), + Type::Custom(x) => x.analyze(parent, ctx), + Type::List(x) => x.analyze(parent, ctx), Type::Map(key_type, value_type) => { - key_type.analyze(parent.clone()) + value_type.analyze(parent) + key_type.analyze(parent.clone(), ctx) + value_type.analyze(parent, ctx) } _ => AnalyzeReport::default(), } @@ -850,13 +884,13 @@ impl Analyzable for Type { } impl Analyzable for InputBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - InputBlockField::From(x) => x.analyze(parent), - InputBlockField::DatumIs(x) => x.analyze(parent), - InputBlockField::MinAmount(x) => x.analyze(parent), - InputBlockField::Redeemer(x) => x.analyze(parent), - InputBlockField::Ref(x) => x.analyze(parent), + InputBlockField::From(x) => x.analyze(parent, ctx), + InputBlockField::DatumIs(x) => x.analyze(parent, ctx), + InputBlockField::MinAmount(x) => x.analyze(parent, ctx), + InputBlockField::Redeemer(x) => x.analyze(parent, ctx), + InputBlockField::Ref(x) => x.analyze(parent, ctx), } } @@ -872,8 +906,8 @@ impl Analyzable for InputBlockField { } impl Analyzable for InputBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -911,10 +945,13 @@ fn validate_metadata_value_size(expr: &DataExpr) -> Result<(), MetadataSizeLimit Ok(()) } -fn validate_metadata_key_type(expr: &DataExpr) -> Result<(), MetadataInvalidKeyTypeError> { +pub fn validate_metadata_key_type( + expr: &DataExpr, + target_type: Option<&Type>, +) -> Result<(), MetadataInvalidKeyTypeError> { match expr { DataExpr::Number(_) => Ok(()), - DataExpr::Identifier(id) => match id.target_type() { + DataExpr::Identifier(id) => match target_type { Some(Type::Int) => Ok(()), Some(other_type) => Err(MetadataInvalidKeyTypeError { key_type: format!("identifier of type {}", other_type), @@ -947,10 +984,12 @@ fn validate_metadata_key_type(expr: &DataExpr) -> Result<(), MetadataInvalidKeyT } impl Analyzable for MetadataBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let mut report = self.key.analyze(parent.clone()) + self.value.analyze(parent.clone()); + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + let mut report = + self.key.analyze(parent.clone(), ctx) + self.value.analyze(parent.clone(), ctx); - validate_metadata_key_type(&self.key) + let key_type = self.key.target_type(Some(ctx)); + validate_metadata_key_type(&self.key, key_type.as_ref()) .map_err(Error::MetadataInvalidKeyType) .err() .map(|e| report.errors.push(e)); @@ -969,8 +1008,8 @@ impl Analyzable for MetadataBlockField { } impl Analyzable for MetadataBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -979,10 +1018,10 @@ impl Analyzable for MetadataBlock { } impl Analyzable for ValidityBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - ValidityBlockField::SinceSlot(x) => x.analyze(parent), - ValidityBlockField::UntilSlot(x) => x.analyze(parent), + ValidityBlockField::SinceSlot(x) => x.analyze(parent, ctx), + ValidityBlockField::UntilSlot(x) => x.analyze(parent, ctx), } } fn is_resolved(&self) -> bool { @@ -994,8 +1033,8 @@ impl Analyzable for ValidityBlockField { } impl Analyzable for ValidityBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1004,11 +1043,28 @@ impl Analyzable for ValidityBlock { } impl Analyzable for OutputBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - OutputBlockField::To(x) => x.analyze(parent), - OutputBlockField::Amount(x) => x.analyze(parent), - OutputBlockField::Datum(x) => x.analyze(parent), + OutputBlockField::To(x) => x.analyze(parent, ctx), + OutputBlockField::Amount(x) => { + ctx.target_type = Type::AnyAsset; + + let expr_report = x.analyze(parent, ctx); + let ty = x.target_type(Some(ctx)); + + let type_report = if !matches!(ty.as_ref(), Some(&Type::AnyAsset)) { + AnalyzeReport::from(Error::invalid_target_type( + &Type::AnyAsset, + ty.as_ref().unwrap_or(&Type::Undefined), + x.as_ref(), + )) + } else { + AnalyzeReport::default() + }; + + expr_report + type_report + } + OutputBlockField::Datum(x) => x.analyze(parent, ctx), } } @@ -1022,13 +1078,12 @@ impl Analyzable for OutputBlockField { } impl Analyzable for OutputBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { validate_optional_output(self) .map(AnalyzeReport::from) .unwrap_or_else(|| AnalyzeReport::default()) - + self.fields.analyze(parent) + + self.fields.analyze(parent, ctx) } - fn is_resolved(&self) -> bool { self.fields.is_resolved() } @@ -1053,8 +1108,8 @@ fn validate_optional_output(output: &OutputBlock) -> Option { } impl Analyzable for RecordField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.r#type.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.r#type.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1063,8 +1118,8 @@ impl Analyzable for RecordField { } impl Analyzable for VariantCase { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1073,8 +1128,8 @@ impl Analyzable for VariantCase { } impl Analyzable for AliasDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.alias_type.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.alias_type.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1083,8 +1138,8 @@ impl Analyzable for AliasDef { } impl Analyzable for TypeDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.cases.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.cases.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1093,10 +1148,10 @@ impl Analyzable for TypeDef { } impl Analyzable for MintBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - MintBlockField::Amount(x) => x.analyze(parent), - MintBlockField::Redeemer(x) => x.analyze(parent), + MintBlockField::Amount(x) => x.analyze(parent, ctx), + MintBlockField::Redeemer(x) => x.analyze(parent, ctx), } } @@ -1109,8 +1164,8 @@ impl Analyzable for MintBlockField { } impl Analyzable for MintBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1119,8 +1174,8 @@ impl Analyzable for MintBlock { } impl Analyzable for SignersBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.signers.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.signers.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1129,8 +1184,8 @@ impl Analyzable for SignersBlock { } impl Analyzable for ReferenceBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.r#ref.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.r#ref.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1139,11 +1194,11 @@ impl Analyzable for ReferenceBlock { } impl Analyzable for CollateralBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - CollateralBlockField::From(x) => x.analyze(parent), - CollateralBlockField::MinAmount(x) => x.analyze(parent), - CollateralBlockField::Ref(x) => x.analyze(parent), + CollateralBlockField::From(x) => x.analyze(parent, ctx), + CollateralBlockField::MinAmount(x) => x.analyze(parent, ctx), + CollateralBlockField::Ref(x) => x.analyze(parent, ctx), } } @@ -1157,8 +1212,8 @@ impl Analyzable for CollateralBlockField { } impl Analyzable for CollateralBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1167,9 +1222,9 @@ impl Analyzable for CollateralBlock { } impl Analyzable for ChainSpecificBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { match self { - ChainSpecificBlock::Cardano(x) => x.analyze(parent), + ChainSpecificBlock::Cardano(x) => x.analyze(parent, ctx), } } @@ -1181,8 +1236,8 @@ impl Analyzable for ChainSpecificBlock { } impl Analyzable for LocalsAssign { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.value.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.value.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1191,8 +1246,8 @@ impl Analyzable for LocalsAssign { } impl Analyzable for LocalsBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.assigns.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.assigns.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1201,8 +1256,8 @@ impl Analyzable for LocalsBlock { } impl Analyzable for ParamDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.r#type.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.r#type.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1211,8 +1266,8 @@ impl Analyzable for ParamDef { } impl Analyzable for ParameterList { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.parameters.analyze(parent) + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { + self.parameters.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -1221,10 +1276,10 @@ impl Analyzable for ParameterList { } impl Analyzable for TxDef { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { // analyze static types before anything else - let params = self.parameters.analyze(parent.clone()); + let params = self.parameters.analyze(parent.clone(), ctx); // create the new scope and populate its symbols @@ -1241,7 +1296,7 @@ impl Analyzable for TxDef { let mut locals = self.locals.take().unwrap_or_default(); - let locals_report = locals.analyze(Some(parent.clone())); + let locals_report = locals.analyze(Some(parent.clone()), ctx); let parent = { let mut current = Scope::new(Some(parent.clone())); @@ -1257,7 +1312,7 @@ impl Analyzable for TxDef { Rc::new(current) }; - let inputs = self.inputs.analyze(Some(parent.clone())); + let inputs = self.inputs.analyze(Some(parent.clone()), ctx); let parent = { let mut current = Scope::new(Some(parent.clone())); @@ -1273,23 +1328,23 @@ impl Analyzable for TxDef { Rc::new(current) }; - let outputs = self.outputs.analyze(Some(parent.clone())); + let outputs = self.outputs.analyze(Some(parent.clone()), ctx); - let mints = self.mints.analyze(Some(parent.clone())); + let mints = self.mints.analyze(Some(parent.clone()), ctx); - let burns = self.burns.analyze(Some(parent.clone())); + let burns = self.burns.analyze(Some(parent.clone()), ctx); - let adhoc = self.adhoc.analyze(Some(parent.clone())); + let adhoc = self.adhoc.analyze(Some(parent.clone()), ctx); - let validity = self.validity.analyze(Some(parent.clone())); + let validity = self.validity.analyze(Some(parent.clone()), ctx); - let metadata = self.metadata.analyze(Some(parent.clone())); + let metadata = self.metadata.analyze(Some(parent.clone()), ctx); - let signers = self.signers.analyze(Some(parent.clone())); + let signers = self.signers.analyze(Some(parent.clone()), ctx); - let references = self.references.analyze(Some(parent.clone())); + let references = self.references.analyze(Some(parent.clone()), ctx); - let collateral = self.collateral.analyze(Some(parent.clone())); + let collateral = self.collateral.analyze(Some(parent.clone()), ctx); self.scope = Some(parent); @@ -1338,6 +1393,7 @@ fn resolve_types_and_aliases( scope_rc: &mut Rc, types: &mut Vec, aliases: &mut Vec, + ctx: &mut Context, ) -> (AnalyzeReport, AnalyzeReport) { let mut types_report = AnalyzeReport::default(); let mut aliases_report = AnalyzeReport::default(); @@ -1357,15 +1413,15 @@ fn resolve_types_and_aliases( scope.track_alias_def(alias_def); } - types_report = types.analyze(Some(scope_rc.clone())); - aliases_report = aliases.analyze(Some(scope_rc.clone())); + types_report = types.analyze(Some(scope_rc.clone()), ctx); + aliases_report = aliases.analyze(Some(scope_rc.clone()), ctx); } (types_report, aliases_report) } impl Analyzable for Program { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze(&mut self, parent: Option>, ctx: &mut Context) -> AnalyzeReport { let mut scope = Scope::new(parent); if let Some(env) = self.env.take() { @@ -1398,20 +1454,20 @@ impl Analyzable for Program { self.scope = Some(Rc::new(scope)); - let parties = self.parties.analyze(self.scope.clone()); + let parties = self.parties.analyze(self.scope.clone(), ctx); - let policies = self.policies.analyze(self.scope.clone()); + let policies = self.policies.analyze(self.scope.clone(), ctx); - let assets = self.assets.analyze(self.scope.clone()); + let assets = self.assets.analyze(self.scope.clone(), ctx); let mut types = self.types.clone(); let mut aliases = self.aliases.clone(); let scope_rc = self.scope.as_mut().unwrap(); - let (types, aliases) = resolve_types_and_aliases(scope_rc, &mut types, &mut aliases); + let (types, aliases) = resolve_types_and_aliases(scope_rc, &mut types, &mut aliases, ctx); - let txs = self.txs.analyze(self.scope.clone()); + let txs = self.txs.analyze(self.scope.clone(), ctx); parties + policies + types + aliases + txs + assets } @@ -1439,7 +1495,8 @@ impl Analyzable for Program { /// # Returns /// * `AnalyzeReport` of the analysis. Empty if no errors are found. pub fn analyze(ast: &mut Program) -> AnalyzeReport { - ast.analyze(None) + let mut ctx = Context::default(); + ast.analyze(None, &mut ctx) } #[cfg(test)] @@ -1592,54 +1649,6 @@ mod tests { assert!(result.errors.is_empty()); } - #[test] - fn test_optional_output_with_datum_error() { - let mut ast = crate::parsing::parse_string( - r#" - party Alice; - type MyDatum { - field1: Int, - } - tx test() { - output ? my_output { - to: Alice, - amount: Ada(1), - datum: MyDatum { field1: 1, }, - } - } - "#, - ) - .unwrap(); - - let report = analyze(&mut ast); - - assert!(!report.errors.is_empty()); - assert!(report - .errors - .iter() - .any(|e| matches!(e, Error::InvalidOptionalOutput(_)))); - } - - #[test] - fn test_optional_output_ok() { - let mut ast = crate::parsing::parse_string( - r#" - party Alice; - - tx test() { - output ? my_output { - to: Alice, - amount: Ada(0), - } - } - "#, - ) - .unwrap(); - - let report = analyze(&mut ast); - assert!(report.errors.is_empty()); - } - #[test] fn test_metadata_value_size_validation_string_within_limit() { let mut ast = crate::parsing::parse_string( @@ -1871,4 +1880,135 @@ mod tests { ), } } + + #[test] + fn test_output_amount_must_be_any_asset_type() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + tx test() { + output my_output { + to: Alice, + amount: 123, + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert!(!result.errors.is_empty()); + + assert_eq!( + result.errors[0], + Error::InvalidTargetType(InvalidTargetTypeError { + expected: "AnyAsset".to_string(), + got: "Int".to_string(), + src: None, + span: Span::DUMMY, + }) + ); + } + + #[test] + fn test_output_amount_accepts_any_asset_expressions() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + tx test(quantity: Int) { + output { + to: Alice, + amount: AnyAsset(0x123, 0x456, 100), + } + output { + to: Alice, + amount: Ada(quantity), + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert!(result.errors.is_empty()); + } + + #[test] + fn test_output_mixed_amount_expressions() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + tx test() { + input source { + from: Alice, + min_amount: Ada(100), + } + output { + to: Alice, + amount: source - 50, + } + } + "#, + ) + .unwrap(); + + let result = analyze(&mut ast); + assert!(!result.errors.is_empty()); + + assert_eq!( + result.errors[0], + Error::InvalidTargetType(InvalidTargetTypeError { + expected: "AnyAsset".to_string(), + got: "Int".to_string(), + src: None, + span: Span::DUMMY, + }) + ); + } + + #[test] + fn test_optional_output_with_datum_error() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + type MyDatum { + field1: Int, + } + tx test() { + output ? my_output { + to: Alice, + amount: Ada(1), + datum: MyDatum { field1: 1, }, + } + } + "#, + ) + .unwrap(); + let report = analyze(&mut ast); + + assert!(!report.errors.is_empty()); + assert!(report + .errors + .iter() + .any(|e| matches!(e, Error::InvalidOptionalOutput(_)))); + } + + #[test] + fn test_optional_output_ok() { + let mut ast = crate::parsing::parse_string( + r#" + party Alice; + tx test() { + output ? my_output { + to: Alice, + amount: Ada(0), + } + } + "#, + ) + .unwrap(); + + let report = analyze(&mut ast); + assert!(report.errors.is_empty()); + } } diff --git a/crates/tx3-lang/src/ast.rs b/crates/tx3-lang/src/ast.rs index b8620754..e02fc748 100644 --- a/crates/tx3-lang/src/ast.rs +++ b/crates/tx3-lang/src/ast.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, rc::Rc}; -use crate::cardano::PlutusWitnessBlock; +use crate::analyzing::Context; #[derive(Debug, PartialEq, Eq)] pub struct Scope { @@ -120,11 +120,20 @@ impl Symbol { } } - pub fn target_type(&self) -> Option { + pub fn target_type(&self, ctx: Option<&Context>) -> Option { match self { Symbol::ParamVar(_, ty) => Some(ty.as_ref().clone()), Symbol::RecordField(x) => Some(x.r#type.clone()), - Symbol::Input(x) => x.datum_is().cloned(), + Symbol::Input(x) => { + let datum_type = x.datum_is().cloned(); + + match ctx { + Some(ctx) if ctx.target_type == Type::AnyAsset => Some(Type::AnyAsset), + _ => datum_type, + } + } + Symbol::LocalExpr(expr) => expr.target_type(ctx), + Symbol::Fees => Some(Type::AnyAsset), x => { dbg!(x); None @@ -161,8 +170,8 @@ impl Identifier { } } - pub fn target_type(&self) -> Option { - self.symbol.as_ref().and_then(|x| x.target_type()) + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.symbol.as_ref().and_then(|x| x.target_type(ctx)) } } @@ -546,7 +555,7 @@ pub struct StaticAssetConstructor { } impl StaticAssetConstructor { - pub fn target_type(&self) -> Option { + pub fn target_type(&self, _ctx: Option<&Context>) -> Option { Some(Type::AnyAsset) } } @@ -560,7 +569,7 @@ pub struct AnyAssetConstructor { } impl AnyAssetConstructor { - pub fn target_type(&self) -> Option { + pub fn target_type(&self, _ctx: Option<&Context>) -> Option { Some(Type::AnyAsset) } } @@ -584,8 +593,8 @@ pub struct StructConstructor { } impl StructConstructor { - pub fn target_type(&self) -> Option { - self.r#type.symbol.as_ref().and_then(|x| x.target_type()) + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.r#type.symbol.as_ref().and_then(|x| x.target_type(ctx)) } } @@ -617,8 +626,8 @@ pub struct ListConstructor { } impl ListConstructor { - pub fn target_type(&self) -> Option { - self.elements.first().and_then(|x| x.target_type()) + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.elements.first().and_then(|x| x.target_type(ctx)) } } @@ -630,8 +639,8 @@ pub struct MapField { } impl MapField { - pub fn target_type(&self) -> Option { - self.key.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.key.target_type(ctx) } } @@ -642,10 +651,10 @@ pub struct MapConstructor { } impl MapConstructor { - pub fn target_type(&self) -> Option { + pub fn target_type(&self, ctx: Option<&Context>) -> Option { if let Some(first_field) = self.fields.first() { - let key_type = first_field.key.target_type()?; - let value_type = first_field.value.target_type()?; + let key_type = first_field.key.target_type(ctx)?; + let value_type = first_field.value.target_type(ctx)?; Some(Type::Map(Box::new(key_type), Box::new(value_type))) } else { None @@ -667,8 +676,8 @@ pub struct NegateOp { } impl NegateOp { - pub fn target_type(&self) -> Option { - self.operand.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.operand.target_type(ctx) } } @@ -684,8 +693,8 @@ pub struct PropertyOp { } impl PropertyOp { - pub fn target_type(&self) -> Option { - self.property.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.property.target_type(ctx) } } @@ -697,8 +706,8 @@ pub struct AddOp { } impl AddOp { - pub fn target_type(&self) -> Option { - self.lhs.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.lhs.target_type(ctx) } } @@ -710,8 +719,8 @@ pub struct SubOp { } impl SubOp { - pub fn target_type(&self) -> Option { - self.lhs.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.lhs.target_type(ctx) } } @@ -723,8 +732,8 @@ pub struct ConcatOp { } impl ConcatOp { - pub fn target_type(&self) -> Option { - self.lhs.target_type() + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + self.lhs.target_type(ctx) } } @@ -762,28 +771,39 @@ impl DataExpr { } } - pub fn target_type(&self) -> Option { + pub fn target_type(&self, ctx: Option<&Context>) -> Option { + let default_ctx = Context::default(); + let ctx = ctx.unwrap_or(&default_ctx); match self { - DataExpr::Identifier(x) => x.target_type(), + DataExpr::Identifier(x) => match &x.symbol { + Some(Symbol::Input(def)) => { + if ctx.target_type == Type::AnyAsset { + Some(Type::AnyAsset) + } else { + def.datum_is().cloned() + } + } + _ => x.target_type(Some(ctx)), + }, DataExpr::None => Some(Type::Undefined), DataExpr::Unit => Some(Type::Unit), DataExpr::Number(_) => Some(Type::Int), DataExpr::Bool(_) => Some(Type::Bool), DataExpr::String(_) => Some(Type::Bytes), DataExpr::HexString(_) => Some(Type::Bytes), - DataExpr::StructConstructor(x) => x.target_type(), - DataExpr::MapConstructor(x) => x.target_type(), - DataExpr::ListConstructor(x) => match x.target_type() { + DataExpr::StructConstructor(x) => x.target_type(Some(ctx)), + DataExpr::MapConstructor(x) => x.target_type(Some(ctx)), + DataExpr::ListConstructor(x) => match x.target_type(Some(ctx)) { Some(inner) => Some(Type::List(Box::new(inner))), None => None, }, - DataExpr::AddOp(x) => x.target_type(), - DataExpr::SubOp(x) => x.target_type(), - DataExpr::ConcatOp(x) => x.target_type(), - DataExpr::NegateOp(x) => x.target_type(), - DataExpr::PropertyOp(x) => x.target_type(), - DataExpr::StaticAssetConstructor(x) => x.target_type(), - DataExpr::AnyAssetConstructor(x) => x.target_type(), + DataExpr::AddOp(x) => x.target_type(Some(ctx)), + DataExpr::SubOp(x) => x.target_type(Some(ctx)), + DataExpr::ConcatOp(x) => x.target_type(Some(ctx)), + DataExpr::NegateOp(x) => x.target_type(Some(ctx)), + DataExpr::PropertyOp(x) => x.target_type(Some(ctx)), + DataExpr::StaticAssetConstructor(x) => x.target_type(Some(ctx)), + DataExpr::AnyAssetConstructor(x) => x.target_type(Some(ctx)), DataExpr::UtxoRef(_) => Some(Type::UtxoRef), DataExpr::MinUtxo(_) => Some(Type::AnyAsset), DataExpr::ComputeTipSlot => Some(Type::Int), @@ -887,7 +907,7 @@ impl Type { .map(|index| DataExpr::Number(index as i64)) } Type::List(_) => property - .target_type() + .target_type(None) .filter(|ty| *ty == Type::Int) .map(|_| property), _ => None, diff --git a/crates/tx3-lang/src/cardano.rs b/crates/tx3-lang/src/cardano.rs index 77cd5861..039b8dab 100644 --- a/crates/tx3-lang/src/cardano.rs +++ b/crates/tx3-lang/src/cardano.rs @@ -90,15 +90,19 @@ impl AstNode for WithdrawalBlock { } impl Analyzable for WithdrawalField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - WithdrawalField::From(x) => x.analyze(parent), + WithdrawalField::From(x) => x.analyze(parent, ctx), WithdrawalField::Amount(x) => { - let amount = x.analyze(parent.clone()); - let amount_type = AnalyzeReport::expect_data_expr_type(x, &Type::Int); + let amount = x.analyze(parent.clone(), ctx); + let amount_type = AnalyzeReport::expect_data_expr_type(x, &Type::Int, ctx); amount + amount_type } - WithdrawalField::Redeemer(x) => x.analyze(parent), + WithdrawalField::Redeemer(x) => x.analyze(parent, ctx), } } @@ -112,8 +116,12 @@ impl Analyzable for WithdrawalField { } impl Analyzable for WithdrawalBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -204,9 +212,13 @@ impl AstNode for VoteDelegationCertificate { } impl Analyzable for VoteDelegationCertificate { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let drep = self.drep.analyze(parent.clone()); - let stake = self.stake.analyze(parent.clone()); + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + let drep = self.drep.analyze(parent.clone(), ctx); + let stake = self.stake.analyze(parent.clone(), ctx); drep + stake } @@ -260,9 +272,13 @@ impl AstNode for StakeDelegationCertificate { } impl Analyzable for StakeDelegationCertificate { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let pool = self.pool.analyze(parent.clone()); - let stake = self.stake.analyze(parent.clone()); + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + let pool = self.pool.analyze(parent.clone(), ctx); + let stake = self.stake.analyze(parent.clone(), ctx); pool + stake } @@ -329,10 +345,14 @@ impl AstNode for PlutusWitnessField { } impl Analyzable for PlutusWitnessField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - PlutusWitnessField::Version(x, _) => x.analyze(parent), - PlutusWitnessField::Script(x, _) => x.analyze(parent), + PlutusWitnessField::Version(x, _) => x.analyze(parent, ctx), + PlutusWitnessField::Script(x, _) => x.analyze(parent, ctx), } } @@ -370,8 +390,12 @@ impl AstNode for PlutusWitnessBlock { } impl Analyzable for PlutusWitnessBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -439,9 +463,13 @@ impl AstNode for NativeWitnessField { } impl Analyzable for NativeWitnessField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - NativeWitnessField::Script(x, _) => x.analyze(parent), + NativeWitnessField::Script(x, _) => x.analyze(parent, ctx), } } @@ -478,8 +506,12 @@ impl AstNode for NativeWitnessBlock { } impl Analyzable for NativeWitnessBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -531,9 +563,13 @@ impl AstNode for TreasuryDonationBlock { } impl Analyzable for TreasuryDonationBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - let coin = self.coin.analyze(parent); - let coin_type = AnalyzeReport::expect_data_expr_type(&self.coin, &Type::Int); + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + let coin = self.coin.analyze(parent, ctx); + let coin_type = AnalyzeReport::expect_data_expr_type(&self.coin, &Type::Int, ctx); coin + coin_type } @@ -615,11 +651,15 @@ impl AstNode for CardanoPublishBlockField { } Rule::cardano_publish_block_version => { let pair = pair.into_inner().next().unwrap(); - Ok(CardanoPublishBlockField::Version(DataExpr::parse(pair)?.into())) + Ok(CardanoPublishBlockField::Version( + DataExpr::parse(pair)?.into(), + )) } Rule::cardano_publish_block_script => { let pair = pair.into_inner().next().unwrap(); - Ok(CardanoPublishBlockField::Script(DataExpr::parse(pair)?.into())) + Ok(CardanoPublishBlockField::Script( + DataExpr::parse(pair)?.into(), + )) } x => unreachable!("Unexpected rule in cardano_publish_block_field: {:?}", x), } @@ -656,13 +696,17 @@ impl AstNode for CardanoPublishBlock { } impl Analyzable for CardanoPublishBlockField { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - CardanoPublishBlockField::To(x) => x.analyze(parent), - CardanoPublishBlockField::Amount(x) => x.analyze(parent), - CardanoPublishBlockField::Datum(x) => x.analyze(parent), - CardanoPublishBlockField::Version(x) => x.analyze(parent), - CardanoPublishBlockField::Script(x) => x.analyze(parent), + CardanoPublishBlockField::To(x) => x.analyze(parent, ctx), + CardanoPublishBlockField::Amount(x) => x.analyze(parent, ctx), + CardanoPublishBlockField::Datum(x) => x.analyze(parent, ctx), + CardanoPublishBlockField::Version(x) => x.analyze(parent, ctx), + CardanoPublishBlockField::Script(x) => x.analyze(parent, ctx), } } @@ -678,8 +722,12 @@ impl Analyzable for CardanoPublishBlockField { } impl Analyzable for CardanoPublishBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.fields.analyze(parent) + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { + self.fields.analyze(parent, ctx) } fn is_resolved(&self) -> bool { @@ -782,15 +830,19 @@ impl AstNode for CardanoBlock { } impl Analyzable for CardanoBlock { - fn analyze(&mut self, parent: Option>) -> AnalyzeReport { + fn analyze( + &mut self, + parent: Option>, + ctx: &mut crate::analyzing::Context, + ) -> AnalyzeReport { match self { - CardanoBlock::VoteDelegationCertificate(x) => x.analyze(parent), - CardanoBlock::StakeDelegationCertificate(x) => x.analyze(parent), - CardanoBlock::Withdrawal(x) => x.analyze(parent), - CardanoBlock::PlutusWitness(x) => x.analyze(parent), - CardanoBlock::NativeWitness(x) => x.analyze(parent), - CardanoBlock::TreasuryDonation(x) => x.analyze(parent), - CardanoBlock::Publish(x) => x.analyze(parent), + CardanoBlock::VoteDelegationCertificate(x) => x.analyze(parent, ctx), + CardanoBlock::StakeDelegationCertificate(x) => x.analyze(parent, ctx), + CardanoBlock::Withdrawal(x) => x.analyze(parent, ctx), + CardanoBlock::PlutusWitness(x) => x.analyze(parent, ctx), + CardanoBlock::NativeWitness(x) => x.analyze(parent, ctx), + CardanoBlock::TreasuryDonation(x) => x.analyze(parent, ctx), + CardanoBlock::Publish(x) => x.analyze(parent, ctx), } } diff --git a/crates/tx3-lang/src/lowering.rs b/crates/tx3-lang/src/lowering.rs index 32abf290..7ec204b1 100644 --- a/crates/tx3-lang/src/lowering.rs +++ b/crates/tx3-lang/src/lowering.rs @@ -414,7 +414,7 @@ impl IntoLower for ast::PropertyOp { let ty = self .operand - .target_type() + .target_type(None) .ok_or(Error::MissingAnalyzePhase(format!("{0:?}", self.operand)))?; let prop_index = diff --git a/examples/cardano_witness.mint_from_plutus.tir b/examples/cardano_witness.mint_from_plutus.tir index 8ff6e2e2..c0f4530f 100644 --- a/examples/cardano_witness.mint_from_plutus.tir +++ b/examples/cardano_witness.mint_from_plutus.tir @@ -202,6 +202,9 @@ { "name": "plutus_witness", "data": { + "version": { + "Number": 3 + }, "script": { "Bytes": [ 81, @@ -223,9 +226,6 @@ 174, 105 ] - }, - "version": { - "Number": 3 } } } diff --git a/examples/lang_tour.my_tx.tir b/examples/lang_tour.my_tx.tir index 80d3416f..5edb9e63 100644 --- a/examples/lang_tour.my_tx.tir +++ b/examples/lang_tour.my_tx.tir @@ -666,15 +666,15 @@ { "name": "withdrawal", "data": { + "amount": { + "Number": 100 + }, "redeemer": { "Struct": { "constructor": 0, "fields": [] } }, - "amount": { - "Number": 100 - }, "credential": { "EvalParam": { "ExpectValue": [ diff --git a/examples/reference_script.publish_native.tir b/examples/reference_script.publish_native.tir index 90fdd3b9..9b516f57 100644 --- a/examples/reference_script.publish_native.tir +++ b/examples/reference_script.publish_native.tir @@ -163,6 +163,9 @@ } ] }, + "version": { + "Number": 0 + }, "to": { "EvalParam": { "ExpectValue": [ @@ -170,9 +173,6 @@ "Address" ] } - }, - "version": { - "Number": 0 } } } diff --git a/examples/reference_script.publish_plutus.tir b/examples/reference_script.publish_plutus.tir index f1291c1f..7506c0f5 100644 --- a/examples/reference_script.publish_plutus.tir +++ b/examples/reference_script.publish_plutus.tir @@ -156,14 +156,6 @@ } ] }, - "to": { - "EvalParam": { - "ExpectValue": [ - "receiver", - "Address" - ] - } - }, "script": { "Bytes": [ 81, @@ -185,6 +177,14 @@ 174, 105 ] + }, + "to": { + "EvalParam": { + "ExpectValue": [ + "receiver", + "Address" + ] + } } } } diff --git a/examples/swap.ast b/examples/swap.ast index 8d915d0f..54ac4be4 100644 --- a/examples/swap.ast +++ b/examples/swap.ast @@ -176,8 +176,8 @@ "value": "Buyer", "span": { "dummy": false, - "start": 421, - "end": 426 + "start": 417, + "end": 422 } } } @@ -190,8 +190,8 @@ "value": "fees", "span": { "dummy": false, - "start": 452, - "end": 456 + "start": 444, + "end": 448 } } }, @@ -200,15 +200,15 @@ "value": "bid", "span": { "dummy": false, - "start": 459, - "end": 462 + "start": 451, + "end": 454 } } }, "span": { "dummy": false, - "start": 457, - "end": 458 + "start": 449, + "end": 450 } } } @@ -216,8 +216,8 @@ ], "span": { "dummy": false, - "start": 391, - "end": 469 + "start": 387, + "end": 461 } } ], @@ -232,8 +232,8 @@ "value": "Dex", "span": { "dummy": false, - "start": 500, - "end": 503 + "start": 488, + "end": 491 } } } @@ -245,8 +245,8 @@ "value": "PoolState", "span": { "dummy": false, - "start": 520, - "end": 529 + "start": 508, + "end": 517 } }, "case": { @@ -264,8 +264,8 @@ "value": "pair_a", "span": { "dummy": false, - "start": 544, - "end": 550 + "start": 532, + "end": 538 } }, "value": { @@ -277,8 +277,8 @@ "value": "pool", "span": { "dummy": false, - "start": 552, - "end": 556 + "start": 540, + "end": 544 } } }, @@ -287,58 +287,39 @@ "value": "pair_a", "span": { "dummy": false, - "start": 557, - "end": 563 + "start": 545, + "end": 551 } } }, "span": { "dummy": false, - "start": 556, - "end": 563 + "start": 544, + "end": 551 } } }, "rhs": { - "PropertyOp": { - "operand": { - "Identifier": { - "value": "bid", - "span": { - "dummy": false, - "start": 566, - "end": 569 - } - } - }, - "property": { - "Identifier": { - "value": "amount", - "span": { - "dummy": false, - "start": 570, - "end": 576 - } - } - }, + "Identifier": { + "value": "bid", "span": { "dummy": false, - "start": 569, - "end": 576 + "start": 554, + "end": 557 } } }, "span": { "dummy": false, - "start": 564, - "end": 565 + "start": 552, + "end": 553 } } }, "span": { "dummy": false, - "start": 544, - "end": 576 + "start": 532, + "end": 557 } }, { @@ -346,8 +327,8 @@ "value": "pair_b", "span": { "dummy": false, - "start": 590, - "end": 596 + "start": 571, + "end": 577 } }, "value": { @@ -359,8 +340,8 @@ "value": "pool", "span": { "dummy": false, - "start": 598, - "end": 602 + "start": 579, + "end": 583 } } }, @@ -369,58 +350,39 @@ "value": "pair_b", "span": { "dummy": false, - "start": 603, - "end": 609 + "start": 584, + "end": 590 } } }, "span": { "dummy": false, - "start": 602, - "end": 609 + "start": 583, + "end": 590 } } }, "rhs": { - "PropertyOp": { - "operand": { - "Identifier": { - "value": "ask", - "span": { - "dummy": false, - "start": 612, - "end": 615 - } - } - }, - "property": { - "Identifier": { - "value": "amount", - "span": { - "dummy": false, - "start": 616, - "end": 622 - } - } - }, + "Identifier": { + "value": "ask", "span": { "dummy": false, - "start": 615, - "end": 622 + "start": 593, + "end": 596 } } }, "span": { "dummy": false, - "start": 610, - "end": 611 + "start": 591, + "end": 592 } } }, "span": { "dummy": false, - "start": 590, - "end": 622 + "start": 571, + "end": 596 } } ], @@ -429,21 +391,21 @@ "value": "pool", "span": { "dummy": false, - "start": 639, - "end": 643 + "start": 613, + "end": 617 } } }, "span": { "dummy": false, - "start": 530, - "end": 653 + "start": 518, + "end": 627 } }, "span": { "dummy": false, - "start": 520, - "end": 653 + "start": 508, + "end": 627 } } } @@ -454,8 +416,8 @@ "value": "pool", "span": { "dummy": false, - "start": 671, - "end": 675 + "start": 645, + "end": 649 } } } @@ -463,8 +425,8 @@ ], "span": { "dummy": false, - "start": 479, - "end": 682 + "start": 467, + "end": 656 } }, { @@ -477,8 +439,8 @@ "value": "Buyer", "span": { "dummy": false, - "start": 709, - "end": 714 + "start": 683, + "end": 688 } } } @@ -495,8 +457,8 @@ "value": "payment", "span": { "dummy": false, - "start": 732, - "end": 739 + "start": 706, + "end": 713 } } }, @@ -505,15 +467,15 @@ "value": "ask", "span": { "dummy": false, - "start": 742, - "end": 745 + "start": 716, + "end": 719 } } }, "span": { "dummy": false, - "start": 740, - "end": 741 + "start": 714, + "end": 715 } } }, @@ -522,15 +484,15 @@ "value": "bid", "span": { "dummy": false, - "start": 748, - "end": 751 + "start": 722, + "end": 725 } } }, "span": { "dummy": false, - "start": 746, - "end": 747 + "start": 720, + "end": 721 } } }, @@ -539,15 +501,15 @@ "value": "fees", "span": { "dummy": false, - "start": 754, - "end": 758 + "start": 728, + "end": 732 } } }, "span": { "dummy": false, - "start": 752, - "end": 753 + "start": 726, + "end": 727 } } } @@ -555,8 +517,8 @@ ], "span": { "dummy": false, - "start": 688, - "end": 765 + "start": 662, + "end": 739 } } ], @@ -568,7 +530,7 @@ "span": { "dummy": false, "start": 161, - "end": 767 + "end": 741 }, "collateral": [], "metadata": null @@ -746,6 +708,6 @@ "span": { "dummy": false, "start": 0, - "end": 767 + "end": 742 } } \ No newline at end of file diff --git a/examples/swap.swap.tir b/examples/swap.swap.tir index c9a20a39..63b0b538 100644 --- a/examples/swap.swap.tir +++ b/examples/swap.swap.tir @@ -145,19 +145,10 @@ } }, { - "EvalBuiltIn": { - "Property": [ - { - "EvalParam": { - "ExpectValue": [ - "bid", - "AnyAsset" - ] - } - }, - { - "Number": 0 - } + "EvalParam": { + "ExpectValue": [ + "bid", + "AnyAsset" ] } } @@ -202,19 +193,10 @@ } }, { - "EvalBuiltIn": { - "Property": [ - { - "EvalParam": { - "ExpectValue": [ - "ask", - "AnyAsset" - ] - } - }, - { - "Number": 0 - } + "EvalParam": { + "ExpectValue": [ + "ask", + "AnyAsset" ] } } diff --git a/examples/swap.tx3 b/examples/swap.tx3 index e13e4262..70abff27 100644 --- a/examples/swap.tx3 +++ b/examples/swap.tx3 @@ -25,17 +25,17 @@ tx swap( bid_value: bid, }, } - + input payment { - from: Buyer, + from: Buyer, min_amount: fees + bid, } - + output { to: Dex, datum: PoolState { - pair_a: pool.pair_a - bid.amount, - pair_b: pool.pair_b + ask.amount, + pair_a: pool.pair_a - bid, + pair_b: pool.pair_b + ask, ...pool }, amount: pool, @@ -45,4 +45,4 @@ tx swap( to: Buyer, amount: payment + ask - bid - fees, } -} \ No newline at end of file +}