Serenity Operating System
1/*
2 * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#pragma once
8
9#include <AK/String.h>
10#include <AK/Types.h>
11#include <math.h>
12
13namespace Web::CSS {
14
15class Number {
16public:
17 enum class Type {
18 Number,
19 IntegerWithExplicitSign, // This only exists for the nightmarish An+B parsing algorithm
20 Integer
21 };
22
23 Number()
24 : m_value(0)
25 , m_type(Type::Number)
26 {
27 }
28 Number(Type type, float value)
29 : m_value(value)
30 , m_type(type)
31 {
32 }
33
34 float value() const { return m_value; }
35 i64 integer_value() const
36 {
37 // https://www.w3.org/TR/css-values-4/#numeric-types
38 // When a value cannot be explicitly supported due to range/precision limitations, it must be converted
39 // to the closest value supported by the implementation, but how the implementation defines "closest"
40 // is explicitly undefined as well.
41 return llroundf(m_value);
42 }
43 bool is_integer() const { return m_type == Type::Integer || m_type == Type::IntegerWithExplicitSign; }
44 bool is_integer_with_explicit_sign() const { return m_type == Type::IntegerWithExplicitSign; }
45
46 Number operator+(Number const& other) const
47 {
48 if (is_integer() && other.is_integer())
49 return { Type::Integer, m_value + other.m_value };
50 return { Type::Number, m_value + other.m_value };
51 }
52
53 Number operator-(Number const& other) const
54 {
55 if (is_integer() && other.is_integer())
56 return { Type::Integer, m_value - other.m_value };
57 return { Type::Number, m_value - other.m_value };
58 }
59
60 Number operator*(Number const& other) const
61 {
62 if (is_integer() && other.is_integer())
63 return { Type::Integer, m_value * other.m_value };
64 return { Type::Number, m_value * other.m_value };
65 }
66
67 Number operator/(Number const& other) const
68 {
69 return { Type::Number, m_value / other.m_value };
70 }
71
72 ErrorOr<String> to_string() const
73 {
74 if (m_type == Type::IntegerWithExplicitSign)
75 return String::formatted("{:+}", m_value);
76 return String::number(m_value);
77 }
78
79 bool operator==(Number const& other) const
80 {
81 return m_type == other.m_type && m_value == other.m_value;
82 }
83
84private:
85 float m_value { 0 };
86 Type m_type;
87};
88}
89
90template<>
91struct AK::Formatter<Web::CSS::Number> : Formatter<StringView> {
92 ErrorOr<void> format(FormatBuilder& builder, Web::CSS::Number const& number)
93 {
94 return Formatter<StringView>::format(builder, TRY(number.to_string()));
95 }
96};