use std::clone::Clone; use std::fmt::{Debug, Formatter, Error}; use std::collections::HashMap; // This represents a block of Modelica code. // A valid .mo file will be a sequence of these #[derive(Clone, PartialEq)] pub enum ModelicaCode { Class, // unimpl; generic Model(ModelicaModel), Record, // unimpl Block(ModelicaBlock), Connector(ModelicaConnector), Type(ModelicaType), Package(ModelicaPackage), Function, // unimpl } // A package is basically a namespace; they can be nested and contain any other // code chunks (models, etc). // They are optional; models don't need to live in a package to be valid. #[derive(Clone, PartialEq)] pub struct ModelicaPackage { pub name: String, pub description: Option, pub children: Vec, } #[derive(Clone, PartialEq)] pub struct ModelicaType { pub name: String, pub description: Option, pub component: ComponentDeclaration, } #[derive(Clone, PartialEq)] pub struct ModelicaBlock { pub name: String, pub description: Option, pub component_clauses: Vec, pub public_component_clauses: Vec, pub protected_component_clauses: Vec, pub equations: Vec, pub connections: Vec, pub extends: Vec, } #[derive(Clone, PartialEq)] pub struct ModelicaConnector { pub name: String, pub description: Option, pub component_clauses: Vec, } #[derive(Clone, PartialEq)] pub struct ModelicaModel { pub name: String, pub description: Option, pub component_clauses: Vec, pub equations: Vec, pub connections: Vec, pub extends: Vec, } #[derive(Copy, Clone, PartialEq)] pub enum ComponentPrefix { // incomplete: eg, can be parameter and input Flow, Stream, Input, Output, Discrete, Parameter, Constant, } #[derive(Clone, PartialEq)] pub struct ComponentClause { pub prefix: Option, pub specifier: String, pub declarations: Vec, } #[derive(Clone, PartialEq)] pub struct ComponentDeclaration { pub name: String, pub dimensions: Option>, pub value: Option, pub quantity: Option, pub mods: HashMap, pub description: Option, } // This isn't part of the parse AST; it's a flattened helper type #[derive(Clone, PartialEq)] pub struct Component { pub prefix: Option, pub specifier: String, pub name: String, pub value: Option, pub mods: HashMap, pub description: Option, } #[derive(Clone, PartialEq)] pub struct Connection { pub a: String, pub b: String, } #[derive(Clone, PartialEq)] pub struct SimpleEquation { pub lhs: Expr, pub rhs: Expr, } #[derive(Clone, PartialEq)] pub enum Expr { Integer(i64), Float(f64), Boolean(bool), StringLiteral(String), Ident(String), Der(Box), Sign(Box), MathUnaryExpr(MathUnaryFunc, Box), BinExpr(BinOperator, Box, Box), Array(Vec), } #[derive(Copy, Clone, PartialEq)] pub enum MathUnaryFunc { Abs, Sqrt, Sin, Cos, Tan, Asin, Acos, Atan, // TODO: atan2(x,y) Sinh, Cosh, Tanh, Exp, Log, Log10, } #[derive(Copy, Clone, PartialEq)] pub enum BinOperator { Multiply, Divide, Add, Subtract, } //// Helpers impl ModelicaModel { // This flattens the ComponentClause/ComponentDeclaration tree into a flat // list of Components // TODO: refactor this into an iterator `components()`? pub fn get_components(&self) -> Vec { let mut vars: Vec = vec![]; for clause in &self.component_clauses { vars.extend(clause.declarations.iter().map(|ref dec| Component { prefix: clause.prefix.clone(), specifier: clause.specifier.clone(), name: dec.name.clone(), value: dec.value.clone(), mods: dec.mods.clone(), description: dec.description.clone(), })) } vars } pub fn get_constant_vars(&self) -> HashMap> { let mut binds = HashMap::new(); // XXX: actually implement this... for c in &self.get_components() { match c.prefix { Some(ComponentPrefix::Constant) => { binds.insert(c.name.clone(), Some(Expr::Integer(123))); }, Some(ComponentPrefix::Parameter) => { binds.insert(c.name.clone(), Some(Expr::Float(4.56))); }, _ => (), } } binds } // This crude function finds "unbound" variables: those which are not constants, parameters, or // the sole element on the LHS of an equation. // Bugs: // if a var is on LHS and RHS of same equation pub fn get_free_vars(&self) -> Vec { // Start with components, and remove constants and parameters let components = self.get_components(); let vars = components.iter().filter(|v| match v.prefix { Some(ComponentPrefix::Constant) | Some(ComponentPrefix::Parameter) => false, _ => true, }); // Remove LHS (bound) vars let mut outputs = vec![]; for eq in self.equations.iter() { // TODO: if let Expr::Ident(ref symb) = eq.lhs { outputs.push(symb.to_string()); } } let vars = vars.filter(|v| !outputs.contains(&v.name)); vars.map(|c| c.name.clone()).collect() } } fn union_strings(a: &Vec, b: &Vec) -> Vec { let mut u = a.clone(); for e in b { if !(u.contains(&e)) { u.push(e.clone()); } } u } impl Expr { // Order is undefined // TODO: should return a HashSet, not a Vec pub fn identifiers(&self) -> Vec { use self::Expr::*; match *self { Integer(_) | Float(_) | Boolean(_) | StringLiteral(_) => vec![], Ident(ref s) => vec![s.clone()], Der(ref e) | Sign(ref e) => e.identifiers(), MathUnaryExpr(_, ref e) => e.identifiers(), BinExpr(_, ref e1, ref e2) => { union_strings(&e1.identifiers(), &e2.identifiers()) }, Array(ref el) => { let mut all: Vec = vec![]; for e in el { all.append(&mut e.identifiers()); } all } } } pub fn contains(&self, ident: &str) -> bool{ self.identifiers().contains(&ident.to_string()) } } impl SimpleEquation { // Order is undefined pub fn identifiers(&self) -> Vec { union_strings(&self.lhs.identifiers(), &self.rhs.identifiers()) } pub fn contains(&self, ident: &str) -> bool{ let s = &ident.to_string(); self.lhs.identifiers().contains(s) || self.rhs.identifiers().contains(s) } } //// Debug Implementations impl Debug for ModelicaModel { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { try!(write!(fmt, "model {}\n", self.name)); for e in self.extends.iter() { try!(write!(fmt, " extends {};\n", e)); } for v in self.get_components().iter() { try!(write!(fmt, " {:?};\n", v)); } try!(write!(fmt, "equation\n")); for c in self.connections.iter() { try!(write!(fmt, " {:?};\n", c)); } for e in self.equations.iter() { try!(write!(fmt, " {:?};\n", e)); } write!(fmt, "end {};\n", self.name) } } impl Debug for ComponentPrefix { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { use self::ComponentPrefix::*; write!(fmt, "{}", match *self { Flow => "flow", Stream => "stream", Input => "input", Output => "output", Discrete => "discrete", Parameter => "parameter", Constant => "constant", }) } } impl Debug for Component { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { write!(fmt, "{}{} {}", match self.prefix { Some(p) => format!("{:?} ", p), None => "".to_string(), }, self.specifier, self.name, ) } } impl Debug for Connection { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { write!(fmt, "connect({}, {})", self.a, self.b) } } impl Debug for SimpleEquation { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { write!(fmt, "{:?} = {:?}", self.lhs, self.rhs) } } impl Debug for Expr { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { use self::Expr::*; match *self { Integer(e) => write!(fmt, "{}", e), Float(e) => write!(fmt, "{}", e), Boolean(e) => write!(fmt, "{}", e), StringLiteral(ref e) => write!(fmt, "\"{}\"", e), Ident(ref e) => write!(fmt, "{}", e), Der(ref e) => write!(fmt, "der({:?})", e), Sign(ref e) => write!(fmt, "sign({:?})", e), MathUnaryExpr(func, ref e) => write!(fmt, "{:?}({:?})", func, e), BinExpr(op, ref l, ref r) => write!(fmt, "({:?} {:?} {:?})", l, op, r), Array(ref el) => { try!(write!(fmt, "[")); for e in el { // XXX: not last comma try!(write!(fmt, "{:?},", e)); } write!(fmt, "]") }, } } } impl Debug for BinOperator { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { use self::BinOperator::*; match *self { Multiply => write!(fmt, "*"), Divide => write!(fmt, "/"), Add => write!(fmt, "+"), Subtract => write!(fmt, "-"), } } } impl Debug for MathUnaryFunc { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { use self::MathUnaryFunc::*; match *self { Abs => write!(fmt, "abs"), Sqrt => write!(fmt, "sqrt"), Sin => write!(fmt, "sin"), Cos => write!(fmt, "cos"), Tan => write!(fmt, "tan"), Asin => write!(fmt, "asin"), Acos => write!(fmt, "acos"), Atan => write!(fmt, "atan"), Sinh => write!(fmt, "sinh"), Cosh => write!(fmt, "cosh"), Tanh => write!(fmt, "tanh"), Exp => write!(fmt, "exp"), Log => write!(fmt, "log"), Log10 => write!(fmt, "log10"), } } }