1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 05:38:11 +00:00

LibWeb: Add the parsing to the calc() handling

This patch adds the parsing of previously tokenized calc() expressions
into the CSS-Parser. The tokens are processed into a complete
CalculatedStyleValue.
This commit is contained in:
Tobias Christiansen 2021-06-11 23:19:03 +02:00 committed by Ali Mohammad Pur
parent e3b68039bf
commit 328afa32c6

View file

@ -7,6 +7,7 @@
#include <AK/GenericLexer.h>
#include <AK/HashMap.h>
#include <AK/NonnullOwnPtr.h>
#include <AK/SourceLocation.h>
#include <LibWeb/CSS/CSSImportRule.h>
#include <LibWeb/CSS/CSSRule.h>
@ -280,26 +281,38 @@ static StringView isolate_calc_expression(const StringView& value)
return value.substring_view(5, substring_length);
}
static Optional<NonnullOwnPtr<CSS::CalculatedStyleValue::CalcSum>> parse_calc_expression(const StringView& expression_string)
struct CalcToken {
enum class Type {
Undefined,
Number,
Unit,
Whitespace,
Plus,
Minus,
Asterisk,
Slash,
OpenBracket,
CloseBracket,
} type { Type::Undefined };
String value {};
};
static void eat_white_space(Vector<CalcToken>&);
static Optional<CSS::CalculatedStyleValue::CalcValue> parse_calc_value(Vector<CalcToken>&);
static OwnPtr<CSS::CalculatedStyleValue::CalcProductPartWithOperator> parse_calc_product_part_with_operator(Vector<CalcToken>&);
static Optional<CSS::CalculatedStyleValue::CalcNumberValue> parse_calc_number_value(Vector<CalcToken>&);
static OwnPtr<CSS::CalculatedStyleValue::CalcProduct> parse_calc_product(Vector<CalcToken>&);
static OwnPtr<CSS::CalculatedStyleValue::CalcSumPartWithOperator> parse_calc_sum_part_with_operator(Vector<CalcToken>&);
static OwnPtr<CSS::CalculatedStyleValue::CalcSum> parse_calc_sum(Vector<CalcToken>&);
static OwnPtr<CSS::CalculatedStyleValue::CalcNumberSum> parse_calc_number_sum(Vector<CalcToken>& tokens);
static OwnPtr<CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator> parse_calc_number_product_part_with_operator(Vector<CalcToken>& tokens);
static OwnPtr<CSS::CalculatedStyleValue::CalcNumberProduct> parse_calc_number_product(Vector<CalcToken>& tokens);
static OwnPtr<CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator> parse_calc_number_sum_part_with_operator(Vector<CalcToken>& tokens);
static OwnPtr<CSS::CalculatedStyleValue::CalcSum> parse_calc_expression(const StringView& expression_string)
{
// First, tokenize
struct CalcToken {
enum class Type {
Undefined,
Number,
Unit,
Whitespace,
Plus,
Minus,
Asterisk,
Slash,
OpenBracket,
CloseBracket,
} type { Type::Undefined };
String value {};
};
Vector<CalcToken> tokens;
GenericLexer lexer(expression_string);
@ -367,7 +380,255 @@ static Optional<NonnullOwnPtr<CSS::CalculatedStyleValue::CalcSum>> parse_calc_ex
VERIFY_NOT_REACHED();
}
return {};
// Then, parse
return parse_calc_sum(tokens);
}
static void eat_white_space(Vector<CalcToken>& tokens)
{
while (tokens.size() > 0 && tokens.first().type == CalcToken::Type::Whitespace)
tokens.take_first();
}
static Optional<CSS::CalculatedStyleValue::CalcValue> parse_calc_value(Vector<CalcToken>& tokens)
{
auto current_token = tokens.take_first();
if (current_token.type == CalcToken::Type::OpenBracket) {
auto parsed_calc_sum = parse_calc_sum(tokens);
if (!parsed_calc_sum)
return {};
return (CSS::CalculatedStyleValue::CalcValue) { parsed_calc_sum.release_nonnull() };
}
if (current_token.type != CalcToken::Type::Number)
return {};
auto try_the_number = try_parse_float(current_token.value);
if (!try_the_number.has_value())
return {};
float the_number = try_the_number.value();
if (tokens.first().type != CalcToken::Type::Unit)
return (CSS::CalculatedStyleValue::CalcValue) { the_number };
auto type = length_type_from_unit(tokens.take_first().value);
if (type == CSS::Length::Type::Undefined)
return {};
return (CSS::CalculatedStyleValue::CalcValue) { CSS::Length(the_number, type) };
}
static OwnPtr<CSS::CalculatedStyleValue::CalcProductPartWithOperator> parse_calc_product_part_with_operator(Vector<CalcToken>& tokens)
{
auto product_with_operator = make<CSS::CalculatedStyleValue::CalcProductPartWithOperator>();
eat_white_space(tokens);
auto op = tokens.first();
if (op.type == CalcToken::Type::Asterisk) {
tokens.take_first();
eat_white_space(tokens);
product_with_operator->op = CSS::CalculatedStyleValue::CalcProductPartWithOperator::Multiply;
auto parsed_calc_value = parse_calc_value(tokens);
if (!parsed_calc_value.has_value())
return nullptr;
product_with_operator->value = { parsed_calc_value.release_value() };
} else if (op.type == CalcToken::Type::Slash) {
tokens.take_first();
eat_white_space(tokens);
product_with_operator->op = CSS::CalculatedStyleValue::CalcProductPartWithOperator::Divide;
auto parsed_calc_number_value = parse_calc_number_value(tokens);
if (!parsed_calc_number_value.has_value())
return nullptr;
product_with_operator->value = { parsed_calc_number_value.release_value() };
} else {
return nullptr;
}
return product_with_operator;
}
static OwnPtr<CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator> parse_calc_number_product_part_with_operator(Vector<CalcToken>& tokens)
{
auto number_product_with_operator = make<CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator>();
eat_white_space(tokens);
auto op = tokens.first();
if (op.type == CalcToken::Type::Asterisk) {
tokens.take_first();
eat_white_space(tokens);
number_product_with_operator->op = CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator::Multiply;
} else if (op.type == CalcToken::Type::Slash) {
tokens.take_first();
eat_white_space(tokens);
number_product_with_operator->op = CSS::CalculatedStyleValue::CalcNumberProductPartWithOperator::Divide;
} else {
return nullptr;
}
auto parsed_calc_value = parse_calc_number_value(tokens);
if (!parsed_calc_value.has_value())
return nullptr;
number_product_with_operator->value = parsed_calc_value.release_value();
return number_product_with_operator;
}
static OwnPtr<CSS::CalculatedStyleValue::CalcNumberProduct> parse_calc_number_product(Vector<CalcToken>& tokens)
{
auto calc_number_product = make<CSS::CalculatedStyleValue::CalcNumberProduct>();
auto first_calc_number_value_or_error = parse_calc_number_value(tokens);
if (!first_calc_number_value_or_error.has_value())
return nullptr;
calc_number_product->first_calc_number_value = first_calc_number_value_or_error.release_value();
while (tokens.size() > 0) {
auto number_product_with_operator = parse_calc_number_product_part_with_operator(tokens);
if (!number_product_with_operator)
break;
calc_number_product->zero_or_more_additional_calc_number_values.append(number_product_with_operator.release_nonnull());
}
return calc_number_product;
}
static OwnPtr<CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator> parse_calc_number_sum_part_with_operator(Vector<CalcToken>& tokens)
{
if (tokens.size() < 3)
return nullptr;
if (!((tokens[0].type == CalcToken::Type::Plus
|| tokens[0].type == CalcToken::Type::Minus)
&& tokens[1].type == CalcToken::Type::Whitespace))
return nullptr;
auto op_token = tokens.take_first().type;
tokens.take_first(); // Whitespace;
CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Operation op;
if (op_token == CalcToken::Type::Plus)
op = CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Operation::Add;
else if (op_token == CalcToken::Type::Minus)
op = CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator::Operation::Subtract;
else
return nullptr;
auto calc_number_product = parse_calc_number_product(tokens);
if (!calc_number_product)
return nullptr;
return make<CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator>(op, calc_number_product.release_nonnull());
}
static OwnPtr<CSS::CalculatedStyleValue::CalcNumberSum> parse_calc_number_sum(Vector<CalcToken>& tokens)
{
if (tokens.take_first().type != CalcToken::Type::OpenBracket)
return nullptr;
auto first_calc_number_product_or_error = parse_calc_number_product(tokens);
if (!first_calc_number_product_or_error)
return nullptr;
NonnullOwnPtrVector<CSS::CalculatedStyleValue::CalcNumberSumPartWithOperator> additional {};
while (tokens.size() > 0 && tokens.first().type != CalcToken::Type::CloseBracket) {
auto calc_sum_part = parse_calc_number_sum_part_with_operator(tokens);
if (!calc_sum_part)
return nullptr;
additional.append(calc_sum_part.release_nonnull());
}
eat_white_space(tokens);
auto calc_number_sum = make<CSS::CalculatedStyleValue::CalcNumberSum>(first_calc_number_product_or_error.release_nonnull(), move(additional));
return calc_number_sum;
}
static Optional<CSS::CalculatedStyleValue::CalcNumberValue> parse_calc_number_value(Vector<CalcToken>& tokens)
{
if (tokens.first().type == CalcToken::Type::OpenBracket) {
auto calc_number_sum = parse_calc_number_sum(tokens);
if (calc_number_sum)
return { calc_number_sum.release_nonnull() };
}
if (tokens.first().type != CalcToken::Type::Number)
return {};
auto the_number_string = tokens.take_first().value;
auto try_the_number = try_parse_float(the_number_string);
if (!try_the_number.has_value())
return {};
return try_the_number.value();
}
static OwnPtr<CSS::CalculatedStyleValue::CalcProduct> parse_calc_product(Vector<CalcToken>& tokens)
{
auto calc_product = make<CSS::CalculatedStyleValue::CalcProduct>();
auto first_calc_value_or_error = parse_calc_value(tokens);
if (!first_calc_value_or_error.has_value())
return nullptr;
calc_product->first_calc_value = first_calc_value_or_error.release_value();
while (tokens.size() > 0) {
auto product_with_operator = parse_calc_product_part_with_operator(tokens);
if (!product_with_operator)
break;
calc_product->zero_or_more_additional_calc_values.append(product_with_operator.release_nonnull());
}
return calc_product;
}
static OwnPtr<CSS::CalculatedStyleValue::CalcSumPartWithOperator> parse_calc_sum_part_with_operator(Vector<CalcToken>& tokens)
{
// The following has to have the shape of <Whitespace><+ or -><Whitespace>
// But the first whitespace gets eaten in parse_calc_product_part_with_operator().
if (tokens.size() < 3)
return {};
if (!((tokens[0].type == CalcToken::Type::Plus
|| tokens[0].type == CalcToken::Type::Minus)
&& tokens[1].type == CalcToken::Type::Whitespace))
return nullptr;
auto op_token = tokens.take_first().type;
tokens.take_first(); // Whitespace;
CSS::CalculatedStyleValue::CalcSumPartWithOperator::Operation op;
if (op_token == CalcToken::Type::Plus)
op = CSS::CalculatedStyleValue::CalcSumPartWithOperator::Operation::Add;
else if (op_token == CalcToken::Type::Minus)
op = CSS::CalculatedStyleValue::CalcSumPartWithOperator::Operation::Subtract;
else
return nullptr;
auto calc_product = parse_calc_product(tokens);
if (!calc_product)
return nullptr;
return make<CSS::CalculatedStyleValue::CalcSumPartWithOperator>(op, calc_product.release_nonnull());
};
static OwnPtr<CSS::CalculatedStyleValue::CalcSum> parse_calc_sum(Vector<CalcToken>& tokens)
{
auto parsed_calc_product = parse_calc_product(tokens);
if (!parsed_calc_product)
return nullptr;
NonnullOwnPtrVector<CSS::CalculatedStyleValue::CalcSumPartWithOperator> additional {};
while (tokens.size() > 0 && tokens.first().type != CalcToken::Type::CloseBracket) {
auto calc_sum_part = parse_calc_sum_part_with_operator(tokens);
if (!calc_sum_part)
return nullptr;
additional.append(calc_sum_part.release_nonnull());
}
eat_white_space(tokens);
return make<CSS::CalculatedStyleValue::CalcSum>(parsed_calc_product.release_nonnull(), move(additional));
}
RefPtr<CSS::StyleValue> parse_css_value(const CSS::DeprecatedParsingContext& context, const StringView& string, CSS::PropertyID property_id)
@ -401,8 +662,8 @@ RefPtr<CSS::StyleValue> parse_css_value(const CSS::DeprecatedParsingContext& con
if (string.starts_with("calc(")) {
auto calc_expression_string = isolate_calc_expression(string);
auto calc_expression = parse_calc_expression(calc_expression_string);
if (calc_expression.has_value())
return CSS::CalculatedStyleValue::create(calc_expression_string, calc_expression.release_value());
if (calc_expression)
return CSS::CalculatedStyleValue::create(calc_expression_string, calc_expression.release_nonnull());
}
auto value_id = CSS::value_id_from_string(string);