diff --git a/lib/src/analysis/type_analyze.rs b/lib/src/analysis/type_analyze.rs index f4f5130..23d9d41 100644 --- a/lib/src/analysis/type_analyze.rs +++ b/lib/src/analysis/type_analyze.rs @@ -1,5 +1,6 @@ use crate::ast::*; use crate::context::Scope; +use smallvec::smallvec; /// Type analysis attribute #[derive(Clone, Default)] @@ -98,13 +99,24 @@ impl AstVisitorMut for TypeAnalyzer { fn visit_operator_expression_mut(&mut self, expr: &mut OperatorExpression) { // collect all operands type - let mut operands_attr = vec![]; + let mut operands_attr: SmallVec3<_> = smallvec![]; for operand in expr.operands_mut() { self.push(TypeAnalyzerAttribute::default()); self.visit_expression_mut(operand); operands_attr.push(self.pop()); } + // Set operator result type + let op_type = match operands_attr.len() { + 1 => operands_attr[0].derived_type.clone(), + 2 => analyze_op_expr_type( + &operands_attr[0].derived_type, + &operands_attr[1].derived_type, + ), + _ => None, + }; + expr.set_ty(op_type); + // let ref mut result_type = self.top_mut().derived_type; // for attr in operands_attr { // if let Some(true) = attr @@ -151,3 +163,54 @@ impl AstVisitorMut for TypeAnalyzer { self.top_mut().derived_type = attr.derived_type } } + +fn analyze_op_expr_type(op1: &Option, op2: &Option) -> Option { + let tc1 = op1.as_ref()?.type_class(); + let tc2 = op2.as_ref()?.type_class(); + + // Usertype is not handled + if matches!(tc1, TypeClass::UserType(..)) { + return None; + } + if matches!(tc2, TypeClass::UserType(..)) { + return None; + } + + // Array is not handled + if matches!(tc1, TypeClass::Array(..)) { + return None; + } + if matches!(tc2, TypeClass::Array(..)) { + return None; + } + + // Same type + if tc1 == tc2 { + return op1.clone(); + } + + match tc1 { + // BitType can implicit convert to any type except String + TypeClass::Bit => match tc2 { + TypeClass::String => None, + _ => op2.clone(), + }, + + TypeClass::Bool => match tc2 { + TypeClass::Bit => op1.clone(), + _ => None, + }, + + TypeClass::Byte => match tc2 { + TypeClass::Bit => op1.clone(), + TypeClass::SInt | TypeClass::UInt | TypeClass::Int => op2.clone(), + _ => unimplemented!("{:?}", op2), + }, + + TypeClass::Int => match tc2 { + TypeClass::Byte | TypeClass::SInt | TypeClass::Bit => op1.clone(), + _ => None, + }, + _ => unimplemented!("{:?}", op1), + } +} diff --git a/lib/src/ast/expression.rs b/lib/src/ast/expression.rs index c0a909e..8837544 100644 --- a/lib/src/ast/expression.rs +++ b/lib/src/ast/expression.rs @@ -22,7 +22,7 @@ pub enum ExprKind { #[derive(Debug)] pub struct Expression { - pub kind: ExprKind, + pub(crate) kind: ExprKind, } impl_ast_display!(Expression, visit_expression); @@ -37,6 +37,7 @@ impl Expression { } /// Get type of this expression + #[inline] pub fn ty(&self) -> Option<&Type> { match &self.kind { ExprKind::Variable(var_expr) => var_expr.ty(), diff --git a/lib/src/ast/mod.rs b/lib/src/ast/mod.rs index b45b204..24f3218 100644 --- a/lib/src/ast/mod.rs +++ b/lib/src/ast/mod.rs @@ -2,6 +2,7 @@ use bitflags::bitflags; use smallvec::SmallVec; use std::cell::RefCell; use std::fmt::{self, Debug, Display, Formatter}; +use std::hash::{Hash, Hasher}; use std::rc::Rc; use crate::parser::{LiteralValue, Operator, StString}; @@ -321,6 +322,34 @@ pub enum TypeClass { Array(RefCell), } +impl Hash for TypeClass { + fn hash(&self, state: &mut H) { + let tag = match self { + TypeClass::Bit => 1, + TypeClass::Bool => 2, + TypeClass::Byte => 3, + TypeClass::UInt => 4, + TypeClass::Int => 5, + _ => unimplemented!("{:?}", self), + }; + + tag.hash(state); + + // TODO: incomplete implements + match self { + TypeClass::UserType(user_type) => { + user_type.borrow().name().hash(state); + } + TypeClass::Array(array_type) => { + let array_type = array_type.borrow(); + let base_type = array_type.base_type().borrow(); + base_type.type_class().hash(state); + } + _ => {} + } + } +} + impl Display for TypeClass { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { diff --git a/lib/src/ast/types.rs b/lib/src/ast/types.rs index 7081517..1ddafed 100644 --- a/lib/src/ast/types.rs +++ b/lib/src/ast/types.rs @@ -190,6 +190,11 @@ impl ArrayType { dimensions, } } + + #[inline] + pub fn base_type(&self) -> &RefCell { + &self.base_type + } } impl Display for ArrayType { @@ -219,4 +224,4 @@ impl From for Type { Type::from_class(class) } -} \ No newline at end of file +} diff --git a/lib/src/ast/visitor.rs b/lib/src/ast/visitor.rs index e746f8e..b758044 100644 --- a/lib/src/ast/visitor.rs +++ b/lib/src/ast/visitor.rs @@ -1,12 +1,5 @@ -#![allow(dead_code)] - use crate::ast::call_expression::CallExpression; -use crate::ast::{ - AliasDeclare, AssignExpression, CompoAccessExpression, DeclKind, Declaration, EnumDeclare, - ExprKind, ExprStatement, Expression, FunctionDeclare, GlobalVariableDeclare, IfStatement, - LiteralExpression, OperatorExpression, Statement, StmtKind, StructDeclare, Variable, - VariableExpression, -}; +use crate::ast::*; use super::RangeExpression; diff --git a/lib/src/parser/mod.rs b/lib/src/parser/mod.rs index 0e5b744..ecabe4b 100644 --- a/lib/src/parser/mod.rs +++ b/lib/src/parser/mod.rs @@ -16,6 +16,15 @@ pub use operator::Operator; mod token; pub use token::TokenKind; +#[macro_export] +macro_rules! parse_statement { + ($code: literal) => {{ + let mut lexer = StLexerBuilder::new().build_str($code); + let parser = ParserBuilder::default().build(); + parser.parse_stmt(&mut lexer) + }}; +} + #[cfg(test)] mod test; diff --git a/lib/src/serde/mod.rs b/lib/src/serde/mod.rs index c7cde75..e47263a 100644 --- a/lib/src/serde/mod.rs +++ b/lib/src/serde/mod.rs @@ -1,2 +1,2 @@ mod xml; -pub use xml::Application; +pub use xml::Project; diff --git a/lib/src/serde/xml.rs b/lib/src/serde/xml.rs index c314f40..942da3f 100644 --- a/lib/src/serde/xml.rs +++ b/lib/src/serde/xml.rs @@ -6,38 +6,38 @@ use std::str::FromStr; use uuid::Uuid; #[derive(Serialize, Deserialize, Debug)] -pub enum AppType { +pub enum ProjectType { App, Library, } -impl Default for AppType { +impl Default for ProjectType { fn default() -> Self { Self::App } } #[derive(Serialize, Deserialize, Debug)] -pub struct Application { +pub struct Project { #[serde(default, rename = "pou-list")] pub pou_list: POUList, #[serde(rename = "@name")] pub name: String, #[serde(rename = "@type", default)] - pub app_type: AppType, + pub project_type: ProjectType, #[serde(rename = "@namespace")] pub namespace: Option, } -impl From for ModuleContext { - fn from(app: Application) -> Self { - let ctx = match app.app_type { - AppType::App => ModuleContext::new(ModuleKind::Application), - AppType::Library => ModuleContext::new(ModuleKind::Library), +impl From for ModuleContext { + fn from(proj: Project) -> Self { + let ctx = match proj.project_type { + ProjectType::App => ModuleContext::new(ModuleKind::Application), + ProjectType::Library => ModuleContext::new(ModuleKind::Library), }; let mut ctx_write = ctx.write(); - for pou in app.pou_list.pou { + for pou in proj.pou_list.pou { let mut lexer = StLexerBuilder::new().build_str(&pou.interface.content); let decl = ParserBuilder::default().build().parse(&mut lexer).unwrap(); let func = ctx_write.add_declaration( diff --git a/lib/src/test/test_type_analyze.rs b/lib/src/test/test_type_analyze.rs index 93c32e0..de6fa6d 100644 --- a/lib/src/test/test_type_analyze.rs +++ b/lib/src/test/test_type_analyze.rs @@ -1,24 +1,21 @@ +use crate::analysis::TypeAnalyzer; +use crate::parse_statement; use crate::parser::{ParserBuilder, StLexerBuilder}; use crate::prelude::*; -use crate::serde::Application; - -use crate::analysis::TypeAnalyzer; +use crate::serde::Project; use quick_xml::de::from_str; #[test] fn test_type_analyze() { - let app: Application = from_str(include_str!("test_projects/test_proj1.xml")).unwrap(); - let mgr = UnitsManager::new(); + let app: Project = from_str(include_str!("test_projects/test_proj1.xml")).unwrap(); let ctx: ModuleContext = app.into(); + + let mgr = UnitsManager::new(); let ctx_id = ctx.read().id(); mgr.write().add_context(ctx.clone()); mgr.write().set_active_application(Some(ctx.read().id())); - let stmt_str = "c := a + b;"; - let mut lexer = StLexerBuilder::new().build_str(stmt_str); - let parser = ParserBuilder::default().build(); - let mut stmt = parser.parse_stmt(&mut lexer).unwrap(); - + let mut stmt = parse_statement!("c := a + b;").unwrap(); let mut type_analyzer = TypeAnalyzer::new(); type_analyzer.analyze_statement(&mut stmt, mgr.module_scope(ctx_id)); diff --git a/lib/src/utils/hasher.rs b/lib/src/utils/hasher.rs index a045d2c..cb799ea 100644 --- a/lib/src/utils/hasher.rs +++ b/lib/src/utils/hasher.rs @@ -46,7 +46,8 @@ impl MyHash for Variable { impl MyHash for Type { fn hash(&self, state: &mut H) { - todo!() + // TODO: incomplete implements + self.type_class().hash(state); } } @@ -115,12 +116,16 @@ impl DeclVisitor<'_> for AstHasher { impl AstVisitor<'_> for AstHasher { fn visit_literal(&mut self, literal: &LiteralExpression) { VisitType::Literal.hash(&mut self.hasher); - literal.literal().hash(&mut self.hasher) + literal.literal().hash(&mut self.hasher); + literal.literal().ty().hash(&mut self.hasher); } fn visit_variable_expression(&mut self, variable: &'_ VariableExpression) { VisitType::Variable.hash(&mut self.hasher); - variable.name().hash(&mut self.hasher) + variable.name().hash(&mut self.hasher); + if let Some(ty) = variable.ty() { + ty.hash(&mut self.hasher); + } } fn visit_expr_statement(&mut self, stmt: &ExprStatement) { diff --git a/screenshots/Screenshot_20231209_230838.png b/screenshots/Screenshot_20231209_230838.png index 8a25c9e..052f945 100644 Binary files a/screenshots/Screenshot_20231209_230838.png and b/screenshots/Screenshot_20231209_230838.png differ diff --git a/viewer/src/app.rs b/viewer/src/app.rs index 0d3ff54..d4e5ff9 100644 --- a/viewer/src/app.rs +++ b/viewer/src/app.rs @@ -1,7 +1,7 @@ use stc::analysis::TypeAnalyzer; use stc::backend::{CodeGenBackend, CodeGenDriver, LuaBackend}; use stc::prelude::*; -use stc::serde::Application; +use stc::serde::Project; use stc::utils::write_ast_to_file; use log::info; @@ -21,7 +21,7 @@ impl StcViewerApp { } #[inline] - pub fn from_app(app: Application) -> Self { + pub fn from_app(app: Project) -> Self { let mgr = UnitsManager::new(); let ctx: ModuleContext = app.into(); mgr.write().add_context(ctx.clone());