float_pigment_css/parser/property_value/
calc.rs

1use alloc::string::ToString;
2use core::{f32::consts::PI, marker::PhantomData};
3
4use super::*;
5
6impl CalcExpr {
7    /// Check if the expression is a number.
8    pub fn is_number(&self) -> bool {
9        if let CalcExpr::Number(_) = self {
10            return true;
11        }
12        false
13    }
14
15    /// Get the number value if the expression is a number.
16    pub fn get_number(&self) -> Option<&Number> {
17        match self {
18            CalcExpr::Number(num) => Some(num.as_ref()),
19            _ => None,
20        }
21    }
22
23    /// Check if the expression is a static number `0`.
24    pub fn is_zero(&self) -> bool {
25        match self {
26            CalcExpr::Number(v) => match *v.as_ref() {
27                Number::F32(f) => f == 0.,
28                Number::I32(i) => i == 0,
29                _ => false,
30            },
31            _ => unreachable!(),
32        }
33    }
34
35    /// Check if the expression is a literal value, a.k.a. does not contain any operator.
36    pub fn is_specified_value(&self) -> bool {
37        match self {
38            CalcExpr::Angle(angle) => !matches!(angle.as_ref(), Angle::Calc(_)),
39            CalcExpr::Number(num) => !matches!(num.as_ref(), Number::Calc(_)),
40            CalcExpr::Length(length) => !matches!(
41                length.as_ref(),
42                Length::Expr(_) | Length::Auto | Length::Undefined
43            ),
44            _ => false,
45        }
46    }
47
48    /// Multiply `rhs` if `mul` is true; divide `rhs` otherwise.
49    pub fn mul_div(&self, rhs: f32, mul: bool) -> Self {
50        match self {
51            CalcExpr::Angle(angle) => {
52                let v = if mul {
53                    angle.to_f32() * rhs
54                } else {
55                    angle.to_f32() / rhs
56                };
57                let ret = match angle.as_ref() {
58                    Angle::Deg(_) => Angle::Deg(v),
59                    Angle::Grad(_) => Angle::Grad(v),
60                    Angle::Rad(_) => Angle::Rad(v),
61                    Angle::Turn(_) => Angle::Turn(v),
62                    Angle::Calc(_) => unreachable!(),
63                };
64                CalcExpr::Angle(Box::new(ret))
65            }
66            CalcExpr::Length(length) => {
67                let v = if mul {
68                    length.to_f32() * rhs
69                } else {
70                    length.to_f32() / rhs
71                };
72                let ret = match length.as_ref() {
73                    Length::Px(_) => Length::Px(v),
74                    Length::Em(_) => Length::Em(v),
75                    Length::Rpx(_) => Length::Rpx(v),
76                    Length::Ratio(_) => Length::Ratio(v),
77                    Length::Rem(_) => Length::Rem(v),
78                    Length::Vh(_) => Length::Vh(v),
79                    Length::Vw(_) => Length::Vw(v),
80                    Length::Vmax(_) => Length::Vmax(v),
81                    Length::Vmin(_) => Length::Vmin(v),
82                    _ => unreachable!(),
83                };
84                CalcExpr::Length(Box::new(ret))
85            }
86            CalcExpr::Number(num) => {
87                let ret = if mul {
88                    num.to_f32() * rhs
89                } else {
90                    num.to_f32() / rhs
91                };
92                CalcExpr::Number(Box::new(Number::F32(ret)))
93            }
94            _ => unreachable!(),
95        }
96    }
97}
98
99impl Angle {
100    /// Convert to `rad` form.
101    ///
102    /// Panics if it is an expression.
103    pub fn to_rad(&self) -> Angle {
104        match self {
105            Angle::Rad(rad) => Angle::Rad(*rad),
106            Angle::Deg(deg) => Angle::Rad(deg * PI / 180.),
107            Angle::Grad(grad) => Angle::Rad(grad * PI / 200.),
108            Angle::Turn(turn) => Angle::Rad(turn * 2. * PI),
109            _ => panic!("not a literal value"),
110        }
111    }
112
113    /// Create a new value from a turn-based value.
114    pub fn from_ratio(turn: f32) -> Angle {
115        Angle::Rad(turn * 2. * PI)
116    }
117
118    /// Erase the unit and leave the `f32` value.
119    ///
120    /// Panics if it is an expression.
121    pub fn to_f32(&self) -> f32 {
122        match self {
123            Angle::Calc(_) => panic!("not a literal value"),
124            Angle::Rad(v) => *v,
125            Angle::Deg(v) => *v,
126            Angle::Grad(v) => *v,
127            Angle::Turn(v) => *v,
128        }
129    }
130}
131
132impl Length {
133    /// Erase the unit and leave the `f32` value.
134    ///
135    /// Panics if it is an expression or it does not contain a unit.
136    pub fn to_f32(&self) -> f32 {
137        match self {
138            Length::Px(v) => *v,
139            Length::Em(v) => *v,
140            Length::Rpx(v) => *v,
141            Length::Ratio(v) => *v,
142            Length::Rem(v) => *v,
143            Length::Vh(v) => *v,
144            Length::Vw(v) => *v,
145            Length::Vmax(v) => *v,
146            Length::Vmin(v) => *v,
147            Length::Expr(_) | Length::Auto | Length::Undefined => panic!("not a literal value"),
148        }
149    }
150}
151
152pub(crate) struct ComputeCalcExpr<T> {
153    _mark: PhantomData<*const T>,
154}
155
156impl ComputeCalcExpr<Angle> {
157    pub fn try_compute(expr: &CalcExpr) -> Option<Angle> {
158        match expr {
159            CalcExpr::Angle(angle) => Some(angle.as_ref().clone().to_rad()),
160            CalcExpr::Plus(l, r) | CalcExpr::Sub(l, r) => {
161                let l = Self::try_compute(l)?;
162
163                let r = Self::try_compute(r)?;
164                match expr {
165                    CalcExpr::Plus(_, _) => Some(Angle::Rad(l.to_f32() + r.to_f32())),
166                    CalcExpr::Sub(_, _) => Some(Angle::Rad(l.to_f32() - r.to_f32())),
167                    _ => None,
168                }
169            }
170            CalcExpr::Mul(l, r) | CalcExpr::Div(l, r) => {
171                let l = Self::try_compute(l)?;
172                let r = ComputeCalcExpr::<Number>::try_compute(r)?;
173                match expr {
174                    CalcExpr::Mul(_, _) => Some(Angle::Rad(l.to_f32() * r.to_f32())),
175                    CalcExpr::Div(_, _) => Some(Angle::Rad(l.to_f32() / r.to_f32())),
176                    _ => None,
177                }
178            }
179            CalcExpr::Length(l) => match l.as_ref() {
180                Length::Ratio(ratio) => Some(Angle::from_ratio(*ratio)),
181                _ => None,
182            },
183            _ => None,
184        }
185    }
186}
187
188impl ComputeCalcExpr<Number> {
189    pub fn try_compute(expr: &CalcExpr) -> Option<Number> {
190        match expr {
191            CalcExpr::Number(num) => Some(*num.clone()),
192            CalcExpr::Plus(l, r)
193            | CalcExpr::Sub(l, r)
194            | CalcExpr::Mul(l, r)
195            | CalcExpr::Div(l, r) => {
196                let l = Self::try_compute(l)?;
197                let r = Self::try_compute(r)?;
198                match expr {
199                    CalcExpr::Plus(_, _) => Some(Number::F32(l.to_f32() + r.to_f32())),
200                    CalcExpr::Sub(_, _) => Some(Number::F32(l.to_f32() - r.to_f32())),
201                    CalcExpr::Mul(_, _) => Some(Number::F32(l.to_f32() * r.to_f32())),
202                    CalcExpr::Div(_, _) => Some(Number::F32(l.to_f32() / r.to_f32())),
203                    _ => None,
204                }
205            }
206            _ => None,
207        }
208    }
209}
210
211#[derive(Copy, Clone, PartialEq, Eq, Debug)]
212pub(crate) enum LengthUnit {
213    Px,
214    Vw,
215    Vh,
216    Rem,
217    Rpx,
218    Em,
219    Ratio,
220    Vmin,
221    Vmax,
222
223    Undefined,
224    Expr,
225    Auto,
226}
227
228impl LengthUnit {
229    pub(crate) fn is_specified_unit(&self) -> bool {
230        !matches!(self, Self::Undefined | Self::Expr | Self::Auto)
231    }
232    pub(crate) fn to_length(unit: LengthUnit, value: f32) -> Length {
233        match unit {
234            LengthUnit::Auto => Length::Auto,
235            LengthUnit::Undefined => Length::Undefined,
236            LengthUnit::Expr => todo!(),
237            LengthUnit::Px => Length::Px(value),
238            LengthUnit::Vw => Length::Vw(value),
239            LengthUnit::Vh => Length::Vh(value),
240            LengthUnit::Rem => Length::Rem(value),
241            LengthUnit::Em => Length::Em(value),
242            LengthUnit::Rpx => Length::Rpx(value),
243            LengthUnit::Ratio => Length::Ratio(value),
244            LengthUnit::Vmin => Length::Vmin(value),
245            LengthUnit::Vmax => Length::Vmax(value),
246        }
247    }
248}
249
250impl ComputeCalcExpr<Length> {
251    pub(crate) fn try_compute(expr: &CalcExpr) -> Option<Length> {
252        match expr {
253            CalcExpr::Length(l) => Some(*l.clone()),
254            CalcExpr::Angle(_) | CalcExpr::Number(_) => None,
255            CalcExpr::Plus(l, r) | CalcExpr::Sub(l, r) => {
256                let l = Self::try_compute(l)?;
257                let r = Self::try_compute(r)?;
258                let ((l_unit, l_v), (r_unit, r_v)) =
259                    (Self::length_unit_value(&l), Self::length_unit_value(&r));
260                // TODO
261                // merge same unit
262                if (l_unit == r_unit) && l_unit.is_specified_unit() {
263                    match expr {
264                        CalcExpr::Plus(_, _) => {
265                            return Some(LengthUnit::to_length(l_unit, l_v + r_v));
266                        }
267                        CalcExpr::Sub(_, _) => {
268                            return Some(LengthUnit::to_length(l_unit, l_v - r_v));
269                        }
270                        _ => unreachable!(),
271                    }
272                }
273                //
274                None
275            }
276            _ => None,
277        }
278    }
279    pub(crate) fn length_unit_value(length: &Length) -> (LengthUnit, f32) {
280        match length {
281            Length::Px(v) => (LengthUnit::Px, *v),
282            Length::Em(v) => (LengthUnit::Em, *v),
283            Length::Ratio(v) => (LengthUnit::Ratio, *v),
284            Length::Rem(v) => (LengthUnit::Rem, *v),
285            Length::Rpx(v) => (LengthUnit::Rpx, *v),
286            Length::Vh(v) => (LengthUnit::Vh, *v),
287            Length::Vw(v) => (LengthUnit::Vw, *v),
288            Length::Vmax(v) => (LengthUnit::Vmax, *v),
289            Length::Vmin(v) => (LengthUnit::Vmin, *v),
290            Length::Undefined => (LengthUnit::Undefined, f32::NAN),
291            Length::Auto => (LengthUnit::Auto, f32::NAN),
292            Length::Expr(_) => (LengthUnit::Expr, f32::NAN),
293        }
294    }
295}
296
297#[inline(never)]
298fn next_operator<'a, 't: 'a, 'i: 't>(parser: &'a mut Parser<'i, 't>) -> Option<Operator> {
299    let mut ret = None;
300    let _ = parser.try_parse::<_, (), ParseError<'_, CustomError>>(|parser| {
301        let token = parser.next()?.clone();
302        ret = match token {
303            Token::Delim(c) => match c {
304                '+' => Some(Operator::Plus),
305                '-' => Some(Operator::Sub),
306                '*' => Some(Operator::Mul),
307                '/' => Some(Operator::Div),
308                _ => None,
309            },
310            _ => None,
311        };
312        Err(parser.new_custom_error(CustomError::Unsupported))
313    });
314    ret
315}
316
317#[derive(Debug, PartialEq, Eq)]
318enum Operator {
319    Plus,
320    Sub,
321    Mul,
322    Div,
323}
324
325#[derive(Debug, Copy, Clone, Eq, PartialEq)]
326pub(crate) enum ExpectValueType {
327    Number,
328    NumberAndLength,
329    NumberAndAngle,
330    AngleAndLength,
331}
332
333#[inline(never)]
334fn combine_calc_expr(operator: Operator, lhs: CalcExpr, rhs: CalcExpr) -> CalcExpr {
335    let mut final_lhs = Box::new(lhs.clone());
336    if let CalcExpr::Length(length_expr) = lhs {
337        if let Length::Expr(LengthExpr::Calc(length_expr)) = *length_expr {
338            final_lhs = length_expr
339        }
340    }
341    let mut final_rhs = Box::new(rhs.clone());
342    if let CalcExpr::Length(length_expr) = rhs {
343        if let Length::Expr(LengthExpr::Calc(length_expr)) = *length_expr {
344            final_rhs = length_expr
345        }
346    }
347
348    match operator {
349        Operator::Plus => CalcExpr::Plus(final_lhs, final_rhs),
350        Operator::Sub => CalcExpr::Sub(final_lhs, final_rhs),
351        Operator::Mul => CalcExpr::Mul(final_lhs, final_rhs),
352        Operator::Div => CalcExpr::Div(final_lhs, final_rhs),
353    }
354}
355
356#[inline(never)]
357pub(crate) fn parse_calc_inner<'a, 't: 'a, 'i: 't>(
358    parser: &'a mut Parser<'i, 't>,
359    properties: &mut Vec<PropertyMeta>,
360    st: &mut ParseState,
361    expect_type: ExpectValueType,
362) -> Result<CalcExpr, ParseError<'i, CustomError>> {
363    parser.parse_nested_block(|parser| {
364        let ret = parse_calc_sum_expr(parser, properties, st, expect_type)?;
365        Ok(ret)
366    })
367}
368
369#[inline(never)]
370fn parse_calc_sum_expr<'a, 't: 'a, 'i: 't>(
371    parser: &'a mut Parser<'i, 't>,
372    properties: &mut Vec<PropertyMeta>,
373    st: &mut ParseState,
374    expect_type: ExpectValueType,
375) -> Result<CalcExpr, ParseError<'i, CustomError>> {
376    let mut expr = parse_calc_product_expr(parser, properties, st, expect_type)?;
377    loop {
378        let op = next_operator(parser);
379        if !(op.is_some() && (op == Some(Operator::Plus) || op == Some(Operator::Sub))) {
380            return Ok(expr);
381        }
382        /*
383         * The + and - operators must be surrounded by whitespace.
384         */
385        parser.next_including_whitespace()?;
386        parser.next()?;
387        parser.next_including_whitespace()?;
388        let rhs = parse_calc_product_expr(parser, properties, st, expect_type)?;
389        if let Some(op) = op {
390            match op {
391                Operator::Plus | Operator::Sub => {
392                    expr = combine_calc_expr(op, expr, rhs);
393                }
394                _ => unreachable!(),
395            }
396        } else {
397            return Err(parser.new_custom_error(CustomError::Unsupported));
398        }
399    }
400}
401
402#[inline(never)]
403fn parse_calc_product_expr<'a, 't: 'a, 'i: 't>(
404    parser: &'a mut Parser<'i, 't>,
405    properties: &mut Vec<PropertyMeta>,
406    st: &mut ParseState,
407    expect_type: ExpectValueType,
408) -> Result<CalcExpr, ParseError<'i, CustomError>> {
409    let mut expr = parse_calc_parenthesis_expr(parser, properties, st, expect_type)?;
410    loop {
411        let op = next_operator(parser);
412        if !(op.is_some() && (op == Some(Operator::Mul) || op == Some(Operator::Div))) {
413            return Ok(expr);
414        }
415        /*
416         * The * and / operators do not require whitespace, but adding it for consistency is recommended.
417         */
418        parser.next()?;
419        let rhs = parse_calc_parenthesis_expr(parser, properties, st, expect_type)?;
420        match op.unwrap() {
421            Operator::Mul => {
422                if expr.is_number() && rhs.is_number() {
423                    let l_v = expr.get_number().unwrap();
424                    let r_v = rhs.get_number().unwrap();
425                    expr = CalcExpr::Number(Box::new(Number::F32(l_v.to_f32() * r_v.to_f32())));
426                } else if expr.is_number() || rhs.is_number() {
427                    if expr.is_number() {
428                        if rhs.is_specified_value() {
429                            expr = rhs.mul_div(expr.get_number().unwrap().to_f32(), true);
430                        } else {
431                            expr = combine_calc_expr(Operator::Mul, rhs, expr);
432                        }
433                    } else if expr.is_specified_value() {
434                        expr = expr.mul_div(rhs.get_number().unwrap().to_f32(), true);
435                    } else {
436                        expr = combine_calc_expr(Operator::Mul, expr, rhs);
437                    }
438                } else {
439                    return Err(parser.new_custom_error(CustomError::Unsupported));
440                }
441            }
442            Operator::Div => {
443                // NAN & zero
444                if !rhs.is_number() || rhs.is_zero() {
445                    return Err(
446                        parser.new_custom_error(CustomError::Reason("divided by zero".to_string()))
447                    );
448                }
449                if expr.is_number() && rhs.is_number() {
450                    let l_v = expr.get_number().unwrap();
451                    let r_v = rhs.get_number().unwrap();
452                    expr = CalcExpr::Number(Box::new(Number::F32(l_v.to_f32() / r_v.to_f32())));
453                } else if expr.is_specified_value() && rhs.is_number() {
454                    expr = expr.mul_div(rhs.get_number().unwrap().to_f32(), false)
455                } else {
456                    expr = combine_calc_expr(Operator::Div, expr, rhs);
457                }
458            }
459            _ => unreachable!(),
460        }
461    }
462}
463
464#[inline(never)]
465fn parse_calc_parenthesis_expr<'a, 't: 'a, 'i: 't>(
466    parser: &'a mut Parser<'i, 't>,
467    properties: &mut Vec<PropertyMeta>,
468    st: &mut ParseState,
469    expect_type: ExpectValueType,
470) -> Result<CalcExpr, ParseError<'i, CustomError>> {
471    let value = parse_calc_value(parser, properties, st, expect_type);
472    if value.is_ok() {
473        return value;
474    }
475    parser.try_parse::<_, CalcExpr, ParseError<'_, CustomError>>(|parser| {
476        parser.expect_parenthesis_block()?;
477        parser.parse_nested_block(|parser| parse_calc_sum_expr(parser, properties, st, expect_type))
478    })
479}
480
481#[inline(never)]
482fn parse_calc_value<'a, 't: 'a, 'i: 't>(
483    parser: &'a mut Parser<'i, 't>,
484    properties: &mut Vec<PropertyMeta>,
485    st: &mut ParseState,
486    expect_type: ExpectValueType,
487) -> Result<CalcExpr, ParseError<'i, CustomError>> {
488    // match number
489    let num =
490        parser.try_parse::<_, Number, ParseError<'_, _>>(|parser| number(parser, properties, st));
491    if let Ok(num) = num {
492        return Ok(CalcExpr::Number(Box::new(num)));
493    }
494    // match length
495    let length =
496        parser.try_parse::<_, Length, ParseError<'_, _>>(|parser| length(parser, properties, st));
497    if let Ok(length) = length {
498        if expect_type == ExpectValueType::NumberAndLength
499            || expect_type == ExpectValueType::AngleAndLength
500        {
501            return Ok(CalcExpr::Length(Box::new(length)));
502        }
503    }
504    // match angle
505    let angle =
506        parser.try_parse::<_, Angle, ParseError<'_, _>>(|parser| angle(parser, properties, st));
507    if let Ok(angle) = angle {
508        if expect_type == ExpectValueType::NumberAndAngle
509            || expect_type == ExpectValueType::AngleAndLength
510        {
511            return Ok(CalcExpr::Angle(Box::new(angle)));
512        }
513    }
514    Err(parser.new_custom_error(CustomError::Unmatched))
515}