Refactor style parsing
This commit is contained in:
parent
3b3de2d83b
commit
ea03f71928
165
src/lib.rs
165
src/lib.rs
@ -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();
|
||||||
|
178
src/style.rs
178
src/style.rs
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user