#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum CalcExpression<U: Unit>
{
CalculablePropertyValue(CalculablePropertyValue<U>),
Number(U::Number),
Parentheses(Box<CalcExpression<U>>),
Addition(Box<CalcExpression<U>>, Box<CalcExpression<U>>),
Subtraction(Box<CalcExpression<U>>, Box<CalcExpression<U>>),
Multiplication(Box<CalcExpression<U>>, Box<CalcExpression<U>>),
Division(Box<CalcExpression<U>>, Box<CalcExpression<U>>),
}
impl<U: Unit> Default for CalcExpression<U>
{
#[inline(always)]
fn default() -> Self
{
CalcExpression::CalculablePropertyValue(CalculablePropertyValue::default())
}
}
impl<U: Unit> ToCss for CalcExpression<U>
{
fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result
{
use self::CalcExpression::*;
match *self
{
CalculablePropertyValue(ref calculable) => calculable.to_css(dest),
Number(ref number) => number.to_css(dest),
Parentheses(ref calcFunctionBody) =>
{
dest.write_char('(')?;
calcFunctionBody.to_css(dest)?;
dest.write_char(')')
},
Addition(ref lhs, ref rhs) =>
{
lhs.to_css(dest)?;
dest.write_str(" + ")?;
rhs.to_css(dest)
}
Subtraction(ref lhs, ref rhs) =>
{
lhs.to_css(dest)?;
dest.write_str(" - ")?;
rhs.to_css(dest)
}
Multiplication(ref lhs, ref rhs) =>
{
lhs.to_css(dest)?;
dest.write_char('*')?;
rhs.to_css(dest)
}
Division(ref lhs, ref rhs) =>
{
lhs.to_css(dest)?;
dest.write_char('/')?;
rhs.to_css(dest)
}
}
}
}
impl<U: Unit> Expression<U> for CalcExpression<U>
{
#[inline(always)]
fn evaluate<Conversion: FontRelativeLengthConversion<U::Number> + ViewportPercentageLengthConversion<U::Number> + PercentageConversion<U::Number> + AttributeConversion<U> + CssVariableConversion>(&self, conversion: &Conversion) -> Option<U::Number>
{
use self::CalcExpression::*;
match *self
{
CalculablePropertyValue(ref calculable) => calculable.evaluate(conversion),
Number(number) => Some(number),
Parentheses(ref subExpression) => subExpression.evaluate(conversion),
Addition(ref lhsSubExpression, ref rhsSubExpression) => match (lhsSubExpression.evaluate(conversion), rhsSubExpression.evaluate(conversion))
{
(Some(lhs), Some(rhs)) => Some(lhs + rhs),
_ => None,
},
Subtraction(ref lhsSubExpression, ref rhsSubExpression) => match (lhsSubExpression.evaluate(conversion), rhsSubExpression.evaluate(conversion))
{
(Some(lhs), Some(rhs)) => Some(lhs - rhs),
_ => None,
},
Multiplication(ref lhsSubExpression, ref rhsSubExpression) => match (lhsSubExpression.evaluate(conversion), rhsSubExpression.evaluate(conversion))
{
(Some(lhs), Some(rhs)) => Some(lhs * rhs),
_ => None,
},
Division(ref lhsSubExpression, ref rhsSubExpression) => match (lhsSubExpression.evaluate(conversion), rhsSubExpression.evaluate(conversion))
{
(Some(lhs), Some(rhs)) => Some(lhs / rhs),
_ => None,
},
}
}
}
impl<U: Unit> CalcExpression<U>
{
#[inline(always)]
pub(crate) fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, CustomParseError<'i>>>
{
input.parse_nested_block(|input| Self::parse_sum(context, input))
}
fn parse_sum<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, CustomParseError<'i>>>
{
use ::cssparser::Token::*;
use self::CalcExpression::*;
let mut currentSum = Self::parse_product(context, input)?;
loop
{
let stateToResetParseToIfNotSum = input.state();
match *input.next_including_whitespace()?
{
WhiteSpace(_) =>
{
if input.is_exhausted()
{
break;
}
}
_ =>
{
input.reset(&stateToResetParseToIfNotSum);
break
}
}
let isAddition = match *input.next()?
{
Delim('+') => true,
Delim('-') => false,
ref unexpectedToken => return CustomParseError::unexpectedToken(unexpectedToken),
};
currentSum = if isAddition
{
Addition(Box::new(currentSum), Box::new(Self::parse_product(context, input)?))
}
else
{
Subtraction(Box::new(currentSum), Box::new(Self::parse_product(context, input)?))
}
}
Ok(currentSum)
}
fn parse_product<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, CustomParseError<'i>>>
{
use ::cssparser::Token::*;
use self::CalcExpression::*;
let mut currentProduct = Self::parse_one(context, input)?;
loop
{
let stateToResetParseToIfNotProduct = input.state();
match *input.next()?
{
Delim('*') =>
{
currentProduct = Multiplication(Box::new(currentProduct), Box::new(Self::parse_one(context, input)?));
}
Delim('/') =>
{
currentProduct = Division(Box::new(currentProduct), Box::new(Self::parse_one(context, input)?));
}
_ =>
{
input.reset(&stateToResetParseToIfNotProduct);
break
}
}
}
Ok(currentProduct)
}
fn parse_one<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, CustomParseError<'i>>>
{
let either = U::parse_one_inside_calc_function(context, input)?;
if either.is_left()
{
Ok(CalcExpression::CalculablePropertyValue(either.left().unwrap()))
}
else
{
Ok(either.right().unwrap())
}
}
#[inline(always)]
pub(crate) fn parse_parentheses<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Either<CalculablePropertyValue<U>, CalcExpression<U>>, ParseError<'i, CustomParseError<'i>>>
{
Ok(Right(CalcExpression::Parentheses(Box::new(CalcExpression::parse(context, input)?))))
}
}