diff options
author | bnewbold <bnewbold@robocracy.org> | 2016-12-17 18:34:47 -0800 |
---|---|---|
committer | bnewbold <bnewbold@robocracy.org> | 2016-12-17 18:34:47 -0800 |
commit | 9f82aceb9fbdb42f332d68f4a423123bd0788b2c (patch) | |
tree | c082b9795be8e9e9d286c8f8f1345d22f3ec1b59 | |
parent | f6364ebcac0d0a88a3cc6812fd2120c97b42cc26 (diff) | |
download | modelthing-9f82aceb9fbdb42f332d68f4a423123bd0788b2c.tar.gz modelthing-9f82aceb9fbdb42f332d68f4a423123bd0788b2c.zip |
refactor modelica parser into separate crate
22 files changed, 458 insertions, 519 deletions
@@ -2,14 +2,12 @@ name = "modelthing" version = "0.1.0" dependencies = [ - "colored 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.6.86 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "lalrpop 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lalrpop-util 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "markdown 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "modelica-parser-lalrpop 0.1.0", "pencil 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.21 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -429,6 +427,15 @@ dependencies = [ ] [[package]] +name = "modelica-parser-lalrpop" +version = "0.1.0" +dependencies = [ + "colored 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lalrpop 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lalrpop-util 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "num_cpus" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2,19 +2,14 @@ name = "modelthing" version = "0.1.0" authors = ["bnewbold <bnewbold@robocracy.org>"] -build = "src/build.rs" # eg, LALRPOP preprocessing - -[build-dependencies] -lalrpop = "0.12" [dependencies] getopts = "^0.2" toml = "0.2" log = "0.3" env_logger = "0.3" -lalrpop-util = "^0.12.4" markdown = "0.1" -colored = "1.3" +modelica-parser-lalrpop = { version = "*", path = "./modelica-parser-lalrpop" } # parser util rustc-serialize = "0.3" @@ -23,7 +18,3 @@ docopt = "0.6" # webface pencil = "0.3" - -[features] -# this effectively enables the feature `no-color` of colored when testing -test = ["colored/no-color"] diff --git a/modelica-parser-lalrpop/.gitignore b/modelica-parser-lalrpop/.gitignore new file mode 100644 index 0000000..03314f7 --- /dev/null +++ b/modelica-parser-lalrpop/.gitignore @@ -0,0 +1 @@ +Cargo.lock diff --git a/modelica-parser-lalrpop/Cargo.toml b/modelica-parser-lalrpop/Cargo.toml new file mode 100644 index 0000000..fabffe5 --- /dev/null +++ b/modelica-parser-lalrpop/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "modelica-parser-lalrpop" +version = "0.1.0" +authors = ["bnewbold <bnewbold@robocracy.org>"] +description = """ +Partial (work in progress) parser for the modelica language using the LALRPOP +rust parser crate. +""" +keywords = ["modelica", "lalrpop", "parser"] +build = "src/build.rs" # LALRPOP preprocessing +license = "MIT" + +[lib] +name = "modelica_parser" + +[dependencies] +lalrpop-util = "^0.12.4" +colored = "1.3" + +[build-dependencies] +lalrpop = "0.12" + +[features] +# this effectively enables the feature `no-color` of colored when testing +test = ["colored/no-color"] diff --git a/examples/minimal.modelica b/modelica-parser-lalrpop/examples/minimal.modelica index 85335a5..85335a5 100644 --- a/examples/minimal.modelica +++ b/modelica-parser-lalrpop/examples/minimal.modelica diff --git a/examples/minimal2.modelica b/modelica-parser-lalrpop/examples/minimal2.modelica index 69790b0..69790b0 100644 --- a/examples/minimal2.modelica +++ b/modelica-parser-lalrpop/examples/minimal2.modelica diff --git a/examples/minimal3.modelica b/modelica-parser-lalrpop/examples/minimal3.modelica index 1e9a211..1e9a211 100644 --- a/examples/minimal3.modelica +++ b/modelica-parser-lalrpop/examples/minimal3.modelica diff --git a/modelica-parser-lalrpop/src/ast.rs b/modelica-parser-lalrpop/src/ast.rs new file mode 100644 index 0000000..99d443c --- /dev/null +++ b/modelica-parser-lalrpop/src/ast.rs @@ -0,0 +1,241 @@ + +use std::clone::Clone; +use std::fmt::{Debug, Formatter, Error}; +use std::collections::HashMap; + +#[derive(Clone, PartialEq)] +pub struct ModelicaModel { + pub name: String, + pub components: Vec<Component>, + pub equations: Vec<SimpleEquation>, + pub connections: Vec<Connection>, + pub extends: Vec<String>, +} + +#[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 Component { + pub prefix: Option<ComponentPrefix>, + pub specifier: String, + pub name: String, + pub value: Option<Expr>, + pub units: Option<String>, + pub description: Option<String>, +} + +#[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), + Ident(String), + Der(Box<Expr>), + Abs(Box<Expr>), + BinExpr(BinOperator, Box<Expr>, Box<Expr>), +} + +#[derive(Copy, Clone, PartialEq)] +pub enum BinOperator { + Multiply, + Divide, + Add, + Subtract, +} + +//// Helpers + +impl ModelicaModel { + + pub fn get_constant_vars(&self) -> HashMap<String,Option<Expr>> { + let mut binds = HashMap::new(); + // XXX: actually implement this... + for c in &self.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<String> { + // Start with components, and remove constants and parameters + let vars = self.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<String>, b: &Vec<String>) -> Vec<String> { + 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<String> { + use self::Expr::*; + match *self { + Integer(_) | Float(_) => vec![], + Ident(ref s) => vec![s.clone()], + Der(ref e) | Abs(ref e) => e.identifiers(), + BinExpr(_, ref e1, ref e2) => { + union_strings(&e1.identifiers(), &e2.identifiers()) + }, + } + } + + pub fn contains(&self, ident: &str) -> bool{ + self.identifiers().contains(&ident.to_string()) + } +} + +impl SimpleEquation { + + // Order is undefined + pub fn identifiers(&self) -> Vec<String> { + 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.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), + Ident(ref e) => write!(fmt, "{}", e), + Der(ref e) => write!(fmt, "der({:?})", e), + Abs(ref e) => write!(fmt, "abs({:?})", e), + BinExpr(op, ref l, ref r) => write!(fmt, "({:?} {:?} {:?})", l, op, r), + } + } +} + +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, "-"), + } + } +} diff --git a/src/build.rs b/modelica-parser-lalrpop/src/build.rs index 23c7d3f..23c7d3f 100644 --- a/src/build.rs +++ b/modelica-parser-lalrpop/src/build.rs diff --git a/modelica-parser-lalrpop/src/lib.rs b/modelica-parser-lalrpop/src/lib.rs new file mode 100644 index 0000000..f6f1e8b --- /dev/null +++ b/modelica-parser-lalrpop/src/lib.rs @@ -0,0 +1,65 @@ + +extern crate lalrpop_util; +extern crate colored; + +pub mod parser; +pub mod ast; + +use colored::*; +use lalrpop_util::ParseError; + + +fn pp_segment(raw: &str, start: usize, end: usize) -> String { + let mut line_start = 0; + let mut num = 0; + let mut ret = String::new(); + for line in raw.lines() { + num += 1; + let line_end = line_start + line.len(); + if (line_start <= start) && (start < line_end) { + ret += &format!(" {}\n{:>3} {} {}{}{}\n {} {}{}\n", + "|".blue().bold(), + num.to_string().blue().bold(), + "|".blue().bold(), + raw[line_start..start].normal(), + raw[start..end].red().bold(), + if end < line_end { + raw[end..line_end].normal() + } else { + "".normal() + }, + "|".blue().bold(), + std::iter::repeat(" ").take(start - line_start).collect::<String>(), + std::iter::repeat("^").take(end - start).collect::<String>().red().bold()); + } + line_start += line.len() + 1; + if line_start > end { break }; + } + ret +} + +pub fn pp_parseerror(raw: &str, pe: ParseError<usize, (usize, &str), ()>) -> String { + match pe { + ParseError::InvalidToken{location} => { + format!("{} invalid token starting at:\n{}", + "parse error:".red().bold(), + pp_segment(raw, location, location+1)) }, + ParseError::UnrecognizedToken{token: Some((start, (_, tok), end)), expected} => { + format!("{} unrecognized token '{}' (expected one of {:?}):\n{}", + "parse error:".red().bold(), + tok, + expected, + pp_segment(raw, start, end)) }, + ParseError::UnrecognizedToken{token: None, expected} => { + format!("{} premature end-of-file (expected one of {:?})", + "parse error:".red().bold(), + expected) }, + ParseError::ExtraToken{token: (start, (_, tok), end)} => { + format!("{} unexpected extra token '{}':\n{}", + "parse error:".red().bold(), + tok, + pp_segment(raw, start, end)) }, + _ => { + format!("{} {:?}", "parse error:".red().bold(), pe) }, + } +} diff --git a/src/modelica_parser.lalrpop b/modelica-parser-lalrpop/src/parser.lalrpop index f5d7958..cdd15b4 100644 --- a/src/modelica_parser.lalrpop +++ b/modelica-parser-lalrpop/src/parser.lalrpop @@ -1,5 +1,5 @@ use std::str::FromStr; -use modelica_ast::{ModelicaModel,Component, ComponentPrefix, Connection, +use ast::{ModelicaModel,Component, ComponentPrefix, Connection, SimpleEquation, Expr, BinOperator}; // This is an incomplete, non-standards-compliant, minimum-viable parser diff --git a/src/modelica_parser.rs b/modelica-parser-lalrpop/src/parser.rs index c7cca51..f364e84 100644 --- a/src/modelica_parser.rs +++ b/modelica-parser-lalrpop/src/parser.rs @@ -1,5 +1,5 @@ use std::str::FromStr; -use modelica_ast::{ModelicaModel,Component, ComponentPrefix, Connection, +use ast::{ModelicaModel,Component, ComponentPrefix, Connection, SimpleEquation, Expr, BinOperator}; extern crate lalrpop_util as __lalrpop_util; @@ -7,7 +7,7 @@ mod __parse__float { #![allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports)] use std::str::FromStr; - use modelica_ast::{ModelicaModel,Component, ComponentPrefix, Connection, + use ast::{ModelicaModel,Component, ComponentPrefix, Connection, SimpleEquation, Expr, BinOperator}; extern crate lalrpop_util as __lalrpop_util; #[allow(dead_code)] @@ -1881,7 +1881,7 @@ mod __parse__identifier { #![allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports)] use std::str::FromStr; - use modelica_ast::{ModelicaModel,Component, ComponentPrefix, Connection, + use ast::{ModelicaModel,Component, ComponentPrefix, Connection, SimpleEquation, Expr, BinOperator}; extern crate lalrpop_util as __lalrpop_util; #[allow(dead_code)] @@ -3755,7 +3755,7 @@ mod __parse__integer { #![allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports)] use std::str::FromStr; - use modelica_ast::{ModelicaModel,Component, ComponentPrefix, Connection, + use ast::{ModelicaModel,Component, ComponentPrefix, Connection, SimpleEquation, Expr, BinOperator}; extern crate lalrpop_util as __lalrpop_util; #[allow(dead_code)] @@ -5629,7 +5629,7 @@ mod __parse__model { #![allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports)] use std::str::FromStr; - use modelica_ast::{ModelicaModel,Component, ComponentPrefix, Connection, + use ast::{ModelicaModel,Component, ComponentPrefix, Connection, SimpleEquation, Expr, BinOperator}; extern crate lalrpop_util as __lalrpop_util; #[allow(dead_code)] diff --git a/tests/modelica_ast.rs b/modelica-parser-lalrpop/tests/ast.rs index d39615e..f9047cb 100644 --- a/tests/modelica_ast.rs +++ b/modelica-parser-lalrpop/tests/ast.rs @@ -1,9 +1,9 @@ -extern crate modelthing; +extern crate modelica_parser; use std::collections::HashSet; use std::iter::FromIterator; -use modelthing::modelica_ast::*; +use modelica_parser::ast::*; fn set_eq(a: Vec<String>, b: Vec<String>) -> bool { let set_a: HashSet<String> = HashSet::from_iter(a); @@ -13,7 +13,7 @@ fn set_eq(a: Vec<String>, b: Vec<String>) -> bool { #[test] fn test_expr_identifiers() { - use modelthing::modelica_ast::Expr::*; + use modelica_parser::ast::Expr::*; assert!(set_eq( vec![], @@ -37,7 +37,7 @@ fn test_expr_identifiers() { #[test] fn test_eqn_identifiers() { - use modelthing::modelica_ast::Expr::*; + use modelica_parser::ast::Expr::*; assert!(set_eq( vec![], @@ -54,25 +54,3 @@ fn test_eqn_identifiers() { Box::new(Ident("z".to_string()))), }.identifiers())); } - -#[test] -fn test_rebalance_for() { - use modelthing::modelica_ast::Expr::*; - - // z = a - 1.2345 - // a = z + 1.2345 - let done = SimpleEquation{ - lhs: Ident("z".to_string()), - rhs: BinExpr(BinOperator::Subtract, - Box::new(Ident("a".to_string())), - Box::new(Float(1.2345)))}; - assert_eq!(done, - done.rebalance_for("z".to_string()).unwrap()); - assert_eq!(SimpleEquation{ - lhs: Ident("a".to_string()), - rhs: BinExpr(BinOperator::Add, - Box::new(Ident("z".to_string())), - Box::new(Float(1.2345)))}, - done.rebalance_for("a".to_string()).unwrap()); - -} diff --git a/modelica-parser-lalrpop/tests/parser.rs b/modelica-parser-lalrpop/tests/parser.rs new file mode 100644 index 0000000..f5f493a --- /dev/null +++ b/modelica-parser-lalrpop/tests/parser.rs @@ -0,0 +1,40 @@ + +extern crate modelica_parser; + +use modelica_parser::parser::{parse_integer, parse_float, parse_model}; + +#[test] +fn test_lexical() { + assert_eq!(&format!("{:?}", parse_integer("+123").unwrap()), + "123"); + assert_eq!(&format!("{:?}", parse_integer("-9").unwrap()), + "-9"); + assert_eq!(&format!("{:?}", parse_float("-1.0e0").unwrap()), + "-1"); + assert_eq!(&format!("{:?}", parse_float("123.456").unwrap()), + "123.456"); +} + +#[test] +fn test_parse() { + let example1 = +r#"model MinimalModel + Real x; +equation + x = 1; +end MinimalModel; +"#; + assert_eq!(&format!("{:?}", parse_model(example1).unwrap()), example1); + + let example2 = +r#"model MinimalModel + parameter Real a; + Real b; +equation + connect(a, b); + a = 1; + b = ((abs(a) + 2) / 4); +end MinimalModel; +"#; + assert_eq!(&format!("{:?}", parse_model(example2).unwrap()), example2); +} diff --git a/src/bin/mt-tool.rs b/src/bin/mt-tool.rs index 8ef2464..95a8938 100644 --- a/src/bin/mt-tool.rs +++ b/src/bin/mt-tool.rs @@ -2,10 +2,11 @@ extern crate modelthing; extern crate rustc_serialize; extern crate getopts; +extern crate modelica_parser; -use modelthing::modelica_parser; use modelthing::transpile_scheme::TranspileScheme; use modelthing::transpile_js::TranspileJS; +use modelthing::modelica_model::ModelicaModelExt; use getopts::Options; use std::env; use std::io::Read; @@ -25,13 +26,13 @@ fn parse_modelica_files(paths: Vec<String>) { } let time_stamp = Instant::now(); - let result = modelica_parser::parse_model(&s); + let result = modelica_parser::parser::parse_model(&s); let elapsed = time_stamp.elapsed(); let elapsed = elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1000_000_000.0; match result { Ok(_) => println!("Input `{}` ({}s): OK", input, elapsed), - Err(err) => println!("Input `{}` ({}sec): ERROR\n{}", input, elapsed, modelthing::pp_parseerror(&s, err)), + Err(err) => println!("Input `{}` ({}sec): ERROR\n{}", input, elapsed, modelica_parser::pp_parseerror(&s, err)), } } } @@ -3,11 +3,9 @@ extern crate log; extern crate toml; -extern crate colored; -extern crate lalrpop_util; +extern crate modelica_parser; -pub mod modelica_parser; -pub mod modelica_ast; +pub mod modelica_model; pub mod transpile_scheme; pub mod transpile_js; @@ -16,9 +14,6 @@ use std::fs; use std::io::Read; use std::fs::File; -use lalrpop_util::ParseError; -use colored::*; - #[derive(Debug, PartialEq)] pub struct ModelMetadata { pub name_en: String, @@ -47,7 +42,7 @@ pub struct ModelVar { #[derive(Debug, PartialEq)] pub struct ModelEntry { - pub ast: modelica_ast::ModelicaModel, + pub ast: modelica_parser::ast::ModelicaModel, pub metadata: ModelMetadata, pub markdown: String, } @@ -93,7 +88,7 @@ pub fn load_model_entry(p: &Path) -> Result<ModelEntry,String> { let ast = { let mut s = String::new(); try!(File::open(p.join("model.modelica")).and_then(|mut f| f.read_to_string(&mut s)).map_err(|e| e.to_string())); - try!(modelica_parser::parse_model(&s).map_err(|e| format!("{:?}", e))) + try!(modelica_parser::parser::parse_model(&s).map_err(|e| format!("{:?}", e))) }; let metadata = { @@ -127,58 +122,3 @@ pub fn search_models(p: &Path) -> Vec<String> { vec![] } } - -fn pp_segment(raw: &str, start: usize, end: usize) -> String { - let mut line_start = 0; - let mut num = 0; - let mut ret = String::new(); - for line in raw.lines() { - num += 1; - let line_end = line_start + line.len(); - if (line_start <= start) && (start < line_end) { - ret += &format!(" {}\n{:>3} {} {}{}{}\n {} {}{}\n", - "|".blue().bold(), - num.to_string().blue().bold(), - "|".blue().bold(), - raw[line_start..start].normal(), - raw[start..end].red().bold(), - if end < line_end { - raw[end..line_end].normal() - } else { - "".normal() - }, - "|".blue().bold(), - std::iter::repeat(" ").take(start - line_start).collect::<String>(), - std::iter::repeat("^").take(end - start).collect::<String>().red().bold()); - } - line_start += line.len() + 1; - if line_start > end { break }; - } - ret -} - -pub fn pp_parseerror(raw: &str, pe: ParseError<usize, (usize, &str), ()>) -> String { - match pe { - ParseError::InvalidToken{location} => { - format!("{} invalid token starting at:\n{}", - "parse error:".red().bold(), - pp_segment(raw, location, location+1)) }, - ParseError::UnrecognizedToken{token: Some((start, (_, tok), end)), expected} => { - format!("{} unrecognized token '{}' (expected one of {:?}):\n{}", - "parse error:".red().bold(), - tok, - expected, - pp_segment(raw, start, end)) }, - ParseError::UnrecognizedToken{token: None, expected} => { - format!("{} premature end-of-file (expected one of {:?})", - "parse error:".red().bold(), - expected) }, - ParseError::ExtraToken{token: (start, (_, tok), end)} => { - format!("{} unexpected extra token '{}':\n{}", - "parse error:".red().bold(), - tok, - pp_segment(raw, start, end)) }, - _ => { - format!("{} {:?}", "parse error:".red().bold(), pe) }, - } -} diff --git a/src/modelica_ast.rs b/src/modelica_model.rs index f6d32b8..bcc00b1 100644 --- a/src/modelica_ast.rs +++ b/src/modelica_model.rs @@ -1,75 +1,24 @@ +extern crate modelica_parser; + use std::clone::Clone; -use std::fmt::{Debug, Formatter, Error}; use std::collections::HashMap; use std::collections::HashSet; -#[derive(Clone, PartialEq)] -pub struct ModelicaModel { - pub name: String, - pub components: Vec<Component>, - pub equations: Vec<SimpleEquation>, - pub connections: Vec<Connection>, - pub extends: Vec<String>, -} - -#[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 Component { - pub prefix: Option<ComponentPrefix>, - pub specifier: String, - pub name: String, - pub value: Option<Expr>, - pub units: Option<String>, - pub description: Option<String>, -} - -#[derive(Clone, PartialEq)] -pub struct Connection { - pub a: String, - pub b: String, -} +use self::modelica_parser::ast::*; -#[derive(Clone, PartialEq)] -pub struct SimpleEquation { - pub lhs: Expr, - pub rhs: Expr, -} -#[derive(Clone, PartialEq)] -pub enum Expr { - Integer(i64), - Float(f64), - Ident(String), - Der(Box<Expr>), - Abs(Box<Expr>), - BinExpr(BinOperator, Box<Expr>, Box<Expr>), -} +//// Helpers -#[derive(Copy, Clone, PartialEq)] -pub enum BinOperator { - Multiply, - Divide, - Add, - Subtract, +pub trait ModelicaModelExt { + fn get_constant_vars(&self) -> HashMap<String,Option<Expr>>; + fn get_free_vars(&self) -> Vec<String>; + fn solve_for(&self, indep_vars: Vec<String>, dep_vars: Vec<String>) -> Result<ModelicaModel,String>; } -//// Helpers - -impl ModelicaModel { +impl ModelicaModelExt for ModelicaModel { - pub fn get_constant_vars(&self) -> HashMap<String,Option<Expr>> { + fn get_constant_vars(&self) -> HashMap<String,Option<Expr>> { let mut binds = HashMap::new(); // XXX: actually implement this... for c in &self.components { @@ -86,7 +35,7 @@ impl ModelicaModel { // 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<String> { + fn get_free_vars(&self) -> Vec<String> { // Start with components, and remove constants and parameters let vars = self.components.iter().filter(|v| match v.prefix { Some(ComponentPrefix::Constant) | Some(ComponentPrefix::Parameter) => false, @@ -114,7 +63,7 @@ impl ModelicaModel { // N equations with unknowns // // TODO: allow passing in Q - pub fn solve_for(&self, indep_vars: Vec<String>, dep_vars: Vec<String>) -> Result<ModelicaModel,String> { + fn solve_for(&self, indep_vars: Vec<String>, dep_vars: Vec<String>) -> Result<ModelicaModel,String> { let constants = self.get_constant_vars(); let mut all_vars: HashSet<String> = HashSet::new(); for eqn in &self.equations { @@ -192,16 +141,6 @@ impl ModelicaModel { } } -fn union_strings(a: &Vec<String>, b: &Vec<String>) -> Vec<String> { - let mut u = a.clone(); - for e in b { - if !(u.contains(&e)) { - u.push(e.clone()); - } - } - u -} - fn intersect_strings(a: &Vec<String>, b: &Vec<String>) -> Vec<String> { let mut both = vec![]; for e in a { @@ -212,40 +151,14 @@ fn intersect_strings(a: &Vec<String>, b: &Vec<String>) -> Vec<String> { both } -impl Expr { - - // Order is undefined - // TODO: should return a HashSet, not a Vec - pub fn identifiers(&self) -> Vec<String> { - use self::Expr::*; - match *self { - Integer(_) | Float(_) => vec![], - Ident(ref s) => vec![s.clone()], - Der(ref e) | Abs(ref e) => e.identifiers(), - BinExpr(_, ref e1, ref e2) => { - union_strings(&e1.identifiers(), &e2.identifiers()) - }, - } - } - - pub fn contains(&self, ident: &str) -> bool{ - self.identifiers().contains(&ident.to_string()) - } +pub trait SimpleEquationExt { + fn rebalance_for(&self, ident: String) -> Result<SimpleEquation,String>; + fn simplify_lhs(&self, ident: &str) -> Result<SimpleEquation,String>; } -impl SimpleEquation { - - // Order is undefined - pub fn identifiers(&self) -> Vec<String> { - 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) - } +impl SimpleEquationExt for SimpleEquation { - pub fn rebalance_for(&self, ident: String) -> Result<SimpleEquation,String> { + fn rebalance_for(&self, ident: String) -> Result<SimpleEquation,String> { let lvars = self.lhs.identifiers(); let rvars = self.rhs.identifiers(); @@ -268,9 +181,9 @@ impl SimpleEquation { } } - pub fn simplify_lhs(&self, ident: &str) -> Result<SimpleEquation,String> { - use self::Expr::*; - use self::BinOperator::*; + fn simplify_lhs(&self, ident: &str) -> Result<SimpleEquation,String> { + use modelica_parser::ast::Expr::*; + use modelica_parser::ast::BinOperator::*; match self.lhs { Ident(ref s) if s == ident => Ok((*self).clone()), Ident(_) | Integer(_) | Float(_) => @@ -316,92 +229,3 @@ impl SimpleEquation { } } - -//// 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.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), - Ident(ref e) => write!(fmt, "{}", e), - Der(ref e) => write!(fmt, "der({:?})", e), - Abs(ref e) => write!(fmt, "abs({:?})", e), - BinExpr(op, ref l, ref r) => write!(fmt, "({:?} {:?} {:?})", l, op, r), - } - } -} - -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, "-"), - } - } -} diff --git a/src/modelica_parser.lalrpop.full b/src/modelica_parser.lalrpop.full deleted file mode 100644 index d453bc7..0000000 --- a/src/modelica_parser.lalrpop.full +++ /dev/null @@ -1,171 +0,0 @@ -use std::str::FromStr; - -// Based on the Modelica 3.3r1 Spec - -grammar; - -//// B.1 Lexical Convetions - -//digit: = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" -//q_ident = "’" ( q_char | s_escape ) { q_char | s_escape } "’" -//nondigit = "_" | letters "a" to "z" | letters "A" to "Z" -//s_char = any member of the Unicode character set (http://www.unicode.org; see Section 13.2.2 for storing as UTF-8 on files) except double-quote """, and backslash "\" -//q_char = nondigit | digit | "!" | "#" | "$" | "%" | "&" | "(" | ")" | "*" | "+" | "," | "-" | "." | "/" | ":" | ";" | "<" | ">" | "=" | "?" | "@" | "[" | "]" | "^" | "{" | "}" | "|" | "~" | " " -//s_escape = "\’" | "\"" | "\?" | "\\" | "\a" | "\b" | "\f" | "\n" | "\r" | "\t" | "\v" -// -//ident = nondigit { digit | nondigit } | q_ident -//string = """ { s_char | s_escape } """ -//unsigned_integer = digit { digit } -//unsigned_number = unsigned_integer [ "." [ unsigned_integer ] ] [ ( "e" | "E" ) [ "+" | "-" ] unsigned_integer ] - -IDENT: () = { - r"[a-zA-Z_][a-zA-Z_0-9]*", - // TODO: quoted identifier: "’" ... "’" -}; - -STRING: () = { - //"\"" r"[^\"^\\]*" "\"", - "\"" r"[^\\]*" "\"", // XXX: not \ and not " -}; - -UNSIGNED_INTEGER: () = { - r"\d+", -}; - -UNSIGNED_NUMBER: () = { - UNSIGNED_INTEGER, - r"\d+\.?\d*([eE][-+]?\d+)?", -}; - - -//// B.2 Grammar - -//// B.2.1 - -//stored_definition: -// [ within [ name ] ";" ] -// { [ final ] class_definition ";" } - -//// B.2.2 Class Definition - - -//// B.2.7 Expressions - -expression: () = { - simple_expression, - if expression then expression { elseif expression then expression } else expression -}; - -simple_expression: () = { - logical_expression [ ":" logical_expression [ ":" logical_expression ] ] -}; - -logical_expression: () = { - logical_term { or logical_term } -}; - -logical_term: () = { - logical_factor { and logical_factor } -}; - -logical_factor: () = { - [ not ] relation -}; - -relation: () = { - arithmetic_expression [ rel_op arithmetic_expression ] -}; - -rel_op: () = { - "<", "<=", ">", ">=", "==", "<>" -}; - -arithmetic_expression: () = { - [ add_op ] term { add_op term } -}; - -add_op: () = { - "+", "-", ".+", ".-" -}; - -term: () = { - factor { mul_op factor } -}; - -mul_op: () = { - "*", "/", ".*", "./" -}; - -factor: () = { - primary [ ("^" | ".^") primary ] -}; - -primary: () = { - UNSIGNED_NUMBER, - STRING, - false, - true, - ( name | der | initial ) function_call_args, - component_reference, - "(" output_expression_list ")", - "[" expression_list { ";" expression_list } "]", - "{" function_arguments "}", - end, -}; - -name: () = { - [ "." ] IDENT { "." IDENT } -}; - -component_reference: () = { - [ "." ] IDENT [ array_subscripts ] { "." IDENT [ array_subscripts ] } -}; - -function_call_args: () = { - "(" [ function_arguments ] ")", - function_arguments, - function_argument [ "," function_arguments | for for_indices ], - named_arguments, -}; - -named_arguments: () = { - named_argument [ "," named_arguments ] -}; - -named_argument: - IDENT "=" function_argument -}; - -function_argument: () = { - function name "(" [ named_arguments ] ")" | expression -}; - - -output_expression_list: - [ expression ] { "," [ expression ] } - -expression_list: () = { - expression { "," expression } -}; - -array_subscripts: () = { - "[" subscript { "," subscript } "]" - -}; - -subscript: () = { - ":" | expression - -}; - -comment: () = { - string_comment [ annotation ] -}; - -string_comment: () = { - [ STRING { "+" STRING } ] -}; - -annotation: () = { - annotation class_modification -}; diff --git a/src/transpile_js.rs b/src/transpile_js.rs index 7c8932f..7a2132f 100644 --- a/src/transpile_js.rs +++ b/src/transpile_js.rs @@ -1,5 +1,7 @@ -use modelica_ast::*; +extern crate modelica_parser; + +use self::modelica_parser::ast::*; pub trait TranspileJS { fn repr_js(&self) -> Result<String, String>; @@ -43,7 +45,7 @@ impl TranspileJS for ModelicaModel { impl TranspileJS for Expr { fn repr_js(&self) -> Result<String, String> { - use modelica_ast::Expr::*; + use modelica_parser::ast::Expr::*; match *self { Integer(e) => Ok(format!("{}", e)), Float(e) => Ok(format!("{}", e)), diff --git a/src/transpile_scheme.rs b/src/transpile_scheme.rs index 5a88cda..5c8bd66 100644 --- a/src/transpile_scheme.rs +++ b/src/transpile_scheme.rs @@ -1,5 +1,7 @@ -use modelica_ast::*; +extern crate modelica_parser; + +use self::modelica_parser::ast::*; pub trait TranspileScheme { fn repr_scheme(&self) -> Result<String, String>; @@ -40,7 +42,7 @@ impl TranspileScheme for ModelicaModel { impl TranspileScheme for Expr { fn repr_scheme(&self) -> Result<String, String> { - use modelica_ast::Expr::*; + use modelica_parser::ast::Expr::*; match *self { Integer(e) => Ok(format!("{}", e)), Float(e) => Ok(format!("{}", e)), diff --git a/tests/lib.rs b/tests/lib.rs index baddf72..bf28476 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,5 +1,6 @@ extern crate modelthing; +extern crate modelica_parser; use std::path::Path; @@ -31,39 +32,3 @@ fn test_load_model_entry() { fn test_search_models() { assert_eq!(search_models(Path::new("./examples/")).len() > 1, true); } - -#[test] -fn test_lexical() { - assert_eq!(&format!("{:?}", modelica_parser::parse_integer("+123").unwrap()), - "123"); - assert_eq!(&format!("{:?}", modelica_parser::parse_integer("-9").unwrap()), - "-9"); - assert_eq!(&format!("{:?}", modelica_parser::parse_float("-1.0e0").unwrap()), - "-1"); - assert_eq!(&format!("{:?}", modelica_parser::parse_float("123.456").unwrap()), - "123.456"); -} - -#[test] -fn test_parse() { - let example1 = -r#"model MinimalModel - Real x; -equation - x = 1; -end MinimalModel; -"#; - assert_eq!(&format!("{:?}", modelica_parser::parse_model(example1).unwrap()), example1); - - let example2 = -r#"model MinimalModel - parameter Real a; - Real b; -equation - connect(a, b); - a = 1; - b = ((abs(a) + 2) / 4); -end MinimalModel; -"#; - assert_eq!(&format!("{:?}", modelica_parser::parse_model(example2).unwrap()), example2); -} diff --git a/tests/rebalance.rs b/tests/rebalance.rs new file mode 100644 index 0000000..f365e84 --- /dev/null +++ b/tests/rebalance.rs @@ -0,0 +1,28 @@ + +extern crate modelica_parser; +extern crate modelthing; + +use modelthing::modelica_model::SimpleEquationExt; +use modelica_parser::ast::*; + +#[test] +fn test_rebalance_for() { + use modelica_parser::ast::Expr::*; + + // z = a - 1.2345 + // a = z + 1.2345 + let done = SimpleEquation{ + lhs: Ident("z".to_string()), + rhs: BinExpr(BinOperator::Subtract, + Box::new(Ident("a".to_string())), + Box::new(Float(1.2345)))}; + assert_eq!(done, + done.rebalance_for("z".to_string()).unwrap()); + assert_eq!(SimpleEquation{ + lhs: Ident("a".to_string()), + rhs: BinExpr(BinOperator::Add, + Box::new(Ident("z".to_string())), + Box::new(Float(1.2345)))}, + done.rebalance_for("a".to_string()).unwrap()); + +} |