Refactor and consolidate interpolation parsing
This commit is contained in:
parent
cdbd4ff9a6
commit
c6fe179eeb
@ -472,7 +472,7 @@ impl<'a> StyleSheetParser<'a> {
|
|||||||
self.lexer.next();
|
self.lexer.next();
|
||||||
devour_whitespace(&mut self.lexer);
|
devour_whitespace(&mut self.lexer);
|
||||||
return Ok(Expr::Selector(Selector::from_tokens(
|
return Ok(Expr::Selector(Selector::from_tokens(
|
||||||
values.iter().peekable(),
|
&mut values.iter().peekable(),
|
||||||
super_selector,
|
super_selector,
|
||||||
scope,
|
scope,
|
||||||
)));
|
)));
|
||||||
@ -745,7 +745,11 @@ mod test_css {
|
|||||||
two_rulesets,
|
two_rulesets,
|
||||||
"a {\n color: red;\n}\nc {\n color: white;\n}\n"
|
"a {\n color: red;\n}\nc {\n color: white;\n}\n"
|
||||||
);
|
);
|
||||||
test!(two_inner_outer_rulesets, "a {\n b {\n color: red;\n}\n c {\n color: white;\n}\n}\na {\n b {\n color: red;\n}\n c {\n color: white;\n}\n}\n", "a b {\n color: red;\n}\na c {\n color: white;\n}\na b {\n color: red;\n}\na c {\n color: white;\n}\n");
|
test!(
|
||||||
|
two_inner_outer_rulesets,
|
||||||
|
"a {\n b {\n color: red;\n}\n c {\n color: white;\n}\n}\na {\n b {\n color: red;\n}\n c {\n color: white;\n}\n}\n",
|
||||||
|
"a b {\n color: red;\n}\na c {\n color: white;\n}\na b {\n color: red;\n}\na c {\n color: white;\n}\n"
|
||||||
|
);
|
||||||
test!(selector_mul, "a, b {\n color: red;\n}\n");
|
test!(selector_mul, "a, b {\n color: red;\n}\n");
|
||||||
test!(
|
test!(
|
||||||
removes_empty_outer_styles,
|
removes_empty_outer_styles,
|
||||||
|
172
src/selector.rs
172
src/selector.rs
@ -1,5 +1,5 @@
|
|||||||
use crate::common::{Scope, Symbol};
|
use crate::common::{Scope, Symbol};
|
||||||
use crate::utils::{deref_variable, devour_whitespace, IsWhitespace};
|
use crate::utils::{devour_whitespace, eat_interpolation, IsWhitespace};
|
||||||
use crate::{Token, TokenKind};
|
use crate::{Token, TokenKind};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
@ -168,176 +168,114 @@ mod test_selector_display {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct SelectorParser<'a> {
|
struct SelectorParser<'a> {
|
||||||
tokens: Peekable<Iter<'a, Token>>,
|
|
||||||
super_selector: &'a Selector,
|
super_selector: &'a Selector,
|
||||||
scope: &'a Scope,
|
scope: &'a Scope,
|
||||||
}
|
selectors: Vec<SelectorKind>,
|
||||||
|
|
||||||
/// Methods to handle dealing with interpolation
|
|
||||||
impl<'a> SelectorParser<'a> {
|
|
||||||
fn consume_interpolation(&mut self) -> SelectorKind {
|
|
||||||
let mut v = Vec::new();
|
|
||||||
let toks = self
|
|
||||||
.tokens
|
|
||||||
.by_ref()
|
|
||||||
.take_while(|x| x.kind != TokenKind::Symbol(Symbol::CloseCurlyBrace))
|
|
||||||
.cloned()
|
|
||||||
.collect::<Vec<Token>>(); //.iter().peekable();
|
|
||||||
let mut toks = toks.iter().peekable();
|
|
||||||
while let Some(Token { kind, .. }) = toks.peek() {
|
|
||||||
if let TokenKind::Variable(ref var) = kind {
|
|
||||||
toks.next();
|
|
||||||
let these_toks = deref_variable(var, self.scope);
|
|
||||||
let mut these_toks = these_toks.iter().peekable();
|
|
||||||
while let Some(s) = self.selector_from_token_stream(&mut these_toks) {
|
|
||||||
v.push(s);
|
|
||||||
}
|
|
||||||
} else if let Some(s) = self.selector_from_token_stream(&mut toks) {
|
|
||||||
v.push(s);
|
|
||||||
} else {
|
|
||||||
return SelectorKind::Several(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SelectorKind::Several(v)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn selector_from_token_stream(
|
|
||||||
&mut self,
|
|
||||||
toks: &mut Peekable<Iter<'_, Token>>,
|
|
||||||
) -> Option<SelectorKind> {
|
|
||||||
if devour_whitespace(toks) {
|
|
||||||
if let Some(&&Token {
|
|
||||||
kind: TokenKind::Symbol(Symbol::Comma),
|
|
||||||
..
|
|
||||||
}) = toks.peek()
|
|
||||||
{
|
|
||||||
toks.next();
|
|
||||||
return Some(SelectorKind::Multiple);
|
|
||||||
}
|
|
||||||
return Some(SelectorKind::Whitespace);
|
|
||||||
}
|
|
||||||
if let Some(Token { kind, .. }) = toks.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) => return self.consume_pseudo_selector(),
|
|
||||||
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::Interpolation => self.consume_interpolation(),
|
|
||||||
TokenKind::Attribute(attr) => SelectorKind::Attribute(attr.clone()),
|
|
||||||
_ => todo!("unimplemented selector"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SelectorParser<'a> {
|
impl<'a> SelectorParser<'a> {
|
||||||
const fn new(
|
const fn new(super_selector: &'a Selector, scope: &'a Scope) -> SelectorParser<'a> {
|
||||||
tokens: Peekable<Iter<'a, Token>>,
|
|
||||||
super_selector: &'a Selector,
|
|
||||||
scope: &'a Scope,
|
|
||||||
) -> SelectorParser<'a> {
|
|
||||||
SelectorParser {
|
SelectorParser {
|
||||||
tokens,
|
|
||||||
super_selector,
|
super_selector,
|
||||||
scope,
|
scope,
|
||||||
|
selectors: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all_selectors(&mut self) -> Selector {
|
fn all_selectors(&mut self, tokens: &'a mut Peekable<Iter<'_, Token>>) -> Selector {
|
||||||
let mut v = Vec::with_capacity(self.tokens.len());
|
self.tokens_to_selectors(tokens);
|
||||||
while let Some(s) = self.consume_selector() {
|
// remove trailing whitespace
|
||||||
if let SelectorKind::Several(sels) = s {
|
while let Some(x) = self.selectors.pop() {
|
||||||
v.extend(sels);
|
|
||||||
} else {
|
|
||||||
v.push(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while let Some(x) = v.pop() {
|
|
||||||
if x != SelectorKind::Whitespace {
|
if x != SelectorKind::Whitespace {
|
||||||
v.push(x);
|
self.selectors.push(x);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Selector(v)
|
Selector(self.selectors.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_pseudo_selector(&mut self) -> Option<SelectorKind> {
|
fn consume_pseudo_selector(&mut self, tokens: &'_ mut Peekable<Iter<'_, Token>>) {
|
||||||
if let Some(Token {
|
if let Some(Token {
|
||||||
kind: TokenKind::Ident(s),
|
kind: TokenKind::Ident(s),
|
||||||
..
|
..
|
||||||
}) = self.tokens.next()
|
}) = tokens.next()
|
||||||
{
|
{
|
||||||
if let Some(Token {
|
if let Some(Token {
|
||||||
kind: TokenKind::Symbol(Symbol::OpenParen),
|
kind: TokenKind::Symbol(Symbol::OpenParen),
|
||||||
..
|
..
|
||||||
}) = self.tokens.peek()
|
}) = tokens.peek()
|
||||||
{
|
{
|
||||||
self.tokens.next();
|
tokens.next();
|
||||||
let mut toks = Vec::new();
|
let mut toks = Vec::new();
|
||||||
while let Some(Token { kind, .. }) = self.tokens.peek() {
|
while let Some(Token { kind, .. }) = tokens.peek() {
|
||||||
if kind == &TokenKind::Symbol(Symbol::CloseParen) {
|
if kind == &TokenKind::Symbol(Symbol::CloseParen) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let tok = self.tokens.next().unwrap();
|
let tok = tokens.next().unwrap();
|
||||||
toks.push(tok.kind.clone());
|
toks.push(tok.kind.clone());
|
||||||
}
|
}
|
||||||
self.tokens.next();
|
tokens.next();
|
||||||
Some(SelectorKind::PseudoParen(s.clone(), toks))
|
self.selectors
|
||||||
|
.push(SelectorKind::PseudoParen(s.clone(), toks))
|
||||||
} else {
|
} else {
|
||||||
Some(SelectorKind::Pseudo(s.clone()))
|
self.selectors.push(SelectorKind::Pseudo(s.clone()))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
todo!("expected ident after `:` in selector")
|
todo!("expected ident after `:` in selector")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_selector(&mut self) -> Option<SelectorKind> {
|
fn tokens_to_selectors(&mut self, tokens: &'_ mut Peekable<Iter<'_, Token>>) {
|
||||||
if devour_whitespace(&mut self.tokens) {
|
while tokens.peek().is_some() {
|
||||||
|
self.consume_selector(tokens)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume_selector(&mut self, tokens: &'_ mut Peekable<Iter<'_, Token>>) {
|
||||||
|
if devour_whitespace(tokens) {
|
||||||
if let Some(&&Token {
|
if let Some(&&Token {
|
||||||
kind: TokenKind::Symbol(Symbol::Comma),
|
kind: TokenKind::Symbol(Symbol::Comma),
|
||||||
..
|
..
|
||||||
}) = self.tokens.peek()
|
}) = tokens.peek()
|
||||||
{
|
{
|
||||||
self.tokens.next();
|
tokens.next();
|
||||||
return Some(SelectorKind::Multiple);
|
self.selectors.push(SelectorKind::Multiple);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return Some(SelectorKind::Whitespace);
|
self.selectors.push(SelectorKind::Whitespace);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if let Some(Token { kind, .. }) = self.tokens.next() {
|
if let Some(Token { kind, .. }) = tokens.next() {
|
||||||
return Some(match &kind {
|
match &kind {
|
||||||
TokenKind::Ident(tok) => SelectorKind::Element(tok.clone()),
|
TokenKind::Ident(tok) => self.selectors.push(SelectorKind::Element(tok.clone())),
|
||||||
TokenKind::Symbol(Symbol::Period) => SelectorKind::Class,
|
TokenKind::Symbol(Symbol::Period) => self.selectors.push(SelectorKind::Class),
|
||||||
TokenKind::Symbol(Symbol::Hash) => SelectorKind::Id,
|
TokenKind::Symbol(Symbol::Hash) => self.selectors.push(SelectorKind::Id),
|
||||||
TokenKind::Symbol(Symbol::Colon) => return self.consume_pseudo_selector(),
|
TokenKind::Symbol(Symbol::Colon) => self.consume_pseudo_selector(tokens),
|
||||||
TokenKind::Symbol(Symbol::Comma) => SelectorKind::Multiple,
|
TokenKind::Symbol(Symbol::Comma) => self.selectors.push(SelectorKind::Multiple),
|
||||||
TokenKind::Symbol(Symbol::Gt) => SelectorKind::ImmediateChild,
|
TokenKind::Symbol(Symbol::Gt) => self.selectors.push(SelectorKind::ImmediateChild),
|
||||||
TokenKind::Symbol(Symbol::Plus) => SelectorKind::Following,
|
TokenKind::Symbol(Symbol::Plus) => self.selectors.push(SelectorKind::Following),
|
||||||
TokenKind::Symbol(Symbol::Tilde) => SelectorKind::Preceding,
|
TokenKind::Symbol(Symbol::Tilde) => self.selectors.push(SelectorKind::Preceding),
|
||||||
TokenKind::Symbol(Symbol::Mul) => SelectorKind::Universal,
|
TokenKind::Symbol(Symbol::Mul) => self.selectors.push(SelectorKind::Universal),
|
||||||
TokenKind::Symbol(Symbol::BitAnd) => SelectorKind::Super,
|
TokenKind::Symbol(Symbol::BitAnd) => self.selectors.push(SelectorKind::Super),
|
||||||
TokenKind::Interpolation => self.consume_interpolation(),
|
TokenKind::Interpolation => self.tokens_to_selectors(
|
||||||
TokenKind::Attribute(attr) => SelectorKind::Attribute(attr.clone()),
|
&mut eat_interpolation(tokens, self.scope).iter().peekable(),
|
||||||
|
),
|
||||||
|
TokenKind::Attribute(attr) => {
|
||||||
|
self.selectors.push(SelectorKind::Attribute(attr.clone()))
|
||||||
|
}
|
||||||
_ => todo!("unimplemented selector"),
|
_ => todo!("unimplemented selector"),
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Selector {
|
impl Selector {
|
||||||
pub fn from_tokens<'a>(
|
pub fn from_tokens<'a>(
|
||||||
tokens: Peekable<Iter<'a, Token>>,
|
tokens: &'a mut Peekable<Iter<'a, Token>>,
|
||||||
super_selector: &'a Selector,
|
super_selector: &'a Selector,
|
||||||
scope: &'a Scope,
|
scope: &'a Scope,
|
||||||
) -> Selector {
|
) -> Selector {
|
||||||
SelectorParser::new(tokens, super_selector, scope).all_selectors()
|
SelectorParser::new(super_selector, scope).all_selectors(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zip(self, other: Selector) -> Selector {
|
pub fn zip(self, other: Selector) -> Selector {
|
||||||
|
43
src/style.rs
43
src/style.rs
@ -1,5 +1,5 @@
|
|||||||
use crate::common::{Scope, Symbol};
|
use crate::common::{Scope, Symbol};
|
||||||
use crate::utils::deref_variable;
|
use crate::utils::{deref_variable, eat_interpolation};
|
||||||
use crate::{Token, TokenKind};
|
use crate::{Token, TokenKind};
|
||||||
use std::fmt::{self, Display};
|
use std::fmt::{self, Display};
|
||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
@ -47,26 +47,6 @@ impl<'a> StyleParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eat_interpolation(&mut self) -> String {
|
|
||||||
let mut val = String::new();
|
|
||||||
while let Some(Token { kind, .. }) = self.tokens.next() {
|
|
||||||
match &kind {
|
|
||||||
TokenKind::Symbol(Symbol::CloseCurlyBrace) => break,
|
|
||||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
|
||||||
todo!("invalid character in interpolation")
|
|
||||||
}
|
|
||||||
TokenKind::Variable(ref v) => val.push_str(
|
|
||||||
&deref_variable(v, self.scope)
|
|
||||||
.iter()
|
|
||||||
.map(|x| x.kind.to_string())
|
|
||||||
.collect::<String>(),
|
|
||||||
),
|
|
||||||
_ => val.push_str(&kind.to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse(&mut self) -> Style {
|
fn parse(&mut self) -> Style {
|
||||||
let mut property = String::new();
|
let mut property = String::new();
|
||||||
// read property until `:`
|
// read property until `:`
|
||||||
@ -74,7 +54,12 @@ impl<'a> StyleParser<'a> {
|
|||||||
match kind {
|
match kind {
|
||||||
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => continue,
|
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => continue,
|
||||||
TokenKind::Ident(ref s) => property.push_str(s),
|
TokenKind::Ident(ref s) => property.push_str(s),
|
||||||
TokenKind::Interpolation => property.push_str(&self.eat_interpolation()),
|
TokenKind::Interpolation => property.push_str(
|
||||||
|
&eat_interpolation(&mut self.tokens, self.scope)
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.kind.to_string())
|
||||||
|
.collect::<String>(),
|
||||||
|
),
|
||||||
TokenKind::Symbol(Symbol::Colon) => break,
|
TokenKind::Symbol(Symbol::Colon) => break,
|
||||||
_ => property.push_str(&kind.to_string()),
|
_ => property.push_str(&kind.to_string()),
|
||||||
};
|
};
|
||||||
@ -104,7 +89,12 @@ impl<'a> StyleParser<'a> {
|
|||||||
}
|
}
|
||||||
TokenKind::Interpolation => {
|
TokenKind::Interpolation => {
|
||||||
self.tokens.next();
|
self.tokens.next();
|
||||||
value.push_str(&self.eat_interpolation());
|
value.push_str(
|
||||||
|
&eat_interpolation(&mut self.tokens, self.scope)
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.kind.to_string())
|
||||||
|
.collect::<String>(),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -120,7 +110,12 @@ impl<'a> StyleParser<'a> {
|
|||||||
.collect::<String>(),
|
.collect::<String>(),
|
||||||
),
|
),
|
||||||
TokenKind::MultilineComment(_) => continue,
|
TokenKind::MultilineComment(_) => continue,
|
||||||
TokenKind::Interpolation => value.push_str(&self.eat_interpolation()),
|
TokenKind::Interpolation => value.push_str(
|
||||||
|
&eat_interpolation(&mut self.tokens, self.scope)
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.kind.to_string())
|
||||||
|
.collect::<String>(),
|
||||||
|
),
|
||||||
_ => value.push_str(&tok.kind.to_string()),
|
_ => value.push_str(&tok.kind.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
src/utils.rs
23
src/utils.rs
@ -1,7 +1,6 @@
|
|||||||
use crate::common::Whitespace;
|
use crate::common::{Symbol, Whitespace};
|
||||||
use crate::{Scope, Token, TokenKind};
|
use crate::{Scope, Token, TokenKind};
|
||||||
use std::iter::Iterator;
|
use std::iter::{Iterator, Peekable};
|
||||||
use std::iter::Peekable;
|
|
||||||
|
|
||||||
pub trait IsWhitespace {
|
pub trait IsWhitespace {
|
||||||
fn is_whitespace(&self) -> bool;
|
fn is_whitespace(&self) -> bool;
|
||||||
@ -44,3 +43,21 @@ pub fn deref_variable(name: &str, scope: &Scope) -> Vec<Token> {
|
|||||||
}
|
}
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn eat_interpolation<'a, I: Iterator<Item = &'a Token>>(
|
||||||
|
tokens: &mut Peekable<I>,
|
||||||
|
scope: &Scope,
|
||||||
|
) -> Vec<Token> {
|
||||||
|
let mut val = Vec::new();
|
||||||
|
for tok in tokens {
|
||||||
|
match &tok.kind {
|
||||||
|
TokenKind::Symbol(Symbol::CloseCurlyBrace) => break,
|
||||||
|
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||||
|
todo!("invalid character in interpolation")
|
||||||
|
}
|
||||||
|
TokenKind::Variable(ref v) => val.extend(deref_variable(v, scope)),
|
||||||
|
_ => val.push(tok.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user