Skip to content

Commit

Permalink
feat(css_parser): CSS Parser pseudo element selector #268 (#812)
Browse files Browse the repository at this point in the history
- move functions to a module
  • Loading branch information
denbezrukov authored Nov 20, 2023
1 parent c616cb7 commit f5ccae3
Show file tree
Hide file tree
Showing 13 changed files with 682 additions and 504 deletions.
503 changes: 0 additions & 503 deletions crates/biome_css_parser/src/syntax/selector/pseudo_class.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::parser::CssParser;
use crate::syntax::parse_error::{expect_any_selector, expected_identifier};
use crate::syntax::parse_regular_identifier;
use crate::syntax::selector::{parse_compound_selector, parse_selector_function_close_token};
use biome_css_syntax::CssSyntaxKind::CSS_PSEUDO_CLASS_FUNCTION_COMPOUND_SELECTOR;
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::{CssSyntaxKind, T};
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::{token_set, Parser, TokenSet};

const PSEUDO_CLASS_FUNCTION_COMPOUND_SELECTOR_SET: TokenSet<CssSyntaxKind> =
token_set![HOST_KW, HOSTCONTEXT_KW];

#[inline]
pub(crate) fn is_at_pseudo_class_function_compound_selector(p: &mut CssParser) -> bool {
p.at_ts(PSEUDO_CLASS_FUNCTION_COMPOUND_SELECTOR_SET) && p.nth_at(1, T!['('])
}

#[inline]
pub(crate) fn parse_pseudo_class_function_compound_selector(p: &mut CssParser) -> ParsedSyntax {
if !is_at_pseudo_class_function_compound_selector(p) {
return Absent;
}

let m = p.start();

parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier);
p.bump(T!['(']);
parse_compound_selector(p).or_add_diagnostic(p, expect_any_selector);
parse_selector_function_close_token(p);

Present(m.complete(p, CSS_PSEUDO_CLASS_FUNCTION_COMPOUND_SELECTOR))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::parser::CssParser;
use crate::syntax::parse_error::{expect_any_selector, expected_identifier};
use crate::syntax::parse_regular_identifier;
use crate::syntax::selector::{parse_compound_selector, parse_selector_function_close_token};
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::CssSyntaxKind::{
CSS_BOGUS_SELECTOR, CSS_COMPOUND_SELECTOR_LIST,
CSS_PSEUDO_CLASS_FUNCTION_COMPOUND_SELECTOR_LIST,
};
use biome_css_syntax::{CssSyntaxKind, T};
use biome_parser::parse_lists::ParseSeparatedList;
use biome_parser::parse_recovery::{ParseRecovery, RecoveryResult};
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::{token_set, Parser, TokenSet};

const PSEUDO_CLASS_FUNCTION_COMPOUND_SELECTOR_LIST_SET: TokenSet<CssSyntaxKind> =
token_set![MOZANY_KW, WEBKITANY_KW, PAST_KW, CURRENT_KW, FUTURE_KW];

#[inline]
pub(crate) fn is_at_pseudo_class_function_compound_selector_list(p: &mut CssParser) -> bool {
p.at_ts(PSEUDO_CLASS_FUNCTION_COMPOUND_SELECTOR_LIST_SET) && p.nth_at(1, T!['('])
}

#[inline]
pub(crate) fn parse_pseudo_class_function_compound_selector_list(
p: &mut CssParser,
) -> ParsedSyntax {
if !is_at_pseudo_class_function_compound_selector_list(p) {
return Absent;
}

let m = p.start();

parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier);
p.bump(T!['(']);
CssCompoundSelectorList.parse_list(p);
parse_selector_function_close_token(p);

Present(m.complete(p, CSS_PSEUDO_CLASS_FUNCTION_COMPOUND_SELECTOR_LIST))
}

struct CssCompoundSelectorList;

impl CssCompoundSelectorList {
const RECOVERY_SET: TokenSet<CssSyntaxKind> = token_set![T!['{'], T![')']];
}

impl ParseSeparatedList for CssCompoundSelectorList {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;

const LIST_KIND: CssSyntaxKind = CSS_COMPOUND_SELECTOR_LIST;

fn parse_element(&mut self, p: &mut CssParser) -> ParsedSyntax {
parse_compound_selector(p)
}

fn is_at_list_end(&self, p: &mut CssParser) -> bool {
p.at(T![')'])
}

fn recover(&mut self, p: &mut CssParser, parsed_element: ParsedSyntax) -> RecoveryResult {
parsed_element.or_recover(
p,
&ParseRecovery::new(CSS_BOGUS_SELECTOR, Self::RECOVERY_SET),
expect_any_selector,
)
}

fn separating_element_kind(&mut self) -> CssSyntaxKind {
T![,]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::parser::CssParser;
use crate::syntax::parse_error::expected_identifier;
use crate::syntax::parse_regular_identifier;
use crate::syntax::selector::parse_selector_function_close_token;
use biome_css_syntax::CssSyntaxKind::CSS_PSEUDO_CLASS_FUNCTION_IDENTIFIER;
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::{CssSyntaxKind, T};
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::{token_set, Parser, TokenSet};

const PSEUDO_CLASS_FUNCTION_IDENTIFIER_SET: TokenSet<CssSyntaxKind> = token_set![DIR_KW];

#[inline]
pub(crate) fn is_at_pseudo_class_function_identifier(p: &mut CssParser) -> bool {
p.at_ts(PSEUDO_CLASS_FUNCTION_IDENTIFIER_SET) && p.nth_at(1, T!['('])
}

#[inline]
pub(crate) fn parse_pseudo_class_function_identifier(p: &mut CssParser) -> ParsedSyntax {
if !is_at_pseudo_class_function_identifier(p) {
return Absent;
}

let m = p.start();

parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier);
p.bump(T!['(']);
parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier);
parse_selector_function_close_token(p);

Present(m.complete(p, CSS_PSEUDO_CLASS_FUNCTION_IDENTIFIER))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
use crate::lexer::CssLexContext;
use crate::parser::CssParser;
use crate::syntax::parse_error::{
expect_any_selector, expected_identifier, expected_number, expected_pseudo_class_nth,
};
use crate::syntax::selector::{parse_selector_function_close_token, CssSelectorList};
use crate::syntax::{parse_number, parse_regular_identifier, parse_regular_number};
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::CssSyntaxKind::{
CSS_NTH_OFFSET, CSS_PSEUDO_CLASS_FUNCTION_NTH, CSS_PSEUDO_CLASS_NTH,
CSS_PSEUDO_CLASS_NTH_IDENTIFIER, CSS_PSEUDO_CLASS_NTH_NUMBER, CSS_PSEUDO_CLASS_NTH_SELECTOR,
CSS_PSEUDO_CLASS_OF_NTH_SELECTOR, OF_KW,
};
use biome_css_syntax::{CssSyntaxKind, T};
use biome_parser::parse_lists::ParseSeparatedList;
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::{token_set, Parser, TokenSet};

const PSEUDO_CLASS_FUNCTION_NTH_SET: TokenSet<CssSyntaxKind> = token_set![
NTHCHILD_KW,
NTHLASTCHILD_KW,
NTHOFTYPE_KW,
NTHLASTOFTYPE_KW,
NTHCOL_KW,
NTHLASTCOL_KW
];

#[inline]
pub(crate) fn is_at_pseudo_class_function_nth(p: &mut CssParser) -> bool {
p.at_ts(PSEUDO_CLASS_FUNCTION_NTH_SET) && p.nth_at(1, T!['('])
}

#[inline]
pub(crate) fn parse_pseudo_class_function_nth(p: &mut CssParser) -> ParsedSyntax {
if !is_at_pseudo_class_function_nth(p) {
return Absent;
}

let m = p.start();

parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier);

p.bump_with_context(T!['('], CssLexContext::PseudoNthSelector);
parse_pseudo_class_nth_selector(p).or_add_diagnostic(p, expect_any_selector);
parse_selector_function_close_token(p);

Present(m.complete(p, CSS_PSEUDO_CLASS_FUNCTION_NTH))
}

const PSEUDO_CLASS_FUNCTION_NTH_CLASS_IDENTIFIER_SET: TokenSet<CssSyntaxKind> =
token_set![T![odd], T![even]];

const PSEUDO_CLASS_FUNCTION_NTH_CLASS_SIGN_SET: TokenSet<CssSyntaxKind> = token_set![T![+], T![-]];

const PSEUDO_CLASS_FUNCTION_NTH_CLASS_SET: TokenSet<CssSyntaxKind> =
PSEUDO_CLASS_FUNCTION_NTH_CLASS_IDENTIFIER_SET
.union(PSEUDO_CLASS_FUNCTION_NTH_CLASS_SIGN_SET)
.union(token_set![T![n], CSS_NUMBER_LITERAL,]);

#[inline]
fn is_at_pseudo_class_nth_selector(p: &mut CssParser) -> bool {
p.at_ts(PSEUDO_CLASS_FUNCTION_NTH_CLASS_SET)
}

#[inline]
fn parse_pseudo_class_nth_selector(p: &mut CssParser) -> ParsedSyntax {
if !is_at_pseudo_class_nth_selector(p) {
return Absent;
}

let m = p.start();

parse_pseudo_class_nth(p).or_add_diagnostic(p, expected_pseudo_class_nth);
parse_pseudo_class_of_nth_selector(p).ok();

Present(m.complete(p, CSS_PSEUDO_CLASS_NTH_SELECTOR))
}

#[inline]
fn parse_pseudo_class_nth(p: &mut CssParser) -> ParsedSyntax {
if !is_at_pseudo_class_nth_selector(p) {
return Absent;
}

let m = p.start();

let kind = if p.eat_ts(PSEUDO_CLASS_FUNCTION_NTH_CLASS_IDENTIFIER_SET) {
CSS_PSEUDO_CLASS_NTH_IDENTIFIER
} else {
p.eat_ts_with_context(
PSEUDO_CLASS_FUNCTION_NTH_CLASS_SIGN_SET,
CssLexContext::PseudoNthSelector,
);

parse_number(p, CssLexContext::PseudoNthSelector).ok();

if p.eat_with_context(T![n], CssLexContext::PseudoNthSelector) {
parse_nth_offset(p).ok();

CSS_PSEUDO_CLASS_NTH
} else {
CSS_PSEUDO_CLASS_NTH_NUMBER
}
};

Present(m.complete(p, kind))
}

#[inline]
fn parse_nth_offset(p: &mut CssParser) -> ParsedSyntax {
if !p.at_ts(PSEUDO_CLASS_FUNCTION_NTH_CLASS_SIGN_SET) {
return Absent;
}

let m = p.start();

p.bump_ts(PSEUDO_CLASS_FUNCTION_NTH_CLASS_SIGN_SET);
parse_regular_number(p).or_add_diagnostic(p, expected_number);

Present(m.complete(p, CSS_NTH_OFFSET))
}

#[inline]
fn parse_pseudo_class_of_nth_selector(p: &mut CssParser) -> ParsedSyntax {
if !p.at(OF_KW) {
return Absent;
}

let m = p.start();

p.bump(OF_KW);

CssSelectorList::default()
.with_end_kind(T![')'])
.parse_list(p);

Present(m.complete(p, CSS_PSEUDO_CLASS_OF_NTH_SELECTOR))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use crate::parser::CssParser;
use crate::syntax::parse_error::{expect_any_selector, expected_identifier};
use crate::syntax::parse_regular_identifier;
use crate::syntax::selector::{
is_at_compound_selector, parse_selector, parse_selector_function_close_token,
};
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::CssSyntaxKind::{
CSS_BOGUS_SELECTOR, CSS_PSEUDO_CLASS_FUNCTION_RELATIVE_SELECTOR_LIST, CSS_RELATIVE_SELECTOR,
CSS_RELATIVE_SELECTOR_LIST,
};
use biome_css_syntax::{CssSyntaxKind, T};
use biome_parser::parse_lists::ParseSeparatedList;
use biome_parser::parse_recovery::{ParseRecovery, RecoveryResult};
use biome_parser::parsed_syntax::ParsedSyntax;
use biome_parser::parsed_syntax::ParsedSyntax::{Absent, Present};
use biome_parser::{token_set, Parser, TokenSet};

const PSEUDO_CLASS_FUNCTION_RELATIVE_SELECTOR_LIST_SET: TokenSet<CssSyntaxKind> =
token_set![HAS_KW];

#[inline]
pub(crate) fn is_at_pseudo_class_function_relative_selector_list(p: &mut CssParser) -> bool {
p.at_ts(PSEUDO_CLASS_FUNCTION_RELATIVE_SELECTOR_LIST_SET) && p.nth_at(1, T!['('])
}

#[inline]
pub(crate) fn parse_pseudo_class_function_relative_selector_list(
p: &mut CssParser,
) -> ParsedSyntax {
if !is_at_pseudo_class_function_relative_selector_list(p) {
return Absent;
}

let m = p.start();

parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier);
p.bump(T!['(']);
CssRelativeSelectorList.parse_list(p);
parse_selector_function_close_token(p);

Present(m.complete(p, CSS_PSEUDO_CLASS_FUNCTION_RELATIVE_SELECTOR_LIST))
}

struct CssRelativeSelectorList;

impl CssRelativeSelectorList {
const RECOVERY_SET: TokenSet<CssSyntaxKind> = token_set![T!['{'], T![')']];
}

impl ParseSeparatedList for CssRelativeSelectorList {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;

const LIST_KIND: CssSyntaxKind = CSS_RELATIVE_SELECTOR_LIST;

fn parse_element(&mut self, p: &mut CssParser) -> ParsedSyntax {
parse_relative_selector(p)
}

fn is_at_list_end(&self, p: &mut CssParser) -> bool {
p.at(T![')'])
}

fn recover(&mut self, p: &mut CssParser, parsed_element: ParsedSyntax) -> RecoveryResult {
parsed_element.or_recover(
p,
&ParseRecovery::new(CSS_BOGUS_SELECTOR, Self::RECOVERY_SET),
expect_any_selector,
)
}

fn separating_element_kind(&mut self) -> CssSyntaxKind {
T![,]
}
}

const RELATIVE_SELECTOR_COMBINATOR_SET: TokenSet<CssSyntaxKind> =
token_set![T![>], T![+], T![~], T![||]];

#[inline]
fn is_at_relative_selector_combinator(p: &mut CssParser) -> bool {
p.at_ts(RELATIVE_SELECTOR_COMBINATOR_SET)
}

#[inline]
fn is_at_relative_selector(p: &mut CssParser) -> bool {
is_at_relative_selector_combinator(p) || is_at_compound_selector(p)
}

#[inline]
fn parse_relative_selector(p: &mut CssParser) -> ParsedSyntax {
if !is_at_relative_selector(p) {
return Absent;
}

let m = p.start();

p.eat_ts(RELATIVE_SELECTOR_COMBINATOR_SET);
parse_selector(p).or_add_diagnostic(p, expect_any_selector);

Present(m.complete(p, CSS_RELATIVE_SELECTOR))
}
Loading

0 comments on commit f5ccae3

Please sign in to comment.