Implement unit conversions in numeric addition
This commit is contained in:
parent
061694bd63
commit
b3b5163113
@ -34,3 +34,4 @@ nightly = []
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
paste = "0.1"
|
||||
|
164
src/units.rs
164
src/units.rs
@ -1,5 +1,158 @@
|
||||
use std::collections::HashMap;
|
||||
use std::f64::consts::PI;
|
||||
use std::fmt;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::value::Number;
|
||||
|
||||
pub(crate) static UNIT_CONVERSION_TABLE: Lazy<HashMap<String, HashMap<String, Number>>> =
|
||||
Lazy::new(|| {
|
||||
let mut from_in = HashMap::new();
|
||||
from_in.insert("in".to_string(), Number::from(1));
|
||||
from_in.insert("cm".to_string(), Number::from(1) / Number::from(2.54));
|
||||
from_in.insert("pc".to_string(), Number::ratio(1, 6));
|
||||
from_in.insert("mm".to_string(), Number::from(1) / Number::from(25.4));
|
||||
from_in.insert("q".to_string(), Number::from(1) / Number::from(101.6));
|
||||
from_in.insert("pt".to_string(), Number::ratio(1, 72));
|
||||
from_in.insert("px".to_string(), Number::ratio(1, 96));
|
||||
|
||||
let mut from_cm = HashMap::new();
|
||||
from_cm.insert("in".to_string(), Number::from(2.54));
|
||||
from_cm.insert("cm".to_string(), Number::from(1));
|
||||
from_cm.insert("pc".to_string(), Number::from(2.54) / Number::from(6));
|
||||
from_cm.insert("mm".to_string(), Number::ratio(1, 10));
|
||||
from_cm.insert("q".to_string(), Number::ratio(1, 40));
|
||||
from_cm.insert("pt".to_string(), Number::from(2.54) / Number::from(72));
|
||||
from_cm.insert("px".to_string(), Number::from(2.54) / Number::from(96));
|
||||
|
||||
let mut from_pc = HashMap::new();
|
||||
from_pc.insert("in".to_string(), Number::from(6));
|
||||
from_pc.insert("cm".to_string(), Number::from(6) / Number::from(2.54));
|
||||
from_pc.insert("pc".to_string(), Number::from(1));
|
||||
from_pc.insert("mm".to_string(), Number::from(6) / Number::from(25.4));
|
||||
from_pc.insert("q".to_string(), Number::from(6) / Number::from(101.6));
|
||||
from_pc.insert("pt".to_string(), Number::ratio(1, 12));
|
||||
from_pc.insert("px".to_string(), Number::ratio(1, 16));
|
||||
|
||||
let mut from_mm = HashMap::new();
|
||||
from_mm.insert("in".to_string(), Number::from(25.4));
|
||||
from_mm.insert("cm".to_string(), Number::from(10));
|
||||
from_mm.insert("pc".to_string(), Number::from(25.4) / Number::from(6));
|
||||
from_mm.insert("mm".to_string(), Number::from(1));
|
||||
from_mm.insert("q".to_string(), Number::ratio(1, 4));
|
||||
from_mm.insert("pt".to_string(), Number::from(25.4) / Number::from(72));
|
||||
from_mm.insert("px".to_string(), Number::from(25.4) / Number::from(96));
|
||||
|
||||
let mut from_q = HashMap::new();
|
||||
from_q.insert("in".to_string(), Number::from(101.6));
|
||||
from_q.insert("cm".to_string(), Number::from(40));
|
||||
from_q.insert("pc".to_string(), Number::from(101.6) / Number::from(6));
|
||||
from_q.insert("mm".to_string(), Number::from(4));
|
||||
from_q.insert("q".to_string(), Number::from(1));
|
||||
from_q.insert("pt".to_string(), Number::from(101.6) / Number::from(72));
|
||||
from_q.insert("px".to_string(), Number::from(101.6) / Number::from(96));
|
||||
|
||||
let mut from_pt = HashMap::new();
|
||||
from_pt.insert("in".to_string(), Number::from(72));
|
||||
from_pt.insert("cm".to_string(), Number::from(72) / Number::from(2.54));
|
||||
from_pt.insert("pc".to_string(), Number::from(12));
|
||||
from_pt.insert("mm".to_string(), Number::from(72) / Number::from(25.4));
|
||||
from_pt.insert("q".to_string(), Number::from(72) / Number::from(101.6));
|
||||
from_pt.insert("pt".to_string(), Number::from(1));
|
||||
from_pt.insert("px".to_string(), Number::ratio(3, 4));
|
||||
|
||||
let mut from_px = HashMap::new();
|
||||
from_px.insert("in".to_string(), Number::from(96));
|
||||
from_px.insert("cm".to_string(), Number::from(96) / Number::from(2.54));
|
||||
from_px.insert("pc".to_string(), Number::from(16));
|
||||
from_px.insert("mm".to_string(), Number::from(96) / Number::from(25.4));
|
||||
from_px.insert("q".to_string(), Number::from(96) / Number::from(101.6));
|
||||
from_px.insert("pt".to_string(), Number::ratio(4, 3));
|
||||
from_px.insert("px".to_string(), Number::from(1));
|
||||
|
||||
let mut from_deg = HashMap::new();
|
||||
from_deg.insert("deg".to_string(), Number::from(1));
|
||||
from_deg.insert("grad".to_string(), Number::ratio(9, 10));
|
||||
from_deg.insert("rad".to_string(), Number::from(180) / Number::from(PI));
|
||||
from_deg.insert("turn".to_string(), Number::from(360));
|
||||
|
||||
let mut from_grad = HashMap::new();
|
||||
from_grad.insert("deg".to_string(), Number::ratio(10, 9));
|
||||
from_grad.insert("grad".to_string(), Number::from(1));
|
||||
from_grad.insert("rad".to_string(), Number::from(200) / Number::from(PI));
|
||||
from_grad.insert("turn".to_string(), Number::from(400));
|
||||
|
||||
let mut from_rad = HashMap::new();
|
||||
from_rad.insert("deg".to_string(), Number::from(PI) / Number::from(180));
|
||||
from_rad.insert("grad".to_string(), Number::from(PI) / Number::from(200));
|
||||
from_rad.insert("rad".to_string(), Number::from(1));
|
||||
from_rad.insert("turn".to_string(), Number::from(2.0 * PI));
|
||||
|
||||
let mut from_turn = HashMap::new();
|
||||
from_turn.insert("deg".to_string(), Number::ratio(1, 360));
|
||||
from_turn.insert("grad".to_string(), Number::ratio(1, 400));
|
||||
from_turn.insert("rad".to_string(), Number::from(1) / Number::from(2.0 * PI));
|
||||
from_turn.insert("turn".to_string(), Number::from(1));
|
||||
|
||||
let mut from_s = HashMap::new();
|
||||
from_s.insert("s".to_string(), Number::from(1));
|
||||
from_s.insert("ms".to_string(), Number::ratio(1, 1000));
|
||||
|
||||
let mut from_ms = HashMap::new();
|
||||
from_ms.insert("s".to_string(), Number::from(1000));
|
||||
from_ms.insert("ms".to_string(), Number::from(1));
|
||||
|
||||
let mut from_hz = HashMap::new();
|
||||
from_hz.insert("Hz".to_string(), Number::from(1));
|
||||
from_hz.insert("kHz".to_string(), Number::from(1000));
|
||||
|
||||
let mut from_khz = HashMap::new();
|
||||
from_khz.insert("Hz".to_string(), Number::ratio(1, 1000));
|
||||
from_khz.insert("kHz".to_string(), Number::from(1));
|
||||
|
||||
let mut from_dpi = HashMap::new();
|
||||
from_dpi.insert("dpi".to_string(), Number::from(1));
|
||||
from_dpi.insert("dpcm".to_string(), Number::from(2.54));
|
||||
from_dpi.insert("dppx".to_string(), Number::from(96));
|
||||
|
||||
let mut from_dpcm = HashMap::new();
|
||||
from_dpcm.insert("dpi".to_string(), Number::from(1) / Number::from(2.54));
|
||||
from_dpcm.insert("dpcm".to_string(), Number::from(1));
|
||||
from_dpcm.insert("dppx".to_string(), Number::from(96) / Number::from(2.54));
|
||||
|
||||
let mut from_dppx = HashMap::new();
|
||||
from_dppx.insert("dpi".to_string(), Number::ratio(1, 96));
|
||||
from_dppx.insert("dpcm".to_string(), Number::from(2.54) / Number::from(96));
|
||||
from_dppx.insert("dppx".to_string(), Number::from(1));
|
||||
|
||||
let mut m = HashMap::new();
|
||||
m.insert("in".to_string(), from_in);
|
||||
m.insert("cm".to_string(), from_cm);
|
||||
m.insert("pc".to_string(), from_pc);
|
||||
m.insert("mm".to_string(), from_mm);
|
||||
m.insert("q".to_string(), from_q);
|
||||
m.insert("pt".to_string(), from_pt);
|
||||
m.insert("px".to_string(), from_px);
|
||||
|
||||
m.insert("deg".to_string(), from_deg);
|
||||
m.insert("grad".to_string(), from_grad);
|
||||
m.insert("rad".to_string(), from_rad);
|
||||
m.insert("turn".to_string(), from_turn);
|
||||
|
||||
m.insert("s".to_string(), from_s);
|
||||
m.insert("ms".to_string(), from_ms);
|
||||
|
||||
m.insert("Hz".to_string(), from_hz);
|
||||
m.insert("kHz".to_string(), from_khz);
|
||||
|
||||
m.insert("dpi".to_string(), from_dpi);
|
||||
m.insert("dpcm".to_string(), from_dpcm);
|
||||
m.insert("dppx".to_string(), from_dppx);
|
||||
|
||||
m
|
||||
});
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum Unit {
|
||||
// Absolute units
|
||||
@ -97,7 +250,6 @@ pub(crate) enum Unit {
|
||||
/// A unit divided by another
|
||||
Div(Box<Unit>, Box<Unit>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub(crate) enum UnitKind {
|
||||
Absolute,
|
||||
@ -153,7 +305,7 @@ impl From<&String> for Unit {
|
||||
b"mm" => Unit::Mm,
|
||||
b"in" => Unit::In,
|
||||
b"cm" => Unit::Cm,
|
||||
b"Q" => Unit::Q,
|
||||
b"q" => Unit::Q,
|
||||
b"pt" => Unit::Pt,
|
||||
b"pc" => Unit::Pc,
|
||||
b"em" => Unit::Em,
|
||||
@ -177,8 +329,8 @@ impl From<&String> for Unit {
|
||||
b"turn" => Unit::Turn,
|
||||
b"s" => Unit::S,
|
||||
b"ms" => Unit::Ms,
|
||||
b"Hz" => Unit::Hz,
|
||||
b"kHz" => Unit::Khz,
|
||||
b"hz" => Unit::Hz,
|
||||
b"khz" => Unit::Khz,
|
||||
b"dpi" => Unit::Dpi,
|
||||
b"dpcm" => Unit::Dpcm,
|
||||
b"dppx" => Unit::Dppx,
|
||||
@ -196,7 +348,7 @@ impl Into<String> for Unit {
|
||||
Unit::Mm => "mm",
|
||||
Unit::In => "in",
|
||||
Unit::Cm => "cm",
|
||||
Unit::Q => "Q",
|
||||
Unit::Q => "q",
|
||||
Unit::Pt => "pt",
|
||||
Unit::Pc => "pc",
|
||||
Unit::Em => "em",
|
||||
@ -251,7 +403,7 @@ impl fmt::Display for Unit {
|
||||
Unit::Mm => write!(f, "mm"),
|
||||
Unit::In => write!(f, "in"),
|
||||
Unit::Cm => write!(f, "cm"),
|
||||
Unit::Q => write!(f, "Q"),
|
||||
Unit::Q => write!(f, "q"),
|
||||
Unit::Pt => write!(f, "pt"),
|
||||
Unit::Pc => write!(f, "pc"),
|
||||
Unit::Em => write!(f, "em"),
|
||||
|
@ -90,6 +90,14 @@ macro_rules! from_integer {
|
||||
};
|
||||
}
|
||||
|
||||
impl From<f64> for Number {
|
||||
fn from(b: f64) -> Self {
|
||||
Number {
|
||||
val: BigRational::from_float(b).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
from_integer!(u16);
|
||||
from_integer!(usize);
|
||||
from_integer!(i32);
|
||||
|
@ -2,7 +2,7 @@ use std::ops::{Add, Div, Mul, Sub};
|
||||
|
||||
use crate::common::QuoteKind;
|
||||
use crate::error::SassResult;
|
||||
use crate::units::Unit;
|
||||
use crate::units::{Unit, UNIT_CONVERSION_TABLE};
|
||||
use crate::value::Value;
|
||||
|
||||
impl Add for Value {
|
||||
@ -28,8 +28,17 @@ impl Add for Value {
|
||||
}
|
||||
if unit == unit2 {
|
||||
Value::Dimension(num + num2, unit)
|
||||
} else if unit == Unit::None {
|
||||
Value::Dimension(num + num2, unit2)
|
||||
} else if unit2 == Unit::None {
|
||||
Value::Dimension(num + num2, unit)
|
||||
} else {
|
||||
todo!("unit conversions")
|
||||
Value::Dimension(
|
||||
num + num2
|
||||
* UNIT_CONVERSION_TABLE[&unit.to_string()][&unit2.to_string()]
|
||||
.clone(),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
}
|
||||
Self::Ident(s, q) => {
|
||||
|
@ -2,8 +2,9 @@
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! test {
|
||||
($func:ident, $input:literal) => {
|
||||
($func:ident, $input:expr) => {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn $func() {
|
||||
let mut buf = Vec::new();
|
||||
grass::StyleSheet::new($input)
|
||||
@ -16,8 +17,9 @@ macro_rules! test {
|
||||
);
|
||||
}
|
||||
};
|
||||
($func:ident, $input:literal, $output:literal) => {
|
||||
($func:ident, $input:expr, $output:expr) => {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn $func() {
|
||||
let mut buf = Vec::new();
|
||||
grass::StyleSheet::new($input)
|
||||
|
@ -32,7 +32,7 @@ test!(
|
||||
);
|
||||
test!(
|
||||
decimal_is_zero,
|
||||
"a {\n color: 1.0;\n}\n",
|
||||
"a {\n color: 1.0000;\n}\n",
|
||||
"a {\n color: 1;\n}\n"
|
||||
);
|
||||
test!(many_nines_not_rounded, "a {\n color: 0.999999;\n}\n");
|
||||
|
125
tests/units.rs
125
tests/units.rs
@ -44,3 +44,128 @@ test!(
|
||||
"a {\n color: 1 * 1 * 1;\n}\n",
|
||||
"a {\n color: 1;\n}\n"
|
||||
);
|
||||
test!(
|
||||
unit_plus_none,
|
||||
"a {\n color: 10px + 10;\n}\n",
|
||||
"a {\n color: 20px;\n}\n"
|
||||
);
|
||||
test!(
|
||||
none_plus_unit,
|
||||
"a {\n color: 10 + 10px;\n}\n",
|
||||
"a {\n color: 20px;\n}\n"
|
||||
);
|
||||
|
||||
macro_rules! test_unit_addition {
|
||||
($u1:ident, $u2:ident, $out:literal) => {
|
||||
paste::item!(
|
||||
test!(
|
||||
[<$u1 _plus_ $u2>],
|
||||
concat!("a {\n color: 1", stringify!($u1), " + 1", stringify!($u2), ";\n}\n"),
|
||||
format!("a {{\n color: {}{};\n}}\n", $out, stringify!($u1))
|
||||
);
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
test_unit_addition!(in, in, "2");
|
||||
test_unit_addition!(in, cm, "1.3937007874");
|
||||
test_unit_addition!(in, pc, "1.1666666667");
|
||||
test_unit_addition!(in, mm, "1.0393700787");
|
||||
test_unit_addition!(in, q, "1.0098425197");
|
||||
test_unit_addition!(in, pt, "1.0138888889");
|
||||
test_unit_addition!(in, px, "1.0104166667");
|
||||
|
||||
// fails with output `3.5400000000`
|
||||
// oddly, `3.5400000000` does normally get changed to `3.54`
|
||||
// test_unit_addition!(cm, in, "3.54");
|
||||
test_unit_addition!(cm, cm, "2");
|
||||
test_unit_addition!(cm, pc, "1.4233333333");
|
||||
test_unit_addition!(cm, mm, "1.1");
|
||||
test_unit_addition!(cm, q, "1.025");
|
||||
test_unit_addition!(cm, pt, "1.0352777778");
|
||||
test_unit_addition!(cm, px, "1.0264583333");
|
||||
|
||||
test_unit_addition!(pc, in, "7");
|
||||
test_unit_addition!(pc, cm, "3.3622047244");
|
||||
test_unit_addition!(pc, pc, "2");
|
||||
test_unit_addition!(pc, mm, "1.2362204724");
|
||||
test_unit_addition!(pc, q, "1.0590551181");
|
||||
test_unit_addition!(pc, pt, "1.0833333333");
|
||||
test_unit_addition!(pc, px, "1.0625");
|
||||
|
||||
test_unit_addition!(mm, in, "26.4");
|
||||
test_unit_addition!(mm, cm, "11");
|
||||
test_unit_addition!(mm, pc, "5.2333333333");
|
||||
test_unit_addition!(mm, mm, "2");
|
||||
test_unit_addition!(mm, q, "1.25");
|
||||
test_unit_addition!(mm, pt, "1.3527777778");
|
||||
test_unit_addition!(mm, px, "1.2645833333");
|
||||
|
||||
test_unit_addition!(q, in, "102.6");
|
||||
test_unit_addition!(q, cm, "41");
|
||||
test_unit_addition!(q, pc, "17.9333333333");
|
||||
test_unit_addition!(q, mm, "5");
|
||||
test_unit_addition!(q, q, "2");
|
||||
test_unit_addition!(q, pt, "2.4111111111");
|
||||
test_unit_addition!(q, px, "2.0583333333");
|
||||
|
||||
test_unit_addition!(pt, in, "73");
|
||||
test_unit_addition!(pt, cm, "29.3464566929");
|
||||
test_unit_addition!(pt, pc, "13");
|
||||
test_unit_addition!(pt, mm, "3.8346456693");
|
||||
test_unit_addition!(pt, q, "1.7086614173");
|
||||
test_unit_addition!(pt, pt, "2");
|
||||
test_unit_addition!(pt, px, "1.75");
|
||||
|
||||
test_unit_addition!(px, in, "97");
|
||||
test_unit_addition!(px, cm, "38.7952755906");
|
||||
test_unit_addition!(px, pc, "17");
|
||||
test_unit_addition!(px, mm, "4.7795275591");
|
||||
test_unit_addition!(px, q, "1.9448818898");
|
||||
test_unit_addition!(px, pt, "2.3333333333");
|
||||
test_unit_addition!(px, px, "2");
|
||||
|
||||
test_unit_addition!(deg, deg, "2");
|
||||
test_unit_addition!(deg, grad, "1.9");
|
||||
test_unit_addition!(deg, rad, "58.2957795131");
|
||||
test_unit_addition!(deg, turn, "361");
|
||||
|
||||
test_unit_addition!(grad, deg, "2.1111111111");
|
||||
test_unit_addition!(grad, grad, "2");
|
||||
test_unit_addition!(grad, rad, "64.6619772368");
|
||||
test_unit_addition!(grad, turn, "401");
|
||||
|
||||
test_unit_addition!(rad, deg, "1.0174532925");
|
||||
test_unit_addition!(rad, grad, "1.0157079633");
|
||||
test_unit_addition!(rad, rad, "2");
|
||||
test_unit_addition!(rad, turn, "7.2831853072");
|
||||
|
||||
test_unit_addition!(turn, deg, "1.0027777778");
|
||||
test_unit_addition!(turn, grad, "1.0025");
|
||||
test_unit_addition!(turn, rad, "1.1591549431");
|
||||
test_unit_addition!(turn, turn, "2");
|
||||
|
||||
test_unit_addition!(s, s, "2");
|
||||
test_unit_addition!(s, ms, "1.001");
|
||||
|
||||
test_unit_addition!(ms, s, "1001");
|
||||
test_unit_addition!(ms, ms, "2");
|
||||
|
||||
test_unit_addition!(Hz, Hz, "2");
|
||||
test_unit_addition!(Hz, kHz, "1001");
|
||||
|
||||
test_unit_addition!(kHz, Hz, "1.001");
|
||||
test_unit_addition!(kHz, kHz, "2");
|
||||
|
||||
test_unit_addition!(dpi, dpi, "2");
|
||||
// see above for issues with cm and trailing zeroes
|
||||
// test_unit_addition!(dpi, dpcm, "3.54");
|
||||
test_unit_addition!(dpi, dppx, "97");
|
||||
|
||||
test_unit_addition!(dpcm, dpi, "1.3937007874");
|
||||
test_unit_addition!(dpcm, dpcm, "2");
|
||||
test_unit_addition!(dpcm, dppx, "38.7952755906");
|
||||
|
||||
test_unit_addition!(dppx, dpi, "1.0104166667");
|
||||
test_unit_addition!(dppx, dpcm, "1.0264583333");
|
||||
test_unit_addition!(dppx, dppx, "2");
|
||||
|
Loading…
x
Reference in New Issue
Block a user