diff --git a/src/rsprocess/assert.rs b/src/rsprocess/assert.rs index 7aaad49..0c50ef9 100644 --- a/src/rsprocess/assert.rs +++ b/src/rsprocess/assert.rs @@ -20,16 +20,7 @@ pub enum Tree { For(Variable, Range, Box), } -#[derive(Debug, Clone)] -pub struct Variable { - data: String, -} - -impl Variable { - pub fn from(data: String) -> Self { - Self { data } - } -} +pub type Variable = String; #[derive(Debug, Clone)] pub enum AssignmentVariable { @@ -37,6 +28,15 @@ pub enum AssignmentVariable { QualifiedVar(Variable, QualifierRestricted) } +impl fmt::Display for AssignmentVariable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Var(v) => write!(f, "{v}"), + Self::QualifiedVar(v, q) => write!(f, "{v}.{q}"), + } + } +} + #[derive(Debug, Clone)] pub enum Expression { True, @@ -66,7 +66,36 @@ pub enum Unary { Empty, Length, ToStr, - Qualifier(Qualifier) + Qualifier(Qualifier), +} + +impl Unary { + fn associated_types_unary(&self) -> Vec { + match self { + Unary::Not => vec![AssertionTypes::Boolean], + Unary::Rand => vec![AssertionTypes::Integer], + Unary::Empty => vec![AssertionTypes::Set], + Unary::Length => vec![AssertionTypes::String, + AssertionTypes::Set], + Unary::ToStr => vec![AssertionTypes::Boolean, + AssertionTypes::Integer, + AssertionTypes::Element], + Unary::Qualifier(_) => vec![AssertionTypes::Set] + } + } +} + +impl fmt::Display for Unary { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Unary::Not => write!(f, "not"), + Unary::Rand => write!(f, "rand"), + Unary::Empty => write!(f, ".empty"), + Unary::Length => write!(f, ".length"), + Unary::ToStr => write!(f, ".tostr"), + Unary::Qualifier(q) => write!(f, ".{q}"), + } + } } @@ -81,6 +110,27 @@ pub enum QualifierRestricted { Products, } +impl fmt::Display for QualifierRestricted { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + QualifierRestricted::Entities => + write!(f, "Entities"), + QualifierRestricted::Context => + write!(f, "Context"), + QualifierRestricted::Reactants => + write!(f, "Reactants"), + QualifierRestricted::ReactantsAbsent => + write!(f, "ReactantsAbsent"), + QualifierRestricted::Inhibitors => + write!(f, "Inhibitors"), + QualifierRestricted::InhibitorsPresent => + write!(f, "InhibitorsPresent"), + QualifierRestricted::Products => + write!(f, "Products"), + } + } +} + #[derive(Debug, Clone, Copy)] pub enum Qualifier { AvailableEntities, @@ -89,6 +139,18 @@ pub enum Qualifier { Restricted(QualifierRestricted), } +impl fmt::Display for Qualifier { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AvailableEntities => write!(f, "AvailableEntities"), + Self::AllReactants => write!(f, "AllReactants"), + Self::AllInhibitors => write!(f, "AllInhibitors"), + Self::Restricted(q) => write!(f, "{q}"), + } + } +} + + #[derive(Debug, Clone, Copy)] pub enum Binary { And, @@ -113,3 +175,387 @@ pub enum Binary { Max, CommonSubStr, } + +impl Binary { + fn associate( + &self, + t1: &AssertionTypes, + t2: &AssertionTypes + ) -> Result { + // + match self { + Self::And | + Self::Or => { + if let (AssertionTypes::Boolean, AssertionTypes::Boolean) = + (t1, t2) + { + return Ok(AssertionTypes::Boolean) + } + }, + Self::Xor => { + match (t1, t2) { + (AssertionTypes::Boolean, AssertionTypes::Boolean) => + return Ok(AssertionTypes::Boolean), + (AssertionTypes::Set, AssertionTypes::Set) => + return Ok(AssertionTypes::Set), + _ => {} + } + } + Self::Less | + Self::LessEq | + Self::More | + Self::MoreEq => + match (t1, t2) { + (AssertionTypes::Integer, AssertionTypes::Integer) | + (AssertionTypes::Set, AssertionTypes::Set ) => + return Ok(AssertionTypes::Boolean), + _ => {} + } + Self::Eq | + Self::NotEq => { + match (t1, t2) { + (AssertionTypes::Integer, AssertionTypes::Integer) | + (AssertionTypes::Boolean, AssertionTypes::Boolean) | + (AssertionTypes::Element, AssertionTypes::Element) | + (AssertionTypes::Label, AssertionTypes::Label) | + (AssertionTypes::String, AssertionTypes::String) | + (AssertionTypes::Set, AssertionTypes::Set ) => + return Ok(AssertionTypes::Boolean), + _ => {} + } + }, + Self::Plus | + Self::Minus | + Self::Times => { + match (t1, t2) { + (AssertionTypes::Integer, AssertionTypes::Integer) => + return Ok(AssertionTypes::Integer), + (AssertionTypes::Set, AssertionTypes::Set ) => + return Ok(AssertionTypes::Set), + _ => {} + } + }, + Self::Exponential | + Self::Quotient | + Self::Reminder => { + if let (AssertionTypes::Integer, AssertionTypes::Integer) = + (t1, t2) { + return Ok(AssertionTypes::Integer) + } + }, + Self::Concat => { + if let (AssertionTypes::String, AssertionTypes::String) = + (t1, t2) { + return Ok(AssertionTypes::String) + } + }, + Self::SubStr => { + if let (AssertionTypes::String, AssertionTypes::String) = + (t1, t2) { + return Ok(AssertionTypes::Boolean) + } + }, + Self::Min | + Self::Max => { + if let (AssertionTypes::Integer, AssertionTypes::Integer) = + (t1, t2) { + return Ok(AssertionTypes::Integer) + } + }, + Self::CommonSubStr => { + if let (AssertionTypes::String, AssertionTypes::String) = + (t1, t2) { + return Ok(AssertionTypes::String) + } + }, + } + Err(format!("Expressions have incompatible types: {t1} and {t2} with \ + operation {self}.")) + } +} + +impl fmt::Display for Binary { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::And => write!(f, "&&"), + Self::Or => write!(f, "||"), + Self::Xor => write!(f, "^^"), + Self::Less => write!(f, "<"), + Self::LessEq => write!(f, "<="), + Self::More => write!(f, ">"), + Self::MoreEq => write!(f, ">="), + Self::Eq => write!(f, "=="), + Self::NotEq => write!(f, "!="), + Self::Plus => write!(f, "+"), + Self::Minus => write!(f, "-"), + Self::Times => write!(f, "*"), + Self::Exponential => write!(f, "^"), + Self::Quotient => write!(f, "/"), + Self::Reminder => write!(f, "%"), + Self::Concat => write!(f, "::"), + Self::SubStr => write!(f, "substr"), + Self::Min => write!(f, "min"), + Self::Max => write!(f, "max"), + Self::CommonSubStr => write!(f, "commonsubstr"), + } + } +} + + +use core::fmt; +// ----------------------------------------------------------------------------- +use std::collections::HashMap; + +struct Context { + data: HashMap, + ty_return: Option +} + +impl Context { + pub fn new() -> Self { + Context { + data: HashMap::new(), + ty_return: None + } + } + + pub fn assign( + &mut self, + v: &AssignmentVariable, + ty: AssertionTypes + ) -> Result<(), String> { + match v { + AssignmentVariable::Var(v) => { + self.data.insert(v.clone(), ty); + Ok(()) + }, + AssignmentVariable::QualifiedVar(v, q) => { + if let Some(AssertionTypes::Label) = self.data.get(v) { + Ok(()) + } else { + Err(format!("Variable {v} does not have type label while \ + trying to assign to qualification {q}")) + } + } + } + } + + pub fn return_type( + &mut self, + ty: AssertionTypes + ) -> Result<(), String> { + if let Some(ty_return) = self.ty_return { + if ty_return == ty { + Ok(()) + } else { + Err(format!("Return statements don't agree: {ty_return} and \ + {ty} found.")) + } + } else { + self.ty_return = Some(ty); + Ok(()) + } + } + + pub fn assign_range( + &mut self, + v: &Variable, + ty: AssertionTypes + ) -> Result<(), String> { + match ty { + AssertionTypes::RangeSet => { + self.data.insert(v.clone(), AssertionTypes::Element); + Ok(()) + }, + AssertionTypes::RangeInteger => { + self.data.insert(v.clone(), AssertionTypes::Integer); + Ok(()) + }, + _ => { + Err(format!("Range has incorrect type {ty}.")) + } + } + } + + pub fn get( + &self, + v: &AssignmentVariable, + ) -> Result { + match v { + AssignmentVariable::Var(v) => { + if let Some(ty) = self.data.get(v) { + Ok(*ty) + } else { + Err(format!("Could not find variable {v}.")) + } + }, + AssignmentVariable::QualifiedVar(var, _) => { + if let Some(ty) = self.data.get(var) { + if *ty == AssertionTypes::Label { + Ok(AssertionTypes::Set) + } else { + Err(format!("Variable {var} is not a label.")) + } + } else { + Err(format!("Could not find variable {v}.")) + } + } + } + + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +enum AssertionTypes { + Boolean, + Integer, + String, + Label, + Set, + Element, + NoType, + RangeInteger, + RangeSet, +} + +impl fmt::Display for AssertionTypes { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AssertionTypes::Boolean => write!(f, "boolean"), + AssertionTypes::Integer => write!(f, "integer"), + AssertionTypes::String => write!(f, "string"), + AssertionTypes::Label => write!(f, "label"), + AssertionTypes::Set => write!(f, "set"), + AssertionTypes::Element => write!(f, "element"), + AssertionTypes::NoType => write!(f, "no type"), + AssertionTypes::RangeInteger => write!(f, "range of integers"), + AssertionTypes::RangeSet => write!(f, "range of set"), + } + } +} + +fn typecheck(tree: &Tree, c: &mut Context) -> Result { + match tree { + Tree::Concat(t1, t2) => { + typecheck(t1, c)?; + typecheck(t2, c) + }, + Tree::If(exp, t) => { + match typecheck_expression(exp, c)? { + AssertionTypes::Boolean => {}, + _ => {return Err("Expression in if statement doesn't return a \ + boolean.".to_string())} + }; + typecheck(t, c) + }, + Tree::IfElse(exp, t1, t2) => { + match typecheck_expression(exp, c)? { + AssertionTypes::Boolean => {}, + _ => {return Err("Expression in if statement doesn't return a \ + boolean.".into())} + }; + let type_t1 = typecheck(t1, c)?; + let type_t2 = typecheck(t2, c)?; + if type_t1 == type_t2 { + Ok(type_t1) + } else { + Err("Branches of if statement do not match.".into()) + } + }, + Tree::Assignment(assignvar, exp) => { + let type_exp = typecheck_expression(exp, c)?; + c.assign(assignvar, type_exp)?; + Ok(AssertionTypes::NoType) + }, + Tree::Return(exp) => { + let type_exp = typecheck_expression(exp, c)?; + c.return_type(type_exp)?; + Ok(AssertionTypes::NoType) + }, + Tree::For(var, range, t) => { + let type_range = typecheck_range(range, c)?; + c.assign_range(var, type_range)?; + typecheck(t, c) + }, + } +} + +fn typecheck_expression( + exp: &Expression, + c: &Context +) -> Result { + match exp { + Expression::True | + Expression::False => Ok(AssertionTypes::Boolean), + Expression::Integer(_) => Ok(AssertionTypes::Integer), + Expression::Label(_) => Ok(AssertionTypes::Label), + Expression::Set(_) => Ok(AssertionTypes::Set), + Expression::Element(_) => Ok(AssertionTypes::Element), + Expression::String(_) => Ok(AssertionTypes::String), + Expression::Var(v) => c.get(v), + + Expression::Unary(u, exp) => { + if let Unary::Qualifier(_) = u { + let type_exp = typecheck_expression(exp, c)?; + if type_exp == AssertionTypes::Label { + Ok(AssertionTypes::Set) + } else { + Err("Trying to get the field of a label, but value is not \ + a label.".into()) + } + } else { + let type_exp = typecheck_expression(exp, c)?; + let possible_types = u.associated_types_unary(); + if possible_types.contains(&type_exp) { + Ok(type_exp) + } else { + Err(format!("Cannot use operation {u} on type {type_exp}")) + } + } + }, + + Expression::Binary(b, exp1, exp2) => { + let type_exp1 = typecheck_expression(exp1, c)?; + let type_exp2 = typecheck_expression(exp2, c)?; + b.associate(&type_exp1, &type_exp2) + } + } +} + + +fn typecheck_range( + range: &Range, + c: &mut Context +) -> Result { + match range { + Range::IterateInRange(exp1, exp2) => { + let type_exp1 = typecheck_expression(exp1, c)?; + let type_exp2 = typecheck_expression(exp2, c)?; + if let (AssertionTypes::Integer, AssertionTypes::Integer) = + (type_exp1, type_exp2) + { + Ok(AssertionTypes::RangeInteger) + } else { + Err(format!("Expressions in range are not integers, but are: \ + {type_exp1} and {type_exp2}.")) + } + }, + Range::IterateOverSet(exp) => { + let type_exp = typecheck_expression(exp, c)?; + if let AssertionTypes::Set = type_exp { + Ok(AssertionTypes::RangeSet) + } else { + Err(format!("Expressions in range is not a set, but is: \ + {type_exp}.")) + } + } + } +} + +impl RSassert { + pub fn typecheck(&self) -> Result<(), String> { + let mut context = Context::new(); + typecheck(&self.tree, &mut context)?; + Ok(()) + } +} diff --git a/src/rsprocess/grammar.lalrpop b/src/rsprocess/grammar.lalrpop index 1b8a5d7..01679c9 100644 --- a/src/rsprocess/grammar.lalrpop +++ b/src/rsprocess/grammar.lalrpop @@ -211,7 +211,7 @@ AssertAssignmentVar: assert::AssignmentVariable = { } AssertVariable: assert::Variable = { - => assert::Variable::from(v) + => v } AssertExpression: assert::Expression = {