From c6fe179eebe56199d1ba7ada19205b06df87d595 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Tue, 14 Jan 2020 17:39:19 -0500 Subject: [PATCH] Refactor and consolidate interpolation parsing --- src/main.rs | 8 ++- src/selector.rs | 172 ++++++++++++++++-------------------------------- src/style.rs | 43 ++++++------ src/utils.rs | 23 ++++++- 4 files changed, 100 insertions(+), 146 deletions(-) diff --git a/src/main.rs b/src/main.rs index 283a3d7..71e341f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, diff --git a/src/selector.rs b/src/selector.rs index 5f68abb..d6d662a 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -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>, 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::>(); //.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>, - ) -> Option { - 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, } impl<'a> SelectorParser<'a> { - const fn new( - tokens: Peekable>, - 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>) -> 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 { + fn consume_pseudo_selector(&mut self, tokens: &'_ mut Peekable>) { 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 { - if devour_whitespace(&mut self.tokens) { + fn tokens_to_selectors(&mut self, tokens: &'_ mut Peekable>) { + while tokens.peek().is_some() { + self.consume_selector(tokens) + } + } + + fn consume_selector(&mut self, tokens: &'_ mut Peekable>) { + 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, .. }) = 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()), + 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())) + } _ => todo!("unimplemented selector"), - }); + }; } - None } } impl Selector { pub fn from_tokens<'a>( - tokens: Peekable>, + tokens: &'a mut Peekable>, 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 { diff --git a/src/style.rs b/src/style.rs index c287afd..932f48c 100644 --- a/src/style.rs +++ b/src/style.rs @@ -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::(), - ), - _ => 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::(), + ), 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::(), + ); break; } _ => {} @@ -120,7 +110,12 @@ impl<'a> StyleParser<'a> { .collect::(), ), 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::(), + ), _ => value.push_str(&tok.kind.to_string()), } } diff --git a/src/utils.rs b/src/utils.rs index fcd132f..729cc95 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -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 { } val } + +pub fn eat_interpolation<'a, I: Iterator>( + tokens: &mut Peekable, + scope: &Scope, +) -> Vec { + 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 +}