Add unit list conversions

e.g. megasecond -> hour;min;sec
This commit is contained in:
Tiffany Bennett 2016-08-24 15:04:13 -04:00
parent 3f32bc30a7
commit 262ee80a7c
2 changed files with 82 additions and 1 deletions

View file

@ -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) |

View file

@ -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,