Refactor style parsing

This commit is contained in:
ConnorSkees 2020-02-01 21:59:23 -05:00
parent 3b3de2d83b
commit ea03f71928
3 changed files with 171 additions and 179 deletions

View File

@ -58,10 +58,7 @@ use crate::lexer::Lexer;
use crate::mixin::{eat_include, Mixin}; use crate::mixin::{eat_include, Mixin};
use crate::selector::{Attribute, Selector}; use crate::selector::{Attribute, Selector};
use crate::style::Style; use crate::style::Style;
use crate::utils::{ use crate::utils::{devour_whitespace, eat_variable_value, IsComment, IsWhitespace, VariableDecl};
devour_whitespace, eat_variable_value, parse_interpolation, IsComment, IsWhitespace,
VariableDecl,
};
use crate::value::Value; use crate::value::Value;
mod args; mod args;
@ -493,148 +490,6 @@ impl<'a> StyleSheetParser<'a> {
} }
} }
pub(crate) fn eat_style_group<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
super_selector: &Selector,
super_property: String,
) -> Result<Option<Expr>, (Pos, String)> {
let mut styles = Vec::new();
devour_whitespace(toks);
while let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
toks.next();
devour_whitespace(toks);
loop {
let property =
parse_property(toks, scope, super_selector, super_property.clone());
if let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
if let Some(Expr::Styles(s)) =
eat_style_group(toks, scope, super_selector, property)?
{
styles.extend(s);
}
devour_whitespace(toks);
if let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
toks.next();
devour_whitespace(toks);
return Ok(Some(Expr::Styles(styles)));
}
_ => continue,
}
}
continue;
}
_ => {}
}
}
let value = parse_style_value(toks, scope, super_selector);
styles.push(Style { property, value });
if let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
toks.next();
devour_whitespace(toks);
return Ok(Some(Expr::Styles(styles)));
}
_ => continue,
}
}
}
}
_ => {
let val = parse_style_value(toks, scope, super_selector);
return Ok(Some(Expr::Style(Style {
property: super_property,
value: val,
})));
}
}
}
Ok(Some(Expr::Styles(styles)))
}
pub(crate) fn parse_style_value<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
super_selector: &Selector,
) -> Value {
let mut style = Vec::new();
let mut n = 0;
devour_whitespace(toks);
while let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::MultilineComment(_) => {
toks.next();
continue;
}
TokenKind::Symbol(Symbol::OpenCurlyBrace) | TokenKind::Interpolation => n += 1,
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
if n == 0 {
break;
} else {
// todo: toks.next() and push
n -= 1;
}
}
TokenKind::Symbol(Symbol::SemiColon) => {
toks.next();
break;
}
TokenKind::Symbol(Symbol::BitAnd) => {
style.push(Token {
kind: TokenKind::Ident(super_selector.to_string()),
pos: Pos::new(),
});
toks.next();
continue;
}
_ => {}
};
style.push(toks.next().unwrap());
}
devour_whitespace(toks);
Value::from_tokens(&mut style.into_iter().peekable(), scope).unwrap()
}
pub(crate) fn parse_property<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
super_selector: &Selector,
mut super_property: String,
) -> String {
let mut property = String::new();
while let Some(Token { kind, .. }) = toks.next() {
match kind {
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => continue,
TokenKind::Ident(ref s) => property.push_str(s),
TokenKind::Interpolation => property.push_str(
&parse_interpolation(toks, scope)
.iter()
.map(|x| x.kind.to_string())
.collect::<String>(),
),
TokenKind::Symbol(Symbol::Colon) => break,
TokenKind::Symbol(Symbol::BitAnd) => property.push_str(&super_selector.to_string()),
_ => property.push_str(&kind.to_string()),
};
}
devour_whitespace(toks);
if !super_property.is_empty() {
super_property.reserve(1 + property.len());
super_property.push('-');
super_property.push_str(&property);
super_property
} else {
property
}
}
pub(crate) fn eat_expr<I: Iterator<Item = Token>>( pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>, toks: &mut Peekable<I>,
scope: &Scope, scope: &Scope,
@ -646,13 +501,13 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
TokenKind::Symbol(Symbol::Colon) => { TokenKind::Symbol(Symbol::Colon) => {
let tok = toks.next(); let tok = toks.next();
if devour_whitespace(toks) { if devour_whitespace(toks) {
let prop = parse_property( let prop = Style::parse_property(
&mut values.into_iter().peekable(), &mut values.into_iter().peekable(),
scope, scope,
super_selector, super_selector,
String::new(), String::new(),
); );
return eat_style_group(toks, scope, super_selector, prop); return Ok(Some(Style::from_tokens(toks, scope, super_selector, prop)?));
} else { } else {
values.push(tok.unwrap()); values.push(tok.unwrap());
} }
@ -660,10 +515,12 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
TokenKind::Symbol(Symbol::SemiColon) => { TokenKind::Symbol(Symbol::SemiColon) => {
toks.next(); toks.next();
devour_whitespace(toks); devour_whitespace(toks);
return Ok(Some(Expr::Style(match Style::from_tokens(values, scope) { // special edge case where there was no space between the colon
Ok(x) => x, // in a style `color:red`. todo: refactor
Err(_) => return Ok(None), let mut v = values.clone().into_iter().peekable();
}))); let property = Style::parse_property(&mut v, scope, super_selector, String::new());
let value = Style::parse_value(&mut v, scope, super_selector);
return Ok(Some(Expr::Style(Style { property, value })));
} }
TokenKind::Symbol(Symbol::CloseCurlyBrace) => { TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
if values.is_empty() { if values.is_empty() {
@ -671,10 +528,6 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
devour_whitespace(toks); devour_whitespace(toks);
return Ok(None); return Ok(None);
} }
return Ok(Some(Expr::Style(match Style::from_tokens(values, scope) {
Ok(x) => x,
Err(_) => return Ok(None),
})));
} }
TokenKind::Symbol(Symbol::OpenCurlyBrace) => { TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
toks.next(); toks.next();

View File

@ -1,10 +1,10 @@
use crate::common::{Scope, Symbol}; use crate::common::{Pos, Scope, Symbol};
use crate::utils::{devour_whitespace_or_comment, parse_interpolation}; use crate::selector::Selector;
use crate::utils::{devour_whitespace, parse_interpolation};
use crate::value::Value; use crate::value::Value;
use crate::{Token, TokenKind}; use crate::{Expr, Token, TokenKind};
use std::fmt::{self, Display}; use std::fmt::{self, Display};
use std::iter::Peekable; use std::iter::Peekable;
use std::vec::IntoIter;
/// A style: `color: red` /// A style: `color: red`
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
@ -20,47 +20,181 @@ impl Display for Style {
} }
impl Style { impl Style {
pub fn from_tokens(tokens: Vec<Token>, scope: &Scope) -> Result<Self, ()> { pub fn parse_property<I: Iterator<Item = Token>>(
Ok(StyleParser::new(tokens, scope)?.parse()) toks: &mut Peekable<I>,
scope: &Scope,
super_selector: &Selector,
super_property: String,
) -> String {
StyleParser::new(scope, super_selector).parse_property(toks, super_property)
}
pub fn parse_value<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
super_selector: &Selector,
) -> Value {
StyleParser::new(scope, super_selector).parse_style_value(toks)
}
pub fn from_tokens<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
super_selector: &Selector,
super_property: String,
) -> Result<Expr, (Pos, String)> {
StyleParser::new(scope, super_selector).eat_style_group(toks, super_property)
} }
} }
struct StyleParser<'a> { struct StyleParser<'a> {
tokens: Peekable<IntoIter<Token>>,
scope: &'a Scope, scope: &'a Scope,
super_selector: &'a Selector,
} }
impl<'a> StyleParser<'a> { impl<'a> StyleParser<'a> {
fn new(tokens: Vec<Token>, scope: &'a Scope) -> Result<Self, ()> { fn new(scope: &'a Scope, super_selector: &'a Selector) -> Self {
if tokens.is_empty() { StyleParser {
return Err(()); scope,
super_selector,
} }
let tokens = tokens.into_iter().peekable();
Ok(StyleParser { tokens, scope })
} }
fn parse(&mut self) -> Style { pub(crate) fn parse_style_value<I: Iterator<Item = Token>>(
&self,
toks: &mut Peekable<I>,
) -> Value {
let mut style = Vec::new();
let mut n = 0;
devour_whitespace(toks);
while let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::MultilineComment(_) => {
toks.next();
continue;
}
TokenKind::Symbol(Symbol::OpenCurlyBrace) | TokenKind::Interpolation => n += 1,
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
if n == 0 {
break;
} else {
// todo: toks.next() and push
n -= 1;
}
}
TokenKind::Symbol(Symbol::SemiColon) => {
toks.next();
break;
}
TokenKind::Symbol(Symbol::BitAnd) => {
style.push(Token {
kind: TokenKind::Ident(self.super_selector.to_string()),
pos: Pos::new(),
});
toks.next();
continue;
}
_ => {}
};
style.push(toks.next().unwrap());
}
devour_whitespace(toks);
Value::from_tokens(&mut style.into_iter().peekable(), self.scope).unwrap()
}
pub(crate) fn eat_style_group<I: Iterator<Item = Token>>(
&self,
toks: &mut Peekable<I>,
super_property: String,
) -> Result<Expr, (Pos, String)> {
let mut styles = Vec::new();
devour_whitespace(toks);
while let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
toks.next();
devour_whitespace(toks);
loop {
let property = self.parse_property(toks, super_property.clone());
if let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
if let Expr::Styles(s) = self.eat_style_group(toks, property)? {
styles.extend(s);
}
devour_whitespace(toks);
if let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
toks.next();
devour_whitespace(toks);
return Ok(Expr::Styles(styles));
}
_ => continue,
}
}
continue;
}
_ => {}
}
}
let value = self.parse_style_value(toks);
styles.push(Style { property, value });
if let Some(tok) = toks.peek() {
match tok.kind {
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
toks.next();
devour_whitespace(toks);
return Ok(Expr::Styles(styles));
}
_ => continue,
}
}
}
}
_ => {
let val = self.parse_style_value(toks);
return Ok(Expr::Style(Style {
property: super_property,
value: val,
}));
}
}
}
Ok(Expr::Styles(styles))
}
pub(crate) fn parse_property<I: Iterator<Item = Token>>(
&self,
toks: &mut Peekable<I>,
mut super_property: String,
) -> String {
let mut property = String::new(); let mut property = String::new();
// read property until `:` while let Some(Token { kind, .. }) = toks.next() {
while let Some(Token { kind, .. }) = self.tokens.next() {
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( TokenKind::Interpolation => property.push_str(
&parse_interpolation(&mut self.tokens, self.scope) &parse_interpolation(toks, self.scope)
.iter() .iter()
.map(|x| x.kind.to_string()) .map(|x| x.kind.to_string())
.collect::<String>(), .collect::<String>(),
), ),
TokenKind::Symbol(Symbol::Colon) => break, TokenKind::Symbol(Symbol::Colon) => break,
TokenKind::Symbol(Symbol::BitAnd) => {
property.push_str(&self.super_selector.to_string())
}
_ => property.push_str(&kind.to_string()), _ => property.push_str(&kind.to_string()),
}; };
} }
devour_whitespace(toks);
devour_whitespace_or_comment(&mut self.tokens); if !super_property.is_empty() {
super_property.reserve(1 + property.len());
let value = Value::from_tokens(&mut self.tokens, self.scope).unwrap(); super_property.push('-');
super_property.push_str(&property);
Style { property, value } super_property
} else {
property
}
} }
} }

View File

@ -548,6 +548,11 @@ mod test_styles {
"a {\n webkit: {\n webkit: {\n color: red;\n }\n }\n}\n", "a {\n webkit: {\n webkit: {\n color: red;\n }\n }\n}\n",
"a {\n webkit-webkit-color: red;\n}\n" "a {\n webkit-webkit-color: red;\n}\n"
); );
test!(
no_space_between_colon,
"a {\n color:red;\n}\n",
"a {\n color: red;\n}\n"
);
} }
mod test_misc { mod test_misc {