diff --git a/nac3core/src/lib.rs b/nac3core/src/lib.rs index 5cc15c3c9..dd3553e57 100644 --- a/nac3core/src/lib.rs +++ b/nac3core/src/lib.rs @@ -7,6 +7,7 @@ extern crate rustpython_parser; pub mod expression_inference; pub mod inference_core; +pub mod statement_inference; mod magic_methods; pub mod primitives; pub mod typedef; diff --git a/nac3core/src/statement_inference.rs b/nac3core/src/statement_inference.rs new file mode 100644 index 000000000..858381290 --- /dev/null +++ b/nac3core/src/statement_inference.rs @@ -0,0 +1,95 @@ +use crate::context::InferenceContext; +use crate::expression_inference::infer_expr; +use crate::inference_core::resolve_call; +use crate::magic_methods::*; +use crate::primitives::*; +use crate::typedef::{Type, TypeEnum::*}; +use rustpython_parser::ast::*; + +fn get_target_type<'b: 'a, 'a>( + ctx: &mut InferenceContext<'a>, + target: &'b Expression, +) -> Result { + match &target.node { + ExpressionType::Subscript { a, b } => { + let int32 = ctx.get_primitive(INT32_TYPE); + if infer_expr(ctx, &a)? == Some(int32) { + let b = get_target_type(ctx, &b)?; + if let ParametricType(LIST_TYPE, t) = b.as_ref() { + Ok(t[0].clone()) + } else { + Err("subscript is only supported for list".to_string()) + } + } else { + Err("subscript must be int32".to_string()) + } + } + ExpressionType::Attribute { value, name } => { + let t = get_target_type(ctx, &value)?; + let base = t.get_base(ctx).ok_or_else(|| "no attributes".to_string())?; + Ok(base + .fields + .get(name.as_str()) + .ok_or_else(|| "no such attribute")? + .clone()) + } + ExpressionType::Identifier { name } => Ok(ctx.resolve(name.as_str())?), + _ => Err("not supported".to_string()), + } +} + +fn check_stmt_binding<'b: 'a, 'a>( + ctx: &mut InferenceContext<'a>, + target: &'b Expression, + ty: Type, +) -> Result<(), String> { + match &target.node { + ExpressionType::Identifier { name } => { + if name.as_str() == "_" { + Ok(()) + } else { + match ctx.resolve(name.as_str()) { + Ok(t) if t == ty => Ok(()), + Err(_) => { + ctx.assign(name.as_str(), ty).unwrap(); + Ok(()) + } + _ => Err("conflicting type".into()), + } + } + } + ExpressionType::Tuple { elements } => { + if let ParametricType(TUPLE_TYPE, ls) = ty.as_ref() { + if ls.len() != elements.len() { + return Err("incorrect pattern length".into()); + } + for (x, y) in elements.iter().zip(ls.iter()) { + check_stmt_binding(ctx, x, y.clone())?; + } + Ok(()) + } else { + Err("pattern matching supports tuple only".into()) + } + } + _ => { + let t = get_target_type(ctx, target)?; + if ty == t { + Ok(()) + } else { + Err("type mismatch".into()) + } + } + } +} + +fn check_assign<'b: 'a, 'a>( + ctx: &mut InferenceContext<'a>, + targets: &'b [Expression], + value: &'b Expression, +) -> Result<(), String> { + let ty = infer_expr(ctx, value)?.ok_or_else(|| "no value".to_string())?; + for t in targets.iter() { + check_stmt_binding(ctx, t, ty.clone())?; + } + Ok(()) +}