mirror of
https://github.com/tiffany352/rink-rs
synced 2024-11-10 13:44:15 +00:00
Add unit list conversions
e.g. megasecond -> hour;min;sec
This commit is contained in:
parent
3f32bc30a7
commit
262ee80a7c
2 changed files with 82 additions and 1 deletions
59
src/eval.rs
59
src/eval.rs
|
@ -549,7 +549,64 @@ impl Context {
|
|||
(_, Err(e), _) => Err(e),
|
||||
(_, _, Err(e)) => Err(e),
|
||||
},
|
||||
Query::Convert(ref _top, Conversion::List(ref _list)) => unimplemented!(),
|
||||
Query::Convert(ref top, Conversion::List(ref list)) => {
|
||||
let top = try!(self.eval(top));
|
||||
let top = match top {
|
||||
Value::Number(num) => num,
|
||||
_ => return Err(format!("Cannot convert <{}> to {:?}",
|
||||
top.show(self), list))
|
||||
};
|
||||
let units = try!(list.iter().map(|x| {
|
||||
match self.lookup(x) {
|
||||
Some(x) => Ok(x),
|
||||
None => Err(format!("Unit {} does not exist", x))
|
||||
}
|
||||
}).collect::<Result<Vec<Number>, _>>());
|
||||
{
|
||||
let first = try!(units.first().ok_or(format!("Expected non-empty unit list")));
|
||||
try!(units.iter().skip(1).map(|x| {
|
||||
if first.1 != x.1 {
|
||||
Err(format!("Units in unit list must conform: <{}> ; <{}>",
|
||||
first.show(self), x.show(self)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}).collect::<Result<Vec<()>, _>>());
|
||||
if top.1 != first.1 {
|
||||
return Err(conformance_err(&top, &first))
|
||||
}
|
||||
}
|
||||
let mut value = top.0;
|
||||
let mut out = vec![];
|
||||
let len = units.len();
|
||||
for (i, unit) in units.into_iter().enumerate() {
|
||||
// value -= unit * floor(value/unit)
|
||||
use gmp::mpz::Mpz;
|
||||
|
||||
let res = &value / &unit.0;
|
||||
let div = &res.get_num() / res.get_den();
|
||||
let rem = &value - &(&unit.0 * &Mpq::ratio(&div, &Mpz::one()));
|
||||
value = rem;
|
||||
if i == len-1 {
|
||||
out.push(res);
|
||||
} else {
|
||||
out.push(Mpq::ratio(&div, &Mpz::one()));
|
||||
}
|
||||
}
|
||||
let mut buf = vec![];
|
||||
for (name, value) in list.into_iter().zip(out.into_iter()) {
|
||||
use std::io::Write;
|
||||
use number;
|
||||
|
||||
write!(buf, "{} {}, ", number::to_string(&value).1, name).unwrap();
|
||||
}
|
||||
buf.pop(); buf.pop();
|
||||
if let Some(res) = self.aliases.get(&top.1) {
|
||||
use std::io::Write;
|
||||
write!(buf, " ({})", res).unwrap();
|
||||
}
|
||||
Ok(String::from_utf8(buf).unwrap())
|
||||
},
|
||||
Query::Convert(ref top, ref which @ Conversion::DegC) |
|
||||
Query::Convert(ref top, ref which @ Conversion::DegF) |
|
||||
Query::Convert(ref top, ref which @ Conversion::DegN) |
|
||||
|
|
|
@ -14,6 +14,7 @@ pub enum Token {
|
|||
Number(String, Option<String>, Option<String>),
|
||||
Quote(String),
|
||||
Slash,
|
||||
Semicolon,
|
||||
Colon,
|
||||
ColonDash,
|
||||
DColonDash,
|
||||
|
@ -71,6 +72,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||
'(' => Token::LPar,
|
||||
')' => Token::RPar,
|
||||
'+' => Token::Plus,
|
||||
';' => Token::Semicolon,
|
||||
'%' => Token::Ident("percent".to_owned()),
|
||||
'-' => match self.0.peek().cloned().unwrap() {
|
||||
'>' => {
|
||||
|
@ -500,6 +502,23 @@ pub fn parse_expr(iter: &mut Iter) -> Expr {
|
|||
parse_eq(iter)
|
||||
}
|
||||
|
||||
pub fn parse_unitlist(mut iter: &mut Iter) -> Option<Vec<String>> {
|
||||
let mut expecting_term = true;
|
||||
let mut res = vec![];
|
||||
loop { match iter.next().unwrap() {
|
||||
Token::Ident(ref ident) if expecting_term => {
|
||||
res.push(ident.clone());
|
||||
expecting_term = false;
|
||||
},
|
||||
Token::Comma | Token::Semicolon if !expecting_term => {
|
||||
expecting_term = true;
|
||||
},
|
||||
Token::Eof | Token::Newline | Token::Comment(_) if !expecting_term => break,
|
||||
_ => return None
|
||||
}}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
pub fn parse_query(mut iter: &mut Iter) -> Query {
|
||||
match iter.peek().cloned() {
|
||||
Some(Token::Ident(ref s)) if s == "factorize" => {
|
||||
|
@ -512,6 +531,11 @@ pub fn parse_query(mut iter: &mut Iter) -> Query {
|
|||
match iter.peek().cloned().unwrap() {
|
||||
Token::DashArrow => {
|
||||
iter.next();
|
||||
let mut copy = iter.clone();
|
||||
if let Some(res) = parse_unitlist(&mut copy) {
|
||||
*iter = copy;
|
||||
return Query::Convert(left, Conversion::List(res))
|
||||
}
|
||||
let right = match iter.peek().cloned().unwrap() {
|
||||
Token::DegC => Conversion::DegC,
|
||||
Token::DegF => Conversion::DegF,
|
||||
|
|
Loading…
Reference in a new issue