Refactor Selector to be Vec rather than recursive enum
The initial implementation of Selector was a recursive enum with boxed variants. This new implementation is linear and relies on only one level of indirection.
This commit is contained in:
parent
dc05c8db2d
commit
ebfeb35341
@ -143,6 +143,7 @@ mod test_scss {
|
||||
test!(selector_el_id_descendant, "a #class {\n}\n");
|
||||
test!(selector_el_universal_descendant, "a * {\n}\n");
|
||||
test!(selector_universal_el_descendant, "* a {\n}\n");
|
||||
|
||||
test!(selector_attribute_any, "[attr] {\n}\n");
|
||||
test!(selector_attribute_equals, "[attr=val] {\n}\n");
|
||||
test!(selector_attribute_single_quotes, "[attr='val'] {\n}\n");
|
||||
@ -164,6 +165,7 @@ mod test_scss {
|
||||
test!(selector_pseudo, ":pseudo {\n}\n");
|
||||
test!(selector_el_pseudo_and, "a:pseudo {\n}\n");
|
||||
test!(selector_el_pseudo_descendant, "a :pseudo {\n}\n");
|
||||
test!(selector_pseudo_el_descendant, ":pseudo a {\n}\n");
|
||||
|
||||
test!(basic_style, "a {\n color: red;\n}\n");
|
||||
test!(two_styles, "a {\n color: red;\n color: blue;\n}\n");
|
||||
|
15
src/lexer.rs
15
src/lexer.rs
@ -3,7 +3,7 @@ use std::iter::Peekable;
|
||||
use std::str::Chars;
|
||||
|
||||
use crate::common::{AtRule, Keyword, Op, Pos, Symbol};
|
||||
use crate::selector::{Attribute, AttributeKind, Selector};
|
||||
use crate::selector::{Attribute, AttributeKind};
|
||||
use crate::units::Unit;
|
||||
use crate::{Token, TokenKind, Whitespace};
|
||||
|
||||
@ -59,6 +59,7 @@ impl<'a> Iterator for Lexer<'a> {
|
||||
'{' => symbol!(self, OpenBrace),
|
||||
'*' => symbol!(self, Mul),
|
||||
'}' => symbol!(self, CloseBrace),
|
||||
'&' => symbol!(self, BitAnd),
|
||||
'/' => self.lex_forward_slash(),
|
||||
'%' => {
|
||||
self.buf.next();
|
||||
@ -226,22 +227,22 @@ impl<'a> Lexer<'a> {
|
||||
.expect("todo! expected kind (should be error)")
|
||||
{
|
||||
']' => {
|
||||
return TokenKind::Selector(Selector::Attribute(Attribute {
|
||||
return TokenKind::Attribute(Attribute {
|
||||
kind: AttributeKind::Any,
|
||||
attr,
|
||||
value: String::new(),
|
||||
case_sensitive: true,
|
||||
}))
|
||||
})
|
||||
}
|
||||
'i' => {
|
||||
self.devour_whitespace();
|
||||
assert!(self.buf.next() == Some(']'));
|
||||
return TokenKind::Selector(Selector::Attribute(Attribute {
|
||||
return TokenKind::Attribute(Attribute {
|
||||
kind: AttributeKind::Any,
|
||||
attr,
|
||||
value: String::new(),
|
||||
case_sensitive: false,
|
||||
}));
|
||||
});
|
||||
}
|
||||
'=' => AttributeKind::Equals,
|
||||
'~' => AttributeKind::InList,
|
||||
@ -297,12 +298,12 @@ impl<'a> Lexer<'a> {
|
||||
|
||||
assert!(self.buf.next() == Some(']'));
|
||||
|
||||
TokenKind::Selector(Selector::Attribute(Attribute {
|
||||
TokenKind::Attribute(Attribute {
|
||||
kind,
|
||||
attr,
|
||||
value,
|
||||
case_sensitive,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
fn lex_variable(&mut self) -> TokenKind {
|
||||
|
111
src/main.rs
111
src/main.rs
@ -21,7 +21,8 @@
|
||||
clippy::unused_self,
|
||||
clippy::too_many_lines,
|
||||
clippy::integer_arithmetic,
|
||||
clippy::missing_errors_doc
|
||||
clippy::missing_errors_doc,
|
||||
clippy::let_underscore_must_use
|
||||
)]
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Display};
|
||||
@ -35,7 +36,7 @@ use crate::css::Css;
|
||||
use crate::error::SassError;
|
||||
use crate::format::PrettyPrinter;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::selector::Selector;
|
||||
use crate::selector::{Attribute, Selector};
|
||||
use crate::style::Style;
|
||||
use crate::units::Unit;
|
||||
|
||||
@ -68,7 +69,7 @@ pub enum TokenKind {
|
||||
Unit(Unit),
|
||||
Whitespace(Whitespace),
|
||||
Variable(String),
|
||||
Selector(Selector),
|
||||
Attribute(Attribute),
|
||||
Style(Vec<Token>),
|
||||
Op(Op),
|
||||
// todo! preserve multi-line comments
|
||||
@ -84,7 +85,7 @@ impl Display for TokenKind {
|
||||
TokenKind::Op(s) => write!(f, "{}", s),
|
||||
TokenKind::Unit(s) => write!(f, "{}", s),
|
||||
TokenKind::Whitespace(s) => write!(f, "{}", s),
|
||||
TokenKind::Selector(s) => write!(f, "{}", s),
|
||||
TokenKind::Attribute(s) => write!(f, "{}", s),
|
||||
TokenKind::Function(name, args) => write!(f, "{}({})", name, args.join(", ")),
|
||||
TokenKind::Keyword(kw) => write!(f, "{}", kw),
|
||||
TokenKind::MultilineComment(s) => write!(f, "/*{}*/", s),
|
||||
@ -94,7 +95,6 @@ impl Display for TokenKind {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Represents a parsed SASS stylesheet with nesting
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct StyleSheet {
|
||||
@ -112,7 +112,7 @@ pub enum Stmt {
|
||||
}
|
||||
|
||||
/// Represents a single rule set. Rule sets can contain other rule sets
|
||||
///
|
||||
///
|
||||
/// a {
|
||||
/// color: blue;
|
||||
/// b {
|
||||
@ -165,10 +165,10 @@ impl StyleSheet {
|
||||
}
|
||||
|
||||
/// Print the internal representation of a parsed stylesheet
|
||||
///
|
||||
///
|
||||
/// Very closely resembles the origin SASS, but contains only things translatable
|
||||
/// to pure CSS
|
||||
///
|
||||
///
|
||||
/// Used mainly in debugging, but can at times be useful
|
||||
pub fn pretty_print<W: std::io::Write>(&self, buf: W) -> io::Result<()> {
|
||||
PrettyPrinter::new(buf).pretty_print(self)
|
||||
@ -199,12 +199,13 @@ impl<'a> StyleSheetParser<'a> {
|
||||
while let Some(Token { kind, .. }) = self.lexer.peek() {
|
||||
match kind.clone() {
|
||||
TokenKind::Ident(_)
|
||||
| TokenKind::Selector(_)
|
||||
| TokenKind::Attribute(_)
|
||||
| TokenKind::Symbol(Symbol::Hash)
|
||||
| TokenKind::Symbol(Symbol::Colon)
|
||||
| TokenKind::Symbol(Symbol::Mul)
|
||||
| TokenKind::Symbol(Symbol::Period) => rules
|
||||
.extend(self.eat_rules(&Selector::None, &mut self.global_variables.clone())),
|
||||
| TokenKind::Symbol(Symbol::Period) => rules.extend(
|
||||
self.eat_rules(&Selector(Vec::new()), &mut self.global_variables.clone()),
|
||||
),
|
||||
TokenKind::Whitespace(_) | TokenKind::Symbol(_) => {
|
||||
self.lexer.next();
|
||||
continue;
|
||||
@ -318,7 +319,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
vars: &mut HashMap<String, Vec<Token>>,
|
||||
) -> Vec<Stmt> {
|
||||
let mut stmts = Vec::new();
|
||||
while let Ok(tok) = self.eat_expr(vars) {
|
||||
while let Ok(tok) = self.eat_expr(vars, super_selector) {
|
||||
match tok {
|
||||
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
||||
Expr::Selector(s) => {
|
||||
@ -348,7 +349,11 @@ impl<'a> StyleSheetParser<'a> {
|
||||
stmts
|
||||
}
|
||||
|
||||
fn eat_expr(&mut self, vars: &HashMap<String, Vec<Token>>) -> Result<Expr, ()> {
|
||||
fn eat_expr(
|
||||
&mut self,
|
||||
vars: &HashMap<String, Vec<Token>>,
|
||||
super_selector: &Selector,
|
||||
) -> Result<Expr, ()> {
|
||||
let mut values = Vec::with_capacity(5);
|
||||
while let Some(tok) = self.lexer.next() {
|
||||
match tok.kind {
|
||||
@ -360,6 +365,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
self.devour_whitespace();
|
||||
return Ok(Expr::Selector(Selector::from_tokens(
|
||||
values.iter().peekable(),
|
||||
super_selector,
|
||||
)));
|
||||
}
|
||||
TokenKind::Variable(name) => {
|
||||
@ -453,10 +459,85 @@ mod test_css {
|
||||
}
|
||||
|
||||
test!(
|
||||
nesting_el_mul_el,
|
||||
selector_nesting_el_mul_el,
|
||||
"a, b {\n a, b {\n color: red\n}\n}\n",
|
||||
"a a, b a, a b, b b {\n color: red;\n}\n"
|
||||
"a a, a b, b a, b b {\n color: red;\n}\n"
|
||||
);
|
||||
|
||||
test!(selector_element, "a {\n color: red;\n}\n");
|
||||
test!(selector_id, "#id {\n color: red;\n}\n");
|
||||
test!(selector_class, ".class {\n color: red;\n}\n");
|
||||
test!(selector_el_descendant, "a a {\n color: red;\n}\n");
|
||||
test!(selector_universal, "* {\n color: red;\n}\n");
|
||||
test!(selector_el_class_and, "a.class {\n color: red;\n}\n");
|
||||
test!(selector_el_id_and, "a#class {\n color: red;\n}\n");
|
||||
test!(
|
||||
selector_el_class_descendant,
|
||||
"a .class {\n color: red;\n}\n"
|
||||
);
|
||||
test!(selector_el_id_descendant, "a #class {\n color: red;\n}\n");
|
||||
test!(
|
||||
selector_el_universal_descendant,
|
||||
"a * {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_universal_el_descendant,
|
||||
"* a {\n color: red;\n}\n"
|
||||
);
|
||||
|
||||
test!(selector_attribute_any, "[attr] {\n color: red;\n}\n");
|
||||
test!(
|
||||
selector_attribute_equals,
|
||||
"[attr=val] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_attribute_single_quotes,
|
||||
"[attr='val'] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_attribute_double_quotes,
|
||||
"[attr=\"val\"] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(selector_attribute_in, "[attr~=val] {\n color: red;\n}\n");
|
||||
test!(
|
||||
selector_attribute_begins_hyphen_or_exact,
|
||||
"[attr|=val] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_attribute_starts_with,
|
||||
"[attr^=val] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_attribute_ends_with,
|
||||
"[attr$=val] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_attribute_contains,
|
||||
"[attr*=val] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(selector_el_attribute_and, "a[attr] {\n color: red;\n}\n");
|
||||
test!(
|
||||
selector_el_attribute_descendant,
|
||||
"a [attr] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(selector_el_mul_el, "a, b {\n color: red;\n}\n");
|
||||
test!(
|
||||
selector_el_immediate_child_el,
|
||||
"a > b {\n color: red;\n}\n"
|
||||
);
|
||||
test!(selector_el_following_el, "a + b {\n color: red;\n}\n");
|
||||
test!(selector_el_preceding_el, "a ~ b {\n color: red;\n}\n");
|
||||
test!(selector_pseudo, ":pseudo {\n color: red;\n}\n");
|
||||
test!(selector_el_pseudo_and, "a:pseudo {\n color: red;\n}\n");
|
||||
test!(
|
||||
selector_el_pseudo_descendant,
|
||||
"a :pseudo {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_pseudo_el_descendant,
|
||||
":pseudo a {\n color: red;\n}\n"
|
||||
);
|
||||
|
||||
test!(basic_style, "a {\n color: red;\n}\n");
|
||||
test!(two_styles, "a {\n color: red;\n color: blue;\n}\n");
|
||||
test!(
|
||||
|
325
src/selector.rs
325
src/selector.rs
@ -5,118 +5,193 @@ use std::iter::Peekable;
|
||||
use std::slice::Iter;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Selector {
|
||||
/// An element selector: `button`
|
||||
Element(String),
|
||||
/// An id selector: `#footer`
|
||||
Id(String),
|
||||
/// A single class selector: `.button-active`
|
||||
Class(String),
|
||||
/// A universal selector: `*`
|
||||
Universal,
|
||||
/// A simple child selector: `ul li`
|
||||
Descendant(Box<Selector>, Box<Selector>),
|
||||
/// And selector: `button.active`
|
||||
And(Box<Selector>, Box<Selector>),
|
||||
/// Multiple unrelated selectors: `button, .active`
|
||||
Multiple(Box<Selector>, Box<Selector>),
|
||||
/// Select all immediate children: `ul > li`
|
||||
ImmediateChild(Box<Selector>, Box<Selector>),
|
||||
/// Select all elements immediately following: `div + p`
|
||||
Following(Box<Selector>, Box<Selector>),
|
||||
/// Select elements preceeded by: `p ~ ul`
|
||||
Preceding(Box<Selector>, Box<Selector>),
|
||||
/// Select elements with attribute: `html[lang|=en]`
|
||||
Attribute(Attribute),
|
||||
/// Pseudo selector: `:hover`
|
||||
Pseudo(String),
|
||||
/// Used to signify no selector (when there is no super_selector of a rule)
|
||||
None,
|
||||
pub struct Selector(pub Vec<SelectorKind>);
|
||||
|
||||
fn devour_whitespace(i: &mut Peekable<Iter<SelectorKind>>) -> bool {
|
||||
let mut found_whitespace = false;
|
||||
while let Some(SelectorKind::Whitespace) = i.peek() {
|
||||
i.next();
|
||||
found_whitespace = true;
|
||||
}
|
||||
found_whitespace
|
||||
}
|
||||
|
||||
impl Display for Selector {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut iter = self.0.iter().peekable();
|
||||
|
||||
while let Some(s) = iter.next() {
|
||||
match s {
|
||||
SelectorKind::Whitespace => continue,
|
||||
SelectorKind::Attribute(_)
|
||||
| SelectorKind::Pseudo(_)
|
||||
| SelectorKind::Class
|
||||
| SelectorKind::Id
|
||||
| SelectorKind::Universal
|
||||
| SelectorKind::Element(_) => {
|
||||
write!(f, "{}", s)?;
|
||||
if devour_whitespace(&mut iter) {
|
||||
match iter.peek() {
|
||||
Some(SelectorKind::Attribute(_))
|
||||
| Some(SelectorKind::Pseudo(_))
|
||||
| Some(SelectorKind::Class)
|
||||
| Some(SelectorKind::Id)
|
||||
| Some(SelectorKind::Universal)
|
||||
| Some(SelectorKind::Element(_)) => {
|
||||
write!(f, " {}", iter.next().expect("already peeked here"))?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => write!(f, "{}", s)?,
|
||||
}
|
||||
}
|
||||
write!(f, "")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum SelectorKind {
|
||||
/// An element selector: `button`
|
||||
Element(String),
|
||||
/// An id selector: `#footer`
|
||||
Id,
|
||||
/// A single class selector: `.button-active`
|
||||
Class,
|
||||
/// A universal selector: `*`
|
||||
Universal,
|
||||
/// Multiple unrelated selectors: `button, .active`
|
||||
Multiple,
|
||||
/// Select all immediate children: `ul > li`
|
||||
ImmediateChild,
|
||||
/// Select all elements immediately following: `div + p`
|
||||
Following,
|
||||
/// Select elements preceeded by: `p ~ ul`
|
||||
Preceding,
|
||||
/// Select elements with attribute: `html[lang|=en]`
|
||||
Attribute(Attribute),
|
||||
/// Pseudo selector: `:hover`
|
||||
Pseudo(String),
|
||||
/// Use the super selector: `&.red`
|
||||
// Super,
|
||||
/// Used to signify no selector (when there is no super_selector of a rule)
|
||||
None,
|
||||
Whitespace,
|
||||
}
|
||||
|
||||
impl Display for SelectorKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Selector::Element(s) => write!(f, "{}", s),
|
||||
Selector::Id(s) => write!(f, "#{}", s),
|
||||
Selector::Class(s) => write!(f, ".{}", s),
|
||||
Selector::Universal => write!(f, "*"),
|
||||
Selector::Descendant(lhs, rhs) => write!(f, "{} {}", lhs, rhs),
|
||||
Selector::And(lhs, rhs) => write!(f, "{}{}", lhs, rhs),
|
||||
Selector::Multiple(lhs, rhs) => write!(f, "{}, {}", lhs, rhs),
|
||||
Selector::ImmediateChild(lhs, rhs) => write!(f, "{} > {}", lhs, rhs),
|
||||
Selector::Following(lhs, rhs) => write!(f, "{} + {}", lhs, rhs),
|
||||
Selector::Preceding(lhs, rhs) => write!(f, "{} ~ {}", lhs, rhs),
|
||||
Selector::Attribute(attr) => write!(f, "{}", attr),
|
||||
Selector::Pseudo(s) => write!(f, ":{}", s),
|
||||
Selector::None => write!(f, ""),
|
||||
SelectorKind::Element(s) => write!(f, "{}", s),
|
||||
SelectorKind::Id => write!(f, "#"),
|
||||
SelectorKind::Class => write!(f, "."),
|
||||
SelectorKind::Universal => write!(f, "*"),
|
||||
SelectorKind::Whitespace => write!(f, " "),
|
||||
SelectorKind::Multiple => write!(f, ", "),
|
||||
SelectorKind::ImmediateChild => write!(f, " > "),
|
||||
SelectorKind::Following => write!(f, " + "),
|
||||
SelectorKind::Preceding => write!(f, " ~ "),
|
||||
SelectorKind::Attribute(attr) => write!(f, "{}", attr),
|
||||
SelectorKind::Pseudo(s) => write!(f, ":{}", s),
|
||||
// SelectorKind::Super => write!(f, "{}"),
|
||||
SelectorKind::None => write!(f, ""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_selector_display {
|
||||
use super::*;
|
||||
use SelectorKind::*;
|
||||
macro_rules! test_selector_display {
|
||||
|
||||
($func:ident, Selector($tok:tt), $output:literal) => {
|
||||
#[test]
|
||||
fn $func() {
|
||||
assert_eq!(format!("{}", Selector(vec!$tok)), $output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test_selector_display!(el, Selector((Element("a".to_string()))), "a");
|
||||
test_selector_display!(
|
||||
keeps_one_whitespace,
|
||||
Selector((
|
||||
Element("a".to_string()),
|
||||
Whitespace,
|
||||
Element("b".to_string()),
|
||||
)),
|
||||
"a b"
|
||||
);
|
||||
test_selector_display!(
|
||||
keeps_one_whitespace_with_two,
|
||||
Selector((
|
||||
Element("a".to_string()),
|
||||
Whitespace,
|
||||
Whitespace,
|
||||
Element("c".to_string()),
|
||||
Whitespace,
|
||||
)),
|
||||
"a c"
|
||||
);
|
||||
}
|
||||
|
||||
struct SelectorParser<'a> {
|
||||
tokens: Peekable<Iter<'a, Token>>,
|
||||
super_selector: &'a Selector,
|
||||
}
|
||||
|
||||
impl<'a> SelectorParser<'a> {
|
||||
const fn new(tokens: Peekable<Iter<'a, Token>>) -> SelectorParser<'a> {
|
||||
SelectorParser { tokens }
|
||||
const fn new(
|
||||
tokens: Peekable<Iter<'a, Token>>,
|
||||
super_selector: &'a Selector,
|
||||
) -> SelectorParser<'a> {
|
||||
SelectorParser {
|
||||
tokens,
|
||||
super_selector,
|
||||
}
|
||||
}
|
||||
|
||||
fn all_selectors(&mut self) -> Selector {
|
||||
self.devour_whitespace();
|
||||
let left = self
|
||||
.consume_selector()
|
||||
.expect("expected left handed selector");
|
||||
let whitespace: bool = self.devour_whitespace();
|
||||
match self.tokens.peek() {
|
||||
Some(tok) => match tok.kind {
|
||||
TokenKind::Ident(_) => {
|
||||
return Selector::Descendant(Box::new(left), Box::new(self.all_selectors()))
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Plus) => {
|
||||
self.tokens.next();
|
||||
self.devour_whitespace();
|
||||
return Selector::Following(Box::new(left), Box::new(self.all_selectors()));
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Tilde) => {
|
||||
self.tokens.next();
|
||||
self.devour_whitespace();
|
||||
return Selector::Preceding(Box::new(left), Box::new(self.all_selectors()));
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Comma) => {
|
||||
self.tokens.next();
|
||||
self.devour_whitespace();
|
||||
return Selector::Multiple(Box::new(left), Box::new(self.all_selectors()));
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Gt) => {
|
||||
self.tokens.next();
|
||||
self.devour_whitespace();
|
||||
return Selector::ImmediateChild(
|
||||
Box::new(left),
|
||||
Box::new(self.all_selectors()),
|
||||
);
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Colon)
|
||||
| TokenKind::Symbol(Symbol::Period)
|
||||
| TokenKind::Symbol(Symbol::Mul)
|
||||
| TokenKind::Selector(_)
|
||||
| TokenKind::Symbol(Symbol::Hash) => {
|
||||
if whitespace {
|
||||
return Selector::Descendant(
|
||||
Box::new(left),
|
||||
Box::new(self.all_selectors()),
|
||||
);
|
||||
let mut v = Vec::new();
|
||||
while let Some(s) = self.consume_selector() {
|
||||
v.push(s);
|
||||
}
|
||||
Selector(v)
|
||||
}
|
||||
|
||||
fn consume_selector(&mut self) -> Option<SelectorKind> {
|
||||
if self.devour_whitespace() {
|
||||
return Some(SelectorKind::Whitespace);
|
||||
}
|
||||
if let Some(Token { kind, .. }) = self.tokens.next() {
|
||||
return Some(match &kind {
|
||||
TokenKind::Ident(tok) => SelectorKind::Element(tok.clone()),
|
||||
TokenKind::Symbol(Symbol::Period) => SelectorKind::Class,
|
||||
TokenKind::Symbol(Symbol::Hash) => SelectorKind::Id,
|
||||
TokenKind::Symbol(Symbol::Colon) => {
|
||||
if let Some(Token {
|
||||
kind: TokenKind::Ident(s),
|
||||
..
|
||||
}) = self.tokens.next()
|
||||
{
|
||||
SelectorKind::Pseudo(s.clone())
|
||||
} else {
|
||||
return Selector::And(Box::new(left), Box::new(self.all_selectors()));
|
||||
todo!("expected ident after `:` in selector")
|
||||
}
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Lt) => {}
|
||||
_ => todo!(),
|
||||
},
|
||||
None => return left,
|
||||
TokenKind::Symbol(Symbol::Comma) => SelectorKind::Multiple,
|
||||
TokenKind::Symbol(Symbol::Gt) => SelectorKind::ImmediateChild,
|
||||
TokenKind::Symbol(Symbol::Plus) => SelectorKind::Following,
|
||||
TokenKind::Symbol(Symbol::Tilde) => SelectorKind::Preceding,
|
||||
TokenKind::Symbol(Symbol::Mul) => SelectorKind::Universal,
|
||||
// TokenKind::Symbol(Symbol::BitAnd) => SelectorKind::Super,
|
||||
TokenKind::Attribute(attr) => SelectorKind::Attribute(attr.clone()),
|
||||
_ => todo!("unimplemented selector"),
|
||||
});
|
||||
}
|
||||
todo!()
|
||||
None
|
||||
}
|
||||
|
||||
fn devour_whitespace(&mut self) -> bool {
|
||||
@ -132,56 +207,42 @@ impl<'a> SelectorParser<'a> {
|
||||
}
|
||||
found_whitespace
|
||||
}
|
||||
|
||||
fn consume_selector(&mut self) -> Option<Selector> {
|
||||
if let Some(tok) = self.tokens.next() {
|
||||
let selector = match &tok.kind {
|
||||
TokenKind::Symbol(Symbol::Period) => {
|
||||
match self.tokens.next().expect("expected ident after `.`").kind {
|
||||
TokenKind::Ident(ref tok) => Selector::Class(tok.clone()),
|
||||
_ => todo!("there should normally be an ident after `.`"),
|
||||
}
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Mul) => Selector::Universal,
|
||||
TokenKind::Symbol(Symbol::Hash) => {
|
||||
match &self.tokens.next().expect("expected ident after `#`").kind {
|
||||
TokenKind::Ident(ref tok) => Selector::Id(tok.clone()),
|
||||
_ => todo!("there should normally be an ident after `#`"),
|
||||
}
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Colon) => {
|
||||
match self.tokens.next().expect("expected ident after `:`").kind {
|
||||
TokenKind::Ident(ref tok) => Selector::Pseudo(tok.clone()),
|
||||
_ => todo!("there should normally be an ident after `:`"),
|
||||
}
|
||||
}
|
||||
TokenKind::Ident(tok) => Selector::Element(tok.clone()),
|
||||
TokenKind::Selector(sel) => sel.clone(),
|
||||
_ => todo!(),
|
||||
};
|
||||
Some(selector)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Selector {
|
||||
pub fn from_tokens(tokens: Peekable<Iter<Token>>) -> Selector {
|
||||
SelectorParser::new(tokens).all_selectors()
|
||||
pub fn from_tokens<'a>(
|
||||
tokens: Peekable<Iter<'a, Token>>,
|
||||
super_selector: &'a Selector,
|
||||
) -> Selector {
|
||||
SelectorParser::new(tokens, super_selector).all_selectors()
|
||||
}
|
||||
|
||||
pub fn zip(self, other: Selector) -> Selector {
|
||||
if let Selector::Multiple(lhs, rhs) = other {
|
||||
return Selector::Multiple(Box::new(self.clone().zip(*lhs)), Box::new(self.zip(*rhs)));
|
||||
if self.0.is_empty() {
|
||||
return Selector(other.0);
|
||||
}
|
||||
match self {
|
||||
Selector::Multiple(lhs, rhs) => {
|
||||
Selector::Multiple(Box::new(lhs.zip(other.clone())), Box::new(rhs.zip(other)))
|
||||
let mut rules: Vec<SelectorKind> = Vec::with_capacity(self.0.len());
|
||||
let sel1_split: Vec<Vec<SelectorKind>> = self
|
||||
.0
|
||||
.split(|sel| sel == &SelectorKind::Multiple)
|
||||
.map(|x| x.to_vec())
|
||||
.collect();
|
||||
let sel2_split: Vec<Vec<SelectorKind>> = other
|
||||
.0
|
||||
.split(|sel| sel == &SelectorKind::Multiple)
|
||||
.map(|x| x.to_vec())
|
||||
.collect();
|
||||
for (idx, sel1) in sel1_split.iter().enumerate() {
|
||||
for (idx2, sel2) in sel2_split.iter().enumerate() {
|
||||
rules.extend(sel1.iter().cloned());
|
||||
rules.push(SelectorKind::Whitespace);
|
||||
rules.extend(sel2.iter().cloned());
|
||||
if !(idx + 1 == sel1_split.len() && idx2 + 1 == sel2_split.len()) {
|
||||
rules.push(SelectorKind::Multiple);
|
||||
}
|
||||
}
|
||||
Selector::None => other,
|
||||
_ => Selector::Descendant(Box::new(self), Box::new(other)),
|
||||
}
|
||||
Selector(rules)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user