Refactor and consolidate interpolation parsing

This commit is contained in:
ConnorSkees 2020-01-14 17:39:19 -05:00
parent cdbd4ff9a6
commit c6fe179eeb
4 changed files with 100 additions and 146 deletions

View File

@ -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,

View File

@ -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 {

View File

@ -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()),
} }
} }

View File

@ -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
}