mirror of
https://github.com/tiffany352/rink-rs
synced 2024-11-10 13:44:15 +00:00
Add Réaumur, Rømer, Delisle, and Newton scales
This commit is contained in:
parent
392431631b
commit
717d8ccc2c
3 changed files with 114 additions and 66 deletions
132
src/eval.rs
132
src/eval.rs
|
@ -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, "Ré", "zerocelsius", "reaumur_absolute"),
|
||||
Expr::Suffix(SuffixOp::Romer, ref left) =>
|
||||
temperature!(left, "Rø", "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!("Ré", "zerocelsius", "reaumur_absolute"),
|
||||
Expr::DegRo => temperature!("Rø", "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),
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue