lewp_css/domain/expressions/
calc_expression.rs1use {
5 super::{CalculablePropertyValue, Expression},
6 crate::{
7 domain::units::{
8 conversions::{
9 AttributeConversion,
10 CssVariableConversion,
11 FontRelativeLengthConversion,
12 PercentageConversion,
13 ViewportPercentageLengthConversion,
14 },
15 Unit,
16 },
17 parsers::ParserContext,
18 CustomParseError,
19 },
20 cssparser::{ParseError, Parser, ToCss},
21 either::{Either, Right},
22 std::fmt,
23};
24
25#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
26pub enum CalcExpression<U: Unit> {
27 CalculablePropertyValue(CalculablePropertyValue<U>),
28
29 Number(U::Number),
30
31 Parentheses(Box<CalcExpression<U>>),
32
33 Addition(Box<CalcExpression<U>>, Box<CalcExpression<U>>),
34
35 Subtraction(Box<CalcExpression<U>>, Box<CalcExpression<U>>),
36
37 Multiplication(Box<CalcExpression<U>>, Box<CalcExpression<U>>),
38
39 Division(Box<CalcExpression<U>>, Box<CalcExpression<U>>),
40}
41
42impl<U: Unit> Default for CalcExpression<U> {
43 #[inline(always)]
44 fn default() -> Self {
45 CalcExpression::CalculablePropertyValue(
46 CalculablePropertyValue::default(),
47 )
48 }
49}
50
51impl<U: Unit> ToCss for CalcExpression<U> {
52 fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
53 use self::CalcExpression::*;
54
55 match *self {
56 CalculablePropertyValue(ref calculable) => calculable.to_css(dest),
57
58 Number(ref number) => number.to_css(dest),
59
60 Parentheses(ref calcFunctionBody) => {
61 dest.write_char('(')?;
62 calcFunctionBody.to_css(dest)?;
63 dest.write_char(')')
64 }
65
66 Addition(ref lhs, ref rhs) => {
67 lhs.to_css(dest)?;
68 dest.write_str(" + ")?;
70 rhs.to_css(dest)
71 }
72
73 Subtraction(ref lhs, ref rhs) => {
74 lhs.to_css(dest)?;
75 dest.write_str(" - ")?;
77 rhs.to_css(dest)
78 }
79
80 Multiplication(ref lhs, ref rhs) => {
81 lhs.to_css(dest)?;
82 dest.write_char('*')?;
83 rhs.to_css(dest)
84 }
85
86 Division(ref lhs, ref rhs) => {
87 lhs.to_css(dest)?;
88 dest.write_char('/')?;
89 rhs.to_css(dest)
90 }
91 }
92 }
93}
94
95impl<U: Unit> Expression<U> for CalcExpression<U> {
96 #[inline(always)]
101 fn evaluate<
102 Conversion: FontRelativeLengthConversion<U::Number>
103 + ViewportPercentageLengthConversion<U::Number>
104 + PercentageConversion<U::Number>
105 + AttributeConversion<U>
106 + CssVariableConversion,
107 >(
108 &self,
109 conversion: &Conversion,
110 ) -> Option<U::Number> {
111 use self::CalcExpression::*;
112
113 match *self {
114 CalculablePropertyValue(ref calculable) => {
115 calculable.evaluate(conversion)
116 }
117
118 Number(number) => Some(number),
119
120 Parentheses(ref subExpression) => {
121 subExpression.evaluate(conversion)
122 }
123
124 Addition(ref lhsSubExpression, ref rhsSubExpression) => match (
125 lhsSubExpression.evaluate(conversion),
126 rhsSubExpression.evaluate(conversion),
127 ) {
128 (Some(lhs), Some(rhs)) => Some(lhs + rhs),
129 _ => None,
130 },
131
132 Subtraction(ref lhsSubExpression, ref rhsSubExpression) => match (
133 lhsSubExpression.evaluate(conversion),
134 rhsSubExpression.evaluate(conversion),
135 ) {
136 (Some(lhs), Some(rhs)) => Some(lhs - rhs),
137 _ => None,
138 },
139
140 Multiplication(ref lhsSubExpression, ref rhsSubExpression) => {
141 match (
142 lhsSubExpression.evaluate(conversion),
143 rhsSubExpression.evaluate(conversion),
144 ) {
145 (Some(lhs), Some(rhs)) => Some(lhs * rhs),
146 _ => None,
147 }
148 }
149
150 Division(ref lhsSubExpression, ref rhsSubExpression) => match (
151 lhsSubExpression.evaluate(conversion),
152 rhsSubExpression.evaluate(conversion),
153 ) {
154 (Some(lhs), Some(rhs)) => Some(lhs / rhs),
155 _ => None,
156 },
157 }
158 }
159}
160
161impl<U: Unit> CalcExpression<U> {
162 #[inline(always)]
177 pub(crate) fn parse<'i, 't>(
178 context: &ParserContext,
179 input: &mut Parser<'i, 't>,
180 ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
181 input.parse_nested_block(|input| Self::parse_sum(context, input))
182 }
183
184 fn parse_sum<'i, 't>(
188 context: &ParserContext,
189 input: &mut Parser<'i, 't>,
190 ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
191 use {self::CalcExpression::*, cssparser::Token::*};
192
193 let mut currentSum = Self::parse_product(context, input)?;
194
195 loop {
196 let stateToResetParseToIfNotSum = input.state();
197 match *input.next_including_whitespace()? {
198 WhiteSpace(_) => {
199 if input.is_exhausted() {
201 break;
202 }
203 }
204
205 _ => {
206 input.reset(&stateToResetParseToIfNotSum);
207 break;
208 }
209 }
210
211 let isAddition = match *input.next()? {
212 Delim('+') => true,
213
214 Delim('-') => false,
215
216 ref unexpectedToken => {
217 return CustomParseError::unexpectedToken(unexpectedToken)
218 }
219 };
220
221 currentSum = if isAddition {
222 Addition(
223 Box::new(currentSum),
224 Box::new(Self::parse_product(context, input)?),
225 )
226 } else {
227 Subtraction(
228 Box::new(currentSum),
229 Box::new(Self::parse_product(context, input)?),
230 )
231 }
232 }
233
234 Ok(currentSum)
235 }
236
237 fn parse_product<'i, 't>(
246 context: &ParserContext,
247 input: &mut Parser<'i, 't>,
248 ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
249 use {self::CalcExpression::*, cssparser::Token::*};
250
251 let mut currentProduct = Self::parse_one(context, input)?;
252
253 loop {
254 let stateToResetParseToIfNotProduct = input.state();
255 match *input.next()? {
256 Delim('*') => {
257 currentProduct = Multiplication(
258 Box::new(currentProduct),
259 Box::new(Self::parse_one(context, input)?),
260 );
261 }
262
263 Delim('/') => {
264 currentProduct = Division(
265 Box::new(currentProduct),
266 Box::new(Self::parse_one(context, input)?),
267 );
268 }
269
270 _ => {
271 input.reset(&stateToResetParseToIfNotProduct);
272 break;
273 }
274 }
275 }
276
277 Ok(currentProduct)
278 }
279
280 fn parse_one<'i, 't>(
281 context: &ParserContext,
282 input: &mut Parser<'i, 't>,
283 ) -> Result<Self, ParseError<'i, CustomParseError<'i>>> {
284 let either = U::parse_one_inside_calc_function(context, input)?;
285 if either.is_left() {
286 Ok(CalcExpression::CalculablePropertyValue(
287 either.left().unwrap(),
288 ))
289 } else {
290 Ok(either.right().unwrap())
291 }
292 }
293
294 #[inline(always)]
295 pub(crate) fn parse_parentheses<'i, 't>(
296 context: &ParserContext,
297 input: &mut Parser<'i, 't>,
298 ) -> Result<
299 Either<CalculablePropertyValue<U>, CalcExpression<U>>,
300 ParseError<'i, CustomParseError<'i>>,
301 > {
302 Ok(Right(CalcExpression::Parentheses(Box::new(
303 CalcExpression::parse(context, input)?,
304 ))))
305 }
306}