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();
devour_whitespace(&mut self.lexer);
return Ok(Expr::Selector(Selector::from_tokens(
values.iter().peekable(),
&mut values.iter().peekable(),
super_selector,
scope,
)));
@ -745,7 +745,11 @@ mod test_css {
two_rulesets,
"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!(
removes_empty_outer_styles,

View File

@ -1,5 +1,5 @@
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 std::fmt::{self, Display};
use std::iter::Peekable;
@ -168,176 +168,114 @@ mod test_selector_display {
}
struct SelectorParser<'a> {
tokens: Peekable<Iter<'a, Token>>,
super_selector: &'a Selector,
scope: &'a Scope,
}
/// 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
}
selectors: Vec<SelectorKind>,
}
impl<'a> SelectorParser<'a> {
const fn new(
tokens: Peekable<Iter<'a, Token>>,
super_selector: &'a Selector,
scope: &'a Scope,
) -> SelectorParser<'a> {
const fn new(super_selector: &'a Selector, scope: &'a Scope) -> SelectorParser<'a> {
SelectorParser {
tokens,
super_selector,
scope,
selectors: Vec::new(),
}
}
fn all_selectors(&mut self) -> Selector {
let mut v = Vec::with_capacity(self.tokens.len());
while let Some(s) = self.consume_selector() {
if let SelectorKind::Several(sels) = s {
v.extend(sels);
} else {
v.push(s);
}
}
while let Some(x) = v.pop() {
fn all_selectors(&mut self, tokens: &'a mut Peekable<Iter<'_, Token>>) -> Selector {
self.tokens_to_selectors(tokens);
// remove trailing whitespace
while let Some(x) = self.selectors.pop() {
if x != SelectorKind::Whitespace {
v.push(x);
self.selectors.push(x);
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 {
kind: TokenKind::Ident(s),
..
}) = self.tokens.next()
}) = tokens.next()
{
if let Some(Token {
kind: TokenKind::Symbol(Symbol::OpenParen),
..
}) = self.tokens.peek()
}) = tokens.peek()
{
self.tokens.next();
tokens.next();
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) {
break;
}
let tok = self.tokens.next().unwrap();
let tok = tokens.next().unwrap();
toks.push(tok.kind.clone());
}
self.tokens.next();
Some(SelectorKind::PseudoParen(s.clone(), toks))
tokens.next();
self.selectors
.push(SelectorKind::PseudoParen(s.clone(), toks))
} else {
Some(SelectorKind::Pseudo(s.clone()))
self.selectors.push(SelectorKind::Pseudo(s.clone()))
}
} else {
todo!("expected ident after `:` in selector")
}
}
fn consume_selector(&mut self) -> Option<SelectorKind> {
if devour_whitespace(&mut self.tokens) {
fn tokens_to_selectors(&mut self, tokens: &'_ mut Peekable<Iter<'_, Token>>) {
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 {
kind: TokenKind::Symbol(Symbol::Comma),
..
}) = self.tokens.peek()
}) = tokens.peek()
{
self.tokens.next();
return Some(SelectorKind::Multiple);
tokens.next();
self.selectors.push(SelectorKind::Multiple);
return;
}
return Some(SelectorKind::Whitespace);
self.selectors.push(SelectorKind::Whitespace);
return;
}
if let Some(Token { kind, .. }) = tokens.next() {
match &kind {
TokenKind::Ident(tok) => self.selectors.push(SelectorKind::Element(tok.clone())),
TokenKind::Symbol(Symbol::Period) => self.selectors.push(SelectorKind::Class),
TokenKind::Symbol(Symbol::Hash) => self.selectors.push(SelectorKind::Id),
TokenKind::Symbol(Symbol::Colon) => self.consume_pseudo_selector(tokens),
TokenKind::Symbol(Symbol::Comma) => self.selectors.push(SelectorKind::Multiple),
TokenKind::Symbol(Symbol::Gt) => self.selectors.push(SelectorKind::ImmediateChild),
TokenKind::Symbol(Symbol::Plus) => self.selectors.push(SelectorKind::Following),
TokenKind::Symbol(Symbol::Tilde) => self.selectors.push(SelectorKind::Preceding),
TokenKind::Symbol(Symbol::Mul) => self.selectors.push(SelectorKind::Universal),
TokenKind::Symbol(Symbol::BitAnd) => self.selectors.push(SelectorKind::Super),
TokenKind::Interpolation => self.tokens_to_selectors(
&mut eat_interpolation(tokens, self.scope).iter().peekable(),
),
TokenKind::Attribute(attr) => {
self.selectors.push(SelectorKind::Attribute(attr.clone()))
}
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) => 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 Selector {
pub fn from_tokens<'a>(
tokens: Peekable<Iter<'a, Token>>,
tokens: &'a mut Peekable<Iter<'a, Token>>,
super_selector: &'a Selector,
scope: &'a Scope,
) -> Selector {
SelectorParser::new(tokens, super_selector, scope).all_selectors()
SelectorParser::new(super_selector, scope).all_selectors(tokens)
}
pub fn zip(self, other: Selector) -> Selector {

View File

@ -1,5 +1,5 @@
use crate::common::{Scope, Symbol};
use crate::utils::deref_variable;
use crate::utils::{deref_variable, eat_interpolation};
use crate::{Token, TokenKind};
use std::fmt::{self, Display};
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 {
let mut property = String::new();
// read property until `:`
@ -74,7 +54,12 @@ impl<'a> StyleParser<'a> {
match kind {
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => continue,
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,
_ => property.push_str(&kind.to_string()),
};
@ -104,7 +89,12 @@ impl<'a> StyleParser<'a> {
}
TokenKind::Interpolation => {
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;
}
_ => {}
@ -120,7 +110,12 @@ impl<'a> StyleParser<'a> {
.collect::<String>(),
),
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()),
}
}

View File

@ -1,7 +1,6 @@
use crate::common::Whitespace;
use crate::common::{Symbol, Whitespace};
use crate::{Scope, Token, TokenKind};
use std::iter::Iterator;
use std::iter::Peekable;
use std::iter::{Iterator, Peekable};
pub trait IsWhitespace {
fn is_whitespace(&self) -> bool;
@ -44,3 +43,21 @@ pub fn deref_variable(name: &str, scope: &Scope) -> Vec<Token> {
}
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
}