mirror of
https://github.com/tiffany352/rink-rs
synced 2024-11-10 13:44:15 +00:00
Show definitions for single-unit queries
This commit is contained in:
parent
a5ddd614bc
commit
4cbffea0ed
3 changed files with 146 additions and 10 deletions
127
src/ast.rs
127
src/ast.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
16
src/eval.rs
16
src/eval.rs
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue