From 3bc4e5baae51ef622a2cac98f677487cf9342286 Mon Sep 17 00:00:00 2001 From: bnewbold Date: Sat, 28 Jan 2017 21:18:19 -0800 Subject: basic python compiling --- src/bin/mt-webface.rs | 5 +- src/lib.rs | 3 +- src/transpile_python.rs | 147 ++++++++++++++++++++++++++++++++++++++ webface/templates/model_view.html | 5 +- 4 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 src/transpile_python.rs diff --git a/src/bin/mt-webface.rs b/src/bin/mt-webface.rs index 4d26d21..8ddcf2e 100644 --- a/src/bin/mt-webface.rs +++ b/src/bin/mt-webface.rs @@ -21,6 +21,7 @@ use pencil::method::{Get, Post}; use regex::Regex; use modelthing::transpile_scheme::TranspileScheme; use modelthing::transpile_js::{TranspileJS, TranspileJSODE}; +use modelthing::transpile_python::{TranspilePython, TranspilePythonODE}; use modelthing::repr_latex::{ReprLaTeX, ReprVarsHTML}; /* @@ -111,9 +112,11 @@ fn model_repr(r: &mut Request) -> PencilResult { match modelthing::load_model_entry(model_path.as_path()) { Ok(me) => { match format { - "scheme" => Ok(Response::from(me.ast.transpile_scheme().unwrap())), "javascript" => Ok(Response::from(me.ast.transpile_js().unwrap())), "javascript_ode" => Ok(Response::from(me.ast.transpile_js_ode().unwrap())), + "python" => Ok(Response::from(me.ast.transpile_python().unwrap())), + "python_ode" => Ok(Response::from(me.ast.transpile_python_ode().unwrap())), + "scheme" => Ok(Response::from(me.ast.transpile_scheme().unwrap())), "latex" => Ok(Response::from(me.ast.repr_latex().unwrap())), _ => abort(403), } diff --git a/src/lib.rs b/src/lib.rs index a83975a..a5941d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,8 +9,9 @@ extern crate toml; pub extern crate modelica_parser; pub mod modelica_model; -pub mod transpile_scheme; pub mod transpile_js; +pub mod transpile_python; +pub mod transpile_scheme; pub mod repr_latex; use std::path::Path; diff --git a/src/transpile_python.rs b/src/transpile_python.rs new file mode 100644 index 0000000..d980e60 --- /dev/null +++ b/src/transpile_python.rs @@ -0,0 +1,147 @@ + +extern crate modelica_parser; + +use self::modelica_parser::*; +use errors::Result; + +pub trait TranspilePython { + fn transpile_python(&self) -> Result; +} + +pub trait TranspilePythonODE { + fn transpile_python_ode(&self) -> Result; +} + + +impl TranspilePython for ModelicaModel { + fn transpile_python(&self) -> Result { + let mut params = vec![]; + let mut constants = vec![]; + for (c, e) in self.get_constant_vars() { + if let Some(v) = e { + constants.push(format!("{} = {}", c, try!(v.transpile_python()))); + } else { + params.push(c); + } + } + // HashMaps are unsorted, so we need to re-sort here + constants.sort(); + params.sort(); + let mut binds = vec![]; + let mut outputs = vec![]; + for eq in self.equations.iter() { + if let Expr::Ident(ref symb) = eq.lhs { + binds.push(format!("{} = {}", + symb, + try!(eq.rhs.transpile_python()))); + outputs.push(symb.to_string()); + + } else { + bail!("Expected an identifier on LHS (in this partial implementation)") + } + } + let mut args: Vec = self.get_free_vars().iter().map(|s| s.clone()).collect(); + args.sort(); + args.extend(params); + Ok(format!( +r#"def {slug}({args}): + """{description} + + Args: + {param_doc} + + Returns array of: + {ret_doc} + """ + {constants} + {binds} + return ({outputs}) +"#, + description = self.description.clone().unwrap_or("(undocumented)".to_string()), + param_doc = args.join("\n "), // whitespace sensitive + ret_doc = outputs.join("\n "), // whitespace sensitive + slug = self.name, + args = args.join(", "), + constants = constants.join("\n "), // whitespace sensitive + binds = binds.join("\n "), // whitespace sensitive + outputs = outputs.join(", "))) + } + +} + +impl TranspilePythonODE for ModelicaModel { + + fn transpile_python_ode(&self) -> Result { + let mut params = vec![]; + let mut constants = vec![]; + for (c, e) in self.get_constant_vars() { + if let Some(v) = e { + constants.push(format!("{} = {}", c, try!(v.transpile_python()))); + } else { + params.push(c); + } + } + // HashMaps are unsorted, so we need to re-sort here + constants.sort(); + params.sort(); + let mut exprs = vec![]; + for eq in self.equations.iter() { + if let Expr::Der(ref der_of) = eq.lhs { + if let &Expr::Ident(_) = der_of.as_ref() { + // Ok + } else { + bail!("Non-trivial derivatives not supported (aka, of non-variable expressions)"); + } + exprs.push(try!(eq.rhs.transpile_python())); + } else { + bail!("Not a simple set of ODEs (aka, all derivatives on LHS of equations)"); + } + } + let mut args: Vec = self.get_free_vars().iter().map(|s| s.clone()).collect(); + args.sort(); + Ok(format!( +r#"def {slug}_ode({params}): + def f({args}): + {constants} + return ( + {expressions} + ) + return f +"#, + slug = self.name, + params = params.join(", "), + args = args.join(", "), + constants = constants.join("\n "), // NB: whitespace + expressions = exprs.join(",\n "))) // NB: whitespace + } +} + +impl TranspilePython for Expr { + fn transpile_python(&self) -> Result { + use modelica_parser::Expr::*; + use modelica_parser::BinOperator::*; + match *self { + Integer(e) => Ok(format!("{}", e)), + Float(e) => Ok(format!("{}", e)), + Boolean(true) => Ok(format!("True")), + Boolean(false) => Ok(format!("False")), + StringLiteral(ref s) => Ok(format!("\"{}\"", s)), + Ident(ref e) => Ok(format!("{}", e)), + Der(_) => unimplemented!(), + Sign(_) => unimplemented!(), + MathUnaryExpr(func, ref e) => Ok(format!("{:?}({})", func, try!(e.transpile_python()))), + BinExpr(op, ref l, ref r) => { + // Have override for Python's exponentiation + let op_str = match op { + Multiply => "*", + Divide => "/", + Exponentiate => "**", + Add => "+", + Subtract => "-", + }; + Ok(format!("({} {} {})", try!(l.transpile_python()), op_str, try!(r.transpile_python()))) + } + Array(_) => unimplemented!(), + } + } +} diff --git a/webface/templates/model_view.html b/webface/templates/model_view.html index 878edaa..4e9e4f7 100644 --- a/webface/templates/model_view.html +++ b/webface/templates/model_view.html @@ -34,8 +34,9 @@ latex {{# if computable }}
compile: - scheme - - javascript + javascript - + python - + scheme {{/if}}

{{ model_description }} -- cgit v1.2.3