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 final_lhs = match lhs {
337 CalcExpr::Length(length_expr) => match *length_expr {
338 Length::Expr(LengthExpr::Calc(calc_expr)) => calc_expr,
339 other => Box::new(CalcExpr::Length(Box::new(other))),
340 },
341 other => Box::new(other),
342 };
343 let final_rhs = match rhs {
344 CalcExpr::Length(length_expr) => match *length_expr {
345 Length::Expr(LengthExpr::Calc(calc_expr)) => calc_expr,
346 other => Box::new(CalcExpr::Length(Box::new(other))),
347 },
348 other => Box::new(other),
349 };
350 match operator {
351 Operator::Plus => CalcExpr::Plus(final_lhs, final_rhs),
352 Operator::Sub => CalcExpr::Sub(final_lhs, final_rhs),
353 Operator::Mul => CalcExpr::Mul(final_lhs, final_rhs),
354 Operator::Div => CalcExpr::Div(final_lhs, final_rhs),
355 }
356}
357
358#[inline(never)]
359pub(crate) fn parse_calc_inner<'a, 't: 'a, 'i: 't>(
360 parser: &'a mut Parser<'i, 't>,
361 properties: &mut Vec<PropertyMeta>,
362 st: &mut ParseState,
363 expect_type: ExpectValueType,
364) -> Result<CalcExpr, ParseError<'i, CustomError>> {
365 parser.parse_nested_block(|parser| {
366 let ret = parse_calc_sum_expr(parser, properties, st, expect_type)?;
367 Ok(ret)
368 })
369}
370
371#[inline(never)]
372fn parse_calc_sum_expr<'a, 't: 'a, 'i: 't>(
373 parser: &'a mut Parser<'i, 't>,
374 properties: &mut Vec<PropertyMeta>,
375 st: &mut ParseState,
376 expect_type: ExpectValueType,
377) -> Result<CalcExpr, ParseError<'i, CustomError>> {
378 let mut expr = parse_calc_product_expr(parser, properties, st, expect_type)?;
379 loop {
380 let op = next_operator(parser);
381 if !(op.is_some() && (op == Some(Operator::Plus) || op == Some(Operator::Sub))) {
382 return Ok(expr);
383 }
384 parser.next_including_whitespace()?;
388 parser.next()?;
389 parser.next_including_whitespace()?;
390 let rhs = parse_calc_product_expr(parser, properties, st, expect_type)?;
391 if let Some(op) = op {
392 match op {
393 Operator::Plus | Operator::Sub => {
394 expr = combine_calc_expr(op, expr, rhs);
395 }
396 _ => unreachable!(),
397 }
398 } else {
399 return Err(parser.new_custom_error(CustomError::Unsupported));
400 }
401 }
402}
403
404#[inline(never)]
405fn parse_calc_product_expr<'a, 't: 'a, 'i: 't>(
406 parser: &'a mut Parser<'i, 't>,
407 properties: &mut Vec<PropertyMeta>,
408 st: &mut ParseState,
409 expect_type: ExpectValueType,
410) -> Result<CalcExpr, ParseError<'i, CustomError>> {
411 let mut expr = parse_calc_parenthesis_expr(parser, properties, st, expect_type)?;
412 loop {
413 let op = next_operator(parser);
414 if !(op.is_some() && (op == Some(Operator::Mul) || op == Some(Operator::Div))) {
415 return Ok(expr);
416 }
417 parser.next()?;
421 let rhs = parse_calc_parenthesis_expr(parser, properties, st, expect_type)?;
422 match op.unwrap() {
423 Operator::Mul => {
424 if expr.is_number() && rhs.is_number() {
425 let l_v = expr.get_number().unwrap();
426 let r_v = rhs.get_number().unwrap();
427 expr = CalcExpr::Number(Box::new(Number::F32(l_v.to_f32() * r_v.to_f32())));
428 } else if expr.is_number() || rhs.is_number() {
429 if expr.is_number() {
430 if rhs.is_specified_value() {
431 expr = rhs.mul_div(expr.get_number().unwrap().to_f32(), true);
432 } else {
433 expr = combine_calc_expr(Operator::Mul, rhs, expr);
434 }
435 } else if expr.is_specified_value() {
436 expr = expr.mul_div(rhs.get_number().unwrap().to_f32(), true);
437 } else {
438 expr = combine_calc_expr(Operator::Mul, expr, rhs);
439 }
440 } else {
441 return Err(parser.new_custom_error(CustomError::Unsupported));
442 }
443 }
444 Operator::Div => {
445 if !rhs.is_number() || rhs.is_zero() {
447 return Err(
448 parser.new_custom_error(CustomError::Reason("divided by zero".to_string()))
449 );
450 }
451 if expr.is_number() && rhs.is_number() {
452 let l_v = expr.get_number().unwrap();
453 let r_v = rhs.get_number().unwrap();
454 expr = CalcExpr::Number(Box::new(Number::F32(l_v.to_f32() / r_v.to_f32())));
455 } else if expr.is_specified_value() && rhs.is_number() {
456 expr = expr.mul_div(rhs.get_number().unwrap().to_f32(), false)
457 } else {
458 expr = combine_calc_expr(Operator::Div, expr, rhs);
459 }
460 }
461 _ => unreachable!(),
462 }
463 }
464}
465
466#[inline(never)]
467fn parse_calc_parenthesis_expr<'a, 't: 'a, 'i: 't>(
468 parser: &'a mut Parser<'i, 't>,
469 properties: &mut Vec<PropertyMeta>,
470 st: &mut ParseState,
471 expect_type: ExpectValueType,
472) -> Result<CalcExpr, ParseError<'i, CustomError>> {
473 let value = parse_calc_value(parser, properties, st, expect_type);
474 if value.is_ok() {
475 return value;
476 }
477 parser.try_parse::<_, CalcExpr, ParseError<'_, CustomError>>(|parser| {
478 parser.expect_parenthesis_block()?;
479 parser.parse_nested_block(|parser| parse_calc_sum_expr(parser, properties, st, expect_type))
480 })
481}
482
483#[inline(never)]
484fn parse_calc_value<'a, 't: 'a, 'i: 't>(
485 parser: &'a mut Parser<'i, 't>,
486 properties: &mut Vec<PropertyMeta>,
487 st: &mut ParseState,
488 expect_type: ExpectValueType,
489) -> Result<CalcExpr, ParseError<'i, CustomError>> {
490 let num =
492 parser.try_parse::<_, Number, ParseError<'_, _>>(|parser| number(parser, properties, st));
493 if let Ok(num) = num {
494 return Ok(CalcExpr::Number(Box::new(num)));
495 }
496 let length = parser.try_parse::<_, Length, ParseError<'_, _>>(|parser| {
498 length_percentage_auto(parser, properties, st)
499 });
500 if let Ok(length) = length {
501 if expect_type == ExpectValueType::NumberAndLength
502 || expect_type == ExpectValueType::AngleAndLength
503 {
504 return Ok(CalcExpr::Length(Box::new(length)));
505 }
506 }
507 let angle =
509 parser.try_parse::<_, Angle, ParseError<'_, _>>(|parser| angle(parser, properties, st));
510 if let Ok(angle) = angle {
511 if expect_type == ExpectValueType::NumberAndAngle
512 || expect_type == ExpectValueType::AngleAndLength
513 {
514 return Ok(CalcExpr::Angle(Box::new(angle)));
515 }
516 }
517 Err(parser.new_custom_error(CustomError::Unmatched))
518}