From 42c1b50e31d06efd8f05201470952138a9572c14 Mon Sep 17 00:00:00 2001 From: German Nikolishin Date: Wed, 6 Mar 2024 14:30:58 +0000 Subject: [PATCH] resolve multiplication --- crates/semantics/src/ast.rs | 16 +++ crates/semantics/src/expression/complex.rs | 2 +- crates/semantics/src/expression/literals.rs | 12 +- crates/semantics/src/expression/mod.rs | 20 ++- crates/semantics/src/expression/nums.rs | 4 +- crates/semantics/src/expression/ops.rs | 142 ++++++++++++++++++++ crates/semantics/src/expression/tests.rs | 45 ++++++- crates/semantics/src/types.rs | 11 +- 8 files changed, 236 insertions(+), 16 deletions(-) create mode 100644 crates/semantics/src/expression/ops.rs diff --git a/crates/semantics/src/ast.rs b/crates/semantics/src/ast.rs index d60b314..8ac1660 100644 --- a/crates/semantics/src/ast.rs +++ b/crates/semantics/src/ast.rs @@ -486,3 +486,19 @@ impl Display for TypeVariant { } } } + +impl Expression { + pub fn is_literal(&self) -> bool { + matches!( + self, + Expression::Int(_) + | Expression::UInt(_) + | Expression::Float(_) + | Expression::Char(_) + | Expression::String(_) + | Expression::Hex(_) + | Expression::Address(_) + | Expression::Boolean(_) + ) + } +} diff --git a/crates/semantics/src/expression/complex.rs b/crates/semantics/src/expression/complex.rs index 785c258..3e415d2 100644 --- a/crates/semantics/src/expression/complex.rs +++ b/crates/semantics/src/expression/complex.rs @@ -106,7 +106,7 @@ pub fn resolve_variable( if &sym.ty != ty { report_type_mismatch( - &ExpectedType::Concrete(ty.clone()), + &[ExpectedType::Concrete(ty.clone())], &sym.ty, &ident.loc, contract, diff --git a/crates/semantics/src/expression/literals.rs b/crates/semantics/src/expression/literals.rs index 0769d6f..14c769f 100644 --- a/crates/semantics/src/expression/literals.rs +++ b/crates/semantics/src/expression/literals.rs @@ -47,7 +47,7 @@ pub fn resolve_bool( })) } a_ty => { - report_type_mismatch(&expected_ty, a_ty, &loc, contract); + report_type_mismatch(&[expected_ty.clone()], a_ty, &loc, contract); Err(()) } } @@ -87,7 +87,7 @@ pub fn resolve_char( })) } a_ty => { - report_type_mismatch(&expected_ty, a_ty, &loc, contract); + report_type_mismatch(&[expected_ty.clone()], a_ty, &loc, contract); Err(()) } } @@ -128,7 +128,7 @@ pub fn resolve_string( })) } a_ty => { - report_type_mismatch(&expected_ty, a_ty, &loc, contract); + report_type_mismatch(&[expected_ty.clone()], a_ty, &loc, contract); Err(()) } } @@ -175,7 +175,7 @@ pub fn resolve_hex( })) } a_ty => { - report_type_mismatch(&expected_ty, a_ty, &loc, contract); + report_type_mismatch(&[expected_ty.clone()], a_ty, &loc, contract); Err(()) } } @@ -217,7 +217,7 @@ pub fn resolve_address( })) } a_ty => { - report_type_mismatch(&expected_ty, a_ty, &loc, contract); + report_type_mismatch(&[expected_ty.clone()], a_ty, &loc, contract); Err(()) } } @@ -294,7 +294,7 @@ pub fn resolve_lists( TypeVariant::Set(ty) => derive_expr(ty, loc), TypeVariant::List(ty) => derive_expr(ty, loc), a_ty => { - report_type_mismatch(&expected_ty, a_ty, &loc, contract); + report_type_mismatch(&[expected_ty.clone()], a_ty, &loc, contract); Err(()) } } diff --git a/crates/semantics/src/expression/mod.rs b/crates/semantics/src/expression/mod.rs index a58f607..15c0eb7 100644 --- a/crates/semantics/src/expression/mod.rs +++ b/crates/semantics/src/expression/mod.rs @@ -1,6 +1,7 @@ mod complex; mod literals; mod nums; +mod ops; #[cfg(test)] mod tests; @@ -32,6 +33,7 @@ use self::{ resolve_float, resolve_integer, }, + ops::resolve_multiply, }; /// Resolve parsed expression to a concrete expression. @@ -42,6 +44,7 @@ pub fn expression( contract: &mut ContractDefinition, ) -> Result { match expr { + // literals parsed_ast::Expression::Number(n) => { resolve_integer(&n.element, n.loc.clone(), contract, expected_ty) } @@ -66,10 +69,17 @@ pub fn expression( parsed_ast::Expression::List(l) => { resolve_lists(&l.element, l.loc.clone(), contract, scope, expected_ty) } - parsed_ast::Expression::Variable(ident) => { - resolve_variable(ident, scope, contract, expected_ty) + // operations + parsed_ast::Expression::Multiply(b) => { + resolve_multiply( + &b.left, + &b.right, + b.loc.clone(), + scope, + contract, + expected_ty, + ) } - parsed_ast::Expression::Multiply(_) => todo!(), parsed_ast::Expression::Divide(_) => todo!(), parsed_ast::Expression::Modulo(_) => todo!(), parsed_ast::Expression::Add(_) => todo!(), @@ -84,6 +94,10 @@ pub fn expression( parsed_ast::Expression::Not(_) => todo!(), parsed_ast::Expression::Or(_) => todo!(), parsed_ast::Expression::And(_) => todo!(), + // complex expressions + parsed_ast::Expression::Variable(ident) => { + resolve_variable(ident, scope, contract, expected_ty) + } parsed_ast::Expression::FunctionCall(_) => todo!(), parsed_ast::Expression::MemberAccess(_) => todo!(), parsed_ast::Expression::Pipe(_) => todo!(), diff --git a/crates/semantics/src/expression/nums.rs b/crates/semantics/src/expression/nums.rs index c5107ae..54fe3b8 100644 --- a/crates/semantics/src/expression/nums.rs +++ b/crates/semantics/src/expression/nums.rs @@ -59,7 +59,7 @@ pub fn resolve_integer( })) } a_ty => { - report_type_mismatch(&expected_ty, a_ty, &loc, contract); + report_type_mismatch(&[expected_ty.clone()], a_ty, &loc, contract); Err(()) } } @@ -115,7 +115,7 @@ pub fn resolve_float( })) } a_ty => { - report_type_mismatch(&expected_ty, a_ty, &loc, contract); + report_type_mismatch(&[expected_ty.clone()], a_ty, &loc, contract); Err(()) } } diff --git a/crates/semantics/src/expression/ops.rs b/crates/semantics/src/expression/ops.rs new file mode 100644 index 0000000..d2dbcdf --- /dev/null +++ b/crates/semantics/src/expression/ops.rs @@ -0,0 +1,142 @@ +use folidity_diagnostics::Report; +use folidity_parser::{ + ast as parsed_ast, + Span, +}; + +use crate::{ + ast::{ + BinaryExpression, + Expression, + TypeVariant, + }, + contract::ContractDefinition, + symtable::Scope, + types::{ + report_type_mismatch, + ExpectedType, + }, +}; + +use super::{ + dynamic_to_concrete_type, + expression, +}; + +/// Resolve multiplication. +pub fn resolve_multiply( + left: &parsed_ast::Expression, + right: &parsed_ast::Expression, + loc: Span, + scope: &mut Scope, + contract: &mut ContractDefinition, + expected_ty: ExpectedType, +) -> Result { + let allowed_tys = &[TypeVariant::Int, TypeVariant::Uint, TypeVariant::Float]; + match &expected_ty { + ExpectedType::Concrete(ty) => { + match ty { + TypeVariant::Int | TypeVariant::Uint | TypeVariant::Float => { + let resolved_left = expression(left, expected_ty.clone(), scope, contract); + let resolved_right = expression(right, expected_ty.clone(), scope, contract); + + if resolved_left.is_err() || resolved_right.is_err() { + return Err(()); + } + + let right = Box::new(resolved_right.unwrap()); + let left = Box::new(resolved_left.unwrap()); + + if right.is_literal() && left.is_literal() { + // todo: resolve it to the number + } + + Ok(Expression::Multiply(BinaryExpression { + loc, + left, + right, + ty: ty.clone(), + })) + } + a_ty => { + let expected: Vec = allowed_tys + .iter() + .map(|ty| ExpectedType::Concrete(ty.clone())) + .collect(); + report_type_mismatch(expected.as_slice(), a_ty, &loc, contract); + Err(()) + } + } + } + ExpectedType::Dynamic(tys) => { + let concrete = deduce_type(left, right, &loc, tys, allowed_tys, scope, contract)?; + resolve_multiply(left, right, loc, scope, contract, concrete) + } + ExpectedType::Empty => { + contract.diagnostics.push(Report::semantic_error( + loc.clone(), + String::from("Multiplication can only be used in expression."), + )); + Err(()) + } + } +} + +/// Find a valid concrete type from the list of allowed types. +/// - If suggested types are empty, we resolve the type from the left hand expression. +/// - Otherwise, we check every possible allowed type and filter out the ones to which the +/// left-hand expression can not be resolved. +/// or check the right one. +/// # Errors +/// - Expression can not be resolved to any of the allowed types. +fn deduce_type( + left: &parsed_ast::Expression, + right: &parsed_ast::Expression, + loc: &Span, + tys: &Vec, + allowed_tys: &[TypeVariant], + scope: &mut Scope, + contract: &mut ContractDefinition, +) -> Result { + if tys.is_empty() { + let expr = expression(left, ExpectedType::Dynamic(vec![]), scope, contract)?; + Ok(ExpectedType::Concrete(expr.ty().clone())) + } else { + // just clone the scope and contract definition as we need to dry run expression + // resolution. + // todo: optimise later + let mut scope = scope.clone(); + let mut contract = contract.clone(); + // we find which types are allowed by checking whether the left hand side expression can + // be resolved to it. + let filtered_tys: Vec = allowed_tys + .iter() + .filter(|&ty| { + expression( + left, + ExpectedType::Concrete(ty.clone()), + &mut scope, + &mut contract, + ) + .is_ok() + || expression( + right, + ExpectedType::Concrete(ty.clone()), + &mut scope, + &mut contract, + ) + .is_ok() + }) + .cloned() + .collect(); + + if filtered_tys.is_empty() { + contract.diagnostics.push(Report::type_error( + loc.clone(), + String::from("Cannot resolve these expression to any of the supported types."), + )); + return Err(()); + } + Ok(dynamic_to_concrete_type(tys, allowed_tys)) + } +} diff --git a/crates/semantics/src/expression/tests.rs b/crates/semantics/src/expression/tests.rs index c80b3bd..fd4d1b3 100644 --- a/crates/semantics/src/expression/tests.rs +++ b/crates/semantics/src/expression/tests.rs @@ -1,5 +1,3 @@ -use std::process::id; - use folidity_parser::{ ast::{ self as parsed_ast, @@ -114,3 +112,46 @@ fn test_var() { assert_eq!(&sym.ty, &TypeVariant::Int); } } + +#[test] +fn test_mul() { + let loc = Span { start: 0, end: 0 }; + let mut contract = ContractDefinition::default(); + let mut scope = Scope::default(); + let nums = vec![ + parsed_ast::Expression::Number(parsed_ast::UnaryExpression { + loc: loc.clone(), + element: "1".to_string(), + }), + parsed_ast::Expression::Number(parsed_ast::UnaryExpression { + loc: loc.clone(), + element: "2".to_string(), + }), + parsed_ast::Expression::Number(parsed_ast::UnaryExpression { + loc: loc.clone(), + element: "3".to_string(), + }), + ]; + + let mul_expr = parsed_ast::Expression::Multiply(parsed_ast::BinaryExpression { + loc: loc.clone(), + left: Box::new(nums[0].clone()), + right: Box::new(nums[1].clone()), + }); + + let resolved_expr = expression( + &mul_expr, + ExpectedType::Dynamic(vec![]), + &mut scope, + &mut contract, + ); + + assert!(resolved_expr.is_ok()); + + let resolved_expr = resolved_expr.unwrap(); + + if let Expression::Multiply(mul) = resolved_expr { + assert_eq!(mul.ty, TypeVariant::Int); + assert!(mul.left.is_literal() && mul.right.is_literal()); + } +} diff --git a/crates/semantics/src/types.rs b/crates/semantics/src/types.rs index 4735e30..a6cf0c9 100644 --- a/crates/semantics/src/types.rs +++ b/crates/semantics/src/types.rs @@ -413,13 +413,20 @@ fn detect_state_cycle(contract: &mut ContractDefinition) { /// Push diagnostic error about the type mismatch. pub(super) fn report_type_mismatch( - expected: &ExpectedType, + expected: &[ExpectedType], actual: &TypeVariant, loc: &Span, contract: &mut ContractDefinition, ) { + let expected = expected + .iter() + .fold(String::new(), |acc, x| format!("{}, {}", acc, x)); contract.diagnostics.push(Report::type_error( loc.clone(), - format!("Mismatched types: expected {}, got {}", expected, actual), + format!( + "Mismatched types: expected {}, got {}", + expected.trim_start_matches(", "), + actual + ), )); }