1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-23 04:47:36 +00:00

LibWeb: Use CSS::Number for Token numeric values

This commit is contained in:
Sam Atkins 2022-03-21 21:01:27 +00:00 committed by Andreas Kling
parent e8ab2dab11
commit fe372cd073
5 changed files with 53 additions and 85 deletions

View file

@ -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 cvs 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 cvs 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

View file

@ -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("' }");

View file

@ -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;

View file

@ -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;
}

View file

@ -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();