Add a conversion operator with conformance errors

This commit is contained in:
Tiffany Bennett 2016-08-03 10:27:38 -04:00
parent 2a241a36cf
commit 3e6fe01923
2 changed files with 120 additions and 5 deletions

View file

@ -123,6 +123,8 @@ impl Context {
}
if let Some(name) = self.aliases.get(&value.1) {
write!(out, " ({})", name).unwrap();
} else if value.1.len() == 1 {
write!(out, " ({})", self.dimensions[*value.1.iter().next().unwrap().0]).unwrap();
}
String::from_utf8(out).unwrap()
}
@ -154,6 +156,61 @@ impl Context {
})
}
pub fn describe_unit(&self, value: &Value) -> (bool, String) {
use std::io::Write;
let mut buf = vec![];
let mut recip = false;
if let Some(name) = self.aliases.get(&value.1) {
write!(buf, "{}", name).unwrap();
} else {
let mut frac = vec![];
let mut found = false;
for (&dim, &pow) in &value.1 {
if pow < 0 {
frac.push((dim, -pow));
} else {
found = true;
let mut map = Unit::new();
map.insert(dim, pow);
if let Some(name) = self.aliases.get(&map) {
write!(buf, " {}", name).unwrap();
} else {
let mut map = Unit::new();
map.insert(dim, 1);
write!(buf, " {}", self.aliases[&map]).unwrap();
if pow != 1 {
write!(buf, "^{}", pow).unwrap();
}
}
}
}
if frac.len() > 0 {
if !found {
recip = true;
} else {
write!(buf, " /").unwrap();
}
for (dim, pow) in frac {
let mut map = Unit::new();
map.insert(dim, pow);
if let Some(name) = self.aliases.get(&map) {
write!(buf, " {}", name).unwrap();
} else {
let mut map = Unit::new();
map.insert(dim, 1);
write!(buf, " {}", self.aliases[&map]).unwrap();
if pow != 1 {
write!(buf, "^{}", pow).unwrap();
}
}
}
}
}
(recip, String::from_utf8(buf).unwrap())
}
pub fn eval(&self, expr: &::unit_defs::Expr) -> Result<Value, String> {
use unit_defs::Expr;
@ -170,6 +227,44 @@ impl Context {
(Err(e), _) => Err(e),
(_, Err(e)) => Err(e),
},
Expr::Convert(ref top, ref bottom) => match (self.eval(&**top), self.eval(&**bottom)) {
(Ok(top), Ok(bottom)) => {
if top.1 != bottom.1 {
use std::io::Write;
let mut buf = vec![];
let width = 12;
writeln!(buf, "Conformance error").unwrap();
let mut topu = top.clone();
topu.0 = 1.0;
let mut bottomu = bottom.clone();
bottomu.0 = 1.0;
writeln!(buf, "{:>width$}: {}", "Left side", self.show(&topu), width=width).unwrap();
writeln!(buf, "{:>width$}: {}", "Right side", self.show(&bottomu), width=width).unwrap();
let diff = topu.mul(&bottomu.invert());
let (recip, desc) = self.describe_unit(&diff.invert());
let word = match recip {
false => "multiply",
true => "divide"
};
writeln!(buf, "{:>width$}: {word} left side by {}", "Suggestion",
desc.trim(), width=width, word=word).unwrap();
let (recip, desc) = self.describe_unit(&diff);
let word = match recip {
false => "multiply",
true => "divide"
};
writeln!(buf, "{:>width$} {word} right side by {}", "",
desc.trim(), width=width, word=word).unwrap();
Err(String::from_utf8(buf).unwrap())
} else {
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));
@ -207,8 +302,12 @@ impl Context {
for (name, def) in defs.defs {
match *def {
Def::Dimension(ref name) => {
ctx.dimensions.push(name.clone());
Def::Dimension(ref dname) => {
let i = ctx.dimensions.len();
ctx.dimensions.push(dname.clone());
let mut map = Unit::new();
map.insert(i, 1);
ctx.aliases.insert(map, name.clone());
},
Def::Unit(ref expr) => match ctx.eval(expr) {
Ok(v) => {

View file

@ -26,6 +26,7 @@ pub enum Token {
Plus,
Minus,
Asterisk,
DashArrow,
ImaginaryUnit,
Error(String),
}
@ -56,7 +57,13 @@ impl<'a> Iterator for TokenIterator<'a> {
'(' => Token::LPar,
')' => Token::RPar,
'+' => Token::Plus,
'-' => Token::Minus,
'-' => match self.0.peek().cloned().unwrap() {
'>' => {
self.0.next();
Token::DashArrow
},
_ => Token::Minus
},
'*' => Token::Asterisk,
'/' => match self.0.peek() {
Some(&'/') => loop {
@ -190,6 +197,7 @@ pub enum Expr {
Pow(Box<Expr>, Box<Expr>),
Neg(Box<Expr>),
Plus(Box<Expr>),
Convert(Box<Expr>, Box<Expr>),
Error(String),
}
@ -242,7 +250,7 @@ fn parse_pow(mut iter: &mut Iter) -> Expr {
fn parse_mul(mut iter: &mut Iter) -> Expr {
let mut terms = vec![parse_pow(iter)];
loop { match *iter.peek().unwrap() {
Token::TriplePipe | Token::RPar | Token::Newline | Token::Comment(_) |
Token::DashArrow | Token::TriplePipe | Token::RPar | Token::Newline | Token::Comment(_) |
Token::Eof => break,
Token::Slash => {
iter.next();
@ -267,7 +275,15 @@ fn parse_mul(mut iter: &mut Iter) -> Expr {
}
pub fn parse_expr(mut iter: &mut Iter) -> Expr {
parse_mul(iter)
let left = parse_mul(iter);
match iter.peek().cloned().unwrap() {
Token::DashArrow => {
iter.next();
let right = parse_mul(iter);
Expr::Convert(Box::new(left), Box::new(right))
},
_ => left
}
}
fn parse_unknown(mut iter: &mut Iter) {