Procházet zdrojové kódy

LibWeb: Use CSS::Number for Token numeric values

Sam Atkins před 3 roky
rodič
revize
fe372cd073

+ 18 - 18
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -1233,14 +1233,14 @@ Optional<MediaFeatureValue> Parser::parse_media_feature_value(MediaFeatureID med
 
     // Boolean (<mq-boolean> in the spec: a 1 or 0)
     if (media_feature_accepts_type(media_feature, MediaFeatureValueType::Boolean)) {
-        if (first.is(Token::Type::Number) && first.token().number_type() == Token::NumberType::Integer
+        if (first.is(Token::Type::Number) && first.token().number().is_integer()
             && (first.token().number_value() == 0 || first.token().number_value() == 1))
             return MediaFeatureValue(first.token().number_value());
     }
 
     // Integer
     if (media_feature_accepts_type(media_feature, MediaFeatureValueType::Integer)) {
-        if (first.is(Token::Type::Number) && first.token().number_type() == Token::NumberType::Integer)
+        if (first.is(Token::Type::Number) && first.token().number().is_integer())
             return MediaFeatureValue(first.token().number_value());
     }
 
@@ -2392,7 +2392,7 @@ RefPtr<StyleValue> Parser::parse_numeric_value(StyleComponentValueRule const& co
 {
     if (component_value.is(Token::Type::Number)) {
         auto number = component_value.token();
-        if (number.number_type() == Token::NumberType::Integer) {
+        if (number.number().is_integer()) {
             return NumericStyleValue::create_integer(number.to_integer());
         } else {
             return NumericStyleValue::create_float(number.number_value());
@@ -2462,9 +2462,9 @@ Optional<Color> Parser::parse_color(StyleComponentValueRule const& component_val
             auto g_val = params[1];
             auto b_val = params[2];
 
-            if (r_val.is(Token::NumberType::Integer)
-                && g_val.is(Token::NumberType::Integer)
-                && b_val.is(Token::NumberType::Integer)) {
+            if (r_val.is(Token::Type::Number) && r_val.number().is_integer()
+                && g_val.is(Token::Type::Number) && g_val.number().is_integer()
+                && b_val.is(Token::Type::Number) && b_val.number().is_integer()) {
 
                 auto r = r_val.to_integer();
                 auto g = g_val.to_integer();
@@ -2490,9 +2490,9 @@ Optional<Color> Parser::parse_color(StyleComponentValueRule const& component_val
             auto b_val = params[2];
             auto a_val = params[3];
 
-            if (r_val.is(Token::NumberType::Integer)
-                && g_val.is(Token::NumberType::Integer)
-                && b_val.is(Token::NumberType::Integer)
+            if (r_val.is(Token::Type::Number) && r_val.number().is_integer()
+                && g_val.is(Token::Type::Number) && g_val.number().is_integer()
+                && b_val.is(Token::Type::Number) && b_val.number().is_integer()
                 && a_val.is(Token::Type::Number)) {
 
                 auto r = r_val.to_integer();
@@ -2571,7 +2571,7 @@ Optional<Color> Parser::parse_color(StyleComponentValueRule const& component_val
         if (cv.is(Token::Type::Number) || cv.is(Token::Type::Dimension)) {
             // 1. If cv’s type flag is not "integer", return an error.
             //    This means that values that happen to use scientific notation, e.g., 5e5e5e, will fail to parse.
-            if (cv.token().number_type() != Token::NumberType::Integer)
+            if (!cv.token().number().is_integer())
                 return {};
 
             // 2. If cv’s value is less than zero, return an error.
@@ -4372,7 +4372,7 @@ Optional<Selector::SimpleSelector::ANPlusBPattern> Parser::parse_a_n_plus_b_patt
     auto is_n_dimension = [](StyleComponentValueRule const& value) -> bool {
         if (!value.is(Token::Type::Dimension))
             return false;
-        if (value.token().number_type() != Token::NumberType::Integer)
+        if (!value.token().number().is_integer())
             return false;
         if (!value.token().dimension_unit().equals_ignoring_case("n"sv))
             return false;
@@ -4381,7 +4381,7 @@ Optional<Selector::SimpleSelector::ANPlusBPattern> Parser::parse_a_n_plus_b_patt
     auto is_ndash_dimension = [](StyleComponentValueRule const& value) -> bool {
         if (!value.is(Token::Type::Dimension))
             return false;
-        if (value.token().number_type() != Token::NumberType::Integer)
+        if (!value.token().number().is_integer())
             return false;
         if (!value.token().dimension_unit().equals_ignoring_case("n-"sv))
             return false;
@@ -4390,7 +4390,7 @@ Optional<Selector::SimpleSelector::ANPlusBPattern> Parser::parse_a_n_plus_b_patt
     auto is_ndashdigit_dimension = [](StyleComponentValueRule const& value) -> bool {
         if (!value.is(Token::Type::Dimension))
             return false;
-        if (value.token().number_type() != Token::NumberType::Integer)
+        if (!value.token().number().is_integer())
             return false;
         auto dimension_unit = value.token().dimension_unit();
         if (!dimension_unit.starts_with("n-"sv, CaseSensitivity::CaseInsensitive))
@@ -4426,13 +4426,13 @@ Optional<Selector::SimpleSelector::ANPlusBPattern> Parser::parse_a_n_plus_b_patt
         return true;
     };
     auto is_integer = [](StyleComponentValueRule const& value) -> bool {
-        return value.is(Token::Type::Number) && value.token().is(Token::NumberType::Integer);
+        return value.is(Token::Type::Number) && value.token().number().is_integer();
     };
-    auto is_signed_integer = [is_integer](StyleComponentValueRule const& value) -> bool {
-        return is_integer(value) && value.token().is_integer_value_signed();
+    auto is_signed_integer = [](StyleComponentValueRule const& value) -> bool {
+        return value.is(Token::Type::Number) && value.token().number().is_integer_with_explicit_sign();
     };
-    auto is_signless_integer = [is_integer](StyleComponentValueRule const& value) -> bool {
-        return is_integer(value) && !value.token().is_integer_value_signed();
+    auto is_signless_integer = [](StyleComponentValueRule const& value) -> bool {
+        return value.is(Token::Type::Number) && !value.token().number().is_integer_with_explicit_sign();
     };
 
     // https://www.w3.org/TR/css-syntax-3/#the-anb-type

+ 10 - 10
Userland/Libraries/LibWeb/CSS/Parser/Token.cpp

@@ -43,11 +43,11 @@ String Token::to_string() const
     case Type::Delim:
         return m_value;
     case Type::Number:
-        return String::number(m_number_value);
+        return String::number(m_number_value.value());
     case Type::Percentage:
-        return String::formatted("{}%", m_number_value);
+        return String::formatted("{}%", m_number_value.value());
     case Type::Dimension:
-        return String::formatted("{}{}", m_number_value, m_unit);
+        return String::formatted("{}{}", m_number_value.value(), dimension_unit());
     case Type::Whitespace:
         return " ";
     case Type::CDO:
@@ -122,8 +122,8 @@ String Token::to_debug_string() const
         return builder.to_string();
     case Type::Number:
         builder.append("Number: ");
-        builder.append(m_value);
-        builder.append(m_number_type == NumberType::Integer ? " (int)" : " (float)");
+        builder.append(m_number_value.value());
+        builder.append(m_number_value.is_integer() ? " (int)" : " (float)");
         return builder.to_string();
     case Type::Percentage:
         builder.append("Percentage: ");
@@ -132,8 +132,8 @@ String Token::to_debug_string() const
         return builder.to_string();
     case Type::Dimension:
         builder.append("Dimension: ");
-        builder.append(m_value);
-        builder.append(m_unit);
+        builder.append(dimension_value());
+        builder.append(dimension_unit());
         return builder.to_string();
     case Type::Whitespace:
         builder.append("Whitespace");
@@ -193,7 +193,7 @@ String Token::to_debug_string() const
 
     if (m_type == Token::Type::Number) {
         builder.append("', number_type: '");
-        if (m_number_type == Token::NumberType::Integer) {
+        if (m_number_value.is_integer()) {
             builder.append("Integer");
         } else {
             builder.append("Number");
@@ -202,14 +202,14 @@ String Token::to_debug_string() const
 
     if (m_type == Token::Type::Dimension) {
         builder.append("', number_type: '");
-        if (m_number_type == Token::NumberType::Integer) {
+        if (m_number_value.is_integer()) {
             builder.append("Integer");
         } else {
             builder.append("Number");
         }
 
         builder.append("', unit: '");
-        builder.append(m_unit);
+        builder.append(dimension_unit());
     }
 
     builder.append("' }");

+ 12 - 35
Userland/Libraries/LibWeb/CSS/Parser/Token.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2020-2021, the SerenityOS developers.
- * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
+ * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -10,7 +10,7 @@
 #include <AK/FlyString.h>
 #include <AK/String.h>
 #include <AK/Utf8View.h>
-#include <math.h>
+#include <LibWeb/CSS/Number.h>
 
 namespace Web::CSS {
 
@@ -52,11 +52,6 @@ public:
         Unrestricted,
     };
 
-    enum class NumberType {
-        Integer,
-        Number,
-    };
-
     struct Position {
         size_t line { 0 };
         size_t column { 0 };
@@ -112,23 +107,21 @@ public:
         return m_value.view();
     }
 
-    bool is(NumberType number_type) const { return is(Token::Type::Number) && m_number_type == number_type; }
-    StringView number_string_value() const
+    Number const& number() const
     {
-        VERIFY(m_type == Type::Number);
-        return m_value.view();
+        VERIFY(m_type == Type::Number || m_type == Type::Dimension || m_type == Type::Percentage);
+        return m_number_value;
     }
     float number_value() const
     {
         VERIFY(m_type == Type::Number);
-        return m_number_value;
+        return m_number_value.value();
     }
     i64 to_integer() const
     {
-        VERIFY(m_type == Type::Number && m_number_type == NumberType::Integer);
-        return to_closest_integer(m_number_value);
+        VERIFY(m_type == Type::Number && m_number_value.is_integer());
+        return m_number_value.integer_value();
     }
-    bool is_integer_value_signed() const { return number_string_value().starts_with('-') || number_string_value().starts_with('+'); }
 
     StringView dimension_unit() const
     {
@@ -138,20 +131,14 @@ public:
     float dimension_value() const
     {
         VERIFY(m_type == Type::Dimension);
-        return m_number_value;
+        return m_number_value.value();
     }
-    i64 dimension_value_int() const { return to_closest_integer(dimension_value()); }
+    i64 dimension_value_int() const { return m_number_value.integer_value(); }
 
     float percentage() const
     {
         VERIFY(m_type == Type::Percentage);
-        return m_number_value;
-    }
-
-    NumberType number_type() const
-    {
-        VERIFY((m_type == Type::Number) || (m_type == Type::Dimension) || (m_type == Type::Percentage));
-        return m_number_type;
+        return m_number_value.value();
     }
 
     Type mirror_variant() const;
@@ -165,21 +152,11 @@ public:
     Position const& end_position() const { return m_end_position; }
 
 private:
-    static i64 to_closest_integer(float value)
-    {
-        // https://www.w3.org/TR/css-values-4/#numeric-types
-        // When a value cannot be explicitly supported due to range/precision limitations, it must be converted
-        // to the closest value supported by the implementation, but how the implementation defines "closest"
-        // is explicitly undefined as well.
-        return llroundf(value);
-    }
-
     Type m_type { Type::Invalid };
 
     FlyString m_value;
     FlyString m_unit;
-    float m_number_value { 0 };
-    NumberType m_number_type { NumberType::Integer };
+    Number m_number_value;
     HashType m_hash_type { HashType::Unrestricted };
 
     Position m_start_position;

+ 12 - 14
Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.cpp

@@ -465,7 +465,7 @@ Token Tokenizer::consume_an_ident_like_token()
 }
 
 // https://www.w3.org/TR/css-syntax-3/#consume-number
-CSSNumber Tokenizer::consume_a_number()
+Number Tokenizer::consume_a_number()
 {
     // This section describes how to consume a number from a stream of code points.
     // It returns a numeric value, and a type which is either "integer" or "number".
@@ -478,12 +478,14 @@ CSSNumber Tokenizer::consume_a_number()
 
     // 1. Initially set type to "integer". Let repr be the empty string.
     StringBuilder repr;
-    Token::NumberType type = Token::NumberType::Integer;
+    Number::Type type = Number::Type::Integer;
 
     // 2. If the next input code point is U+002B PLUS SIGN (+) or U+002D HYPHEN-MINUS (-),
     // consume it and append it to repr.
+    bool has_explicit_sign = false;
     auto next_input = peek_code_point();
     if (is_plus_sign(next_input) || is_hyphen_minus(next_input)) {
+        has_explicit_sign = true;
         repr.append_code_point(next_code_point());
     }
 
@@ -505,7 +507,7 @@ CSSNumber Tokenizer::consume_a_number()
         repr.append_code_point(next_code_point());
 
         // 3. Set type to "number".
-        type = Token::NumberType::Number;
+        type = Number::Type::Number;
 
         // 4. While the next input code point is a digit, consume it and append it to repr.
         for (;;) {
@@ -537,7 +539,7 @@ CSSNumber Tokenizer::consume_a_number()
         }
 
         // 3. Set type to "number".
-        type = Token::NumberType::Number;
+        type = Number::Type::Number;
 
         // 4. While the next input code point is a digit, consume it and append it to repr.
         for (;;) {
@@ -553,7 +555,9 @@ CSSNumber Tokenizer::consume_a_number()
     auto value = convert_a_string_to_a_number(repr.string_view());
 
     // 7. Return value and type.
-    return { repr.to_string(), value, type };
+    if (type == Number::Type::Integer && has_explicit_sign)
+        return Number { Number::Type::IntegerWithExplicitSign, value };
+    return Number { type, value };
 }
 
 // https://www.w3.org/TR/css-syntax-3/#convert-string-to-number
@@ -836,9 +840,7 @@ Token Tokenizer::consume_a_numeric_token()
         // 1. Create a <dimension-token> with the same value and type flag as number,
         //    and a unit set initially to the empty string.
         auto token = create_new_token(Token::Type::Dimension);
-        token.m_value = move(number.string);
-        token.m_number_type = number.type;
-        token.m_number_value = number.value;
+        token.m_number_value = number;
 
         // 2. Consume a name. Set the <dimension-token>’s unit to the returned value.
         auto unit = consume_a_name();
@@ -855,17 +857,13 @@ Token Tokenizer::consume_a_numeric_token()
 
         // Create a <percentage-token> with the same value as number, and return it.
         auto token = create_new_token(Token::Type::Percentage);
-        token.m_value = move(number.string);
-        token.m_number_type = number.type;
-        token.m_number_value = number.value;
+        token.m_number_value = number;
         return token;
     }
 
     // Otherwise, create a <number-token> with the same value and type flag as number, and return it.
     auto token = create_new_token(Token::Type::Number);
-    token.m_value = move(number.string);
-    token.m_number_type = number.type;
-    token.m_number_value = number.value;
+    token.m_number_value = number;
     return token;
 }
 

+ 1 - 8
Userland/Libraries/LibWeb/CSS/Parser/Tokenizer.h

@@ -57,13 +57,6 @@ public:
     u32 third {};
 };
 
-class CSSNumber {
-public:
-    String string;
-    float value { 0 };
-    Token::NumberType type {};
-};
-
 class Tokenizer {
 
 public:
@@ -89,7 +82,7 @@ private:
     [[nodiscard]] Token consume_string_token(u32 ending_code_point);
     [[nodiscard]] Token consume_a_numeric_token();
     [[nodiscard]] Token consume_an_ident_like_token();
-    [[nodiscard]] CSSNumber consume_a_number();
+    [[nodiscard]] Number consume_a_number();
     [[nodiscard]] float convert_a_string_to_a_number(StringView);
     [[nodiscard]] String consume_a_name();
     [[nodiscard]] u32 consume_escaped_code_point();