mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 06:07:34 +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:
parent
e3b68039bf
commit
328afa32c6
1 changed files with 281 additions and 20 deletions
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include <AK/GenericLexer.h>
|
#include <AK/GenericLexer.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
|
#include <AK/NonnullOwnPtr.h>
|
||||||
#include <AK/SourceLocation.h>
|
#include <AK/SourceLocation.h>
|
||||||
#include <LibWeb/CSS/CSSImportRule.h>
|
#include <LibWeb/CSS/CSSImportRule.h>
|
||||||
#include <LibWeb/CSS/CSSRule.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);
|
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
|
// 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;
|
Vector<CalcToken> tokens;
|
||||||
|
|
||||||
GenericLexer lexer(expression_string);
|
GenericLexer lexer(expression_string);
|
||||||
|
@ -367,7 +380,255 @@ static Optional<NonnullOwnPtr<CSS::CalculatedStyleValue::CalcSum>> parse_calc_ex
|
||||||
VERIFY_NOT_REACHED();
|
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)
|
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(")) {
|
if (string.starts_with("calc(")) {
|
||||||
auto calc_expression_string = isolate_calc_expression(string);
|
auto calc_expression_string = isolate_calc_expression(string);
|
||||||
auto calc_expression = parse_calc_expression(calc_expression_string);
|
auto calc_expression = parse_calc_expression(calc_expression_string);
|
||||||
if (calc_expression.has_value())
|
if (calc_expression)
|
||||||
return CSS::CalculatedStyleValue::create(calc_expression_string, calc_expression.release_value());
|
return CSS::CalculatedStyleValue::create(calc_expression_string, calc_expression.release_nonnull());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value_id = CSS::value_id_from_string(string);
|
auto value_id = CSS::value_id_from_string(string);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue