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