diff --git a/nac3core/src/typecheck/expression_inference.rs b/nac3core/src/typecheck/expression_inference.rs
index a7303e9f..2ba5212c 100644
--- a/nac3core/src/typecheck/expression_inference.rs
+++ b/nac3core/src/typecheck/expression_inference.rs
@@ -5,13 +5,15 @@ use crate::typecheck::typedef::{Type, TypeEnum};
use crate::typecheck::primitives;
use rustpython_parser::ast;
+use super::magic_methods;
+
pub struct ExpressionTypeInferencer<'a> {
- ctx: InferenceContext<'a>
+ pub ctx: InferenceContext<'a> //FIXME: may need to remove this pub
}
-impl<'a> ExpressionTypeInferencer<'a>{ // NOTE: add location here in the function parameter for better error message?
+impl<'a> ExpressionTypeInferencer<'a> { // NOTE: add location here in the function parameter for better error message?
- fn infer_constant_val(&mut self, constant: &ast::Constant) -> Result, String> {
+ fn infer_constant_val(&self, constant: &ast::Constant) -> Result , String> {
match constant {
ast::Constant::Bool(_) =>
Ok(Some(self.ctx.get_primitive(primitives::BOOL_TYPE))),
@@ -56,12 +58,12 @@ impl<'a> ExpressionTypeInferencer<'a>{ // NOTE: add location here in the functio
}
- fn infer_list_val(&mut self, elts: &Vec>>) -> Result, String> {
+ fn infer_list_val(&self, elts: &Vec>>) -> Result, String> {
if elts.is_empty() {
Ok(Some(TypeEnum::ParametricType(primitives::LIST_TYPE, vec![TypeEnum::BotType.into()]).into()))
} else {
let types = elts
- .into_iter()
+ .iter()
.map(|x| &x.custom)
.collect::>();
@@ -75,12 +77,26 @@ impl<'a> ExpressionTypeInferencer<'a>{ // NOTE: add location here in the functio
} else {
Err("list elements must have some type".into())
}
+ }
+ }
+ fn infer_tuple_val(&self, elts: &Vec>>) -> Result, String> {
+ let types = elts
+ .iter()
+ .map(|x| (x.custom).clone())
+ .collect::>();
+
+ if types.iter().all(|x| x.is_some()) {
+ Ok(Some(TypeEnum::ParametricType(
+ primitives::TUPLE_TYPE,
+ types.into_iter().map(|x| x.unwrap()).collect()).into())) // unwrap alone should be fine after the previous check
+ } else {
+ Err("tuple elements must have some type".into())
}
}
}
-// REVIEW: field custom: from None to Option or just Option ?
+// REVIEW: field custom: from () to Option or just Option?
impl<'a> ast::fold::Fold> for ExpressionTypeInferencer<'a> {
type TargetU = Option;
type Error = String;
@@ -90,7 +106,7 @@ impl<'a> ast::fold::Fold> for ExpressionTypeInferencer<'a> {
}
fn fold_expr(&mut self, expr: ast::Expr >) -> Result, Self::Error> {
- let ast::Expr {location, custom: cus, node} = expr;
+ let ast::Expr {location, custom, node} = expr;
match node {
ast::ExprKind::Constant {value, kind} =>
Ok(ast::Expr {
@@ -107,7 +123,21 @@ impl<'a> ast::fold::Fold> for ExpressionTypeInferencer<'a> {
}),
ast::ExprKind::List {elts, ctx} => {
- let elts= elts
+ /* let folded = ast::fold::fold_expr(
+ self,
+ ast::Expr {location, custom, node: ast::ExprKind::List {elts, ctx}})?;
+
+ if let ast::Expr {location: _, custom: _, node: ast::ExprKind::List {elts, ctx}} = folded {
+ Ok(ast::Expr {
+ location,
+ custom: self.infer_list_val(&elts)?,
+ node: ast::ExprKind::List {elts, ctx}
+ })
+ } else {
+ Err("something wrong here".into())
+ } */
+
+ let elts = elts
.into_iter()
.map(|x| self.fold_expr(x))
.collect::>>, _>>()?; // elements inside the vector should now have type info
@@ -118,20 +148,185 @@ impl<'a> ast::fold::Fold> for ExpressionTypeInferencer<'a> {
node: ast::ExprKind::List {elts, ctx}
})
}
+
+ ast::ExprKind::Tuple {elts, ctx} => {
+ // let folded_tup_expr = ast::fold::fold_expr(self, ast::Expr {location, custom, node})?;
+ let elts= elts
+ .into_iter()
+ .map(|x| self.fold_expr(x))
+ .collect::>>, _>>()?; // elements inside the vector should now have type info
+
+ Ok(ast::Expr {
+ location,
+ custom: self.infer_tuple_val(&elts)?,
+ node: ast::ExprKind::Tuple {elts, ctx}
+ })
+ }
+
+ ast::ExprKind::Attribute {value, attr, ctx} => {
+ let folded_val = self.fold_expr(*value)?;
+
+ match folded_val.custom {
+ Some(ref ty) => {
+ if let TypeEnum::TypeVariable(_) = ty.as_ref() {
+ Err("no fields for type variable".into())
+ } else {
+ ty
+ .clone()
+ .get_base(&self.ctx)
+ .and_then(|b| b.fields.get(&*attr).clone())
+ .map_or_else(
+ || Err("no such field".into()),
+ |v| Ok(ast::Expr {
+ location,
+ custom: Some(v.clone()),
+ node: ast::ExprKind::Attribute {value: Box::new(folded_val), attr, ctx}
+ }))
+ }
+ },
+ None => Err("no value".into())
+ }
+ }
+
+ ast::ExprKind::BoolOp {op, values} => {
+ assert_eq!(values.len(), 2); // NOTE: should panic
+ let folded = values
+ .into_iter()
+ .map(|x| self.fold_expr(x))
+ .collect::>>, _>>()?;
+
+ if (&folded)
+ .iter()
+ .all(|x| x.custom == Some(self.ctx.get_primitive(primitives::BOOL_TYPE))) {
+ Ok(ast::Expr {
+ location,
+ node: ast::ExprKind::BoolOp {op, values: folded},
+ custom: Some(self.ctx.get_primitive(primitives::BOOL_TYPE))
+ })
+ } else {
+ Err("bool operands must be bool".into())
+ }
+ }
+
+ ast::ExprKind::BinOp {op, left, right} => {
+ let folded_left = self.fold_expr(*left)?;
+ let folded_right = self.fold_expr(*right)?;
+ let fun = magic_methods::binop_name(&op);
+ let left_type = folded_left.custom.clone().ok_or_else(|| "no value".to_string())?;
+ let right_type = folded_right.custom.clone().ok_or_else(|| "no value".to_string())?;
+
+ let result = crate::typecheck::inference_core::resolve_call(
+ &self.ctx,
+ Some(left_type),
+ fun,
+ &[right_type])?;
+
+ Ok(ast::Expr {
+ location,
+ custom: result,
+ node: ast::ExprKind::BinOp {op, left: Box::new(folded_left), right: Box::new(folded_right)}
+ })
+ }
+ ast::ExprKind::UnaryOp {op, operand} => {
+ let folded = self.fold_expr(*operand)?;
+ let ty = folded.custom.clone().ok_or_else(|| "no value".to_string())?;
+ if let ast::Unaryop::Not = op {
+ if ty == self.ctx.get_primitive(primitives::BOOL_TYPE) {
+ Ok(ast::Expr {
+ location,
+ node: ast::ExprKind::UnaryOp {op, operand: Box::new(folded)},
+ custom: Some(self.ctx.get_primitive(primitives::BOOL_TYPE))
+ })
+ } else {
+ Err("logical not must be applied to bool".into())
+ }
+ } else {
+ Ok(ast::Expr {
+ location,
+ custom: crate::typecheck::inference_core::resolve_call(
+ &self.ctx,
+ Some(ty),
+ magic_methods::unaryop_name(&op),
+ &[])?,
+ node: ast::ExprKind::UnaryOp {op, operand: Box::new(folded)},
+ })
+ }
+
+ }
+
+ ast::ExprKind::Compare {left, ops, comparators} => {
+ Err("not sure".into()) // FIXME: what is the `left` field here?
+ }
+
+ ast::ExprKind::Call {func, args, keywords} => {
+ if !keywords.is_empty() {
+ Err("keyword is not supported yet".into())
+ } else {
+ let folded_args = args
+ .into_iter()
+ .map(|x| self.fold_expr(x))
+ .collect::>>, _>>()?;
+
+ if !folded_args.iter().all(|x| x.custom.is_some()) {
+ Err("function params must have type".into())
+ } else {
+ match &func.node {
+ ast::ExprKind::Name {id, ctx} => {
+ Ok(ast::Expr {
+ location,
+ custom: crate::typecheck::inference_core::resolve_call(
+ &self.ctx,
+ None,
+ id,
+ &folded_args
+ .iter()
+ .map(|x| (x.custom.clone().unwrap()))
+ .collect::>())?,
+ node: ast::ExprKind::Call {func, args: folded_args, keywords}
+ })
+ }
+
+ ast::ExprKind::Attribute {value, attr, ctx} => {
+ // Err("sdf".into())
+ let folded_value = self.fold_expr(**value)?;
+ Ok(ast::Expr {
+ location,
+ node: ast::ExprKind::Call {func, args: folded_args, keywords},
+ custom: crate::typecheck::inference_core::resolve_call(
+ &self.ctx,
+ folded_value.custom,
+ attr,
+ &folded_args
+ .iter()
+ .map(|x| (x.custom.clone().unwrap()))
+ .collect::>())?
+ })
+ }
+
+
+ _ => Err("not supported".into())
+ }
+ // Err("sdf".into())
+ }
+
+ }
+ }
+
+
_ =>
- Ok(ast::Expr {location, custom: cus, node: node})
+ Ok(ast::Expr {location, custom, node})
}
}
}
-mod test {
+pub mod test {
use crate::typecheck::{symbol_resolver::SymbolResolver, typedef::*, symbol_resolver::*, location::*};
use rustpython_parser::ast::{self, Expr, fold::Fold};
use super::*;
- fn new_ctx<'a>() -> ExpressionTypeInferencer<'a>{
+ pub fn new_ctx<'a>() -> ExpressionTypeInferencer<'a>{
struct S;
impl SymbolResolver for S {