Use the type system to enforce that product nodes are simplified away

This commit is contained in:
David Zbarsky 2015-08-14 23:44:25 -04:00
parent 164af05ba9
commit dcac654057

View file

@ -372,9 +372,42 @@ pub mod specified {
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum CalcAstNode { struct SimplifiedSumNode {
Add(CalcSumNode), values: Vec<SimplifiedValueNode>,
Value(CalcValueNode), }
impl<'a> Mul<CSSFloat> for &'a SimplifiedSumNode {
type Output = SimplifiedSumNode;
#[inline]
fn mul(self, scalar: CSSFloat) -> SimplifiedSumNode {
SimplifiedSumNode {
values: self.values.iter().map(|p| p * scalar).collect()
}
}
}
#[derive(Clone, Debug)]
enum SimplifiedValueNode {
Length(Length),
Percentage(CSSFloat),
Number(CSSFloat),
Sum(Box<SimplifiedSumNode>),
}
impl<'a> Mul<CSSFloat> for &'a SimplifiedValueNode {
type Output = SimplifiedValueNode;
#[inline]
fn mul(self, scalar: CSSFloat) -> SimplifiedValueNode {
match self {
&SimplifiedValueNode::Length(l) => SimplifiedValueNode::Length(l * scalar),
&SimplifiedValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p * scalar),
&SimplifiedValueNode::Number(n) => SimplifiedValueNode::Number(n * scalar),
&SimplifiedValueNode::Sum(box ref s) => {
let sum = s * scalar;
SimplifiedValueNode::Sum(box sum)
}
}
}
} }
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)] #[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
@ -460,19 +493,19 @@ pub mod specified {
} }
} }
fn simplify_value_numerically(node: &CalcValueNode) -> Option<CSSFloat> { fn simplify_value_to_number(node: &CalcValueNode) -> Option<CSSFloat> {
match node { match node {
&CalcValueNode::Number(number) => Some(number), &CalcValueNode::Number(number) => Some(number),
&CalcValueNode::Sum(box ref sum) => Calc::simplify_sum_numerically(sum), &CalcValueNode::Sum(box ref sum) => Calc::simplify_sum_to_number(sum),
_ => None _ => None
} }
} }
fn simplify_sum_numerically(node: &CalcSumNode) -> Option<CSSFloat> { fn simplify_sum_to_number(node: &CalcSumNode) -> Option<CSSFloat> {
let node = node.clone(); let node = node.clone();
let mut sum = 0.; let mut sum = 0.;
for product in node.products { for product in node.products {
match Calc::simplify_product_numerically(product) { match Calc::simplify_product_to_number(product) {
Some(number) => sum += number, Some(number) => sum += number,
_ => return None _ => return None
} }
@ -480,10 +513,10 @@ pub mod specified {
Some(sum) Some(sum)
} }
fn simplify_product_numerically(node: CalcProductNode) -> Option<CSSFloat> { fn simplify_product_to_number(node: CalcProductNode) -> Option<CSSFloat> {
let mut product = 1.; let mut product = 1.;
for value in node.values { for value in node.values {
match Calc::simplify_value_numerically(&value) { match Calc::simplify_value_to_number(&value) {
Some(number) => product *= number, Some(number) => product *= number,
_ => return None _ => return None
} }
@ -491,36 +524,36 @@ pub mod specified {
Some(product) Some(product)
} }
fn simplify_products_in_sum(node: &CalcSumNode) -> Result<CalcValueNode, ()> { fn simplify_products_in_sum(node: &CalcSumNode) -> Result<SimplifiedValueNode, ()> {
let mut simplified = Vec::new(); let mut simplified = Vec::new();
let node = node.clone(); let node = node.clone();
for product in node.products { for product in node.products {
match try!(Calc::simplify_product(product)) { match try!(Calc::simplify_product(product)) {
CalcAstNode::Value(val) => SimplifiedValueNode::Sum(box sum) => simplified.push_all(&sum.values),
simplified.push(CalcProductNode { values: vec!(val) }), val => simplified.push(val),
CalcAstNode::Add(sum) => simplified.push_all(&sum.products),
} }
} }
if simplified.len() == 1 { if simplified.len() == 1 {
assert!(simplified[0].values.len() == 1); Ok(simplified[0].clone())
Ok(simplified[0].values[0].clone())
} else { } else {
Ok(CalcValueNode::Sum(box CalcSumNode { products: simplified } )) Ok(SimplifiedValueNode::Sum(box SimplifiedSumNode { values: simplified } ))
} }
} }
fn simplify_product(node: CalcProductNode) -> Result<CalcAstNode, ()> { fn simplify_product(node: CalcProductNode) -> Result<SimplifiedValueNode, ()> {
let mut multiplier = 1.; let mut multiplier = 1.;
let mut node_with_unit = None; let mut node_with_unit = None;
for node in node.values { for node in node.values {
match Calc::simplify_value_numerically(&node) { match Calc::simplify_value_to_number(&node) {
Some(number) => multiplier *= number, Some(number) => multiplier *= number,
_ if node_with_unit.is_none() => { _ if node_with_unit.is_none() => {
node_with_unit = Some(match node { node_with_unit = Some(match node {
CalcValueNode::Sum(box ref sum) => CalcValueNode::Sum(box ref sum) =>
try!(Calc::simplify_products_in_sum(sum)), try!(Calc::simplify_products_in_sum(sum)),
val => val, CalcValueNode::Length(l) => SimplifiedValueNode::Length(l),
CalcValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p),
_ => unreachable!("Numbers should have been handled by simplify_value_to_nubmer")
}) })
}, },
//_ if node_with_unit.is_none() => node_with_unit = Some(node), //_ if node_with_unit.is_none() => node_with_unit = Some(node),
@ -529,40 +562,8 @@ pub mod specified {
} }
match node_with_unit { match node_with_unit {
None => Ok(CalcAstNode::Value(CalcValueNode::Number(multiplier))), None => Ok(SimplifiedValueNode::Number(multiplier)),
Some(CalcValueNode::Sum(box ref sum)) => Some(ref value) => Ok(value * multiplier)
Ok(CalcAstNode::Add(Calc::multiply_sum(sum, multiplier))),
Some(ref value) =>
Ok(CalcAstNode::Value(Calc::multiply_value(value, multiplier))),
}
}
fn multiply_product(node: &CalcProductNode, multiplier: CSSFloat) -> CalcProductNode {
CalcProductNode {
values: node.values
.iter()
.map(|v| Calc::multiply_value(v, multiplier))
.collect()
}
}
fn multiply_sum(node: &CalcSumNode, multiplier: CSSFloat) -> CalcSumNode {
CalcSumNode {
products: node.products
.iter()
.map(|p| Calc::multiply_product(p, multiplier))
.collect()
}
}
fn multiply_value(node: &CalcValueNode, multiplier: CSSFloat) -> CalcValueNode {
println!("multiplying {:?} by {}", node, multiplier);
match node {
&CalcValueNode::Number(num) => CalcValueNode::Number(num * multiplier),
&CalcValueNode::Percentage(p) => CalcValueNode::Percentage(p * multiplier),
&CalcValueNode::Sum(box ref sum) =>
CalcValueNode::Sum(box Calc::multiply_sum(sum, multiplier)),
&CalcValueNode::Length(l) => CalcValueNode::Length(l * multiplier),
} }
} }
@ -571,16 +572,9 @@ pub mod specified {
let mut simplified = Vec::new(); let mut simplified = Vec::new();
for node in ast.products { for node in ast.products {
let node = try!(Calc::simplify_product(node)); match try!(Calc::simplify_product(node)) {
match node { SimplifiedValueNode::Sum(sum) => simplified.push_all(&sum.values),
CalcAstNode::Value(value) => simplified.push(value), value => simplified.push(value),
CalcAstNode::Add(sum) => {
for product in sum.products {
println!(" Matching product AST: {:?}", product);
assert!(product.values.len() == 1);
simplified.push(product.values[0].clone());
}
}
} }
} }
@ -597,11 +591,11 @@ pub mod specified {
for value in simplified { for value in simplified {
match value { match value {
CalcValueNode::Percentage(p) => SimplifiedValueNode::Percentage(p) =>
percentage = Some(percentage.unwrap_or(0.) + p), percentage = Some(percentage.unwrap_or(0.) + p),
CalcValueNode::Length(Length::Absolute(Au(au))) => SimplifiedValueNode::Length(Length::Absolute(Au(au))) =>
absolute = Some(absolute.unwrap_or(0) + au), absolute = Some(absolute.unwrap_or(0) + au),
CalcValueNode::Length(Length::ViewportPercentage(v)) => SimplifiedValueNode::Length(Length::ViewportPercentage(v)) =>
match v { match v {
ViewportPercentageLength::Vw(val) => ViewportPercentageLength::Vw(val) =>
vw = Some(vw.unwrap_or(0.) + val), vw = Some(vw.unwrap_or(0.) + val),
@ -612,7 +606,7 @@ pub mod specified {
ViewportPercentageLength::Vmax(val) => ViewportPercentageLength::Vmax(val) =>
vmax = Some(vmax.unwrap_or(0.) + val), vmax = Some(vmax.unwrap_or(0.) + val),
}, },
CalcValueNode::Length(Length::FontRelative(f)) => SimplifiedValueNode::Length(Length::FontRelative(f)) =>
match f { match f {
FontRelativeLength::Em(val) => FontRelativeLength::Em(val) =>
em = Some(em.unwrap_or(0.) + val), em = Some(em.unwrap_or(0.) + val),
@ -621,7 +615,7 @@ pub mod specified {
FontRelativeLength::Rem(val) => FontRelativeLength::Rem(val) =>
rem = Some(rem.unwrap_or(0.) + val), rem = Some(rem.unwrap_or(0.) + val),
}, },
CalcValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val), SimplifiedValueNode::Number(val) => number = Some(number.unwrap_or(0.) + val),
_ => unreachable!() _ => unreachable!()
} }
} }