mirror of
https://github.com/tiffany352/rink-rs
synced 2024-11-10 13:44:15 +00:00
Implement evaluator and do REPL unit conversions
This commit is contained in:
parent
ae8568b991
commit
c8afaaa62a
3 changed files with 219 additions and 9 deletions
180
src/eval.rs
Normal file
180
src/eval.rs
Normal file
|
@ -0,0 +1,180 @@
|
|||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub type Dim = usize;
|
||||
pub type Unit = BTreeMap<Dim, i64>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Value(f64, Unit);
|
||||
|
||||
pub struct Context {
|
||||
dimensions: Vec<String>,
|
||||
units: HashMap<String, Value>,
|
||||
aliases: HashMap<Unit, String>,
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn new(num: f64) -> Value {
|
||||
Value(num, Unit::new())
|
||||
}
|
||||
|
||||
pub fn new_unit(num: f64, unit: Dim) -> Value {
|
||||
let mut map = Unit::new();
|
||||
map.insert(unit, 1);
|
||||
Value(num, map)
|
||||
}
|
||||
|
||||
pub fn invert(&self) -> Value {
|
||||
Value(1.0 / self.0,
|
||||
self.1.iter()
|
||||
.map(|(&k, &power)| (k, -power))
|
||||
.collect::<Unit>())
|
||||
}
|
||||
|
||||
pub fn mul(&self, other: &Value) -> Value {
|
||||
let mut val = Unit::new();
|
||||
let mut a = self.1.iter().peekable();
|
||||
let mut b = other.1.iter().peekable();
|
||||
loop {
|
||||
match (a.peek().cloned(), b.peek().cloned()) {
|
||||
(Some((ka, pa)), Some((kb, pb))) if ka == kb => {
|
||||
// merge
|
||||
let power = pa+pb;
|
||||
if power != 0 {
|
||||
val.insert(ka.clone(), power);
|
||||
}
|
||||
a.next();
|
||||
b.next();
|
||||
},
|
||||
(Some((ka, _)), Some((kb, vb))) if ka > kb => {
|
||||
// push vb, advance
|
||||
val.insert(kb.clone(), vb.clone());
|
||||
b.next();
|
||||
},
|
||||
(Some((ka, va)), Some((kb, _))) if ka < kb => {
|
||||
// push va, advance
|
||||
val.insert(ka.clone(), va.clone());
|
||||
a.next();
|
||||
},
|
||||
(Some(_), Some(_)) => panic!(),
|
||||
(None, Some((kb, vb))) => {
|
||||
// push vb, advance
|
||||
val.insert(kb.clone(), vb.clone());
|
||||
b.next();
|
||||
},
|
||||
(Some((ka, va)), None) => {
|
||||
// push va, advance
|
||||
val.insert(ka.clone(), va.clone());
|
||||
a.next();
|
||||
},
|
||||
(None, None) => break
|
||||
}
|
||||
}
|
||||
Value(self.0 * other.0, val)
|
||||
}
|
||||
|
||||
pub fn pow(&self, exp: i32) -> Value {
|
||||
Value(self.0.powi(exp),
|
||||
self.1.iter()
|
||||
.map(|(&k, &power)| (k, power * exp as i64))
|
||||
.collect::<Unit>())
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn print(&self, value: &Value) {
|
||||
print!("{}", value.0);
|
||||
for (&dim, &exp) in &value.1 {
|
||||
print!(" {}^{}", self.dimensions[dim], exp);
|
||||
}
|
||||
if let Some(name) = self.aliases.get(&value.1) {
|
||||
print!(" ({})", name);
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
|
||||
pub fn lookup(&self, name: &str) -> Option<Value> {
|
||||
self.units.get(name).cloned().or_else(|| {
|
||||
for (i, ref k) in self.dimensions.iter().enumerate() {
|
||||
if name == *k {
|
||||
return Some(Value::new_unit(1.0, i))
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn eval(&self, expr: &::unit_defs::Expr) -> Result<Value, String> {
|
||||
use unit_defs::Expr;
|
||||
|
||||
match *expr {
|
||||
Expr::Unit(ref name) => self.lookup(name).ok_or(format!("Unknown unit {}", name)),
|
||||
Expr::Const(num) => Ok(Value::new(num)),
|
||||
Expr::Neg(ref expr) => self.eval(&**expr).and_then(|mut v| {
|
||||
v.0 = -v.0;
|
||||
Ok(v)
|
||||
}),
|
||||
Expr::Plus(ref expr) => self.eval(&**expr),
|
||||
Expr::Frac(ref top, ref bottom) => match (self.eval(&**top), self.eval(&**bottom)) {
|
||||
(Ok(top), Ok(bottom)) => Ok(top.mul(&bottom.invert())),
|
||||
(Err(e), _) => Err(e),
|
||||
(_, Err(e)) => Err(e),
|
||||
},
|
||||
Expr::Mul(ref args) => args.iter().fold(Ok(Value::new(1.0)), |a, b| {
|
||||
a.and_then(|a| {
|
||||
let b = try!(self.eval(b));
|
||||
Ok(a.mul(&b))
|
||||
})
|
||||
}),
|
||||
Expr::Pow(ref base, ref exp) => {
|
||||
let base = try!(self.eval(&**base));
|
||||
let exp = try!(self.eval(&**exp));
|
||||
if exp.1.len() != 0 {
|
||||
Err(format!("Exponent not dimensionless"))
|
||||
} else if exp.0.trunc() != exp.0 {
|
||||
Err(format!("Exponent not integer"))
|
||||
} else {
|
||||
Ok(base.pow(exp.0 as i32))
|
||||
}
|
||||
},
|
||||
Expr::Error(ref e) => Err(e.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(defs: ::unit_defs::Defs) -> Context {
|
||||
use unit_defs::Def;
|
||||
|
||||
let mut ctx = Context {
|
||||
dimensions: Vec::new(),
|
||||
units: HashMap::new(),
|
||||
aliases: HashMap::new(),
|
||||
};
|
||||
|
||||
for (name, def) in defs.defs {
|
||||
match *def {
|
||||
Def::Dimension(ref name) => {
|
||||
ctx.dimensions.push(name.clone());
|
||||
},
|
||||
Def::Unit(ref expr) => match ctx.eval(expr) {
|
||||
Ok(v) => {
|
||||
ctx.units.insert(name.clone(), v);
|
||||
},
|
||||
Err(e) => println!("Unit {} is malformed: {}", name, e)
|
||||
},
|
||||
Def::Error(ref err) => println!("Def {}: {}", name, err),
|
||||
_ => ()
|
||||
};
|
||||
}
|
||||
|
||||
for (expr, name) in defs.aliases {
|
||||
match ctx.eval(&expr) {
|
||||
Ok(v) => {
|
||||
ctx.aliases.insert(v.1, name);
|
||||
},
|
||||
Err(e) => println!("Alias {}: {}", name, e)
|
||||
}
|
||||
}
|
||||
|
||||
ctx
|
||||
}
|
||||
}
|
28
src/main.rs
28
src/main.rs
|
@ -1,14 +1,36 @@
|
|||
pub mod unit_defs;
|
||||
pub mod eval;
|
||||
|
||||
fn main() {
|
||||
use std::io::{stdin, Read};
|
||||
use std::io::{stdin, stdout, Read, Write};
|
||||
use std::fs::File;
|
||||
|
||||
let mut f = stdin();//File::open("units.txt").unwrap();
|
||||
let mut f = File::open("units.txt").unwrap();
|
||||
let mut buf = vec![];
|
||||
f.read_to_end(&mut buf).unwrap();
|
||||
let string = String::from_utf8_lossy(&*buf);
|
||||
let mut iter = unit_defs::TokenIterator::new(&*string).peekable();
|
||||
//let res = unit_defs::tokens(&mut iter);
|
||||
let res = unit_defs::parse(&mut iter);
|
||||
println!("{:#?}", res);
|
||||
let ctx = eval::Context::new(res);
|
||||
//println!("{:#?}", res);
|
||||
let f = stdin();
|
||||
let mut line = String::new();
|
||||
loop {
|
||||
print!("> ");
|
||||
stdout().flush().unwrap();
|
||||
match f.read_line(&mut line) {
|
||||
Ok(_) => (),
|
||||
Err(_) => return
|
||||
};
|
||||
{
|
||||
let mut iter = unit_defs::TokenIterator::new(&*line).peekable();
|
||||
let expr = unit_defs::parse_expr(&mut iter);
|
||||
match ctx.eval(&expr) {
|
||||
Ok(value) => ctx.print(&value),
|
||||
Err(e) => println!("{}", e)
|
||||
};
|
||||
}
|
||||
line.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::collections::HashMap;
|
||||
use std::str::Chars;
|
||||
use std::iter::Peekable;
|
||||
use std::str::FromStr;
|
||||
|
@ -160,6 +159,12 @@ pub enum Def {
|
|||
Error(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Defs {
|
||||
pub defs: Vec<(String, Rc<Def>)>,
|
||||
pub aliases: Vec<(Expr, String)>,
|
||||
}
|
||||
|
||||
fn parse_term(mut iter: &mut Iter) -> Expr {
|
||||
match iter.next().unwrap() {
|
||||
Token::Ident(name) => Expr::Unit(name),
|
||||
|
@ -194,7 +199,7 @@ fn parse_frac(mut iter: &mut Iter) -> Expr {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_expr(mut iter: &mut Iter) -> Expr {
|
||||
pub fn parse_expr(mut iter: &mut Iter) -> Expr {
|
||||
let mut terms = vec![];
|
||||
loop {
|
||||
match *iter.peek().unwrap() {
|
||||
|
@ -239,8 +244,8 @@ fn parse_alias(mut iter: &mut Iter) -> Option<(Expr, String)> {
|
|||
Some((expr, name))
|
||||
}
|
||||
|
||||
pub fn parse(mut iter: &mut Iter) -> (HashMap<String, Rc<Def>>, Vec<(Expr, String)>) {
|
||||
let mut map = HashMap::new();
|
||||
pub fn parse(mut iter: &mut Iter) -> Defs {
|
||||
let mut map = vec![];
|
||||
let mut aliases = vec![];
|
||||
loop {
|
||||
let mut copy = iter.clone();
|
||||
|
@ -277,12 +282,15 @@ pub fn parse(mut iter: &mut Iter) -> (HashMap<String, Rc<Def>>, Vec<(Expr, Strin
|
|||
Def::Error(format!("Unknown definition"))
|
||||
}
|
||||
};
|
||||
map.insert(name.clone(), Rc::new(def));
|
||||
map.push((name.clone(), Rc::new(def)));
|
||||
},
|
||||
_ => parse_unknown(iter)
|
||||
};
|
||||
}
|
||||
(map, aliases)
|
||||
Defs {
|
||||
defs: map,
|
||||
aliases: aliases
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tokens(mut iter: &mut Iter) -> Vec<Token> {
|
||||
|
|
Loading…
Reference in a new issue