Add Réaumur, Rømer, Delisle, and Newton scales

This commit is contained in:
Tiffany Bennett 2016-08-14 19:12:11 -04:00
parent 392431631b
commit 717d8ccc2c
3 changed files with 114 additions and 66 deletions

View file

@ -260,6 +260,24 @@ 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(format!("Expected number, got: <{}> °{}",
left.show(self), stringify!($name)))
};
if left.1 != BTreeMap::new() {
Err(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) => self.lookup(name).ok_or(format!("Unknown unit {}", name)).map(Value::Number),
Expr::Quote(ref name) => Ok(Value::Number(Number::one_unit(Rc::new(name.clone())))),
@ -274,40 +292,28 @@ impl Context {
Expr::Plus(ref expr) => self.eval(&**expr),
Expr::DegC => Err(format!("°C is an operator")),
Expr::DegF => Err(format!("°F is an operator")),
Expr::DegRe => Err(format!("°Ré is an operator")),
Expr::DegRo => Err(format!("°Rø is an operator")),
Expr::DegDe => Err(format!("°De is an operator")),
Expr::DegN => Err(format!("°N is an operator")),
Expr::Frac(ref left, ref right) => operator!(left div / right),
Expr::Add(ref left, ref right) => operator!(left add + right),
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) => {
let left = try!(self.eval(&**left));
let left = match left {
Value::Number(left) => left,
_ => return Err(format!("Left-hand side of celsius literal must be number"))
};
if left.1 != BTreeMap::new() {
Err(format!("Left-hand side of celsius literal must be dimensionless"))
} else {
let left = (&left * &self.lookup("kelvin").expect("Missing kelvin unit")).unwrap();
Ok(Value::Number((&left + &self.lookup("zerocelsius")
.expect("Missing zerocelsius constant")).unwrap()))
}
},
Expr::Suffix(SuffixOp::Fahrenheit, ref left) => {
let left = try!(self.eval(&**left));
let left = match left {
Value::Number(left) => left,
_ => return Err(format!("Left-hand side of fahrenheit literal must be number"))
};
if left.1 != BTreeMap::new() {
Err(format!("Left-hand side of fahrenheit literal must be dimensionless"))
} else {
let left = (&left * &self.lookup("Rankine").expect("Missing rankine unit")).unwrap();
Ok(Value::Number((&left + &self.lookup("zerofahrenheit")
.expect("Missing zerofahrenheit constant")).unwrap()))
}
},
Expr::Suffix(SuffixOp::Celsius, ref left) =>
temperature!(left, "C", "zerocelsius", "kelvin"),
Expr::Suffix(SuffixOp::Fahrenheit, ref left) =>
temperature!(left, "F", "zerofahrenheit", "Rankine"),
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"),
// TODO: A type might not implement * on Number, and this would fail
Expr::Mul(ref args) => args.iter().fold(Ok(Value::Number(Number::one())), |a, b| {
@ -385,7 +391,8 @@ impl Context {
},
Expr::Neg(ref v) => self.eval_unit_name(v),
Expr::Plus(ref v) => self.eval_unit_name(v),
Expr::Suffix(_, _) | Expr::DegC | Expr::DegF =>
Expr::Suffix(_, _) | Expr::DegC | Expr::DegF | Expr::DegRe | Expr::DegRo |
Expr::DegDe | Expr::DegN =>
Err(format!("Temperature conversions must not be compound units")),
Expr::Date(_) => Err(format!("Dates are not allowed in the right hand side of conversions")),
Expr::Convert(_, _) => Err(format!("Conversions are not allowed in the right hand of conversions")),
@ -497,41 +504,38 @@ impl Context {
Ok(show(&raw, &bottom, bottom_name))
}
},
(Ok(ref top), Err(ref e), _) => match **bottom {
Expr::DegC => {
let top = match *top {
Value::Number(ref num) => num,
_ => return Err(format!("Expected number"))
};
let bottom = self.lookup("kelvin").expect("Unit kelvin missing");
if top.1 != bottom.1 {
Err(conformance_err(&top, &bottom))
} else {
let res = (top - &self.lookup("zerocelsius")
.expect("Unit zerocelsius missing")).unwrap();
let mut name = BTreeMap::new();
name.insert("°C".to_owned(), 1);
Ok(show(&res, &bottom, name))
}
},
Expr::DegF => {
let top = match *top {
Value::Number(ref num) => num,
_ => return Err(format!("Expected number"))
};
let bottom = self.lookup("kelvin").expect("Unit kelvin missing");
if top.1 != bottom.1 {
Err(conformance_err(&top, &bottom))
} else {
let res = (top - &self.lookup("zerofahrenheit")
.expect("Unit zerofahrenheit missing")).unwrap();
let res = (&res / &self.lookup("Rankine").expect("Unit Rankine missing")).unwrap();
let mut name = BTreeMap::new();
name.insert("°F".to_owned(), 1);
Ok(show(&res, &bottom, name))
}
},
_ => Err(e.clone())
(Ok(ref top), Err(ref e), _) => {
macro_rules! temperature {
($name:expr, $base:expr, $scale:expr) => {{
let top = match *top {
Value::Number(ref num) => num,
_ => return Err(format!("Cannot convert <{}> to °{}",
top.show(self), $name))
};
let bottom = self.lookup($scale)
.expect(&*format!("Unit {} missing", $scale));
if top.1 != bottom.1 {
Err(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(show(&res, &bottom, name))
}
}}
}
match **bottom {
Expr::DegC => temperature!("C", "zerocelsius", "kelvin"),
Expr::DegF => temperature!("F", "zerofahrenheit", "Rankine"),
Expr::DegRe => temperature!("", "zerocelsius", "reaumur_absolute"),
Expr::DegRo => temperature!("", "zeroromer", "romer_absolute"),
Expr::DegDe => temperature!("De", "zerodelisle", "delisle_absolute"),
Expr::DegN => temperature!("N", "zerocelsius", "newton_absolute"),
_ => Err(e.clone())
}
},
(Err(e), _, _) => Err(e),
(_, _, Err(e)) => Err(e),

View file

@ -33,6 +33,10 @@ pub enum Token {
ImaginaryUnit,
DegC,
DegF,
DegRe,
DegRo,
DegDe,
DegN,
Error(String),
}
@ -251,6 +255,10 @@ 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,
_ => Token::Ident(buf)
}
}
@ -265,6 +273,10 @@ pub type Iter<'a> = Peekable<TokenIterator<'a>>;
pub enum SuffixOp {
Celsius,
Fahrenheit,
Reaumur,
Romer,
Delisle,
Newton,
}
#[derive(Debug, Clone)]
@ -285,6 +297,10 @@ pub enum Expr {
Suffix(SuffixOp, Box<Expr>),
DegC,
DegF,
DegRe,
DegRo,
DegDe,
DegN,
Error(String),
}
@ -361,8 +377,9 @@ fn parse_pow(mut iter: &mut Iter) -> Expr {
fn parse_mul(mut iter: &mut Iter) -> Expr {
let mut terms = vec![parse_pow(iter)];
loop { match iter.peek().cloned().unwrap() {
Token::DegC | Token::DegF | Token::Equals | Token::Plus | Token::Minus | Token::DashArrow |
Token::TriplePipe | Token::RPar | Token::Newline | Token::Comment(_) | Token::Eof => break,
Token::DegC | Token::DegF | Token::DegRe | Token::DegRo | Token::DegDe | Token::DegN |
Token::Equals | Token::Plus | Token::Minus | Token::DashArrow | Token::TriplePipe |
Token::RPar | Token::Newline | Token::Comment(_) | Token::Eof => break,
Token::Slash => {
iter.next();
let right = parse_pow(iter);
@ -396,6 +413,22 @@ fn parse_suffix(mut iter: &mut Iter) -> Expr {
iter.next();
Expr::Suffix(SuffixOp::Fahrenheit, Box::new(left))
},
Token::DegRe => {
iter.next();
Expr::Suffix(SuffixOp::Reaumur, Box::new(left))
},
Token::DegRo => {
iter.next();
Expr::Suffix(SuffixOp::Romer, Box::new(left))
},
Token::DegDe => {
iter.next();
Expr::Suffix(SuffixOp::Delisle, Box::new(left))
},
Token::DegN => {
iter.next();
Expr::Suffix(SuffixOp::Newton, Box::new(left))
},
_ => left
}
}
@ -437,6 +470,10 @@ pub fn parse_expr(mut iter: &mut Iter) -> Expr {
let right = match iter.peek().cloned().unwrap() {
Token::DegC => Expr::DegC,
Token::DegF => Expr::DegF,
Token::DegRe => Expr::DegRe,
Token::DegRo => Expr::DegRo,
Token::DegDe => Expr::DegDe,
Token::DegN => Expr::DegN,
_ => parse_eq(iter)
};
Expr::Convert(Box::new(left), Box::new(right))

View file

@ -1264,7 +1264,14 @@ degreerankine := degrankine // is at absolute zero.
degR := degrankine
Rankine := degreesrankine
reaumur_absolute := 10/8 kelvin
romer_absolute := 40/21 kelvin
delisle_absolute := -2/3 kelvin
newton_absolute := 100/33 kelvin
zerofahrenheit := zerocelsius - 32 degR
zerodelisle := 373.15 kelvin
zeroromer := zerocelsius - 7.5 romer_absolute
//degreaumur := 10/8 kelvin // The Reaumur scale was used in Europe and
// particularly in France. It is defined