Show definitions for single-unit queries

This commit is contained in:
Tiffany Bennett 2016-08-21 22:16:28 -04:00
parent a5ddd614bc
commit 4cbffea0ed
3 changed files with 146 additions and 10 deletions

View file

@ -1,4 +1,5 @@
use std::rc::Rc;
use std::fmt;
#[derive(Debug, Clone)]
pub enum SuffixOp {
@ -73,3 +74,129 @@ pub enum Def {
pub struct Defs {
pub defs: Vec<(String, Rc<Def>)>,
}
impl fmt::Display for SuffixOp {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
SuffixOp::Celsius => write!(fmt, "°C"),
SuffixOp::Fahrenheit => write!(fmt, "°F"),
SuffixOp::Newton => write!(fmt, "°N"),
SuffixOp::Reaumur => write!(fmt, "°Ré"),
SuffixOp::Romer => write!(fmt, "°Rø"),
SuffixOp::Delisle => write!(fmt, "°De"),
}
}
}
impl fmt::Display for Expr {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
#[derive(PartialOrd, Ord, PartialEq, Eq)]
enum Prec {
Term, Plus, Pow, Mul, Div, Add, Equals, Convert
}
fn recurse(expr: &Expr, fmt: &mut fmt::Formatter, prec: Prec) -> fmt::Result {
macro_rules! binop {
($left:expr, $right:expr, $prec:expr, $succ:expr, $sym:expr) => {{
if prec < $prec {
try!(write!(fmt, "("));
}
try!(recurse($left, fmt, $succ));
try!(write!(fmt, $sym));
try!(recurse($right, fmt, $prec));
if prec < $prec {
try!(write!(fmt, ")"));
}
Ok(())
}}
}
match *expr {
Expr::Unit(ref name) => write!(fmt, "{}", name),
Expr::Quote(ref name) => write!(fmt, "'{}'", name),
Expr::Const(ref integer, ref frac, ref exp) => {
try!(write!(fmt, "{}", integer));
if let Some(ref frac) = *frac {
try!(write!(fmt, ".{}", frac));
}
if let Some(ref exp) = *exp {
try!(write!(fmt, "e{}", exp));
}
Ok(())
},
Expr::Date(ref _date) => write!(fmt, "NYI: date expr Display"),
Expr::Mul(ref exprs) => {
if prec < Prec::Mul {
try!(write!(fmt, "("));
}
if let Some(first) = exprs.first() {
try!(recurse(first, fmt, Prec::Pow));
}
for expr in exprs.iter().skip(1) {
try!(write!(fmt, " "));
try!(recurse(expr, fmt, Prec::Pow));
}
if prec < Prec::Mul {
try!(write!(fmt, ")"));
}
Ok(())
},
Expr::Call(ref name, ref args) => {
try!(write!(fmt, "{}(", name));
if let Some(first) = args.first() {
try!(recurse(first, fmt, Prec::Convert));
}
for arg in args.iter().skip(1) {
try!(write!(fmt, ", "));
try!(recurse(arg, fmt, Prec::Convert));
}
write!(fmt, ")")
},
Expr::Pow(ref left, ref right) => binop!(left, right, Prec::Pow, Prec::Term, "^"),
Expr::Frac(ref left, ref right) => binop!(left, right, Prec::Div, Prec::Mul, " / "),
Expr::Add(ref left, ref right) => binop!(left, right, Prec::Add, Prec::Div, " + "),
Expr::Sub(ref left, ref right) => binop!(left, right, Prec::Add, Prec::Div, " - "),
Expr::Plus(ref expr) => {
try!(write!(fmt, "+"));
recurse(expr, fmt, Prec::Plus)
},
Expr::Neg(ref expr) => {
try!(write!(fmt, "-"));
recurse(expr, fmt, Prec::Plus)
},
Expr::Convert(ref left, ref right) => binop!(left, right, Prec::Convert, Prec::Equals, " -> "),
Expr::Equals(ref left, ref right) => binop!(left, right, Prec::Equals, Prec::Add, " = "),
Expr::Suffix(ref op, ref expr) => {
if prec < Prec::Mul {
try!(write!(fmt, "("));
}
try!(recurse(expr, fmt, Prec::Mul));
try!(write!(fmt, " {}", op));
if prec < Prec::Mul {
try!(write!(fmt, ")"));
}
Ok(())
},
Expr::Factorize(ref op) => {
if prec < Prec::Convert {
try!(write!(fmt, "("));
}
try!(write!(fmt, "factorize "));
try!(recurse(op, fmt, Prec::Convert));
if prec < Prec::Convert {
try!(write!(fmt, ")"));
}
Ok(())
}
Expr::DegC => write!(fmt, "°C"),
Expr::DegF => write!(fmt, "°F"),
Expr::DegRe => write!(fmt, "°Ré"),
Expr::DegRo => write!(fmt, "°Rø"),
Expr::DegDe => write!(fmt, "°De"),
Expr::DegN => write!(fmt, "°N"),
Expr::Error(ref err) => write!(fmt, "<error: {}>", err)
}
}
recurse(self, fmt, Prec::Convert)
}
}

View file

@ -23,6 +23,7 @@ pub struct Context {
pub aliases: HashMap<Unit, String>,
pub reverse: HashMap<Unit, String>,
pub prefixes: Vec<(String, Number)>,
pub definitions: HashMap<String, Expr>,
pub datepatterns: Vec<Vec<DatePattern>>,
pub short_output: bool,
}
@ -509,6 +510,16 @@ impl Context {
};
match *expr {
Expr::Unit(ref name) if self.definitions.contains_key(name) => {
let ref def = self.definitions[name];
let res = self.lookup(name).unwrap();
let alias = if let Some(name) = self.aliases.get(&res.1) {
format!(" ({})", name)
} else {
format!("")
};
Ok(format!("Definition: {} = {} = {}{}", name, def, Number::unit_to_string(&res.1), alias))
},
Expr::Convert(ref top, ref bottom) => match (self.eval(&**top), self.eval(&**bottom), self.eval_unit_name(&**bottom)) {
(Ok(top), Ok(bottom), Ok(bottom_name)) => {
let (top, bottom) = match (top, bottom) {
@ -614,6 +625,7 @@ impl Context {
aliases: HashMap::new(),
reverse: HashMap::new(),
prefixes: Vec::new(),
definitions: HashMap::new(),
datepatterns: Vec::new(),
short_output: false,
};
@ -791,6 +803,7 @@ impl Context {
if v.0 == Mpq::one() && reverse.contains(&*name) {
ctx.reverse.insert(v.1.clone(), name.clone());
}
ctx.definitions.insert(name.clone(), expr.clone());
ctx.units.insert(name.clone(), v);
},
Ok(_) => println!("Unit {} is not a number", name),
@ -814,6 +827,9 @@ impl Context {
Def::Quantity(ref expr) => match ctx.eval(expr) {
Ok(Value::Number(v)) => {
let res = ctx.aliases.insert(v.1, name.clone());
if !ctx.definitions.contains_key(&name) {
ctx.definitions.insert(name.clone(), expr.clone());
}
if let Some(old) = res {
println!("Warning: Conflicting quantities {} and {}", name, old);
}

View file

@ -247,7 +247,7 @@ impl Number {
String::from_utf8(out).unwrap()
}
fn unit_to_string(unit: &Unit) -> String {
pub fn unit_to_string(unit: &Unit) -> String {
use std::io::Write;
let mut out = vec![];
@ -279,16 +279,9 @@ impl Number {
}
pub fn unit_name(&self, context: &::eval::Context) -> String {
let raw = Number::unit_to_string(&self.1);
let pretty = ::factorize::fast_decompose(self, &context.reverse);
if self.1 != pretty {
let pretty = Number::unit_to_string(&pretty);
format!("{} = {}", pretty, raw)
} else {
raw
}
let pretty = Number::unit_to_string(&pretty);
pretty
}
pub fn complexity_score(&self) -> i64 {