1use alloc::string::ToString;
2use core::{f32::consts::PI, marker::PhantomData};
3
4use super::*;
5
6impl CalcExpr {
7 pub fn is_number(&self) -> bool {
9 if let CalcExpr::Number(_) = self {
10 return true;
11 }
12 false
13 }
14
15 pub fn get_number(&self) -> Option<&Number> {
17 match self {
18 CalcExpr::Number(num) => Some(num.as_ref()),
19 _ => None,
20 }
21 }
22
23 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 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 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 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 pub fn from_ratio(turn: f32) -> Angle {
115 Angle::Rad(turn * 2. * PI)
116 }
117
118 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 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 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 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 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 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 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 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 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 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}