1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-23 20:57:41 +00:00

LibWeb: Pull out larger parsing parts from Parser::parse_simple_selector

This lowers its cognitive complexity from 271 to under 100.
The new `parse_pseudo_simple_selector` still has a complexity of 114.
This commit is contained in:
Hendiadyoin1 2022-03-13 18:17:37 +01:00 committed by Andreas Kling
parent 397d8b4aca
commit fff12847d5
2 changed files with 295 additions and 280 deletions

View file

@ -307,70 +307,8 @@ Optional<Selector::Combinator> Parser::parse_selector_combinator(TokenStream<Sty
return {}; return {};
} }
Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_selector(TokenStream<StyleComponentValueRule>& tokens) Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_attribute_simple_selector(StyleComponentValueRule const& first_value)
{ {
auto peek_token_ends_selector = [&]() -> bool {
auto const& value = tokens.peek_token();
return (value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma));
};
if (peek_token_ends_selector())
return ParsingResult::Done;
auto const& first_value = tokens.next_token();
if (first_value.is(Token::Type::Delim)) {
u32 delim = first_value.token().delim();
switch (delim) {
case '*':
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::Universal
};
case '.': {
if (peek_token_ends_selector())
return ParsingResult::SyntaxError;
auto const& class_name_value = tokens.next_token();
if (!class_name_value.is(Token::Type::Ident)) {
dbgln_if(CSS_PARSER_DEBUG, "Expected an ident after '.', got: {}", class_name_value.to_debug_string());
return ParsingResult::SyntaxError;
}
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::Class,
.value = class_name_value.token().ident()
};
}
case '>':
case '+':
case '~':
case '|':
// Whitespace is not required between the compound-selector and a combinator.
// So, if we see a combinator, return that this compound-selector is done, instead of a syntax error.
tokens.reconsume_current_input_token();
return ParsingResult::Done;
default:
dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid simple selector!");
return ParsingResult::SyntaxError;
}
}
if (first_value.is(Token::Type::Hash)) {
if (first_value.token().hash_type() != Token::HashType::Id) {
dbgln_if(CSS_PARSER_DEBUG, "Selector contains hash token that is not an id: {}", first_value.to_debug_string());
return ParsingResult::SyntaxError;
}
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::Id,
.value = first_value.token().hash_value()
};
}
if (first_value.is(Token::Type::Ident)) {
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::TagName,
.value = first_value.token().ident()
};
}
if (first_value.is_block() && first_value.block().is_square()) {
auto attribute_tokens = TokenStream { first_value.block().values() }; auto attribute_tokens = TokenStream { first_value.block().values() };
attribute_tokens.skip_whitespace(); attribute_tokens.skip_whitespace();
@ -461,8 +399,15 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
// FIXME: Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case // FIXME: Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case
return simple_selector; return simple_selector;
} }
if (first_value.is(Token::Type::Colon)) {
Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_pseudo_simple_selector(TokenStream<StyleComponentValueRule>& tokens)
{
auto peek_token_ends_selector = [&]() -> bool {
auto const& value = tokens.peek_token();
return (value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma));
};
if (peek_token_ends_selector()) if (peek_token_ends_selector())
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
@ -571,7 +516,6 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
return simple_selector; return simple_selector;
} }
if (pseudo_class_token.is_function()) { if (pseudo_class_token.is_function()) {
auto parse_nth_child_pattern = [this](Selector::SimpleSelector& simple_selector, StyleFunctionRule const& pseudo_function, bool allow_of = false) -> bool { auto parse_nth_child_pattern = [this](Selector::SimpleSelector& simple_selector, StyleFunctionRule const& pseudo_function, bool allow_of = false) -> bool {
auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values()); auto function_values = TokenStream<StyleComponentValueRule>(pseudo_function.values());
auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values, allow_of ? AllowTrailingTokens::Yes : AllowTrailingTokens::No); auto nth_child_pattern = parse_a_n_plus_b_pattern(function_values, allow_of ? AllowTrailingTokens::Yes : AllowTrailingTokens::No);
@ -649,7 +593,76 @@ Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_sel
} }
dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string()); dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string());
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
}
Result<Selector::SimpleSelector, Parser::ParsingResult> Parser::parse_simple_selector(TokenStream<StyleComponentValueRule>& tokens)
{
auto peek_token_ends_selector = [&]() -> bool {
auto const& value = tokens.peek_token();
return (value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma));
};
if (peek_token_ends_selector())
return ParsingResult::Done;
auto const& first_value = tokens.next_token();
if (first_value.is(Token::Type::Delim)) {
u32 delim = first_value.token().delim();
switch (delim) {
case '*':
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::Universal
};
case '.': {
if (peek_token_ends_selector())
return ParsingResult::SyntaxError;
auto const& class_name_value = tokens.next_token();
if (!class_name_value.is(Token::Type::Ident)) {
dbgln_if(CSS_PARSER_DEBUG, "Expected an ident after '.', got: {}", class_name_value.to_debug_string());
return ParsingResult::SyntaxError;
} }
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::Class,
.value = class_name_value.token().ident()
};
}
case '>':
case '+':
case '~':
case '|':
// Whitespace is not required between the compound-selector and a combinator.
// So, if we see a combinator, return that this compound-selector is done, instead of a syntax error.
tokens.reconsume_current_input_token();
return ParsingResult::Done;
default:
dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid simple selector!");
return ParsingResult::SyntaxError;
}
}
if (first_value.is(Token::Type::Hash)) {
if (first_value.token().hash_type() != Token::HashType::Id) {
dbgln_if(CSS_PARSER_DEBUG, "Selector contains hash token that is not an id: {}", first_value.to_debug_string());
return ParsingResult::SyntaxError;
}
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::Id,
.value = first_value.token().hash_value()
};
}
if (first_value.is(Token::Type::Ident)) {
return Selector::SimpleSelector {
.type = Selector::SimpleSelector::Type::TagName,
.value = first_value.token().ident()
};
}
if (first_value.is_block() && first_value.block().is_square())
return parse_attribute_simple_selector(first_value);
if (first_value.is(Token::Type::Colon))
return parse_pseudo_simple_selector(tokens);
dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid simple selector!"); dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid simple selector!");
return ParsingResult::SyntaxError; return ParsingResult::SyntaxError;
@ -4901,7 +4914,6 @@ TimePercentage Parser::Dimension::time_percentage() const
return percentage(); return percentage();
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();
} }
} }
namespace Web { namespace Web {

View file

@ -315,6 +315,9 @@ private:
Result<NonnullRefPtr<Selector>, ParsingResult> parse_complex_selector(TokenStream<StyleComponentValueRule>&, SelectorType); Result<NonnullRefPtr<Selector>, ParsingResult> parse_complex_selector(TokenStream<StyleComponentValueRule>&, SelectorType);
Result<Selector::CompoundSelector, ParsingResult> parse_compound_selector(TokenStream<StyleComponentValueRule>&); Result<Selector::CompoundSelector, ParsingResult> parse_compound_selector(TokenStream<StyleComponentValueRule>&);
Optional<Selector::Combinator> parse_selector_combinator(TokenStream<StyleComponentValueRule>&); Optional<Selector::Combinator> parse_selector_combinator(TokenStream<StyleComponentValueRule>&);
Result<Selector::SimpleSelector, ParsingResult> parse_attribute_simple_selector(StyleComponentValueRule const&);
Result<Selector::SimpleSelector, ParsingResult> parse_pseudo_simple_selector(TokenStream<StyleComponentValueRule>&);
Result<Selector::SimpleSelector, ParsingResult> parse_simple_selector(TokenStream<StyleComponentValueRule>&); Result<Selector::SimpleSelector, ParsingResult> parse_simple_selector(TokenStream<StyleComponentValueRule>&);
NonnullRefPtr<MediaQuery> parse_media_query(TokenStream<StyleComponentValueRule>&); NonnullRefPtr<MediaQuery> parse_media_query(TokenStream<StyleComponentValueRule>&);