aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbnewbold <bnewbold@robocracy.org>2016-12-17 18:34:47 -0800
committerbnewbold <bnewbold@robocracy.org>2016-12-17 18:34:47 -0800
commit9f82aceb9fbdb42f332d68f4a423123bd0788b2c (patch)
treec082b9795be8e9e9d286c8f8f1345d22f3ec1b59
parentf6364ebcac0d0a88a3cc6812fd2120c97b42cc26 (diff)
downloadmodelthing-9f82aceb9fbdb42f332d68f4a423123bd0788b2c.tar.gz
modelthing-9f82aceb9fbdb42f332d68f4a423123bd0788b2c.zip
refactor modelica parser into separate crate
-rw-r--r--Cargo.lock13
-rw-r--r--Cargo.toml11
-rw-r--r--modelica-parser-lalrpop/.gitignore1
-rw-r--r--modelica-parser-lalrpop/Cargo.toml25
-rw-r--r--modelica-parser-lalrpop/examples/minimal.modelica (renamed from examples/minimal.modelica)0
-rw-r--r--modelica-parser-lalrpop/examples/minimal2.modelica (renamed from examples/minimal2.modelica)0
-rw-r--r--modelica-parser-lalrpop/examples/minimal3.modelica (renamed from examples/minimal3.modelica)0
-rw-r--r--modelica-parser-lalrpop/src/ast.rs241
-rw-r--r--modelica-parser-lalrpop/src/build.rs (renamed from src/build.rs)0
-rw-r--r--modelica-parser-lalrpop/src/lib.rs65
-rw-r--r--modelica-parser-lalrpop/src/parser.lalrpop (renamed from src/modelica_parser.lalrpop)2
-rw-r--r--modelica-parser-lalrpop/src/parser.rs (renamed from src/modelica_parser.rs)10
-rw-r--r--modelica-parser-lalrpop/tests/ast.rs (renamed from tests/modelica_ast.rs)30
-rw-r--r--modelica-parser-lalrpop/tests/parser.rs40
-rw-r--r--src/bin/mt-tool.rs7
-rw-r--r--src/lib.rs68
-rw-r--r--src/modelica_model.rs (renamed from src/modelica_ast.rs)216
-rw-r--r--src/modelica_parser.lalrpop.full171
-rw-r--r--src/transpile_js.rs6
-rw-r--r--src/transpile_scheme.rs6
-rw-r--r--tests/lib.rs37
-rw-r--r--tests/rebalance.rs28
22 files changed, 458 insertions, 519 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 14c1291..afb3b0b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
diff --git a/Cargo.toml b/Cargo.toml
index 57aa54b..cd16e93 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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)),
}
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 7020a9b..7c3bb8b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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());
+
+}