Skip to content

Commit

Permalink
resolve multiplication
Browse files Browse the repository at this point in the history
  • Loading branch information
SkymanOne committed Mar 6, 2024
1 parent 9dbb080 commit 42c1b50
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 16 deletions.
16 changes: 16 additions & 0 deletions crates/semantics/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(_)
)
}
}
2 changes: 1 addition & 1 deletion crates/semantics/src/expression/complex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 6 additions & 6 deletions crates/semantics/src/expression/literals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
}
Expand Down Expand Up @@ -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(())
}
}
Expand Down Expand Up @@ -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(())
}
}
Expand Down Expand Up @@ -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(())
}
}
Expand Down Expand Up @@ -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(())
}
}
Expand Down Expand Up @@ -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(())
}
}
Expand Down
20 changes: 17 additions & 3 deletions crates/semantics/src/expression/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod complex;
mod literals;
mod nums;
mod ops;
#[cfg(test)]
mod tests;

Expand Down Expand Up @@ -32,6 +33,7 @@ use self::{
resolve_float,
resolve_integer,
},
ops::resolve_multiply,
};

/// Resolve parsed expression to a concrete expression.
Expand All @@ -42,6 +44,7 @@ pub fn expression(
contract: &mut ContractDefinition,
) -> Result<Expression, ()> {
match expr {
// literals
parsed_ast::Expression::Number(n) => {
resolve_integer(&n.element, n.loc.clone(), contract, expected_ty)
}
Expand All @@ -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!(),
Expand All @@ -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!(),
Expand Down
4 changes: 2 additions & 2 deletions crates/semantics/src/expression/nums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
}
Expand Down Expand Up @@ -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(())
}
}
Expand Down
142 changes: 142 additions & 0 deletions crates/semantics/src/expression/ops.rs
Original file line number Diff line number Diff line change
@@ -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<Expression, ()> {
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<ExpectedType> = 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<TypeVariant>,
allowed_tys: &[TypeVariant],
scope: &mut Scope,
contract: &mut ContractDefinition,
) -> Result<ExpectedType, ()> {
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<TypeVariant> = 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))
}
}
45 changes: 43 additions & 2 deletions crates/semantics/src/expression/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use std::process::id;

use folidity_parser::{
ast::{
self as parsed_ast,
Expand Down Expand Up @@ -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());
}
}
11 changes: 9 additions & 2 deletions crates/semantics/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
),
));
}

0 comments on commit 42c1b50

Please sign in to comment.