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:
parent
e3b68039bf
commit
328afa32c6
1 changed files with 281 additions and 20 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue