SelectorParsing.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077
  1. /*
  2. * Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2020-2021, the SerenityOS developers.
  4. * Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
  5. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  6. * Copyright (c) 2022, MacDue <macdue@dueutil.tech>
  7. *
  8. * SPDX-License-Identifier: BSD-2-Clause
  9. */
  10. #include <AK/Debug.h>
  11. #include <LibWeb/CSS/Parser/Parser.h>
  12. #include <LibWeb/Infra/Strings.h>
  13. namespace Web::CSS::Parser {
  14. Optional<SelectorList> Parser::parse_as_selector(SelectorParsingMode parsing_mode)
  15. {
  16. auto selector_list = parse_a_selector_list(m_token_stream, SelectorType::Standalone, parsing_mode);
  17. if (!selector_list.is_error())
  18. return selector_list.release_value();
  19. return {};
  20. }
  21. Optional<SelectorList> Parser::parse_as_relative_selector(SelectorParsingMode parsing_mode)
  22. {
  23. auto selector_list = parse_a_selector_list(m_token_stream, SelectorType::Relative, parsing_mode);
  24. if (!selector_list.is_error())
  25. return selector_list.release_value();
  26. return {};
  27. }
  28. Optional<Selector::PseudoElement> Parser::parse_as_pseudo_element_selector()
  29. {
  30. // FIXME: This is quite janky. Selector parsing is not at all designed to allow parsing just a single part of a selector.
  31. // So, this code parses a whole selector, then rejects it if it's not a single pseudo-element simple selector.
  32. // Come back and fix this, future Sam!
  33. auto maybe_selector_list = parse_a_selector_list(m_token_stream, SelectorType::Standalone, SelectorParsingMode::Standard);
  34. if (maybe_selector_list.is_error())
  35. return {};
  36. auto& selector_list = maybe_selector_list.value();
  37. if (selector_list.size() != 1)
  38. return {};
  39. auto& selector = selector_list.first();
  40. if (selector->compound_selectors().size() != 1)
  41. return {};
  42. auto& first_compound_selector = selector->compound_selectors().first();
  43. if (first_compound_selector.simple_selectors.size() != 1)
  44. return {};
  45. auto& simple_selector = first_compound_selector.simple_selectors.first();
  46. if (simple_selector.type != Selector::SimpleSelector::Type::PseudoElement)
  47. return {};
  48. return simple_selector.pseudo_element();
  49. }
  50. static NonnullRefPtr<Selector> create_invalid_selector(Selector::Combinator combinator, Vector<ComponentValue> component_values)
  51. {
  52. // Trim leading and trailing whitespace
  53. while (!component_values.is_empty() && component_values.first().is(Token::Type::Whitespace)) {
  54. component_values.take_first();
  55. }
  56. while (!component_values.is_empty() && component_values.last().is(Token::Type::Whitespace)) {
  57. component_values.take_last();
  58. }
  59. Selector::SimpleSelector simple {
  60. .type = Selector::SimpleSelector::Type::Invalid,
  61. .value = Selector::SimpleSelector::Invalid {
  62. .component_values = move(component_values),
  63. }
  64. };
  65. Selector::CompoundSelector compound {
  66. .combinator = combinator,
  67. .simple_selectors = { move(simple) }
  68. };
  69. return Selector::create({ move(compound) });
  70. }
  71. template<typename T>
  72. Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<T>& tokens, SelectorType mode, SelectorParsingMode parsing_mode)
  73. {
  74. SelectorList selectors;
  75. for (;;) {
  76. auto selector_parts = consume_a_list_of_component_values(tokens, Token::Type::Comma);
  77. auto stream = TokenStream(selector_parts);
  78. auto selector = parse_complex_selector(stream, mode);
  79. if (selector.is_error()) {
  80. if (parsing_mode == SelectorParsingMode::Forgiving) {
  81. // Keep the invalid selector around for serialization and nesting
  82. auto combinator = mode == SelectorType::Standalone ? Selector::Combinator::None : Selector::Combinator::Descendant;
  83. selectors.append(create_invalid_selector(combinator, move(selector_parts)));
  84. } else {
  85. return selector.error();
  86. }
  87. } else {
  88. selectors.append(selector.release_value());
  89. }
  90. if (tokens.is_empty())
  91. break;
  92. tokens.discard_a_token();
  93. }
  94. if (selectors.is_empty() && parsing_mode != SelectorParsingMode::Forgiving)
  95. return ParseError::SyntaxError;
  96. return selectors;
  97. }
  98. template Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<ComponentValue>&, SelectorType, SelectorParsingMode);
  99. template Parser::ParseErrorOr<SelectorList> Parser::parse_a_selector_list(TokenStream<Token>&, SelectorType, SelectorParsingMode);
  100. Parser::ParseErrorOr<NonnullRefPtr<Selector>> Parser::parse_complex_selector(TokenStream<ComponentValue>& tokens, SelectorType mode)
  101. {
  102. Vector<Selector::CompoundSelector> compound_selectors;
  103. auto first_selector = TRY(parse_compound_selector(tokens));
  104. if (!first_selector.has_value())
  105. return ParseError::SyntaxError;
  106. if (mode == SelectorType::Standalone) {
  107. if (first_selector->combinator != Selector::Combinator::Descendant)
  108. return ParseError::SyntaxError;
  109. first_selector->combinator = Selector::Combinator::None;
  110. }
  111. compound_selectors.append(first_selector.release_value());
  112. while (tokens.has_next_token()) {
  113. auto compound_selector = TRY(parse_compound_selector(tokens));
  114. if (!compound_selector.has_value())
  115. break;
  116. compound_selectors.append(compound_selector.release_value());
  117. }
  118. if (compound_selectors.is_empty())
  119. return ParseError::SyntaxError;
  120. return Selector::create(move(compound_selectors));
  121. }
  122. Parser::ParseErrorOr<Optional<Selector::CompoundSelector>> Parser::parse_compound_selector(TokenStream<ComponentValue>& tokens)
  123. {
  124. tokens.discard_whitespace();
  125. auto combinator = parse_selector_combinator(tokens).value_or(Selector::Combinator::Descendant);
  126. tokens.discard_whitespace();
  127. Vector<Selector::SimpleSelector> simple_selectors;
  128. while (tokens.has_next_token()) {
  129. auto component = TRY(parse_simple_selector(tokens));
  130. if (!component.has_value())
  131. break;
  132. if (component->type == Selector::SimpleSelector::Type::TagName && !simple_selectors.is_empty()) {
  133. // Tag-name selectors can only go at the beginning of a compound selector.
  134. return ParseError::SyntaxError;
  135. }
  136. simple_selectors.append(component.release_value());
  137. }
  138. if (simple_selectors.is_empty()) {
  139. if (tokens.has_next_token() || combinator != Selector::Combinator::Descendant)
  140. return ParseError::SyntaxError;
  141. return Optional<Selector::CompoundSelector> {};
  142. }
  143. return Selector::CompoundSelector { combinator, move(simple_selectors) };
  144. }
  145. Optional<Selector::Combinator> Parser::parse_selector_combinator(TokenStream<ComponentValue>& tokens)
  146. {
  147. auto const& current_value = tokens.consume_a_token();
  148. if (current_value.is(Token::Type::Delim)) {
  149. switch (current_value.token().delim()) {
  150. case '>':
  151. return Selector::Combinator::ImmediateChild;
  152. case '+':
  153. return Selector::Combinator::NextSibling;
  154. case '~':
  155. return Selector::Combinator::SubsequentSibling;
  156. case '|': {
  157. auto const& next = tokens.next_token();
  158. if (next.is(Token::Type::EndOfFile))
  159. return {};
  160. if (next.is_delim('|')) {
  161. tokens.discard_a_token();
  162. return Selector::Combinator::Column;
  163. }
  164. }
  165. }
  166. }
  167. tokens.reconsume_current_input_token();
  168. return {};
  169. }
  170. Optional<Selector::SimpleSelector::QualifiedName> Parser::parse_selector_qualified_name(TokenStream<ComponentValue>& tokens, AllowWildcardName allow_wildcard_name)
  171. {
  172. auto is_name = [](ComponentValue const& token) {
  173. return token.is_delim('*') || token.is(Token::Type::Ident);
  174. };
  175. auto get_name = [](ComponentValue const& token) {
  176. if (token.is_delim('*'))
  177. return "*"_fly_string;
  178. return token.token().ident();
  179. };
  180. // There are 3 possibilities here:
  181. // (Where <name> and <namespace> are either an <ident> or a `*` delim)
  182. // 1) `|<name>`
  183. // 2) `<namespace>|<name>`
  184. // 3) `<name>`
  185. // Whitespace is forbidden between any of these parts. https://www.w3.org/TR/selectors-4/#white-space
  186. auto transaction = tokens.begin_transaction();
  187. auto const& first_token = tokens.consume_a_token();
  188. if (first_token.is_delim('|')) {
  189. // Case 1: `|<name>`
  190. if (is_name(tokens.next_token())) {
  191. auto const& name_token = tokens.consume_a_token();
  192. if (allow_wildcard_name == AllowWildcardName::No && name_token.is_delim('*'))
  193. return {};
  194. transaction.commit();
  195. return Selector::SimpleSelector::QualifiedName {
  196. .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::None,
  197. .name = get_name(name_token),
  198. };
  199. }
  200. return {};
  201. }
  202. if (!is_name(first_token))
  203. return {};
  204. if (tokens.next_token().is_delim('|') && is_name(tokens.peek_token(1))) {
  205. // Case 2: `<namespace>|<name>`
  206. tokens.discard_a_token(); // `|`
  207. auto namespace_ = get_name(first_token);
  208. auto name = get_name(tokens.consume_a_token());
  209. if (allow_wildcard_name == AllowWildcardName::No && name == "*"sv)
  210. return {};
  211. auto namespace_type = namespace_ == "*"sv
  212. ? Selector::SimpleSelector::QualifiedName::NamespaceType::Any
  213. : Selector::SimpleSelector::QualifiedName::NamespaceType::Named;
  214. transaction.commit();
  215. return Selector::SimpleSelector::QualifiedName {
  216. .namespace_type = namespace_type,
  217. .namespace_ = namespace_,
  218. .name = name,
  219. };
  220. }
  221. // Case 3: `<name>`
  222. auto& name_token = first_token;
  223. if (allow_wildcard_name == AllowWildcardName::No && name_token.is_delim('*'))
  224. return {};
  225. transaction.commit();
  226. return Selector::SimpleSelector::QualifiedName {
  227. .namespace_type = Selector::SimpleSelector::QualifiedName::NamespaceType::Default,
  228. .name = get_name(name_token),
  229. };
  230. }
  231. Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_attribute_simple_selector(ComponentValue const& first_value)
  232. {
  233. auto attribute_tokens = TokenStream { first_value.block().value };
  234. attribute_tokens.discard_whitespace();
  235. if (!attribute_tokens.has_next_token()) {
  236. dbgln_if(CSS_PARSER_DEBUG, "CSS attribute selector is empty!");
  237. return ParseError::SyntaxError;
  238. }
  239. auto maybe_qualified_name = parse_selector_qualified_name(attribute_tokens, AllowWildcardName::No);
  240. if (!maybe_qualified_name.has_value()) {
  241. dbgln_if(CSS_PARSER_DEBUG, "Expected qualified-name for attribute name, got: '{}'", attribute_tokens.next_token().to_debug_string());
  242. return ParseError::SyntaxError;
  243. }
  244. auto qualified_name = maybe_qualified_name.release_value();
  245. Selector::SimpleSelector simple_selector {
  246. .type = Selector::SimpleSelector::Type::Attribute,
  247. .value = Selector::SimpleSelector::Attribute {
  248. .match_type = Selector::SimpleSelector::Attribute::MatchType::HasAttribute,
  249. .qualified_name = qualified_name,
  250. .case_type = Selector::SimpleSelector::Attribute::CaseType::DefaultMatch,
  251. }
  252. };
  253. attribute_tokens.discard_whitespace();
  254. if (!attribute_tokens.has_next_token())
  255. return simple_selector;
  256. auto const& delim_part = attribute_tokens.consume_a_token();
  257. if (!delim_part.is(Token::Type::Delim)) {
  258. dbgln_if(CSS_PARSER_DEBUG, "Expected a delim for attribute comparison, got: '{}'", delim_part.to_debug_string());
  259. return ParseError::SyntaxError;
  260. }
  261. if (delim_part.token().delim() == '=') {
  262. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::ExactValueMatch;
  263. } else {
  264. if (!attribute_tokens.has_next_token()) {
  265. dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended part way through a match type.");
  266. return ParseError::SyntaxError;
  267. }
  268. auto const& delim_second_part = attribute_tokens.consume_a_token();
  269. if (!delim_second_part.is_delim('=')) {
  270. dbgln_if(CSS_PARSER_DEBUG, "Expected a double delim for attribute comparison, got: '{}{}'", delim_part.to_debug_string(), delim_second_part.to_debug_string());
  271. return ParseError::SyntaxError;
  272. }
  273. switch (delim_part.token().delim()) {
  274. case '~':
  275. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsWord;
  276. break;
  277. case '*':
  278. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::ContainsString;
  279. break;
  280. case '|':
  281. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithSegment;
  282. break;
  283. case '^':
  284. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::StartsWithString;
  285. break;
  286. case '$':
  287. simple_selector.attribute().match_type = Selector::SimpleSelector::Attribute::MatchType::EndsWithString;
  288. break;
  289. default:
  290. attribute_tokens.reconsume_current_input_token();
  291. }
  292. }
  293. attribute_tokens.discard_whitespace();
  294. if (!attribute_tokens.has_next_token()) {
  295. dbgln_if(CSS_PARSER_DEBUG, "Attribute selector ended without a value to match.");
  296. return ParseError::SyntaxError;
  297. }
  298. auto const& value_part = attribute_tokens.consume_a_token();
  299. if (!value_part.is(Token::Type::Ident) && !value_part.is(Token::Type::String)) {
  300. dbgln_if(CSS_PARSER_DEBUG, "Expected a string or ident for the value to match attribute against, got: '{}'", value_part.to_debug_string());
  301. return ParseError::SyntaxError;
  302. }
  303. auto const& value_string = value_part.token().is(Token::Type::Ident) ? value_part.token().ident() : value_part.token().string();
  304. simple_selector.attribute().value = value_string.to_string();
  305. attribute_tokens.discard_whitespace();
  306. // Handle case-sensitivity suffixes. https://www.w3.org/TR/selectors-4/#attribute-case
  307. if (attribute_tokens.has_next_token()) {
  308. auto const& case_sensitivity_part = attribute_tokens.consume_a_token();
  309. if (case_sensitivity_part.is(Token::Type::Ident)) {
  310. auto case_sensitivity = case_sensitivity_part.token().ident();
  311. if (case_sensitivity.equals_ignoring_ascii_case("i"sv)) {
  312. simple_selector.attribute().case_type = Selector::SimpleSelector::Attribute::CaseType::CaseInsensitiveMatch;
  313. } else if (case_sensitivity.equals_ignoring_ascii_case("s"sv)) {
  314. simple_selector.attribute().case_type = Selector::SimpleSelector::Attribute::CaseType::CaseSensitiveMatch;
  315. } else {
  316. dbgln_if(CSS_PARSER_DEBUG, "Expected a \"i\" or \"s\" attribute selector case sensitivity identifier, got: '{}'", case_sensitivity_part.to_debug_string());
  317. return ParseError::SyntaxError;
  318. }
  319. } else {
  320. dbgln_if(CSS_PARSER_DEBUG, "Expected an attribute selector case sensitivity identifier, got: '{}'", case_sensitivity_part.to_debug_string());
  321. return ParseError::SyntaxError;
  322. }
  323. }
  324. if (attribute_tokens.has_next_token()) {
  325. dbgln_if(CSS_PARSER_DEBUG, "Was not expecting anything else inside attribute selector.");
  326. return ParseError::SyntaxError;
  327. }
  328. return simple_selector;
  329. }
  330. Parser::ParseErrorOr<Selector::SimpleSelector> Parser::parse_pseudo_simple_selector(TokenStream<ComponentValue>& tokens)
  331. {
  332. auto peek_token_ends_selector = [&]() -> bool {
  333. auto const& value = tokens.next_token();
  334. return (value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma));
  335. };
  336. if (peek_token_ends_selector())
  337. return ParseError::SyntaxError;
  338. bool is_pseudo = false;
  339. if (tokens.next_token().is(Token::Type::Colon)) {
  340. is_pseudo = true;
  341. tokens.discard_a_token();
  342. if (peek_token_ends_selector())
  343. return ParseError::SyntaxError;
  344. }
  345. if (is_pseudo) {
  346. auto const& name_token = tokens.consume_a_token();
  347. if (!name_token.is(Token::Type::Ident)) {
  348. dbgln_if(CSS_PARSER_DEBUG, "Expected an ident for pseudo-element, got: '{}'", name_token.to_debug_string());
  349. return ParseError::SyntaxError;
  350. }
  351. auto pseudo_name = name_token.token().ident();
  352. // Note: We allow the "ignored" -webkit prefix here for -webkit-progress-bar/-webkit-progress-bar
  353. if (auto pseudo_element = Selector::PseudoElement::from_string(pseudo_name); pseudo_element.has_value()) {
  354. // :has() is fussy about pseudo-elements inside it
  355. if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(pseudo_element->type())) {
  356. return ParseError::SyntaxError;
  357. }
  358. return Selector::SimpleSelector {
  359. .type = Selector::SimpleSelector::Type::PseudoElement,
  360. .value = pseudo_element.release_value()
  361. };
  362. }
  363. // https://www.w3.org/TR/selectors-4/#compat
  364. // All other pseudo-elements whose names begin with the string “-webkit-” (matched ASCII case-insensitively)
  365. // and that are not functional notations must be treated as valid at parse time. (That is, ::-webkit-asdf is
  366. // valid at parse time, but ::-webkit-jkl() is not.) If they’re not otherwise recognized and supported, they
  367. // must be treated as matching nothing, and are unknown -webkit- pseudo-elements.
  368. if (pseudo_name.starts_with_bytes("-webkit-"sv, CaseSensitivity::CaseInsensitive)) {
  369. // :has() only allows a limited set of pseudo-elements inside it, which doesn't include unknown ones.
  370. if (m_pseudo_class_context.contains_slow(PseudoClass::Has))
  371. return ParseError::SyntaxError;
  372. return Selector::SimpleSelector {
  373. .type = Selector::SimpleSelector::Type::PseudoElement,
  374. // Unknown -webkit- pseudo-elements must be serialized in ASCII lowercase.
  375. .value = Selector::PseudoElement { Selector::PseudoElement::Type::UnknownWebKit, pseudo_name.to_string().to_ascii_lowercase() },
  376. };
  377. }
  378. if (has_ignored_vendor_prefix(pseudo_name))
  379. return ParseError::IncludesIgnoredVendorPrefix;
  380. dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-element: '::{}'", pseudo_name);
  381. return ParseError::SyntaxError;
  382. }
  383. if (peek_token_ends_selector())
  384. return ParseError::SyntaxError;
  385. auto const& pseudo_class_token = tokens.consume_a_token();
  386. if (pseudo_class_token.is(Token::Type::Ident)) {
  387. auto pseudo_name = pseudo_class_token.token().ident();
  388. if (has_ignored_vendor_prefix(pseudo_name))
  389. return ParseError::IncludesIgnoredVendorPrefix;
  390. auto make_pseudo_class_selector = [](auto pseudo_class) {
  391. return Selector::SimpleSelector {
  392. .type = Selector::SimpleSelector::Type::PseudoClass,
  393. .value = Selector::SimpleSelector::PseudoClassSelector { .type = pseudo_class }
  394. };
  395. };
  396. if (auto pseudo_class = pseudo_class_from_string(pseudo_name); pseudo_class.has_value()) {
  397. if (!pseudo_class_metadata(pseudo_class.value()).is_valid_as_identifier) {
  398. dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is only valid as a function", pseudo_name);
  399. return ParseError::SyntaxError;
  400. }
  401. return make_pseudo_class_selector(pseudo_class.value());
  402. }
  403. // Single-colon syntax allowed for ::after, ::before, ::first-letter and ::first-line for compatibility.
  404. // https://www.w3.org/TR/selectors/#pseudo-element-syntax
  405. if (auto pseudo_element = Selector::PseudoElement::from_string(pseudo_name); pseudo_element.has_value()) {
  406. switch (pseudo_element.value().type()) {
  407. case Selector::PseudoElement::Type::After:
  408. case Selector::PseudoElement::Type::Before:
  409. case Selector::PseudoElement::Type::FirstLetter:
  410. case Selector::PseudoElement::Type::FirstLine:
  411. // :has() is fussy about pseudo-elements inside it
  412. if (m_pseudo_class_context.contains_slow(PseudoClass::Has) && !is_has_allowed_pseudo_element(pseudo_element->type())) {
  413. return ParseError::SyntaxError;
  414. }
  415. return Selector::SimpleSelector {
  416. .type = Selector::SimpleSelector::Type::PseudoElement,
  417. .value = pseudo_element.value()
  418. };
  419. default:
  420. break;
  421. }
  422. }
  423. dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class: ':{}'", pseudo_name);
  424. return ParseError::SyntaxError;
  425. }
  426. if (pseudo_class_token.is_function()) {
  427. auto parse_nth_child_selector = [this](auto pseudo_class, Vector<ComponentValue> const& function_values, bool allow_of = false) -> ParseErrorOr<Selector::SimpleSelector> {
  428. auto tokens = TokenStream<ComponentValue>(function_values);
  429. auto nth_child_pattern = parse_a_n_plus_b_pattern(tokens);
  430. if (!nth_child_pattern.has_value()) {
  431. dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid An+B format for {}", pseudo_class_name(pseudo_class));
  432. return ParseError::SyntaxError;
  433. }
  434. tokens.discard_whitespace();
  435. if (!tokens.has_next_token()) {
  436. return Selector::SimpleSelector {
  437. .type = Selector::SimpleSelector::Type::PseudoClass,
  438. .value = Selector::SimpleSelector::PseudoClassSelector {
  439. .type = pseudo_class,
  440. .nth_child_pattern = nth_child_pattern.release_value() }
  441. };
  442. }
  443. if (!allow_of)
  444. return ParseError::SyntaxError;
  445. // Parse the `of <selector-list>` syntax
  446. auto const& maybe_of = tokens.consume_a_token();
  447. if (!maybe_of.is_ident("of"sv))
  448. return ParseError::SyntaxError;
  449. tokens.discard_whitespace();
  450. auto selector_list = TRY(parse_a_selector_list(tokens, SelectorType::Standalone));
  451. tokens.discard_whitespace();
  452. if (tokens.has_next_token())
  453. return ParseError::SyntaxError;
  454. return Selector::SimpleSelector {
  455. .type = Selector::SimpleSelector::Type::PseudoClass,
  456. .value = Selector::SimpleSelector::PseudoClassSelector {
  457. .type = pseudo_class,
  458. .nth_child_pattern = nth_child_pattern.release_value(),
  459. .argument_selector_list = move(selector_list) }
  460. };
  461. };
  462. auto const& pseudo_function = pseudo_class_token.function();
  463. auto maybe_pseudo_class = pseudo_class_from_string(pseudo_function.name);
  464. if (!maybe_pseudo_class.has_value()) {
  465. dbgln_if(CSS_PARSER_DEBUG, "Unrecognized pseudo-class function: ':{}'()", pseudo_function.name);
  466. return ParseError::SyntaxError;
  467. }
  468. auto pseudo_class = maybe_pseudo_class.value();
  469. auto metadata = pseudo_class_metadata(pseudo_class);
  470. if (!metadata.is_valid_as_function) {
  471. dbgln_if(CSS_PARSER_DEBUG, "Pseudo-class ':{}' is not valid as a function", pseudo_function.name);
  472. return ParseError::SyntaxError;
  473. }
  474. if (pseudo_function.value.is_empty()) {
  475. dbgln_if(CSS_PARSER_DEBUG, "Empty :{}() selector", pseudo_function.name);
  476. return ParseError::SyntaxError;
  477. }
  478. // "The :has() pseudo-class cannot be nested; :has() is not valid within :has()."
  479. // https://drafts.csswg.org/selectors/#relational
  480. if (pseudo_class == PseudoClass::Has && m_pseudo_class_context.contains_slow(PseudoClass::Has)) {
  481. dbgln_if(CSS_PARSER_DEBUG, ":has() is not allowed inside :has()");
  482. return ParseError::SyntaxError;
  483. }
  484. m_pseudo_class_context.append(pseudo_class);
  485. ScopeGuard guard = [&] { m_pseudo_class_context.take_last(); };
  486. switch (metadata.parameter_type) {
  487. case PseudoClassMetadata::ParameterType::ANPlusB:
  488. return parse_nth_child_selector(pseudo_class, pseudo_function.value, false);
  489. case PseudoClassMetadata::ParameterType::ANPlusBOf:
  490. return parse_nth_child_selector(pseudo_class, pseudo_function.value, true);
  491. case PseudoClassMetadata::ParameterType::CompoundSelector: {
  492. auto function_token_stream = TokenStream(pseudo_function.value);
  493. auto compound_selector_or_error = parse_compound_selector(function_token_stream);
  494. if (compound_selector_or_error.is_error() || !compound_selector_or_error.value().has_value()) {
  495. dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as a compound selector", pseudo_function.name);
  496. return ParseError::SyntaxError;
  497. }
  498. auto compound_selector = compound_selector_or_error.release_value().release_value();
  499. compound_selector.combinator = Selector::Combinator::None;
  500. Vector compound_selectors { move(compound_selector) };
  501. auto selector = Selector::create(move(compound_selectors));
  502. return Selector::SimpleSelector {
  503. .type = Selector::SimpleSelector::Type::PseudoClass,
  504. .value = Selector::SimpleSelector::PseudoClassSelector {
  505. .type = pseudo_class,
  506. .argument_selector_list = { move(selector) } }
  507. };
  508. }
  509. case PseudoClassMetadata::ParameterType::ForgivingRelativeSelectorList:
  510. case PseudoClassMetadata::ParameterType::ForgivingSelectorList: {
  511. auto function_token_stream = TokenStream(pseudo_function.value);
  512. auto selector_type = metadata.parameter_type == PseudoClassMetadata::ParameterType::ForgivingSelectorList
  513. ? SelectorType::Standalone
  514. : SelectorType::Relative;
  515. // NOTE: Because it's forgiving, even complete garbage will parse OK as an empty selector-list.
  516. auto argument_selector_list = MUST(parse_a_selector_list(function_token_stream, selector_type, SelectorParsingMode::Forgiving));
  517. return Selector::SimpleSelector {
  518. .type = Selector::SimpleSelector::Type::PseudoClass,
  519. .value = Selector::SimpleSelector::PseudoClassSelector {
  520. .type = pseudo_class,
  521. .is_forgiving = true,
  522. .argument_selector_list = move(argument_selector_list) }
  523. };
  524. }
  525. case PseudoClassMetadata::ParameterType::Ident: {
  526. auto function_token_stream = TokenStream(pseudo_function.value);
  527. function_token_stream.discard_whitespace();
  528. auto const& maybe_keyword_token = function_token_stream.consume_a_token();
  529. function_token_stream.discard_whitespace();
  530. if (!maybe_keyword_token.is(Token::Type::Ident) || function_token_stream.has_next_token()) {
  531. dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as a keyword: not an ident", pseudo_function.name);
  532. return ParseError::SyntaxError;
  533. }
  534. auto maybe_keyword = keyword_from_string(maybe_keyword_token.token().ident());
  535. if (!maybe_keyword.has_value()) {
  536. dbgln_if(CSS_PARSER_DEBUG, "Failed to parse :{}() parameter as a keyword: unrecognized keyword", pseudo_function.name);
  537. return ParseError::SyntaxError;
  538. }
  539. return Selector::SimpleSelector {
  540. .type = Selector::SimpleSelector::Type::PseudoClass,
  541. .value = Selector::SimpleSelector::PseudoClassSelector {
  542. .type = pseudo_class,
  543. .keyword = maybe_keyword.value() }
  544. };
  545. }
  546. case PseudoClassMetadata::ParameterType::LanguageRanges: {
  547. Vector<FlyString> languages;
  548. auto function_token_stream = TokenStream(pseudo_function.value);
  549. auto language_token_lists = parse_a_comma_separated_list_of_component_values(function_token_stream);
  550. for (auto const& language_token_list : language_token_lists) {
  551. auto language_token_stream = TokenStream(language_token_list);
  552. language_token_stream.discard_whitespace();
  553. auto const& language_token = language_token_stream.consume_a_token();
  554. if (!(language_token.is(Token::Type::Ident) || language_token.is(Token::Type::String))) {
  555. dbgln_if(CSS_PARSER_DEBUG, "Invalid language range in :{}() - not a string/ident", pseudo_function.name);
  556. return ParseError::SyntaxError;
  557. }
  558. auto language_string = language_token.is(Token::Type::String) ? language_token.token().string() : language_token.token().ident();
  559. languages.append(language_string);
  560. language_token_stream.discard_whitespace();
  561. if (language_token_stream.has_next_token()) {
  562. dbgln_if(CSS_PARSER_DEBUG, "Invalid language range in :{}() - trailing tokens", pseudo_function.name);
  563. return ParseError::SyntaxError;
  564. }
  565. }
  566. return Selector::SimpleSelector {
  567. .type = Selector::SimpleSelector::Type::PseudoClass,
  568. .value = Selector::SimpleSelector::PseudoClassSelector {
  569. .type = pseudo_class,
  570. .languages = move(languages) }
  571. };
  572. }
  573. case PseudoClassMetadata::ParameterType::RelativeSelectorList:
  574. case PseudoClassMetadata::ParameterType::SelectorList: {
  575. auto function_token_stream = TokenStream(pseudo_function.value);
  576. auto selector_type = metadata.parameter_type == PseudoClassMetadata::ParameterType::SelectorList
  577. ? SelectorType::Standalone
  578. : SelectorType::Relative;
  579. auto not_selector = TRY(parse_a_selector_list(function_token_stream, selector_type));
  580. return Selector::SimpleSelector {
  581. .type = Selector::SimpleSelector::Type::PseudoClass,
  582. .value = Selector::SimpleSelector::PseudoClassSelector {
  583. .type = pseudo_class,
  584. .argument_selector_list = move(not_selector) }
  585. };
  586. }
  587. case PseudoClassMetadata::ParameterType::None:
  588. // `None` means this is not a function-type pseudo-class, so this state should be impossible.
  589. VERIFY_NOT_REACHED();
  590. }
  591. }
  592. dbgln_if(CSS_PARSER_DEBUG, "Unexpected Block in pseudo-class name, expected a function or identifier. '{}'", pseudo_class_token.to_debug_string());
  593. return ParseError::SyntaxError;
  594. }
  595. Parser::ParseErrorOr<Optional<Selector::SimpleSelector>> Parser::parse_simple_selector(TokenStream<ComponentValue>& tokens)
  596. {
  597. auto peek_token_ends_selector = [&]() -> bool {
  598. auto const& value = tokens.next_token();
  599. return (value.is(Token::Type::EndOfFile) || value.is(Token::Type::Whitespace) || value.is(Token::Type::Comma));
  600. };
  601. if (peek_token_ends_selector())
  602. return Optional<Selector::SimpleSelector> {};
  603. // Handle universal and tag-name types together, since both can be namespaced
  604. if (auto qualified_name = parse_selector_qualified_name(tokens, AllowWildcardName::Yes); qualified_name.has_value()) {
  605. if (qualified_name->name.name == "*"sv) {
  606. return Selector::SimpleSelector {
  607. .type = Selector::SimpleSelector::Type::Universal,
  608. .value = qualified_name.release_value(),
  609. };
  610. }
  611. return Selector::SimpleSelector {
  612. .type = Selector::SimpleSelector::Type::TagName,
  613. .value = qualified_name.release_value(),
  614. };
  615. }
  616. auto const& first_value = tokens.consume_a_token();
  617. if (first_value.is(Token::Type::Delim)) {
  618. u32 delim = first_value.token().delim();
  619. switch (delim) {
  620. case '*':
  621. // Handled already
  622. VERIFY_NOT_REACHED();
  623. case '&':
  624. return Selector::SimpleSelector {
  625. .type = Selector::SimpleSelector::Type::Nesting,
  626. };
  627. case '.': {
  628. if (peek_token_ends_selector())
  629. return ParseError::SyntaxError;
  630. auto const& class_name_value = tokens.consume_a_token();
  631. if (!class_name_value.is(Token::Type::Ident)) {
  632. dbgln_if(CSS_PARSER_DEBUG, "Expected an ident after '.', got: {}", class_name_value.to_debug_string());
  633. return ParseError::SyntaxError;
  634. }
  635. return Selector::SimpleSelector {
  636. .type = Selector::SimpleSelector::Type::Class,
  637. .value = Selector::SimpleSelector::Name { class_name_value.token().ident() }
  638. };
  639. }
  640. case '>':
  641. case '+':
  642. case '~':
  643. case '|':
  644. // Whitespace is not required between the compound-selector and a combinator.
  645. // So, if we see a combinator, return that this compound-selector is done, instead of a syntax error.
  646. tokens.reconsume_current_input_token();
  647. return Optional<Selector::SimpleSelector> {};
  648. default:
  649. dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid simple selector!");
  650. return ParseError::SyntaxError;
  651. }
  652. }
  653. if (first_value.is(Token::Type::Hash)) {
  654. if (first_value.token().hash_type() != Token::HashType::Id) {
  655. dbgln_if(CSS_PARSER_DEBUG, "Selector contains hash token that is not an id: {}", first_value.to_debug_string());
  656. return ParseError::SyntaxError;
  657. }
  658. return Selector::SimpleSelector {
  659. .type = Selector::SimpleSelector::Type::Id,
  660. .value = Selector::SimpleSelector::Name { first_value.token().hash_value() }
  661. };
  662. }
  663. if (first_value.is_block() && first_value.block().is_square())
  664. return TRY(parse_attribute_simple_selector(first_value));
  665. if (first_value.is(Token::Type::Colon))
  666. return TRY(parse_pseudo_simple_selector(tokens));
  667. dbgln_if(CSS_PARSER_DEBUG, "!!! Invalid simple selector!");
  668. return ParseError::SyntaxError;
  669. }
  670. Optional<Selector::SimpleSelector::ANPlusBPattern> Parser::parse_a_n_plus_b_pattern(TokenStream<ComponentValue>& values)
  671. {
  672. auto transaction = values.begin_transaction();
  673. auto syntax_error = [&]() -> Optional<Selector::SimpleSelector::ANPlusBPattern> {
  674. if constexpr (CSS_PARSER_DEBUG) {
  675. dbgln_if(CSS_PARSER_DEBUG, "Invalid An+B value:");
  676. values.dump_all_tokens();
  677. }
  678. return {};
  679. };
  680. auto is_sign = [](ComponentValue const& value) -> bool {
  681. return value.is(Token::Type::Delim) && (value.token().delim() == '+' || value.token().delim() == '-');
  682. };
  683. auto is_n_dimension = [](ComponentValue const& value) -> bool {
  684. if (!value.is(Token::Type::Dimension))
  685. return false;
  686. if (!value.token().number().is_integer())
  687. return false;
  688. if (!value.token().dimension_unit().equals_ignoring_ascii_case("n"sv))
  689. return false;
  690. return true;
  691. };
  692. auto is_ndash_dimension = [](ComponentValue const& value) -> bool {
  693. if (!value.is(Token::Type::Dimension))
  694. return false;
  695. if (!value.token().number().is_integer())
  696. return false;
  697. if (!value.token().dimension_unit().equals_ignoring_ascii_case("n-"sv))
  698. return false;
  699. return true;
  700. };
  701. auto is_ndashdigit_dimension = [](ComponentValue const& value) -> bool {
  702. if (!value.is(Token::Type::Dimension))
  703. return false;
  704. if (!value.token().number().is_integer())
  705. return false;
  706. auto dimension_unit = value.token().dimension_unit();
  707. if (!dimension_unit.starts_with_bytes("n-"sv, CaseSensitivity::CaseInsensitive))
  708. return false;
  709. for (size_t i = 2; i < dimension_unit.bytes_as_string_view().length(); ++i) {
  710. if (!is_ascii_digit(dimension_unit.bytes_as_string_view()[i]))
  711. return false;
  712. }
  713. return true;
  714. };
  715. auto is_ndashdigit_ident = [](ComponentValue const& value) -> bool {
  716. if (!value.is(Token::Type::Ident))
  717. return false;
  718. auto ident = value.token().ident();
  719. if (!ident.starts_with_bytes("n-"sv, CaseSensitivity::CaseInsensitive))
  720. return false;
  721. for (size_t i = 2; i < ident.bytes_as_string_view().length(); ++i) {
  722. if (!is_ascii_digit(ident.bytes_as_string_view()[i]))
  723. return false;
  724. }
  725. return true;
  726. };
  727. auto is_dashndashdigit_ident = [](ComponentValue const& value) -> bool {
  728. if (!value.is(Token::Type::Ident))
  729. return false;
  730. auto ident = value.token().ident();
  731. if (!ident.starts_with_bytes("-n-"sv, CaseSensitivity::CaseInsensitive))
  732. return false;
  733. if (ident.bytes_as_string_view().length() == 3)
  734. return false;
  735. for (size_t i = 3; i < ident.bytes_as_string_view().length(); ++i) {
  736. if (!is_ascii_digit(ident.bytes_as_string_view()[i]))
  737. return false;
  738. }
  739. return true;
  740. };
  741. auto is_integer = [](ComponentValue const& value) -> bool {
  742. return value.is(Token::Type::Number) && value.token().number().is_integer();
  743. };
  744. auto is_signed_integer = [](ComponentValue const& value) -> bool {
  745. return value.is(Token::Type::Number) && value.token().number().is_integer_with_explicit_sign();
  746. };
  747. auto is_signless_integer = [](ComponentValue const& value) -> bool {
  748. return value.is(Token::Type::Number) && !value.token().number().is_integer_with_explicit_sign();
  749. };
  750. // https://www.w3.org/TR/css-syntax-3/#the-anb-type
  751. // Unfortunately these can't be in the same order as in the spec.
  752. values.discard_whitespace();
  753. auto const& first_value = values.consume_a_token();
  754. // odd | even
  755. if (first_value.is(Token::Type::Ident)) {
  756. auto ident = first_value.token().ident();
  757. if (ident.equals_ignoring_ascii_case("odd"sv)) {
  758. transaction.commit();
  759. return Selector::SimpleSelector::ANPlusBPattern { 2, 1 };
  760. }
  761. if (ident.equals_ignoring_ascii_case("even"sv)) {
  762. transaction.commit();
  763. return Selector::SimpleSelector::ANPlusBPattern { 2, 0 };
  764. }
  765. }
  766. // <integer>
  767. if (is_integer(first_value)) {
  768. int b = first_value.token().to_integer();
  769. transaction.commit();
  770. return Selector::SimpleSelector::ANPlusBPattern { 0, b };
  771. }
  772. // <n-dimension>
  773. // <n-dimension> <signed-integer>
  774. // <n-dimension> ['+' | '-'] <signless-integer>
  775. if (is_n_dimension(first_value)) {
  776. int a = first_value.token().dimension_value_int();
  777. values.discard_whitespace();
  778. // <n-dimension> <signed-integer>
  779. if (is_signed_integer(values.next_token())) {
  780. int b = values.consume_a_token().token().to_integer();
  781. transaction.commit();
  782. return Selector::SimpleSelector::ANPlusBPattern { a, b };
  783. }
  784. // <n-dimension> ['+' | '-'] <signless-integer>
  785. {
  786. auto child_transaction = transaction.create_child();
  787. auto const& second_value = values.consume_a_token();
  788. values.discard_whitespace();
  789. auto const& third_value = values.consume_a_token();
  790. if (is_sign(second_value) && is_signless_integer(third_value)) {
  791. int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
  792. child_transaction.commit();
  793. return Selector::SimpleSelector::ANPlusBPattern { a, b };
  794. }
  795. }
  796. // <n-dimension>
  797. transaction.commit();
  798. return Selector::SimpleSelector::ANPlusBPattern { a, 0 };
  799. }
  800. // <ndash-dimension> <signless-integer>
  801. if (is_ndash_dimension(first_value)) {
  802. values.discard_whitespace();
  803. auto const& second_value = values.consume_a_token();
  804. if (is_signless_integer(second_value)) {
  805. int a = first_value.token().dimension_value_int();
  806. int b = -second_value.token().to_integer();
  807. transaction.commit();
  808. return Selector::SimpleSelector::ANPlusBPattern { a, b };
  809. }
  810. return syntax_error();
  811. }
  812. // <ndashdigit-dimension>
  813. if (is_ndashdigit_dimension(first_value)) {
  814. auto const& dimension = first_value.token();
  815. int a = dimension.dimension_value_int();
  816. auto maybe_b = dimension.dimension_unit().bytes_as_string_view().substring_view(1).to_number<int>();
  817. if (maybe_b.has_value()) {
  818. transaction.commit();
  819. return Selector::SimpleSelector::ANPlusBPattern { a, maybe_b.value() };
  820. }
  821. return syntax_error();
  822. }
  823. // <dashndashdigit-ident>
  824. if (is_dashndashdigit_ident(first_value)) {
  825. auto maybe_b = first_value.token().ident().bytes_as_string_view().substring_view(2).to_number<int>();
  826. if (maybe_b.has_value()) {
  827. transaction.commit();
  828. return Selector::SimpleSelector::ANPlusBPattern { -1, maybe_b.value() };
  829. }
  830. return syntax_error();
  831. }
  832. // -n
  833. // -n <signed-integer>
  834. // -n ['+' | '-'] <signless-integer>
  835. if (first_value.is_ident("-n"sv)) {
  836. values.discard_whitespace();
  837. // -n <signed-integer>
  838. if (is_signed_integer(values.next_token())) {
  839. int b = values.consume_a_token().token().to_integer();
  840. transaction.commit();
  841. return Selector::SimpleSelector::ANPlusBPattern { -1, b };
  842. }
  843. // -n ['+' | '-'] <signless-integer>
  844. {
  845. auto child_transaction = transaction.create_child();
  846. auto const& second_value = values.consume_a_token();
  847. values.discard_whitespace();
  848. auto const& third_value = values.consume_a_token();
  849. if (is_sign(second_value) && is_signless_integer(third_value)) {
  850. int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
  851. child_transaction.commit();
  852. return Selector::SimpleSelector::ANPlusBPattern { -1, b };
  853. }
  854. }
  855. // -n
  856. transaction.commit();
  857. return Selector::SimpleSelector::ANPlusBPattern { -1, 0 };
  858. }
  859. // -n- <signless-integer>
  860. if (first_value.is_ident("-n-"sv)) {
  861. values.discard_whitespace();
  862. auto const& second_value = values.consume_a_token();
  863. if (is_signless_integer(second_value)) {
  864. int b = -second_value.token().to_integer();
  865. transaction.commit();
  866. return Selector::SimpleSelector::ANPlusBPattern { -1, b };
  867. }
  868. return syntax_error();
  869. }
  870. // All that's left now are these:
  871. // '+'?† n
  872. // '+'?† n <signed-integer>
  873. // '+'?† n ['+' | '-'] <signless-integer>
  874. // '+'?† n- <signless-integer>
  875. // '+'?† <ndashdigit-ident>
  876. // In all of these cases, the + is optional, and has no effect.
  877. // So, we just skip the +, and carry on.
  878. if (!first_value.is_delim('+')) {
  879. values.reconsume_current_input_token();
  880. // We do *not* skip whitespace here.
  881. }
  882. auto const& first_after_plus = values.consume_a_token();
  883. // '+'?† n
  884. // '+'?† n <signed-integer>
  885. // '+'?† n ['+' | '-'] <signless-integer>
  886. if (first_after_plus.is_ident("n"sv)) {
  887. values.discard_whitespace();
  888. // '+'?† n <signed-integer>
  889. if (is_signed_integer(values.next_token())) {
  890. int b = values.consume_a_token().token().to_integer();
  891. transaction.commit();
  892. return Selector::SimpleSelector::ANPlusBPattern { 1, b };
  893. }
  894. // '+'?† n ['+' | '-'] <signless-integer>
  895. {
  896. auto child_transaction = transaction.create_child();
  897. auto const& second_value = values.consume_a_token();
  898. values.discard_whitespace();
  899. auto const& third_value = values.consume_a_token();
  900. if (is_sign(second_value) && is_signless_integer(third_value)) {
  901. int b = third_value.token().to_integer() * (second_value.is_delim('+') ? 1 : -1);
  902. child_transaction.commit();
  903. return Selector::SimpleSelector::ANPlusBPattern { 1, b };
  904. }
  905. }
  906. // '+'?† n
  907. transaction.commit();
  908. return Selector::SimpleSelector::ANPlusBPattern { 1, 0 };
  909. }
  910. // '+'?† n- <signless-integer>
  911. if (first_after_plus.is_ident("n-"sv)) {
  912. values.discard_whitespace();
  913. auto const& second_value = values.consume_a_token();
  914. if (is_signless_integer(second_value)) {
  915. int b = -second_value.token().to_integer();
  916. transaction.commit();
  917. return Selector::SimpleSelector::ANPlusBPattern { 1, b };
  918. }
  919. return syntax_error();
  920. }
  921. // '+'?† <ndashdigit-ident>
  922. if (is_ndashdigit_ident(first_after_plus)) {
  923. auto maybe_b = first_after_plus.token().ident().bytes_as_string_view().substring_view(1).to_number<int>();
  924. if (maybe_b.has_value()) {
  925. transaction.commit();
  926. return Selector::SimpleSelector::ANPlusBPattern { 1, maybe_b.value() };
  927. }
  928. return syntax_error();
  929. }
  930. return syntax_error();
  931. }
  932. }