After merging PR#40

This commit is contained in:
Alex Alemi 2020-04-22 21:43:47 -04:00
commit 7197d91e47
22 changed files with 731 additions and 949 deletions

View file

@ -30,6 +30,7 @@ xml-rs = { version = "0.3.4", optional = true }
json = { version = "0.10.2", optional = true }
serde = { version = "0.8.16", optional = true }
serde_derive = { version = "0.8.16", optional = true }
dirs = "1.0.4"
[[bin]]
name = "rink"

View file

@ -50,7 +50,7 @@ fn main() {
let mut i = 0;
let reply = eval(line);
for line in reply.lines() {
if line.trim().len() > 0 {
if !line.trim().is_empty() {
server.send(Command::NOTICE(reply_to.to_owned(), line.to_owned())).unwrap();
i += 1;
}

View file

@ -8,7 +8,7 @@ use num::Num;
use chrono_tz::Tz;
#[derive(Debug, Clone)]
pub enum SuffixOp {
pub enum Degree {
Celsius,
Fahrenheit,
Reaumur,
@ -42,22 +42,95 @@ pub enum Expr {
Neg(Box<Expr>),
Plus(Box<Expr>),
Equals(Box<Expr>, Box<Expr>),
Suffix(SuffixOp, Box<Expr>),
Suffix(Degree, Box<Expr>),
Of(String, Box<Expr>),
Call(String, Vec<Expr>),
Call(Function, Vec<Expr>),
Error(String),
}
#[derive(Debug, Clone)]
pub enum Function {
Sqrt,
Exp,
Ln,
Log2,
Log10,
Sin,
Cos,
Tan,
Asin,
Acos,
Atan,
Sinh,
Cosh,
Tanh,
Asinh,
Acosh,
Atanh,
Log,
Hypot,
Atan2,
}
impl Function {
pub fn name(&self) -> &str {
match *self {
Function::Sqrt => "sqrt",
Function::Exp => "exp",
Function::Ln => "ln",
Function::Log2 => "log2",
Function::Log10 => "log10",
Function::Sin => "sin",
Function::Cos => "cos",
Function::Tan => "tan",
Function::Asin => "asin",
Function::Acos => "acos",
Function::Atan => "atan",
Function::Sinh => "sinh",
Function::Cosh => "cosh",
Function::Tanh => "tanh",
Function::Asinh => "asinh",
Function::Acosh => "acosh",
Function::Atanh => "atanh",
Function::Log => "log",
Function::Hypot => "hypot",
Function::Atan2 => "atan2",
}
}
pub fn from_name(s: &str) -> Option<Self> {
let func = match s {
"sqrt" => Function::Sqrt,
"exp" => Function::Exp,
"ln" => Function::Ln,
"log2" => Function::Log2,
"log10" => Function::Log10,
"sin" => Function::Sin,
"cos" => Function::Cos,
"tan" => Function::Tan,
"asin" => Function::Asin,
"acos" => Function::Acos,
"atan" => Function::Atan,
"sinh" => Function::Sinh,
"cosh" => Function::Cosh,
"tanh" => Function::Tanh,
"asinh" => Function::Asinh,
"acosh" => Function::Acosh,
"atanh" => Function::Atanh,
"log" => Function::Log,
"hypot" => Function::Hypot,
"atan2" => Function::Atan2,
_ => return None,
};
Some(func)
}
}
#[derive(Debug, Clone)]
pub enum Conversion {
None,
Expr(Expr),
DegC,
DegF,
DegRe,
DegRo,
DegDe,
DegN,
Degree(Degree),
List(Vec<String>),
Offset(i64),
Timezone(Tz),
@ -135,16 +208,11 @@ impl fmt::Display for Conversion {
match *self {
Conversion::None => write!(fmt, "nothing"),
Conversion::Expr(ref expr) => write!(fmt, "{}", expr),
Conversion::DegC => write!(fmt, "°C"),
Conversion::DegF => write!(fmt, "°F"),
Conversion::DegRe => write!(fmt, "°Ré"),
Conversion::DegRo => write!(fmt, "°Rø"),
Conversion::DegDe => write!(fmt, "°De"),
Conversion::DegN => write!(fmt, "°N"),
Conversion::Degree(ref deg) => write!(fmt, "{}", deg),
Conversion::List(ref list) => {
let list = list
.iter()
.map(|x| format!("{}", x))
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ");
write!(fmt, "{}", list)
@ -157,15 +225,28 @@ impl fmt::Display for Conversion {
}
}
impl fmt::Display for SuffixOp {
impl fmt::Display for Degree {
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"),
Degree::Celsius => write!(fmt, "°C"),
Degree::Fahrenheit => write!(fmt, "°F"),
Degree::Newton => write!(fmt, "°N"),
Degree::Reaumur => write!(fmt, "°Ré"),
Degree::Romer => write!(fmt, "°Rø"),
Degree::Delisle => write!(fmt, "°De"),
}
}
}
impl Degree {
pub fn name_base_scale(&self) -> (&str, &str, &str) {
match *self {
Degree::Celsius => ("C", "zerocelsius", "kelvin"),
Degree::Fahrenheit => ("F", "zerofahrenheit", "degrankine"),
Degree::Reaumur => ("", "zerocelsius", "reaumur_absolute"),
Degree::Romer => ("", "zeroromer", "romer_absolute"),
Degree::Delisle => ("De", "zerodelisle", "delisle_absolute"),
Degree::Newton => ("N", "zerocelsius", "newton_absolute"),
}
}
}
@ -216,8 +297,8 @@ impl fmt::Display for Expr {
}
Ok(())
},
Expr::Call(ref name, ref args) => {
try!(write!(fmt, "{}(", name));
Expr::Call(ref func, ref args) => {
try!(write!(fmt, "{}(", func.name()));
if let Some(first) = args.first() {
try!(recurse(first, fmt, Prec::Equals));
}
@ -317,9 +398,10 @@ impl fmt::Display for DateToken {
#[cfg(test)]
mod test {
use super::Expr::{self, *};
use super::Function;
fn check<T: ::std::fmt::Display>(e: T, expected: &str) {
assert_eq!(format!("{}", e), expected);
assert_eq!(e.to_string(), expected);
}
impl From<i64> for Expr {
@ -330,9 +412,9 @@ mod test {
#[test]
fn test_display_call() {
check(Call("f".into(), vec![]), "f()");
check(Call("f".into(), vec![1.into()]), "f(1)");
check(Call("f".into(), vec![1.into(), 2.into()]), "f(1, 2)");
check(Call("f".into(), vec![1.into(), 2.into(), 3.into()]), "f(1, 2, 3)");
check(Call(Function::Sin, vec![]), "sin()");
check(Call(Function::Sin, vec![1.into()]), "sin(1)");
check(Call(Function::Sin, vec![1.into(), 2.into()]), "sin(1, 2)");
check(Call(Function::Sin, vec![1.into(), 2.into(), 3.into()]), "sin(1, 2, 3)");
}
}

View file

@ -27,16 +27,14 @@ fn main_noninteractive<T: BufRead>(mut f: T, show_prompt: bool) {
print!("> ");
}
stdout().flush().unwrap();
match f.read_line(&mut line) {
Ok(_) => (),
Err(_) => return
};
if f.read_line(&mut line).is_err() {
return
}
// the underlying file object has hit an EOF if we try to read a
// line but do not find the newline at the end, so let's break
// out of the loop
match line.find('\n') {
Some(_) => (),
None => return
if line.find('\n').is_none() {
return;
}
match one_line(&mut ctx, &*line) {
Ok(v) => println!("{}", v),
@ -80,13 +78,10 @@ fn main_interactive() {
});
}
}
for (ref k, _) in &ctx.units {
for k in ctx.units.keys() {
if k.starts_with(name) {
let ref def = ctx.definitions.get(&**k);
let def = match def {
&Some(ref def) => format!("{} = ", def),
&None => format!("")
};
let def = &ctx.definitions.get(&**k);
let def = def.map(|def| format!("{} = ", def)).unwrap_or_default();
let res = ctx.lookup(k).unwrap();
let parts = res.to_parts(ctx);
out.push(Completion {
@ -236,7 +231,7 @@ fn main_interactive() {
let readline = rl.read_line();
match readline {
Ok(ReadResult::Input(ref line)) if line == "quit" => {
println!("");
println!();
break
},
Ok(ReadResult::Input(ref line)) if line == "help" => {
@ -251,7 +246,7 @@ fn main_interactive() {
};
},
Ok(ReadResult::Eof) => {
println!("");
println!();
let hfile = hpath.and_then(|hpath| File::create(hpath).map_err(|x| x.to_string()));
if let Ok(mut hfile) = hfile {
for line in rl.history() {

View file

@ -13,13 +13,13 @@ static URL: &'static str = "https://blockchain.info/stats?format=json";
pub fn parse(mut f: File) -> Result<Defs, String> {
let mut buf = String::new();
try!(f.read_to_string(&mut buf).map_err(|x| format!("{}", x)));
let parsed = try!(json::parse(&*buf).map_err(|x| format!("{}", x)));
try!(f.read_to_string(&mut buf).map_err(|x| x.to_string()));
let parsed = try!(json::parse(&*buf).map_err(|x| x.to_string()));
let mut out = vec![];
if let Some(price) = parsed["market_price_usd"].as_number() {
let (sign, mantissa, exp) = price.as_parts();
let integer = format!("{}{}", if sign { "" } else { "-" }, mantissa);
if let Ok(price) = ::Number::from_parts(&*integer, None, Some(&*format!("{}", exp))) {
if let Ok(price) = ::Number::from_parts(&*integer, None, Some(&*exp.to_string())) {
out.push(DefEntry {
name: "BTC".to_owned(),
def: Rc::new(Def::Unit(
@ -27,7 +27,7 @@ pub fn parse(mut f: File) -> Result<Defs, String> {
Expr::Const(price),
Expr::Unit("USD".to_owned())
]))),
doc: Some(format!("Sourced from blockchain.info.")),
doc: Some("Sourced from blockchain.info.".to_string()),
category: Some("currencies".to_owned()),
});
}

View file

@ -11,7 +11,7 @@ use substance::Substance;
use reply::NotFoundError;
/// The evaluation context that contains unit definitions.
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct Context {
pub dimensions: BTreeSet<Dim>,
pub canonicalizations: BTreeMap<String, String>,
@ -35,22 +35,9 @@ impl Context {
/// Creates a new, empty context
pub fn new() -> Context {
Context {
dimensions: BTreeSet::new(),
canonicalizations: BTreeMap::new(),
units: BTreeMap::new(),
quantities: BTreeMap::new(),
reverse: BTreeMap::new(),
prefixes: Vec::new(),
definitions: BTreeMap::new(),
docs: BTreeMap::new(),
categories: BTreeMap::new(),
category_names: BTreeMap::new(),
datepatterns: Vec::new(),
substances: BTreeMap::new(),
substance_symbols: BTreeMap::new(),
temporaries: BTreeMap::new(),
short_output: false,
use_humanize: true,
..Context::default()
}
}
@ -81,31 +68,33 @@ impl Context {
}
None
}
if let Some(v) = inner(self, name) {
return Some(v)
}
for &(ref pre, ref value) in &self.prefixes {
if name.starts_with(pre) {
if let Some(v) = inner(self, &name[pre.len()..]) {
return Some((&v * &value).unwrap())
}
}
}
// after so that "ks" is kiloseconds
if name.ends_with("s") {
let name = &name[0..name.len()-1];
let outer = |name: &str| -> Option<Number> {
if let Some(v) = inner(self, name) {
return Some(v)
}
for &(ref pre, ref value) in &self.prefixes {
if name.starts_with(pre) {
if let Some(v) = inner(self, &name[pre.len()..]) {
return Some((&v * &value).unwrap())
return Some((&v * value).unwrap())
}
}
}
None
};
let res = outer(name);
if res.is_some() {
return res;
}
// after so that "ks" is kiloseconds
if name.ends_with('s') {
let name = &name[0..name.len()-1];
outer(name)
} else {
None
}
None
}
/// Given a unit name, try to return a canonical name (expanding aliases and such)
@ -131,24 +120,8 @@ impl Context {
}
None
}
if let Some(v) = inner(self, name) {
return Some(v)
}
for &(ref pre, ref val) in &self.prefixes {
if name.starts_with(pre) {
if let Some(v) = inner(self, &name[pre.len()..]) {
let mut pre = pre;
for &(ref other, ref otherval) in &self.prefixes {
if other.len() > pre.len() && val == otherval {
pre = other;
}
}
return Some(format!("{}{}", pre, v))
}
}
}
if name.ends_with("s") {
let name = &name[0..name.len()-1];
let outer = |name: &str| -> Option<String> {
if let Some(v) = inner(self, name) {
return Some(v)
}
@ -165,8 +138,20 @@ impl Context {
}
}
}
None
};
let res = outer(name);
if res.is_some() {
return res;
}
if name.ends_with('s') {
let name = &name[0..name.len()-1];
outer(name)
} else {
None
}
None
}
/// Describes a value's unit, gives true if the unit is reciprocal
@ -193,6 +178,25 @@ impl Context {
recip = true;
write!(buf, "{}", name).unwrap();
} else {
let helper = |dim: &Dim, pow: i64, buf: &mut Vec<u8>| {
let mut map = Unit::new();
map.insert(dim.clone(), pow);
if let Some(name) = self.quantities.get(&map) {
write!(buf, " {}", name).unwrap();
} else {
let mut map = Unit::new();
map.insert(dim.clone(), 1);
if let Some(name) = self.quantities.get(&map) {
write!(buf, " {}", name).unwrap();
} else {
write!(buf, " '{}'", dim).unwrap();
}
if pow != 1 {
write!(buf, "^{}", pow).unwrap();
}
}
};
let mut frac = vec![];
let mut found = false;
for (dim, &pow) in &value.unit {
@ -200,25 +204,10 @@ impl Context {
frac.push((dim, -pow));
} else {
found = true;
let mut map = Unit::new();
map.insert(dim.clone(), pow);
if let Some(name) = self.quantities.get(&map) {
write!(buf, " {}", name).unwrap();
} else {
let mut map = Unit::new();
map.insert(dim.clone(), 1);
if let Some(name) = self.quantities.get(&map) {
write!(buf, " {}", name).unwrap();
} else {
write!(buf, " '{}'", dim).unwrap();
}
if pow != 1 {
write!(buf, "^{}", pow).unwrap();
}
}
helper(dim, pow, &mut buf);
}
}
if frac.len() > 0 {
if !frac.is_empty() {
if !found {
recip = true;
} else {
@ -230,16 +219,7 @@ impl Context {
if let Some(name) = self.quantities.get(&map) {
write!(buf, " {}", name).unwrap();
} else {
let mut map = Unit::new();
map.insert(dim.clone(), 1);
if let Some(name) = self.quantities.get(&map) {
write!(buf, " {}", name).unwrap();
} else {
write!(buf, " '{}'", dim).unwrap();
}
if pow != 1 {
write!(buf, "^{}", pow).unwrap();
}
helper(dim, pow, &mut buf);
}
}
}

View file

@ -34,7 +34,7 @@ pub fn parse(f: File) -> Result<Defs, String> {
}
}
if let (Some(currency), Some(rate)) = (currency, rate) {
let mut iter = rate.split(".");
let mut iter = rate.split('.');
let integer = iter.next().unwrap();
let frac = iter.next();
if let Ok(num) = ::number::Number::from_parts(integer, frac, None) {
@ -46,13 +46,13 @@ pub fn parse(f: File) -> Result<Defs, String> {
Box::new(Expr::Const(num))),
Expr::Unit("EUR".to_string())
]))),
doc: Some(format!("Sourced from European Central Bank.")),
doc: Some("Sourced from European Central Bank.".to_string()),
category: Some("currencies".to_owned()),
});
}
}
},
Err(e) => return Err(format!("{}", e)),
Err(e) => return Err(e.to_string()),
_ => (),
}
}

View file

@ -90,7 +90,7 @@ pub fn parse_date<I>(
DateToken::Dash => (-1, None),
DateToken::Plus => (1, None),
DateToken::Number(i, None) => (1, Some(i)),
_ => panic!()
_ => unreachable!()
};
let num = match num {
Some(x) => x,
@ -169,7 +169,7 @@ pub fn parse_date<I>(
}
} else {
let s = match take!(DateToken::Plus | DateToken::Dash) {
DateToken::Plus => 1, DateToken::Dash => -1, _ => panic!()
DateToken::Plus => 1, DateToken::Dash => -1, _ => unreachable!()
};
let h = take!(DateToken::Number(s, None), s);
if h.len() == 4 {
@ -253,10 +253,7 @@ pub fn parse_date<I>(
if advance {
date.next();
}
match res {
Ok(()) => parse_date(out, out_tz, date, &pat[1..]),
Err(e) => Err(e)
}
res.and_then(|_| parse_date(out, out_tz, date, &pat[1..]))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -294,17 +291,19 @@ fn attempt(date: &[DateToken], pat: &[DatePattern]) -> Result<GenericDateTime, (
if let Some(tz) = tz {
match (time, date) {
(Ok(time), Ok(date)) =>
tz.from_local_datetime(&date.and_time(time)).earliest().ok_or_else(|| (format!(
"Datetime does not represent a valid moment in time"
), count)).map(GenericDateTime::Timezone),
tz.from_local_datetime(&date.and_time(time)).earliest().ok_or_else(|| (
"Datetime does not represent a valid moment in time".to_string(),
count,
)).map(GenericDateTime::Timezone),
(Ok(time), Err(_)) =>
Ok(UTC::now().with_timezone(&tz).date().and_time(time).unwrap()).map(
GenericDateTime::Timezone),
(Err(_), Ok(date)) =>
tz.from_local_date(&date).earliest().map(|x| x.and_hms(0, 0, 0)).ok_or_else(|| (format!(
"Datetime does not represent a valid moment in time"
), count)).map(GenericDateTime::Timezone),
_ => Err((format!("Failed to construct a useful datetime"), count))
tz.from_local_date(&date).earliest().map(|x| x.and_hms(0, 0, 0)).ok_or_else(|| (
"Datetime does not represent a valid moment in time".to_string(),
count,
)).map(GenericDateTime::Timezone),
_ => Err(("Failed to construct a useful datetime".to_string(), count))
}
} else {
let offset = parsed.to_fixed_offset().unwrap_or(FixedOffset::east(0));
@ -348,13 +347,13 @@ pub fn try_decode(date: &[DateToken], context: &Context) -> Result<GenericDateTi
if let Some((_, pat, err)) = best {
Err(format!("Most likely pattern `{}` failed: {}", show_datepattern(pat), err))
} else {
Err(format!("Invalid date literal"))
Err("Invalid date literal".to_string())
}
}
pub fn to_duration(num: &Number) -> Result<Duration, String> {
if num.unit.len() != 1 || num.unit.get("s") != Some(&1) {
return Err(format!("Expected seconds"))
return Err("Expected seconds".to_string())
}
let max = Num::from(i64::max_value() / 1000);
if num.value.abs() > max {
@ -395,7 +394,7 @@ pub fn parse_datepattern<I>(iter: &mut Peekable<I>)
iter.next();
let res = DatePattern::Optional(try!(parse_datepattern(iter)));
if iter.peek().cloned() != Some(']') {
return Err(format!("Expected ]"))
return Err("Expected ]".to_string())
} else {
res
}
@ -447,7 +446,7 @@ pub fn parse_datefile(file: &str) -> Vec<Vec<DatePattern>> {
for (num, line) in file.lines().enumerate() {
let line = line.split('#').next().unwrap();
let line = line.trim();
if line.len() == 0 {
if line.is_empty() {
continue
}
let res = parse_datepattern(&mut line.chars().peekable());
@ -464,7 +463,7 @@ impl Context {
pub fn humanize<Tz: TimeZone>(&self, date: DateTime<Tz>) -> Option<String> {
if self.use_humanize {
use chrono_humanize::HumanTime;
Some(format!("{}", HumanTime::from(date)))
Some(HumanTime::from(date).to_string())
} else {
None
}
@ -517,14 +516,14 @@ mod tests {
let date = vec![
DateToken::Plus,
DateToken::Number(format!("{}", expected.year.unwrap()), None),
DateToken::Number(expected.year.unwrap().to_string(), None),
];
let (res, parsed) = parse(date.clone(), "year");
assert!(res.is_ok());
assert_eq!(parsed, expected);
let date = vec![DateToken::Number(
format!("{}", expected.year.unwrap()),
expected.year.unwrap().to_string(),
None,
)];
let (res, parsed2) = parse(date.clone(), "year");
@ -543,13 +542,13 @@ mod tests {
expected.set_minute(57).unwrap();
let date = vec![
DateToken::Number(format!("{}", expected.day.unwrap()), None),
DateToken::Number(expected.day.unwrap().to_string(), None),
DateToken::Space,
DateToken::Literal("Pm".into()),
DateToken::Dash,
DateToken::Number(format!("{:02}", expected.month.unwrap()), None),
DateToken::Colon,
DateToken::Number(format!("{}", expected.year.unwrap()), None),
DateToken::Number(expected.year.unwrap().to_string(), None),
DateToken::Space,
DateToken::Number(format!("{:02}", expected.hour_mod_12.unwrap()), None),
DateToken::Dash,
@ -571,7 +570,7 @@ mod tests {
expected.set_hour(7).unwrap();
let date = vec![
DateToken::Number(format!("{}", year.abs()), None),
DateToken::Number(year.abs().to_string(), None),
DateToken::Space,
DateToken::Literal("bce".into()),
DateToken::Space,

View file

@ -6,7 +6,7 @@ use std::collections::BTreeMap;
use number::{Number, Dim, NumberParts, pow};
use num::{Num, Int};
use date;
use ast::{Expr, SuffixOp, Query, Conversion, Digits};
use ast::{Expr, Query, Conversion, Digits, Function};
use std::rc::Rc;
use factorize::{factorize, Factors};
use value::{Value, Show};
@ -41,30 +41,6 @@ impl Context {
}}
}
macro_rules! temperature {
($left:ident, $name:expr, $base:expr, $scale:expr) => {{
let left = try!(self.eval(&**$left));
let left = match left {
Value::Number(left) => left,
_ => return Err(QueryError::Generic(format!(
"Expected number, got: <{}> °{}",
left.show(self), stringify!($name)
)))
};
if left.unit != BTreeMap::new() {
Err(QueryError::Generic(format!(
"Expected dimensionless, got: <{}>",
left.show(self)
)))
} else {
let left = (&left * &self.lookup($scale).expect(
&*format!("Missing {} unit", $scale))).unwrap();
Ok(Value::Number((&left + &self.lookup($base)
.expect(&*format!("Missing {} constant", $base))).unwrap()))
}
}}
}
match *expr {
Expr::Unit(ref name) if name == "now" =>
Ok(Value::DateTime(date::GenericDateTime::Fixed(date::now()))),
@ -98,18 +74,29 @@ impl Context {
Expr::Sub(ref left, ref right) => operator!(left sub - right),
Expr::Pow(ref left, ref right) => operator!(left pow ^ right),
Expr::Suffix(SuffixOp::Celsius, ref left) =>
temperature!(left, "C", "zerocelsius", "kelvin"),
Expr::Suffix(SuffixOp::Fahrenheit, ref left) =>
temperature!(left, "F", "zerofahrenheit", "degrankine"),
Expr::Suffix(SuffixOp::Reaumur, ref left) =>
temperature!(left, "", "zerocelsius", "reaumur_absolute"),
Expr::Suffix(SuffixOp::Romer, ref left) =>
temperature!(left, "", "zeroromer", "romer_absolute"),
Expr::Suffix(SuffixOp::Delisle, ref left) =>
temperature!(left, "De", "zerodelisle", "delisle_absolute"),
Expr::Suffix(SuffixOp::Newton, ref left) =>
temperature!(left, "N", "zerocelsius", "newton_absolute"),
Expr::Suffix(ref deg, ref left) => {
let (name, base, scale) = deg.name_base_scale();
let left = try!(self.eval(&**left));
let left = match left {
Value::Number(left) => left,
_ => return Err(QueryError::Generic(format!(
"Expected number, got: <{}> °{}",
left.show(self), name
)))
};
if left.unit != BTreeMap::new() {
Err(QueryError::Generic(format!(
"Expected dimensionless, got: <{}>",
left.show(self)
)))
} else {
let left = (&left * &self.lookup(scale).expect(
&*format!("Missing {} unit", scale))).unwrap();
Ok(Value::Number((&left + &self.lookup(base)
.expect(&*format!("Missing {} constant", base))).unwrap()))
}
}
Expr::Mul(ref args) => args.iter().fold(Ok(Value::Number(Number::one())), |a, b| {
a.and_then(|a| {
@ -150,7 +137,7 @@ impl Context {
}
})
},
Expr::Call(ref name, ref args) => {
Expr::Call(ref func, ref args) => {
let args = try!(
args.iter()
.map(|x| self.eval(x))
@ -200,27 +187,25 @@ impl Context {
}
}}
match &**name {
"sqrt" => func!(fn sqrt(num: Number) {
match func {
Function::Sqrt => func!(fn sqrt(num: Number) {
num.root(2).map(Value::Number)
}),
"exp" => func!(fn exp(num: Number) {
Function::Exp => func!(fn exp(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().exp()),
unit: num.unit.clone(),
}))
}),
"ln" => func!(fn ln(num: Number) {
Function::Ln => func!(fn ln(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().ln()),
unit: num.unit.clone(),
}))
}),
"log" => func!(fn log(num: Number, base: Number) {
if base.unit.len() > 0 {
Err(format!(
"Base must be dimensionless"
))
Function::Log => func!(fn log(num: Number, base: Number) {
if !base.unit.is_empty() {
Err("Base must be dimensionless".to_string())
} else {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64()
@ -229,24 +214,21 @@ impl Context {
}))
}
}),
"log2" => func!(fn log2(num: Number) {
Function::Log2 => func!(fn log2(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().log2()),
unit: num.unit.clone(),
}))
}),
"log10" => func!(fn ln(num: Number) {
Function::Log10 => func!(fn ln(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().log10()),
unit: num.unit.clone(),
}))
}),
"hypot" => func!(fn hypot(x: Number, y: Number) {
Function::Hypot => func!(fn hypot(x: Number, y: Number) {
if x.unit != y.unit {
Err(format!(
"Arguments to hypot must have matching \
dimensionality"
))
Err("Arguments to hypot must have matching dimensionality".to_string())
} else {
Ok(Value::Number(Number {
value: Num::Float(x.value.to_f64().hypot(y.value.to_f64())),
@ -254,48 +236,45 @@ impl Context {
}))
}
}),
"sin" => func!(fn sin(num: Number) {
Function::Sin => func!(fn sin(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().sin()),
unit: num.unit.clone(),
}))
}),
"cos" => func!(fn cos(num: Number) {
Function::Cos => func!(fn cos(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().cos()),
unit: num.unit.clone(),
}))
}),
"tan" => func!(fn tan(num: Number) {
Function::Tan => func!(fn tan(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().tan()),
unit: num.unit.clone(),
}))
}),
"asin" => func!(fn asin(num: Number) {
Function::Asin => func!(fn asin(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().asin()),
unit: num.unit.clone(),
}))
}),
"acos" => func!(fn acos(num: Number) {
Function::Acos => func!(fn acos(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().acos()),
unit: num.unit.clone(),
}))
}),
"atan" => func!(fn atan(num: Number) {
Function::Atan => func!(fn atan(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().atan()),
unit: num.unit.clone(),
}))
}),
"atan2" => func!(fn atan2(x: Number, y: Number) {
Function::Atan2 => func!(fn atan2(x: Number, y: Number) {
if x.unit != y.unit {
Err(format!(
"Arguments to atan2 must have matching \
dimensionality"
))
Err("Arguments to atan2 must have matching dimensionality".to_string())
} else {
Ok(Value::Number(Number {
value: Num::Float(x.value.to_f64()
@ -304,45 +283,42 @@ impl Context {
}))
}
}),
"sinh" => func!(fn sinh(num: Number) {
Function::Sinh => func!(fn sinh(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().sinh()),
unit: num.unit.clone(),
}))
}),
"cosh" => func!(fn cosh(num: Number) {
Function::Cosh => func!(fn cosh(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().cosh()),
unit: num.unit.clone(),
}))
}),
"tanh" => func!(fn tanh(num: Number) {
Function::Tanh => func!(fn tanh(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().tanh()),
unit: num.unit.clone(),
}))
}),
"asinh" => func!(fn asinh(num: Number) {
Function::Asinh => func!(fn asinh(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().asinh()),
unit: num.unit.clone(),
}))
}),
"acosh" => func!(fn acosh(num: Number) {
Function::Acosh => func!(fn acosh(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().acosh()),
unit: num.unit.clone(),
}))
}),
"atanh" => func!(fn atanh(num: Number) {
Function::Atanh => func!(fn atanh(num: Number) {
Ok(Value::Number(Number {
value: Num::Float(num.value.to_f64().atanh()),
unit: num.unit.clone(),
}))
}),
_ => Err(QueryError::Generic(format!(
"Function not found: {}", name
)))
}
},
Expr::Error(ref e) => Err(QueryError::Generic(e.clone())),
@ -361,9 +337,9 @@ impl Context {
"Expected identifier, got {:?}", x
)))
},
Expr::Call(_, _) => Err(QueryError::Generic(format!(
"Calls are not allowed in the right hand side of conversions"
))),
Expr::Call(_, _) => Err(QueryError::Generic(
"Calls are not allowed in the right hand side of conversions".to_string()
)),
Expr::Unit(ref name) | Expr::Quote(ref name) => {
let mut map = BTreeMap::new();
map.insert(self.canonicalize(&**name)
@ -398,14 +374,14 @@ impl Context {
let res = try!(self.eval(exp));
let res = match res {
Value::Number(num) => num,
_ => return Err(QueryError::Generic(format!(
"Exponents must be numbers"
)))
_ => return Err(QueryError::Generic(
"Exponents must be numbers".to_string()
))
};
if !res.dimless() {
return Err(QueryError::Generic(format!(
"Exponents must be dimensionless"
)))
return Err(QueryError::Generic(
"Exponents must be dimensionless".to_string()
))
}
let res = res.value.to_f64();
let (left, lv) = try!(self.eval_unit_name(left));
@ -425,9 +401,9 @@ impl Context {
let res = try!(self.eval(expr));
let res = match res {
Value::Substance(sub) => sub,
_ => return Err(QueryError::Generic(format!(
"Property access on non-substance"
)))
_ => return Err(QueryError::Generic(
"Property access on non-substance".to_string()
))
};
let name = if let Some(prop) = res.properties.properties.get(name) {
if prop.input == Number::one() {
@ -448,27 +424,35 @@ impl Context {
let left = try!(self.eval_unit_name(left));
let right = try!(self.eval_unit_name(right));
if left != right {
return Err(QueryError::Generic(format!(
return Err(QueryError::Generic(
"Add of values with differing \
dimensions is not meaningful"
)))
dimensions is not meaningful".to_string()
))
}
Ok(left)
},
Expr::Neg(ref v) => self.eval_unit_name(v).map(|(u, v)| (u, -&v)),
Expr::Plus(ref v) => self.eval_unit_name(v),
Expr::Suffix(_, _) =>
Err(QueryError::Generic(format!(
"Temperature conversions must not be compound units"
))),
Expr::Date(_) => Err(QueryError::Generic(format!(
"Dates are not allowed in the right hand side of conversions"
))),
Err(QueryError::Generic(
"Temperature conversions must not be compound units".to_string()
)),
Expr::Date(_) => Err(QueryError::Generic(
"Dates are not allowed in the right hand side of conversions".to_string()
)),
Expr::Error(ref e) => Err(QueryError::Generic(e.clone())),
}
}
fn conformance_err(&self, top: &Number, bottom: &Number) -> ConformanceError {
fn multiply_or_divide(recip: bool) -> &'static str {
if recip {
"divide"
} else {
"multiply"
}
}
let mut topu = top.clone();
topu.value = Num::one();
let mut bottomu = bottom.clone();
@ -476,27 +460,21 @@ impl Context {
let mut suggestions = vec![];
let diff = (&topu * &bottomu).unwrap();
if diff.dimless() {
suggestions.push(format!("Reciprocal conversion, invert one side"));
suggestions.push("Reciprocal conversion, invert one side".to_string());
} else {
let diff = (&topu / &bottomu).unwrap();
let (recip, desc) = self.describe_unit(&diff.invert());
let word = match recip {
false => "multiply",
true => "divide"
};
let word = multiply_or_divide(recip);
suggestions.push(format!("{word} left side by {}", desc.trim(), word=word));
let (recip, desc) = self.describe_unit(&diff);
let word = match recip {
false => "multiply",
true => "divide"
};
let word = multiply_or_divide(recip);
suggestions.push(format!("{word} right side by {}", desc.trim(), word=word));
}
ConformanceError {
left: top.to_parts(self),
right: bottom.to_parts(self),
suggestions: suggestions,
suggestions,
}
}
@ -518,12 +496,12 @@ impl Context {
exact_value: exact,
approx_value: approx,
factor: if num != Int::one() {
Some(format!("{}", num))
Some(num.to_string())
} else {
None
},
divfactor: if den != Int::one() {
Some(format!("{}", den))
Some(den.to_string())
} else {
None
},
@ -542,7 +520,7 @@ impl Context {
}).collect::<Result<Vec<Number>, _>>());
{
let first = try!(units.first().ok_or(
format!("Expected non-empty unit list")));
"Expected non-empty unit list".to_string()));
try!(units.iter().skip(1).map(|x| {
if first.unit != x.unit {
Err(format!(
@ -571,7 +549,7 @@ impl Context {
}
Ok(list.iter().zip(out.into_iter()).map(|(name, value)| {
let pretty = Number {
value: value,
value,
unit: Number::one_unit(Dim::new(name)).unit
}.to_parts(self);
let unit: String = pretty.unit.or(pretty.dimensions)
@ -645,18 +623,18 @@ impl Context {
let def = if let Some(ref q) = parts.quantity {
format!("base unit of {}", q)
} else {
format!("base unit")
"base unit".to_string()
};
(Some(def), None, None)
} else {
let def = self.definitions.get(&name);
(def.as_ref().map(|x| format!("{}", x)),
(def.as_ref().map(|x| x.to_string()),
def,
self.lookup(&name).map(|x| x.to_parts(self)))
};
Ok(QueryReply::Def(DefReply {
canon_name: canon,
def: def,
def,
def_expr: def_expr.as_ref().map(|x| ExprReply::from(*x)),
value: res,
doc: self.docs.get(&name).cloned(),
@ -688,7 +666,7 @@ impl Context {
"<{}> to {} is not defined",
top.show(self),
match digits {
Digits::Default => panic!(),
Digits::Default => unreachable!(),
Digits::FullInt => "digits".to_owned(),
Digits::Digits(n) => format!("{} digits", n)
}
@ -707,10 +685,10 @@ impl Context {
}))
},
Query::Convert(ref top, Conversion::Expr(ref bottom), base, digits) => match
(self.eval(top), self.eval(bottom), self.eval_unit_name(bottom))
(try!(self.eval(top)), try!(self.eval(bottom)), try!(self.eval_unit_name(bottom)))
{
(Ok(Value::Number(top)), Ok(Value::Number(bottom)),
Ok((bottom_name, bottom_const))) => {
(Value::Number(top), Value::Number(bottom),
(bottom_name, bottom_const)) => {
if top.unit == bottom.unit {
let raw = match &top / &bottom {
Some(raw) => raw,
@ -729,8 +707,8 @@ impl Context {
&top, &bottom)))
}
},
(Ok(Value::Substance(sub)), Ok(Value::Number(bottom)),
Ok((bottom_name, bottom_const))) => {
(Value::Substance(sub), Value::Number(bottom),
(bottom_name, bottom_const)) => {
sub.get_in_unit(
bottom,
self,
@ -744,8 +722,8 @@ impl Context {
QueryReply::Substance
)
},
(Ok(Value::Number(top)), Ok(Value::Substance(mut sub)),
Ok((bottom_name, bottom_const))) => {
(Value::Number(top), Value::Substance(mut sub),
(bottom_name, bottom_const)) => {
let unit = sub.amount.clone();
sub.amount = top;
sub.get_in_unit(
@ -761,14 +739,11 @@ impl Context {
QueryReply::Substance
)
},
(Ok(x), Ok(y), Ok(_)) => Err(QueryError::Generic(format!(
(x, y, _) => Err(QueryError::Generic(format!(
"Operation is not defined: <{}> -> <{}>",
x.show(self),
y.show(self)
))),
(Err(e), _, _) => Err(e),
(_, Err(e), _) => Err(e),
(_, _, Err(e)) => Err(e),
},
Query::Convert(ref top, Conversion::List(ref list), None, Digits::Default) => {
let top = try!(self.eval(top));
@ -788,7 +763,7 @@ impl Context {
quantity: self.quantities.get(&top.unit).cloned(),
..Default::default()
},
list: list,
list,
})
})
},
@ -814,49 +789,32 @@ impl Context {
let top = top.with_timezone(&tz);
Ok(QueryReply::Date(DateReply::new(self, top)))
},
Query::Convert(ref top, ref which @ Conversion::DegC, None, digits) |
Query::Convert(ref top, ref which @ Conversion::DegF, None, digits) |
Query::Convert(ref top, ref which @ Conversion::DegN, None, digits) |
Query::Convert(ref top, ref which @ Conversion::DegRe, None, digits) |
Query::Convert(ref top, ref which @ Conversion::DegRo, None, digits) |
Query::Convert(ref top, ref which @ Conversion::DegDe, None, digits) => {
let top = try!(self.eval(top));
macro_rules! temperature {
($name:expr, $base:expr, $scale:expr) => {{
let top = match top {
Value::Number(ref num) => num,
_ => return Err(QueryError::Generic(format!(
"Cannot convert <{}> to °{}", top.show(self), $name)))
};
let bottom = self.lookup($scale)
.expect(&*format!("Unit {} missing", $scale));
if top.unit != bottom.unit {
Err(QueryError::Conformance(
self.conformance_err(&top, &bottom)))
} else {
let res = (top - &self.lookup($base)
.expect(&*format!("Constant {} missing", $base))).unwrap();
let res = (&res / &bottom).unwrap();
let mut name = BTreeMap::new();
name.insert(format!("°{}", $name), 1);
Ok(QueryReply::Conversion(self.show(
&res, &bottom,
name, Num::one(),
10,
digits
)))
}
}}
}
Query::Convert(ref top, Conversion::Degree(ref deg), None, digits) => {
let (name, base, scale) = deg.name_base_scale();
match *which {
Conversion::DegC => temperature!("C", "zerocelsius", "kelvin"),
Conversion::DegF => temperature!("F", "zerofahrenheit", "degrankine"),
Conversion::DegRe => temperature!("", "zerocelsius", "reaumur_absolute"),
Conversion::DegRo => temperature!("", "zeroromer", "romer_absolute"),
Conversion::DegDe => temperature!("De", "zerodelisle", "delisle_absolute"),
Conversion::DegN => temperature!("N", "zerocelsius", "newton_absolute"),
_ => panic!()
let top = try!(self.eval(top));
let top = match top {
Value::Number(ref num) => num,
_ => return Err(QueryError::Generic(format!(
"Cannot convert <{}> to °{}", top.show(self), name)))
};
let bottom = self.lookup(scale)
.expect(&*format!("Unit {} missing", scale));
if top.unit != bottom.unit {
Err(QueryError::Conformance(
self.conformance_err(&top, &bottom)))
} else {
let res = (top - &self.lookup(base)
.expect(&*format!("Constant {} missing", base))).unwrap();
let res = (&res / &bottom).unwrap();
let mut name = BTreeMap::new();
name.insert(deg.to_string(), 1);
Ok(QueryReply::Conversion(self.show(
&res, &bottom,
name, Num::one(),
10,
digits
)))
}
},
Query::Convert(ref _expr, ref which, Some(base), _digits) => {
@ -892,12 +850,11 @@ impl Context {
let val = match val {
None => {
let val = try!(self.eval(expr));
let val = match val {
match val {
Value::Number(val) => val,
_ => return Err(QueryError::Generic(format!(
"Cannot find derivatives of <{}>", val.show(self))),)
};
val
}
},
Some(val) => val
};
@ -974,7 +931,7 @@ impl Context {
let mut cur_cat = None;
for (category, name) in out {
if category != cur_cat {
if cur.len() > 0 {
if !cur.is_empty() {
let cat_name = cur_cat.and_then(|x| self.category_names.get(x));
categories.push(UnitsInCategory {
category: cat_name.map(ToOwned::to_owned),
@ -985,7 +942,7 @@ impl Context {
}
cur.push(name.clone());
}
if cur.len() > 0 {
if !cur.is_empty() {
let cat_name = cur_cat.and_then(|x| self.category_names.get(x));
categories.push(UnitsInCategory {
category: cat_name.map(ToOwned::to_owned),

View file

@ -29,7 +29,7 @@ impl<'a> Iterator for TokenIterator<'a> {
type Item = Token;
fn next(&mut self) -> Option<Token> {
if self.0.peek() == None {
if self.0.peek().is_none() {
return None
}
let res = match self.0.next().unwrap() {

View file

@ -53,7 +53,7 @@ impl<'a> Iterator for TokenIterator<'a> {
type Item = Token;
fn next(&mut self) -> Option<Token> {
if self.0.peek() == None {
if self.0.peek().is_none() {
return Some(Token::Eof)
}
let res = match self.0.next().unwrap() {
@ -90,17 +90,16 @@ impl<'a> Iterator for TokenIterator<'a> {
'\\' => match self.0.next() {
Some('\r') => match self.0.next() {
Some('\n') => self.next().unwrap(),
_ => Token::Error(format!("Expected LF or CRLF line endings"))
_ => Token::Error("Expected LF or CRLF line endings".to_string())
},
Some('\n') => self.next().unwrap(),
Some(x) => Token::Error(format!("Invalid escape: \\{}", x)),
None => Token::Error(format!("Unexpected EOF")),
None => Token::Error("Unexpected EOF".to_string()),
},
'#' => {
while let Some(c) = self.0.next() {
match c {
'\n' => break,
_ => ()
if c == '\n' {
break;
}
}
Token::Newline
@ -134,7 +133,7 @@ impl<'a> Iterator for TokenIterator<'a> {
_ => break
}
}
if buf.len() > 0 {
if !buf.is_empty() {
frac = Some(buf)
}
}
@ -159,7 +158,7 @@ impl<'a> Iterator for TokenIterator<'a> {
_ => break
}
}
if buf.len() > 0 {
if !buf.is_empty() {
exp = Some(buf)
}
}
@ -214,7 +213,7 @@ fn parse_term(iter: &mut Iter) -> Expr {
Token::Number(num, frac, exp) =>
::number::Number::from_parts(&*num, frac.as_ref().map(|x| &**x), exp.as_ref().map(|x| &**x))
.map(Expr::Const)
.unwrap_or_else(|e| Expr::Error(format!("{}", e))),
.unwrap_or_else(|e| Expr::Error(e.to_string())),
Token::Plus => Expr::Plus(Box::new(parse_term(iter))),
Token::Dash => Expr::Neg(Box::new(parse_term(iter))),
Token::Slash => Expr::Frac(
@ -364,14 +363,14 @@ pub fn parse(iter: &mut Iter) -> Defs {
if name.ends_with("-") {
name.pop();
map.push(DefEntry {
name: name,
name,
def: Rc::new(Def::Prefix(expr)),
doc: doc.take(),
category: category.clone(),
});
} else {
map.push(DefEntry {
name: name,
name,
def: Rc::new(Def::SPrefix(expr)),
doc: doc.take(),
category: category.clone(),
@ -409,7 +408,7 @@ pub fn parse(iter: &mut Iter) -> Defs {
iter.next();
let expr = parse_expr(iter);
map.push(DefEntry {
name: name,
name,
def: Rc::new(Def::Quantity(expr)),
doc: doc.take(),
category: category.clone(),
@ -455,10 +454,10 @@ pub fn parse(iter: &mut Iter) -> Defs {
let output = parse_div(iter);
props.push(Property {
output_name: name.clone(),
name: name,
name,
input: Expr::Const(Num::one()),
input_name: input_name,
output: output,
input_name,
output,
doc: prop_doc.take()
});
continue
@ -486,16 +485,16 @@ pub fn parse(iter: &mut Iter) -> Defs {
};
let input = parse_mul(iter);
props.push(Property {
name: name,
input: input,
input_name: input_name,
output: output,
output_name: output_name,
name,
input,
input_name,
output,
output_name,
doc: prop_doc.take()
});
}
map.push(DefEntry {
name: name,
name,
def: Rc::new(Def::Substance {
symbol: None,
properties: props
@ -507,7 +506,7 @@ pub fn parse(iter: &mut Iter) -> Defs {
// derived
let expr = parse_expr(iter);
map.push(DefEntry {
name: name,
name,
def: Rc::new(Def::Unit(expr)),
doc: doc.take(),
category: category.clone(),

View file

@ -53,6 +53,7 @@ extern crate serde;
#[cfg(feature = "nightly")]
#[macro_use]
extern crate serde_derive;
extern crate dirs;
pub mod text_query;
pub mod context;
@ -78,8 +79,6 @@ pub use number::Number;
pub use context::Context;
pub use value::Value;
use std::env;
use std::convert::From;
use std::path::PathBuf;
use std::collections::BTreeMap;
use std::fs::File;
@ -87,36 +86,10 @@ use std::time::Duration;
const DATA_FILE_URL: &'static str = "https://raw.githubusercontent.com/tiffany352/rink-rs/master/definitions.units";
#[cfg(all(target_family = "unix", not(target_os = "macos")))]
pub fn config_dir() -> Result<PathBuf, String> {
env::var("XDG_CONFIG_HOME")
.map(From::from)
.or_else(|_| {
env::home_dir()
.ok_or("Home dir not present".to_owned())
.map(From::from)
.map(|mut x: PathBuf| { x.push(".config/"); x })
})
}
#[cfg(target_os = "windows")]
pub fn config_dir() -> Result<PathBuf, String> {
env::var("APPDATA")
.map(From::from)
.or_else(|_| {
env::home_dir()
.ok_or("Home dir not present".to_owned())
.map(From::from)
.map(|mut x: PathBuf| { x.push("AppData\\Roaming"); x })
})
}
#[cfg(target_os = "macos")]
pub fn config_dir() -> Result<PathBuf, String> {
env::home_dir()
.ok_or("Home dir not present".to_owned())
.map(From::from)
.map(|mut x: PathBuf| { x.push("Library/Application Support"); x})
dirs::config_dir()
.map(|mut x: PathBuf| { x.push("rink"); x })
.ok_or_else(|| "Could not find config directory".into())
}
#[cfg(feature = "currency")]
@ -152,8 +125,7 @@ pub fn load() -> Result<Context, String> {
use std::io::Read;
use std::path::Path;
let mut path = try!(config_dir());
path.push("rink/");
let path = try!(config_dir());
let load = |name| {
File::open(name)
.and_then(|mut f| {
@ -165,8 +137,8 @@ pub fn load() -> Result<Context, String> {
let units =
load(Path::new("definitions.units").to_path_buf())
.or_else(|_| load(path.join("definitions.units")))
.or_else(|_| DEFAULT_FILE.map(|x| x.to_owned()).ok_or(format!(
"Did not exist in search path and binary is not compiled with `gpl` feature")))
.or_else(|_| DEFAULT_FILE.map(|x| x.to_owned()).ok_or(
"Did not exist in search path and binary is not compiled with `gpl` feature".to_string()))
.map_err(|e| format!(
"Failed to open definitions.units: {}\n\
If you installed with `gpl` disabled, then you need to obtain definitions.units \
@ -209,7 +181,7 @@ pub fn load() -> Result<Context, String> {
let mut currency_defs = currency_defs;
defs.append(&mut currency_defs.defs);
ast::Defs {
defs: defs
defs,
}
};
@ -312,11 +284,11 @@ pub fn one_line_sandbox(line: &str) -> String {
let res = match rx.try_recv() {
Ok(res) => res,
Err(_) if unsafe { libc::WIFSIGNALED(status) && libc::WTERMSIG(status) == libc::SIGXCPU } =>
format!("Calculation timed out"),
"Calculation timed out".to_string(),
// :(
Err(ref e) if format!("{}", e) == "IoError: Connection reset by peer (os error 104)" =>
format!("Calculation ran out of memory"),
Err(e) => format!("{}", e)
Err(ref e) if e.to_string() == "IoError: Connection reset by peer (os error 104)" =>
"Calculation ran out of memory".to_string(),
Err(e) => e.to_string()
};
println!("Calculation result: {:?}", res);
@ -347,7 +319,7 @@ fn btree_merge<K: ::std::cmp::Ord+Clone, V:Clone, F:Fn(&V, &V) -> Option<V>>(
res.insert(akey.clone(), aval.clone());
a.next();
},
(Some(_), Some(_)) => panic!(),
(Some(_), Some(_)) => unreachable!(),
(None, Some((bkey, bval))) => {
res.insert(bkey.clone(), bval.clone());
b.next();
@ -369,10 +341,9 @@ fn cached(file: &str, url: &str, expiration: Duration) -> Result<File, String> {
use std::fs;
fn ts<T:Display>(x: T) -> String {
format!("{}", x)
x.to_string()
}
let mut path = try!(config_dir());
path.push("rink/");
let mut tmppath = path.clone();
path.push(file);
let tmpfile = format!("{}.part", file);
@ -386,14 +357,14 @@ fn cached(file: &str, url: &str, expiration: Duration) -> Result<File, String> {
let now = SystemTime::now();
let elapsed = try!(now.duration_since(mtime).map_err(ts));
if elapsed > expiration {
Err(format!("File is out of date"))
Err("File is out of date".to_string())
} else {
Ok(f)
}
})
.or_else(|_| {
try!(fs::create_dir_all(path.parent().unwrap()).map_err(|x| format!("{}", x)));
let mut f = try!(File::create(tmppath.clone()).map_err(|x| format!("{}", x)));
try!(fs::create_dir_all(path.parent().unwrap()).map_err(|x| x.to_string()));
let mut f = try!(File::create(tmppath.clone()).map_err(|x| x.to_string()));
reqwest::get(url)
.map_err(|err| format!("Request failed: {}", err))?
@ -402,7 +373,7 @@ fn cached(file: &str, url: &str, expiration: Duration) -> Result<File, String> {
try!(f.sync_all().map_err(|x| format!("{}", x)));
drop(f);
try!(fs::rename(tmppath.clone(), path.clone())
.map_err(|x| format!("{}", x)));
File::open(path).map_err(|x| format!("{}", x))
.map_err(|x| x.to_string()));
File::open(path).map_err(|x| x.to_string())
})
}

View file

@ -11,155 +11,142 @@ use std::rc::Rc;
use value::Value;
use Context;
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone)]
enum Name {
Unit(Rc<String>),
Prefix(Rc<String>),
Quantity(Rc<String>),
Category(Rc<String>),
}
impl Name {
fn name(&self) -> String {
match &self {
Name::Unit(ref name) |
Name::Prefix(ref name) |
Name::Quantity(ref name) |
Name::Category(ref name) => (**name).clone(),
}
}
}
struct Resolver {
interned: BTreeSet<Rc<String>>,
input: BTreeMap<Name, Rc<Def>>,
sorted: Vec<Name>,
unmarked: BTreeSet<Name>,
temp_marks: BTreeSet<Name>,
docs: BTreeMap<Name, String>,
categories: BTreeMap<Name, String>,
}
impl Resolver {
fn intern(&mut self, name: &String) -> Rc<String> {
if let Some(v) = self.interned.get(name).cloned() {
v
} else {
let v = Rc::new(name.to_owned());
self.interned.insert(v.clone());
v
}
}
fn lookup(&mut self, name: &Rc<String>) -> bool {
fn inner(ctx: &mut Resolver, name: &Rc<String>) -> bool {
[Name::Unit, Name::Prefix, Name::Quantity].iter().any(|f| {
let unit = f(name.clone());
ctx.input.contains_key(&unit) && {
ctx.visit(&unit);
true
}
})
}
let mut outer = |name: &Rc<String>| -> bool {
if inner(self, name) {
return true;
}
let mut found = vec![];
for pre in self.input.keys() {
if let Name::Prefix(ref pre) = *pre {
if (*name).starts_with(&**pre) {
found.push(pre.clone());
}
}
}
found.into_iter().any(|pre| {
inner(self, &Rc::new(name[pre.len()..].to_owned())) && {
let unit = Name::Prefix(pre);
self.visit(&unit);
true
}
})
};
outer(name) || name.ends_with('s') && {
let name = &Rc::new(name[0..name.len()-1].to_owned());
outer(name)
}
}
fn eval(&mut self, expr: &Expr) {
match *expr {
Expr::Unit(ref name) => {
let name = self.intern(name);
self.lookup(&name);
},
Expr::Frac(ref left, ref right) |
Expr::Pow(ref left, ref right) |
Expr::Add(ref left, ref right) |
Expr::Sub(ref left, ref right) => {
self.eval(left);
self.eval(right);
},
Expr::Neg(ref expr) | Expr::Plus(ref expr) |
Expr::Suffix(_, ref expr) | Expr::Of(_, ref expr) =>
self.eval(expr),
Expr::Mul(ref exprs) | Expr::Call(_, ref exprs) => for expr in exprs {
self.eval(expr);
},
_ => ()
}
}
fn visit(&mut self, name: &Name) {
if self.temp_marks.get(name).is_some() {
println!("Unit {:?} has a dependency cycle", name);
return;
}
if self.unmarked.get(name).is_some() {
self.temp_marks.insert(name.clone());
if let Some(v) = self.input.get(name).cloned() {
match *v {
Def::Prefix(ref e) | Def::SPrefix(ref e) | Def::Unit(ref e) |
Def::Quantity(ref e) =>
self.eval(e),
Def::Canonicalization(ref e) => {
self.lookup(&Rc::new(e.clone()));
},
Def::Substance { ref properties, .. } => {
for prop in properties {
self.eval(&prop.input);
self.eval(&prop.output);
}
},
_ => (),
}
}
self.unmarked.remove(name);
self.temp_marks.remove(name);
self.sorted.push(name.clone());
}
}
}
impl Context {
/// Takes a parsed definitions.units from
/// `gnu_units::parse()`. Prints if there are errors in the file.
pub fn load(&mut self, defs: Defs) {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone)]
enum Name {
Unit(Rc<String>),
Prefix(Rc<String>),
Quantity(Rc<String>),
Category(Rc<String>),
}
struct Resolver {
interned: BTreeSet<Rc<String>>,
input: BTreeMap<Name, Rc<Def>>,
sorted: Vec<Name>,
unmarked: BTreeSet<Name>,
temp_marks: BTreeSet<Name>,
docs: BTreeMap<Name, String>,
categories: BTreeMap<Name, String>,
}
impl Resolver {
fn intern(&mut self, name: &String) -> Rc<String> {
if let Some(v) = self.interned.get(name).cloned() {
v
} else {
let v = Rc::new(name.to_owned());
self.interned.insert(v.clone());
v
}
}
fn lookup(&mut self, name: &Rc<String>) -> Option<()> {
fn inner(ctx: &mut Resolver, name: &Rc<String>) -> Option<()> {
let unit = Name::Unit(name.clone());
if ctx.input.get(&unit).is_some() {
ctx.visit(&unit);
return Some(())
}
let unit = Name::Prefix(name.clone());
if ctx.input.get(&unit).is_some() {
ctx.visit(&unit);
return Some(())
}
let unit = Name::Quantity(name.clone());
if ctx.input.get(&unit).is_some() {
ctx.visit(&unit);
return Some(())
}
None
}
if let Some(()) = inner(self, name) {
return Some(())
}
let mut found = vec![];
for (pre, _) in &self.input {
if let &Name::Prefix(ref pre) = pre {
if (*name).starts_with(&**pre) {
found.push(pre.clone());
}
}
}
for pre in found {
if let Some(()) = inner(self, &Rc::new(name[pre.len()..].to_owned())) {
let unit = Name::Prefix(pre);
self.visit(&unit);
return Some(())
}
}
if name.ends_with("s") {
let name = &Rc::new(name[0..name.len()-1].to_owned());
if let Some(()) = inner(self, name) {
return Some(())
}
let mut found = vec![];
for (pre, _) in &self.input {
if let &Name::Prefix(ref pre) = pre {
if (*name).starts_with(&**pre) {
found.push(pre.clone());
}
}
}
for pre in found {
if let Some(()) = inner(self, &Rc::new(name[pre.len()..].to_owned())) {
let unit = Name::Prefix(pre);
self.visit(&unit);
return Some(())
}
}
}
None
}
fn eval(&mut self, expr: &Expr) {
match *expr {
Expr::Unit(ref name) => {
let name = self.intern(name);
let _ = self.lookup(&name);
},
Expr::Frac(ref left, ref right) |
Expr::Pow(ref left, ref right) |
Expr::Add(ref left, ref right) |
Expr::Sub(ref left, ref right) => {
self.eval(left);
self.eval(right);
},
Expr::Neg(ref expr) | Expr::Plus(ref expr) |
Expr::Suffix(_, ref expr) | Expr::Of(_, ref expr) =>
self.eval(expr),
Expr::Mul(ref exprs) | Expr::Call(_, ref exprs) => for expr in exprs {
self.eval(expr);
},
_ => ()
}
}
fn visit(&mut self, name: &Name) {
if self.temp_marks.get(name).is_some() {
println!("Unit {:?} has a dependency cycle", name);
return;
}
if self.unmarked.get(name).is_some() {
self.temp_marks.insert(name.clone());
if let Some(v) = self.input.get(name).cloned() {
match *v {
Def::Prefix(ref e) | Def::SPrefix(ref e) | Def::Unit(ref e) |
Def::Quantity(ref e) =>
self.eval(e),
Def::Canonicalization(ref e) => {
self.lookup(&Rc::new(e.clone()));
},
Def::Substance { ref properties, .. } => {
for prop in properties {
self.eval(&prop.input);
self.eval(&prop.output);
}
},
_ => (),
}
}
self.unmarked.remove(name);
self.temp_marks.remove(name);
self.sorted.push(name.clone());
}
}
}
let mut resolver = Resolver {
interned: BTreeSet::new(),
input: BTreeMap::new(),
@ -227,12 +214,7 @@ impl Context {
reverse.insert("katal");
for (name, def) in udefs {
let name = match name {
Name::Unit(name) => (*name).clone(),
Name::Prefix(name) => (*name).clone(),
Name::Quantity(name) => (*name).clone(),
Name::Category(name) => (*name).clone(),
};
let name = name.name();
match *def {
Def::Dimension => {
self.dimensions.insert(Dim::new(&*name));
@ -256,7 +238,7 @@ impl Context {
self.units.insert(name.clone(), v);
},
Ok(Value::Substance(sub)) => {
let sub = if sub.properties.name.contains("+") {
let sub = if sub.properties.name.contains('+') {
sub.rename(name.clone())
} else {
sub
@ -351,9 +333,9 @@ impl Context {
);
}
Ok((prop.name.clone(), Property {
input: input,
input,
input_name: prop.input_name.clone(),
output: output,
output,
output_name: prop.output_name.clone(),
doc: prop.doc.clone(),
}))
@ -368,7 +350,7 @@ impl Context {
properties: res,
}),
});
if let &Some(ref symbol) = symbol {
if let Some(ref symbol) = symbol {
self.substance_symbols.insert(symbol.clone(), name.clone());
}
},
@ -383,24 +365,14 @@ impl Context {
}
for (name, val) in resolver.docs {
let name = match name {
Name::Unit(name) => (*name).clone(),
Name::Prefix(name) => (*name).clone(),
Name::Quantity(name) => (*name).clone(),
Name::Category(name) => (*name).clone(),
};
let name = name.name();
if self.docs.insert(name.clone(), val).is_some() {
println!("Doc conflict for {}", name);
}
}
for (name, val) in resolver.categories {
let name = match name {
Name::Unit(name) => (*name).clone(),
Name::Prefix(name) => (*name).clone(),
Name::Quantity(name) => (*name).clone(),
Name::Category(name) => (*name).clone(),
};
let name = name.name();
if self.categories.insert(name.clone(), val).is_some() {
println!("Category conflict for {}", name);
}

View file

@ -91,10 +91,11 @@ impl Num {
t = m[1][0] * ai + m[1][1];
m[1][1] = m[1][0];
m[1][0] = t;
if x == ai as f64 {
let tmp = x - ai as f64;
if tmp == 0.0 {
break; // division by zero
}
x = 1.0/(x - ai as f64);
x = tmp.recip();
if x as i64 > i64::max_value() / 2 {
break; // representation failure
}

View file

@ -156,7 +156,7 @@ pub fn to_string(rational: &Num, base: u8, digits: Digits) -> (bool, String) {
/// Several stringified properties of a number which are useful for
/// displaying it to a user.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "nightly", derive(Serialize, Deserialize))]
pub struct NumberParts {
/// Present if the number can be concisely represented exactly.
@ -179,21 +179,6 @@ pub struct NumberParts {
pub dimensions: Option<String>,
}
impl Default for NumberParts {
fn default() -> Self {
NumberParts {
exact_value: None,
approx_value: None,
factor: None,
divfactor: None,
raw_unit: None,
unit: None,
quantity: None,
dimensions: None,
}
}
}
impl NumberParts {
/// A DSL for formatting numbers.
///
@ -235,34 +220,32 @@ impl NumberParts {
(None, None) => continue,
},
'u' => if let Some(unit) = self.raw_unit.as_ref() {
if unit.len() == 0 { continue }
if unit.is_empty() { continue }
let mut frac = vec![];
let mut toks = vec![];
if let Some(f) = self.factor.as_ref() {
toks.push(format!("*"));
toks.push(format!("{}", f));
toks.push("*".to_string());
toks.push(f.to_string());
}
for (dim, &exp) in unit {
if exp < 0 {
frac.push((dim, exp));
} else if exp == 1 {
toks.push(dim.to_string())
} else {
if exp == 1 {
toks.push(format!("{}", dim))
} else {
toks.push(format!("{}^{}", dim, exp))
}
toks.push(format!("{}^{}", dim, exp))
}
}
if frac.len() > 0 {
toks.push(format!("/"));
if !frac.is_empty() {
toks.push("/".to_string());
if let Some(d) = self.divfactor.as_ref() {
toks.push(format!("{}", d));
toks.push(d.to_string());
}
for (dim, exp) in frac {
let exp = -exp;
if exp == 1 {
toks.push(format!("{}", dim))
toks.push(dim.to_string())
} else {
toks.push(format!("{}^{}", dim, exp))
}
@ -270,7 +253,7 @@ impl NumberParts {
}
write!(out, "{}", toks.join(" ")).unwrap();
} else if let Some(unit) = self.unit.as_ref() {
if unit.len() == 0 { continue }
if unit.is_empty() { continue }
if let Some(f) = self.factor.as_ref() {
write!(out, "* {} ", f).unwrap();
}
@ -279,7 +262,7 @@ impl NumberParts {
}
write!(out, "{}", unit).unwrap();
} else if let Some(dim) = self.dimensions.as_ref() {
if dim.len() == 0 { continue }
if dim.is_empty() { continue }
if let Some(f) = self.factor.as_ref() {
write!(out, "* {} ", f).unwrap();
}
@ -301,19 +284,19 @@ impl NumberParts {
continue
},
'd' => if let Some(dim) = self.dimensions.as_ref() {
if self.unit.is_none() || dim.len() == 0 { continue }
if self.unit.is_none() || dim.is_empty() { continue }
write!(out, "{}", dim).unwrap();
} else {
continue
},
'D' => if let Some(dim) = self.dimensions.as_ref() {
if dim.len() == 0 { continue }
if dim.is_empty() { continue }
write!(out, "{}", dim).unwrap();
} else {
continue
},
'p' => match (self.quantity.as_ref(), self.dimensions.as_ref().and_then(|x| {
if self.unit.is_some() && x.len() > 0 {
if self.unit.is_some() && !x.is_empty() {
Some(x)
} else {
None
@ -432,7 +415,7 @@ impl Number {
.collect::<Unit>();
Number {
value: pow(&self.value, exp),
unit: unit
unit,
}
}
@ -440,13 +423,12 @@ impl Number {
/// powers divisible by n.
pub fn root(&self, exp: i32) -> Result<Number, String> {
if self.value < Num::zero() {
return Err(format!("Complex numbers are not implemented"))
return Err("Complex numbers are not implemented".to_string())
}
let mut res = Unit::new();
for (dim, &power) in &self.unit {
if power % exp as i64 != 0 {
return Err(format!(
"Result must have integer dimensions"))
return Err("Result must have integer dimensions".to_string())
} else {
res.insert(dim.clone(), power / exp as i64);
}
@ -459,10 +441,10 @@ impl Number {
pub fn pow(&self, exp: &Number) -> Result<Number, String> {
if !exp.dimless() {
return Err(format!("Exponent must be dimensionless"))
return Err("Exponent must be dimensionless".to_string())
}
if exp.value.abs() >= Num::from(1 << 31) {
return Err(format!("Exponent is too large"))
return Err("Exponent is too large".to_string())
}
let (num, den) = exp.value.to_rational();
let one = Int::one();
@ -472,19 +454,16 @@ impl Number {
} else if num == one {
let exp: Option<i64> = (&den).into();
self.root(exp.unwrap() as i32)
} else if !self.dimless() {
Err("Exponentiation must result in integer dimensions".to_string())
} else {
if !self.dimless() {
Err(format!(
"Exponentiation must result in integer dimensions"))
} else {
let exp = exp.value.to_f64();
Ok(Number {
value: Num::Float(
self.value.to_f64().powf(exp)
),
unit: self.unit.clone()
})
}
let exp = exp.value.to_f64();
Ok(Number {
value: Num::Float(
self.value.to_f64().powf(exp)
),
unit: self.unit.clone()
})
}
}
@ -496,8 +475,8 @@ impl Number {
match to_string(&self.value, base, digits) {
(true, v) => (Some(v), None),
(false, v) => if {den > Mpz::from(1_000) ||
num > Mpz::from(1_000_000u64)} {
(false, v) => if den > Mpz::from(1_000) ||
num > Mpz::from(1_000_000u64) {
(None, Some(v))
} else {
(Some(format!("{}/{}", num, den)), Some(v))
@ -543,13 +522,13 @@ impl Number {
continue;
}
let abs = val.abs();
if { abs >= pow(&v.value, (*orig.1) as i32) &&
abs < pow(&(&v.value * &Num::from(1000)),
(*orig.1) as i32) } {
if abs >= pow(&v.value, (*orig.1) as i32) &&
abs < pow(&(&v.value * &Num::from(1000)),
(*orig.1) as i32) {
let res = &val / &pow(&v.value, (*orig.1) as i32);
// tonne special case
let unit = if &**(orig.0).0 == "gram" && p == "mega" {
format!("tonne")
"tonne".to_string()
} else {
format!("{}{}", p, orig.0)
};
@ -570,7 +549,7 @@ impl Number {
} else {
Number {
value: self.value.clone(),
unit: unit,
unit,
}
}
}
@ -598,7 +577,7 @@ impl Number {
approx_value: approx,
unit: if value.unit != self.unit { Some(Number::unit_to_string(&value.unit)) } else { None },
raw_unit: if value.unit != self.unit { Some(value.unit) } else { None },
quantity: quantity,
quantity,
dimensions: Some(Number::unit_to_string(&self.unit)),
..Default::default()
}
@ -620,7 +599,7 @@ impl Number {
}
}
}
if frac.len() > 0 {
if !frac.is_empty() {
write!(out, " /").unwrap();
for (dim, exp) in frac {
let exp = -exp;
@ -631,7 +610,7 @@ impl Number {
}
}
if out.len() > 0 {
if !out.is_empty() {
out.remove(0);
}
String::from_utf8(out).unwrap()
@ -646,11 +625,11 @@ impl Number {
}
pub fn complexity_score(&self) -> i64 {
self.unit.iter().map(|(_, p)| 1 + p.abs()).fold(0, |a,x| a+x)
self.unit.iter().map(|(_, p)| 1 + p.abs()).sum()
}
pub fn dimless(&self) -> bool {
self.unit.len() == 0
self.unit.is_empty()
}
}
@ -664,7 +643,7 @@ impl fmt::Debug for Number {
impl Show for Number {
fn show(&self, context: &Context) -> String {
let parts = self.to_parts(context);
format!("{}", parts)
parts.to_string()
}
}

View file

@ -189,7 +189,7 @@ impl ExprReply {
Expr::Quote(ref name) => literal!(format!("'{}'", name)),
Expr::Const(ref num) => {
let (_exact, val) = ::number::to_string(num, 10, Digits::Default);
literal!(format!("{}", val))
literal!(val.to_string())
},
Expr::Date(ref _date) => literal!("NYI: date expr to expr parts"),
Expr::Mul(ref exprs) => {
@ -203,8 +203,8 @@ impl ExprReply {
literal!(")");
}
},
Expr::Call(ref name, ref args) => {
literal!(format!("{}(", name));
Expr::Call(ref func, ref args) => {
literal!(format!("{}(", func.name()));
if let Some(first) = args.first() {
recurse(first, parts, Prec::Equals);
}
@ -232,7 +232,7 @@ impl ExprReply {
literal!("(");
}
recurse(expr, parts, Prec::Mul);
literal!(format!("{}", op));
literal!(op.to_string());
if prec < Prec::Mul {
literal!(")");
}
@ -325,7 +325,7 @@ impl DateReply {
where Tz: TimeZone, Tz::Offset: Display {
use chrono::{Datelike, Timelike};
DateReply {
string: format!("{}", date),
string: date.to_string(),
year: date.year(),
month: date.month() as i32,
day: date.day() as i32,
@ -392,7 +392,7 @@ impl Display for FactorizeReply {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
write!(fmt, "Factorizations: {}", self.factorizations.iter().map(|x| {
x.iter().map(|(u, p)| {
if *p == 1 { format!("{}", u) }
if *p == 1 { u.to_string() }
else { format!("{}^{}", u, p) }
}).collect::<Vec<_>>().join(" ")
}).collect::<Vec<_>>().join("; "))
@ -416,16 +416,10 @@ impl Display for DurationReply {
let res = [&self.years, &self.months, &self.weeks, &self.days,
&self.hours, &self.minutes]
.iter()
.filter_map(|x| {
if x.exact_value.as_ref().map(|x| &**x) == Some("0") {
None
} else {
Some(x)
}
})
.filter(|x| x.exact_value.as_ref().map(|x| &**x) != Some("0"))
.chain(once(&&self.seconds))
.map(|x| {
format!("{}", x)
x.to_string()
})
.collect::<Vec<_>>()
.join(", ");
@ -442,7 +436,7 @@ impl Display for UnitListReply {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
try!(write!(fmt, "{}",
self.list.iter()
.map(|x| format!("{}", x))
.map(|x| x.to_string())
.collect::<Vec<_>>()
.join(", ")));
if let Some(q) = self.rest.quantity.as_ref() {

View file

@ -59,13 +59,13 @@ pub fn search<'a>(ctx: &'a Context, query: &str, num_results: usize) -> Vec<&'a
for k in &ctx.dimensions {
try(&**k.0);
}
for (k, _v) in &ctx.units {
for k in ctx.units.keys() {
try(&**k);
}
for (_u, k) in &ctx.quantities {
for k in ctx.quantities.values() {
try(&**k);
}
for (k, _sub) in &ctx.substances {
for k in ctx.substances.keys() {
try(&**k);
}
}

View file

@ -13,6 +13,18 @@ use std::iter::once;
use std::rc::Rc;
use ast::Digits;
macro_rules! try_div {
($x:expr, $y:expr, $context:expr) => {
(&$x / &$y).ok_or_else(|| {
format!(
"Division by zero: <{}> / <{}>",
$x.show($context),
$y.show($context)
)
})?
};
}
#[derive(Debug, Clone)]
pub struct Property {
pub input: Number,
@ -44,7 +56,7 @@ impl Substance {
Substance {
amount: self.amount,
properties: Rc::new(Properties {
name: name,
name,
properties: self.properties.properties.clone()
})
}
@ -61,7 +73,7 @@ impl Substance {
.expect("Non-zero property")
})
} else {
for (_name, prop) in &self.properties.properties {
for prop in self.properties.properties.values() {
if name == prop.output_name {
let input = try!(
(&prop.input / &self.amount).ok_or_else(
@ -117,12 +129,7 @@ impl Substance {
properties: try!(self.properties.properties.iter().map(|(k, v)| {
let (input, output) = if v.input.dimless() {
let res = (&v.output * &self.amount).unwrap();
(None, try!((&res / &v.input)
.ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
res.show(context),
v.input.show(context)
))))
(None, try_div!(res, v.input, context))
} else {
(Some(v.input.clone()), v.output.clone())
};
@ -140,26 +147,14 @@ impl Substance {
(input, output)
};
let output_show = context.show(
&try!((
&output / &unit
).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
output.show(context),
unit.show(context)
))),
&try_div!(output, unit, context),
&unit,
bottom_name.clone(),
bottom_const.clone(),
base,
digits
).value;
let output = try!((
&output / &unit
).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
output.show(context),
unit.show(context)
)));
let output = try_div!(output, unit, context);
let input: Option<Number> = input;
Ok(Some(PropertyReply {
name: k.clone(),
@ -168,14 +163,8 @@ impl Substance {
let mut output_pretty = output.clone();
output_pretty.unit = bottom_name.iter()
.map(|(k,v)| (Dim::new(&k), *v as i64)).collect();
let mut res = try!((
&output_pretty / &input_pretty
).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
output.show(context),
input.show(context)
))).to_parts(context);
let value = (&unit / &input)
let mut res = try_div!(output_pretty, input_pretty, context).to_parts(context);
let value = (&unit / input)
.expect("Already known safe").to_parts(context);
res.quantity = value.quantity;
res
@ -190,80 +179,42 @@ impl Substance {
})
} else {
let func = |(_k, v): (&String, &Property)| {
let input = try!((&v.input / &self.amount).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
v.input.show(context),
self.amount.show(context)
)));
let output = try!((&v.output / &self.amount).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
v.output.show(context),
self.amount.show(context)
)));
let input = try_div!(v.input, self.amount, context);
let output = try_div!(v.output, self.amount, context);
let (name, input, output) = if input.dimless() {
if v.output.unit != unit.unit {
return Ok(None)
}
let div = try!(
(&v.output / &input).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
v.output.show(context),
input.show(context)
))
);
let div = try_div!(v.output, input, context);
(v.output_name.clone(), None, div)
} else if output.dimless() {
if v.input.unit != unit.unit {
return Ok(None)
}
let div = try!(
(&v.input / &output).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
v.input.show(context),
output.show(context)
))
);
let div = try_div!(v.input, output, context);
(v.input_name.clone(), None, div)
} else {
return Ok(None)
};
let output_show = context.show(
&try!((
&output / &unit
).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
output.show(context),
unit.show(context)
))),
&try_div!(output, unit, context),
&unit,
bottom_name.clone(),
bottom_const.clone(),
base,
digits
).value;
let output = try!((
&output / &unit
).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
output.show(context),
unit.show(context)
)));
let output = try_div!(output, unit, context);
let input: Option<Number> = input;
Ok(Some(PropertyReply {
name: name,
name,
value: if let Some(input) = input.as_ref() {
let input_pretty = input.prettify(context);
let mut output_pretty = output.clone();
output_pretty.unit = bottom_name.iter()
.map(|(k,v)| (Dim::new(&k), *v as i64)).collect();
let mut res = try!((
&output_pretty / &input_pretty
).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
output.show(context),
input.show(context)
))).to_parts(context);
let value = (&unit / &input)
let mut res = try_div!(output_pretty, input_pretty, context).to_parts(context);
let value = (&unit / input)
.expect("Already known safe").to_parts(context);
res.quantity = value.quantity;
res
@ -303,12 +254,7 @@ impl Substance {
properties: try!(self.properties.properties.iter().map(|(k, v)| {
let (input, output) = if v.input.dimless() {
let res = (&v.output * &self.amount).unwrap();
(None, try!((&res / &v.input)
.ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
res.show(context),
v.input.show(context)
))))
(None, try_div!(res, v.input, context))
} else {
(Some(v.input.clone()), v.output.clone())
};
@ -317,14 +263,8 @@ impl Substance {
value: if let Some(input) = input.as_ref() {
let input_pretty = input.prettify(context);
let output_pretty = output.prettify(context);
let mut res = try!((
&output_pretty / &input_pretty
).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
output.show(context),
input.show(context)
))).to_parts(context);
let value = (&output / &input)
let mut res = try_div!(output_pretty, input_pretty, context).to_parts(context);
let value = (&output / input)
.expect("Already known safe").to_parts(context);
res.quantity = value.quantity;
res
@ -337,51 +277,25 @@ impl Substance {
})
} else {
let func = |(_k, v): (&String, &Property)| {
let input = try!((&v.input / &self.amount).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
v.input.show(context),
self.amount.show(context)
)));
let output = try!((&v.output / &self.amount).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
v.output.show(context),
self.amount.show(context)
)));
let input = try_div!(v.input, self.amount, context);
let output = try_div!(v.output, self.amount, context);
let (name, input, output) = if input.dimless() {
let div = try!(
(&v.output / &input).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
v.output.show(context),
input.show(context)
))
);
let div = try_div!(v.output, input, context);
(v.output_name.clone(), None, div)
} else if output.dimless() {
let div = try!(
(&v.input / &output).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
v.input.show(context),
output.show(context)
))
);
let div = try_div!(v.input, output, context);
(v.input_name.clone(), None, div)
} else {
return Ok(None)
};
let input: Option<Number> = input;
Ok(Some(PropertyReply {
name: name,
name,
value: if let Some(input) = input.as_ref() {
let input_pretty = input.prettify(context);
let output_pretty = output.prettify(context);
let mut res = try!((
&output_pretty / &input_pretty
).ok_or_else(|| format!(
"Division by zero: <{}> / <{}>",
output.show(context),
input.show(context)
))).to_parts(context);
let value = (&output / &input)
let mut res = try_div!(output_pretty, input_pretty, context).to_parts(context);
let value = (&output / input)
.expect("Already known safe").to_parts(context);
res.quantity = value.quantity;
res
@ -486,8 +400,8 @@ impl<'a, 'b> Add<&'b Substance> for &'a Substance {
}).collect()
})
};
if res.properties.properties.len() == 0 {
Err(format!("No shared properties"))
if res.properties.properties.is_empty() {
Err("No shared properties".to_string())
} else {
Ok(res)
}

View file

@ -35,12 +35,7 @@ pub enum Token {
Colon,
Date(Vec<DateToken>),
Comma,
DegC,
DegF,
DegRe,
DegRo,
DegDe,
DegN,
Degree(Degree),
Percent,
Error(String),
}
@ -69,13 +64,8 @@ fn describe(token: &Token) -> String {
Token::Colon => "`:`".to_owned(),
Token::Date(_) => "date literal".to_owned(),
Token::Comma => "`,`".to_owned(),
Token::DegC => "`°C`".to_owned(),
Token::DegF => "`°F`".to_owned(),
Token::DegRe => "`°Ré`".to_owned(),
Token::DegRo => "`°Rø`".to_owned(),
Token::DegDe => "`°De`".to_owned(),
Token::DegN => "`°N`".to_owned(),
Token::Percent => "%".to_owned(),
Token::Degree(ref deg) => format!("`{}`", deg),
Token::Error(ref e) => format!("<{}>", e)
}
}
@ -93,7 +83,7 @@ impl<'a> Iterator for TokenIterator<'a> {
type Item = Token;
fn next(&mut self) -> Option<Token> {
if self.0.peek() == None {
if self.0.peek().is_none() {
return Some(Token::Eof)
}
let res = match self.0.next().unwrap() {
@ -144,7 +134,7 @@ impl<'a> Iterator for TokenIterator<'a> {
}
}
if self.0.peek() == None {
return Some(Token::Error(format!("Expected `*/`, got EOF")))
return Some(Token::Error("Expected `*/`, got EOF".to_string()))
}
}
},
@ -165,7 +155,7 @@ impl<'a> Iterator for TokenIterator<'a> {
_ => break
}
}
if hex.len() == 0 {
if hex.is_empty() {
return Some(Token::Error(
"Malformed hexadecimal literal: No digits after 0x".to_owned()))
}
@ -186,7 +176,7 @@ impl<'a> Iterator for TokenIterator<'a> {
_ => break
}
}
if oct.len() == 0 {
if oct.is_empty() {
return Some(Token::Error(
"Malformed octal literal: No digits after 0o".to_owned()))
}
@ -207,7 +197,7 @@ impl<'a> Iterator for TokenIterator<'a> {
_ => break
}
}
if bin.len() == 0 {
if bin.is_empty() {
return Some(Token::Error(
"Malformed binary literal: No digits after 0b".to_owned()))
}
@ -248,7 +238,7 @@ impl<'a> Iterator for TokenIterator<'a> {
_ => break
}
}
if buf.len() == 0 {
if buf.is_empty() {
return Some(Token::Error(
"Malformed number literal: No digits after decimal point".to_owned()))
}
@ -281,7 +271,7 @@ impl<'a> Iterator for TokenIterator<'a> {
_ => break
}
}
if buf.len() == 0 {
if buf.is_empty() {
return Some(Token::Error(
"Malformed number literal: No digits after exponent".to_owned()))
}
@ -308,19 +298,19 @@ impl<'a> Iterator for TokenIterator<'a> {
Token::Error(format!("Invalid unicode scalar: {:x}", v))
}
},
_ => Token::Error(format!("Unexpected \\"))
_ => Token::Error("Unexpected \\".to_string())
},
'\'' => {
let mut buf = String::new();
loop {
match self.0.next() {
None | Some('\n') => return Some(Token::Error(format!("Unexpected newline or EOF"))),
None | Some('\n') => return Some(Token::Error("Unexpected newline or EOF".to_string())),
Some('\\') => match self.0.next() {
Some('\'') => buf.push('\''),
Some('n') => buf.push('\n'),
Some('t') => buf.push('\t'),
Some(c) => return Some(Token::Error(format!("Invalid escape sequence \\{}", c))),
None => return Some(Token::Error(format!("Unexpected EOF"))),
None => return Some(Token::Error("Unexpected EOF".to_string())),
},
Some('\'') => break,
Some(c) => buf.push(c),
@ -421,12 +411,12 @@ impl<'a> Iterator for TokenIterator<'a> {
}
}
match &*buf {
"degC" | "°C" | "celsius" | "" => Token::DegC,
"degF" | "°F" | "fahrenheit" | "" => Token::DegF,
"degRé" | "°Ré" | "degRe" | "°Re" | "réaumur" | "reaumur" => Token::DegRe,
"degRø" | "°Rø" | "degRo" | "°Ro" | "rømer" | "romer" => Token::DegRo,
"degDe" | "°De" | "delisle" => Token::DegDe,
"degN" | "°N" | "degnewton" => Token::DegN,
"degC" | "°C" | "celsius" | "" => Token::Degree(Degree::Celsius),
"degF" | "°F" | "fahrenheit" | "" => Token::Degree(Degree::Fahrenheit),
"degRé" | "°Ré" | "degRe" | "°Re" | "réaumur" | "reaumur" => Token::Degree(Degree::Reaumur),
"degRø" | "°Rø" | "degRo" | "°Ro" | "rømer" | "romer" => Token::Degree(Degree::Romer),
"degDe" | "°De" | "delisle" => Token::Degree(Degree::Delisle),
"degN" | "°N" | "degnewton" => Token::Degree(Degree::Newton),
"per" => Token::Slash,
"to" | "in" => Token::DashArrow,
_ => Token::Ident(buf)
@ -439,33 +429,7 @@ impl<'a> Iterator for TokenIterator<'a> {
pub type Iter<'a> = Peekable<TokenIterator<'a>>;
fn is_func(name: &str) -> bool {
match name {
"sqrt" => true,
"exp" => true,
"ln" => true,
"log" => true,
"log2" => true,
"log10" => true,
"hypot" => true,
"sin" => true,
"cos" => true,
"tan" => true,
"asin" => true,
"acos" => true,
"atan" => true,
"atan2" => true,
"sinh" => true,
"cosh" => true,
"tanh" => true,
"asinh" => true,
"acosh" => true,
"atanh" => true,
_ => false
}
}
fn is_attr(name: &str) -> Option<&'static str> {
fn attr_from_name(name: &str) -> Option<&'static str> {
match name {
"int" | "international" => Some("int"),
"UKSJJ" => Some("UKSJJ"),
@ -484,74 +448,73 @@ fn is_attr(name: &str) -> Option<&'static str> {
}
}
fn parse_function(iter: &mut Iter, func: Function) -> Expr {
let args = match iter.peek().cloned().unwrap() {
Token::LPar => {
iter.next();
let mut args = vec![];
loop {
if let Some(&Token::RPar) = iter.peek() {
iter.next();
break;
}
args.push(parse_expr(iter));
match iter.peek().cloned().unwrap() {
Token::Comma => {
iter.next();
},
Token::RPar => (),
x => return Expr::Error(format!("Expected `,` or `)`, got {}",
describe(&x)))
}
}
args
},
_ => vec![parse_pow(iter)],
};
Expr::Call(func, args)
}
fn parse_radix(num: &str, base: u8, description: &str) -> Expr {
Mpz::from_str_radix(&*num, base)
.map(|x| Mpq::ratio(&x, &Mpz::one()))
.map(Num::Mpq)
.map(Expr::Const)
.unwrap_or_else(|_| Expr::Error(format!("Failed to parse {}", description)))
}
fn parse_term(iter: &mut Iter) -> Expr {
match iter.next().unwrap() {
Token::Ident(ref name) if is_func(name) => {
match iter.peek().cloned().unwrap() {
Token::LPar => {
iter.next();
let mut args = vec![];
loop {
if let Some(&Token::RPar) = iter.peek() {
iter.next();
break;
}
args.push(parse_expr(iter));
match iter.peek().cloned().unwrap() {
Token::Comma => {
iter.next();
},
Token::RPar => (),
x => return Expr::Error(format!("Expected `,` or `)`, got {}",
describe(&x)))
}
}
Expr::Call(name.clone(), args)
},
_ => Expr::Call(name.clone(), vec![parse_pow(iter)]),
Token::Ident(ref id) => {
if let Some(func) = Function::from_name(id) {
parse_function(iter, func)
} else if let Some(attr) = attr_from_name(id) {
match iter.peek().cloned().unwrap() {
Token::Ident(ref name) => {
iter.next();
Expr::Unit(format!("{}{}", attr, name))
},
x => Expr::Error(format!("Attribute must be followed by ident, got {}",
describe(&x)))
}
} else {
match iter.peek().cloned().unwrap() {
Token::Ident(ref s) if s == "of" => {
iter.next();
Expr::Of(id.clone(), Box::new(parse_juxt(iter)))
},
_ => Expr::Unit(id.to_string())
}
}
},
Token::Ident(ref attr) if is_attr(attr).is_some() => {
match iter.peek().cloned().unwrap() {
Token::Ident(ref name) => {
let attr = is_attr(attr).unwrap();
iter.next();
Expr::Unit(format!("{}{}", attr, name))
},
x => Expr::Error(format!("Attribute must be followed by ident, got {}",
describe(&x)))
}
},
Token::Ident(name) => match iter.peek().cloned().unwrap() {
Token::Ident(ref s) if s == "of" => {
iter.next();
Expr::Of(name.clone(), Box::new(parse_juxt(iter)))
},
_ => Expr::Unit(name)
},
Token::Quote(name) => Expr::Quote(name),
Token::Decimal(num, frac, exp) =>
::number::Number::from_parts(&*num, frac.as_ref().map(|x| &**x), exp.as_ref().map(|x| &**x))
.map(Expr::Const)
.unwrap_or_else(|e| Expr::Error(format!("{}", e))),
Token::Hex(num) =>
Mpz::from_str_radix(&*num, 16)
.map(|x| Mpq::ratio(&x, &Mpz::one()))
.map(Num::Mpq)
.map(Expr::Const)
.unwrap_or_else(|_| Expr::Error(format!("Failed to parse hex"))),
Token::Oct(num) =>
Mpz::from_str_radix(&*num, 8)
.map(|x| Mpq::ratio(&x, &Mpz::one()))
.map(Num::Mpq)
.map(Expr::Const)
.unwrap_or_else(|_| Expr::Error(format!("Failed to parse octal"))),
Token::Bin(num) =>
Mpz::from_str_radix(&*num, 2)
.map(|x| Mpq::ratio(&x, &Mpz::one()))
.map(Num::Mpq)
.map(Expr::Const)
.unwrap_or_else(|_| Expr::Error(format!("Failed to parse binary"))),
.unwrap_or_else(|e| Expr::Error(e.to_string())),
Token::Hex(num) => parse_radix(&*num, 16, "hex"),
Token::Oct(num) => parse_radix(&*num, 8, "octal"),
Token::Bin(num) => parse_radix(&*num, 2, "binary"),
Token::Plus => Expr::Plus(Box::new(parse_term(iter))),
Token::Minus => Expr::Neg(Box::new(parse_term(iter))),
Token::LPar => {
@ -614,29 +577,9 @@ fn parse_juxt(iter: &mut Iter) -> Expr {
Token::Plus | Token::Minus | Token::DashArrow |
Token::RPar | Token::Newline |
Token::Comment(_) | Token::Eof => break,
Token::DegC => {
Token::Degree(deg) => {
iter.next();
terms = vec![Expr::Suffix(SuffixOp::Celsius, Box::new(Expr::Mul(terms)))]
},
Token::DegF => {
iter.next();
terms = vec![Expr::Suffix(SuffixOp::Fahrenheit, Box::new(Expr::Mul(terms)))]
},
Token::DegRe => {
iter.next();
terms = vec![Expr::Suffix(SuffixOp::Reaumur, Box::new(Expr::Mul(terms)))]
},
Token::DegRo => {
iter.next();
terms = vec![Expr::Suffix(SuffixOp::Romer, Box::new(Expr::Mul(terms)))]
},
Token::DegDe => {
iter.next();
terms = vec![Expr::Suffix(SuffixOp::Delisle, Box::new(Expr::Mul(terms)))]
},
Token::DegN => {
iter.next();
terms = vec![Expr::Suffix(SuffixOp::Newton, Box::new(Expr::Mul(terms)))]
terms = vec![Expr::Suffix(deg, Box::new(Expr::Mul(terms)))]
},
_ => terms.push(parse_frac(iter))
}}
@ -738,10 +681,10 @@ pub fn parse_offset(iter: &mut Iter) -> Option<i64> {
Token::Decimal(ref i, None, None) if i.len() == 2 => i.clone(),
_ => return None
};
let _col = match iter.next().unwrap() {
match iter.next().unwrap() {
Token::Colon => (),
_ => return None
};
}
let min = match iter.next().unwrap() {
Token::Decimal(ref i, None, None) if i.len() == 2 => i.clone(),
_ => return None
@ -817,8 +760,9 @@ pub fn parse_query(iter: &mut Iter) -> Query {
},
Some(x) => return Query::Error(format!(
"Expected decimal base, got {}", describe(&x))),
None => return Query::Error(format!(
"Expected decimal base, got eof"))
None => return Query::Error(
"Expected decimal base, got eof".to_string()
)
}
},
Token::Ident(ref s) if s == "hex" || s == "hexadecimal" || s == "base16" => {
@ -837,12 +781,7 @@ pub fn parse_query(iter: &mut Iter) -> Query {
};
let right = match iter.peek().cloned().unwrap() {
Token::Eof => Conversion::None,
Token::DegC => Conversion::DegC,
Token::DegF => Conversion::DegF,
Token::DegRe => Conversion::DegRe,
Token::DegRo => Conversion::DegRo,
Token::DegDe => Conversion::DegDe,
Token::DegN => Conversion::DegN,
Token::Degree(deg) => Conversion::Degree(deg),
Token::Plus | Token::Minus => {
let mut old = iter.clone();
if let Some(off) = parse_offset(iter) {

View file

@ -28,7 +28,7 @@ impl Show for DateTime<FixedOffset> {
if let Some(h) = context.humanize(*self) {
format!("{} ({})", self, h)
} else {
format!("{}", self)
self.to_string()
}
}
}
@ -38,7 +38,7 @@ impl Show for DateTime<Tz> {
if let Some(h) = context.humanize(*self) {
format!("{} ({})", self, h)
} else {
format!("{}", self)
self.to_string()
}
}
}
@ -67,7 +67,7 @@ impl Value {
match (self, exp) {
(&Value::Number(ref left), &Value::Number(ref right)) =>
left.pow(right).map(Value::Number),
(_, _) => Err(format!("Operation is not defined"))
(_, _) => Err("Operation is not defined".to_string())
}
}
}
@ -79,7 +79,7 @@ impl<'a,'b> Add<&'b Value> for &'a Value {
match (self, other) {
(&Value::Number(ref left), &Value::Number(ref right)) =>
(left + right)
.ok_or(format!("Addition of units with mismatched units is not meaningful"))
.ok_or("Addition of units with mismatched units is not meaningful".to_string())
.map(Value::Number),
(&Value::DateTime(ref left), &Value::Number(ref right)) |
(&Value::Number(ref right), &Value::DateTime(ref left)) =>
@ -91,12 +91,12 @@ impl<'a,'b> Add<&'b Value> for &'a Value {
right
))).map(GenericDateTime::Timezone),
}
.ok_or(format!("Implementation error: value is out of range representable by datetime"))
.ok_or("Implementation error: value is out of range representable by datetime".to_string())
.map(Value::DateTime),
(&Value::Substance(ref left), &Value::Substance(ref right)) =>
left.add(right)
.map(Value::Substance),
(_, _) => Err(format!("Operation is not defined"))
(_, _) => Err("Operation is not defined".to_string())
}
}
}
@ -108,7 +108,7 @@ impl<'a,'b> Sub<&'b Value> for &'a Value {
match (self, other) {
(&Value::Number(ref left), &Value::Number(ref right)) =>
(left - right)
.ok_or(format!("Subtraction of units with mismatched units is not meaningful"))
.ok_or("Subtraction of units with mismatched units is not meaningful".to_string())
.map(Value::Number),
(&Value::DateTime(ref left), &Value::Number(ref right)) |
(&Value::Number(ref right), &Value::DateTime(ref left)) =>
@ -120,7 +120,7 @@ impl<'a,'b> Sub<&'b Value> for &'a Value {
right
))).map(GenericDateTime::Timezone),
}
.ok_or(format!("Implementation error: value is out of range representable by datetime"))
.ok_or("Implementation error: value is out of range representable by datetime".to_string())
.map(Value::DateTime),
(&Value::DateTime(ref left), &Value::DateTime(ref right)) =>
date::from_duration(&match (left, right) {
@ -134,7 +134,7 @@ impl<'a,'b> Sub<&'b Value> for &'a Value {
*left - *right,
})
.map(Value::Number),
(_, _) => Err(format!("Operation is not defined"))
(_, _) => Err("Operation is not defined".to_string())
}
}
}
@ -145,8 +145,8 @@ impl<'a> Neg for &'a Value {
fn neg(self) -> Self::Output {
match *self {
Value::Number(ref num) =>
(-num).ok_or(format!("Bug: Negation should not fail")).map(Value::Number),
_ => Err(format!("Operation is not defined"))
(-num).ok_or("Bug: Negation should not fail".to_string()).map(Value::Number),
_ => Err("Operation is not defined".to_string())
}
}
}
@ -158,12 +158,12 @@ impl<'a,'b> Mul<&'b Value> for &'a Value {
match (self, other) {
(&Value::Number(ref left), &Value::Number(ref right)) =>
(left * right)
.ok_or(format!("Bug: Mul should not fail"))
.ok_or("Bug: Mul should not fail".to_string())
.map(Value::Number),
(&Value::Number(ref co), &Value::Substance(ref sub)) |
(&Value::Substance(ref sub), &Value::Number(ref co)) =>
(sub * co).map(Value::Substance),
(_, _) => Err(format!("Operation is not defined"))
(_, _) => Err("Operation is not defined".to_string())
}
}
}
@ -175,11 +175,11 @@ impl<'a,'b> Div<&'b Value> for &'a Value {
match (self, other) {
(&Value::Number(ref left), &Value::Number(ref right)) =>
(left / right)
.ok_or(format!("Division by zero"))
.ok_or("Division by zero".to_string())
.map(Value::Number),
(&Value::Substance(ref sub), &Value::Number(ref co)) =>
(sub / co).map(Value::Substance),
(_, _) => Err(format!("Operation is not defined"))
(_, _) => Err("Operation is not defined".to_string())
}
}
}

View file

@ -55,8 +55,7 @@ fn root(rink: &Rink, req: &mut Request) -> IronResult<Response> {
let map = req.get_ref::<Params>().unwrap();
match map.find(&["q"]) {
Some(&Value::String(ref query)) if query == "" => (),
Some(&Value::String(ref query)) => {
Some(&Value::String(ref query)) if !query.is_empty() => {
let mut reply = eval_json(query);
reply.as_object_mut().unwrap().insert("input".to_owned(), query.to_json());
println!("{}", reply.pretty());
@ -66,7 +65,7 @@ fn root(rink: &Rink, req: &mut Request) -> IronResult<Response> {
_ => (),
};
if data.len() == 0 {
if data.is_empty() {
data.insert("main-page".to_owned(), true.to_json());
}
@ -82,10 +81,10 @@ impl AfterMiddleware for ErrorMiddleware {
let mut data = BTreeMap::new();
let mut error = BTreeMap::new();
if let Some(status) = err.response.status {
error.insert("status".to_owned(), format!("{}", status));
data.insert("title".to_owned(), format!("{}", status).to_json());
error.insert("status".to_owned(), status.to_string());
data.insert("title".to_owned(), status.to_string().to_json());
}
error.insert("message".to_owned(), format!("{}", err.error));
error.insert("message".to_owned(), err.error.to_string());
data.insert("error".to_owned(), error.to_json());
data.insert("config".to_owned(), self.0.config.to_json());
println!("{:#?}", data);
@ -162,7 +161,7 @@ fn urlescapehelper(
let res = utf8_percent_encode(&res, QUERY_ENCODE_SET).collect::<String>();
let res = res.split("%20").collect::<Vec<_>>().join("+");
try!(rc.writer.write_all(res.as_bytes()).map_err(
|e| RenderError::new(&format!("{}", e))));
|e| RenderError::new(&e.to_string())));
Ok(())
}
@ -201,7 +200,7 @@ fn main() {
).unwrap()
};
let rink = Arc::new(Rink {
config: config,
config,
});
let (logger_before, logger_after) = Logger::new(None);

View file

@ -37,7 +37,7 @@ impl Serialize for Error {
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Generic(format!("{}", err))
Error::Generic(err.to_string())
}
}
@ -112,7 +112,7 @@ pub fn eval(query: &str) -> Result<QueryReply, Error> {
Sandbox stdout: {}\n\
Sandbox stderr: {}",
e,
output.status.signal().map(|x| format!("{}", x)).unwrap_or("None".to_owned()),
output.status.signal().map(|x| x.to_string()).unwrap_or("None".to_owned()),
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
)))
@ -122,11 +122,11 @@ pub fn eval(query: &str) -> Result<QueryReply, Error> {
pub fn eval_text(query: &str) -> String {
match eval(query) {
Ok(v) => format!("{}", v),
Err(Error::Generic(e)) => format!("{}", e),
Err(Error::Memory) => format!("Calculation ran out of memory"),
Err(Error::Time) => format!("Calculation timed out"),
Err(Error::Rink(e)) => format!("{}", e),
Ok(v) => v.to_string(),
Err(Error::Generic(e)) => e.to_string(),
Err(Error::Memory) => "Calculation ran out of memory".to_string(),
Err(Error::Time) => "Calculation timed out".to_string(),
Err(Error::Rink(e)) => e.to_string(),
}
}