grass/src/style.rs

222 lines
8.0 KiB
Rust
Raw Normal View History

use peekmore::PeekMoreIterator;
2020-03-01 14:53:52 -05:00
use codemap::{Span, Spanned};
2020-04-12 19:37:12 -04:00
2020-02-16 10:54:25 -05:00
use crate::error::SassResult;
use crate::scope::Scope;
2020-02-01 21:59:23 -05:00
use crate::selector::Selector;
use crate::utils::{devour_whitespace, devour_whitespace_or_comment, eat_ident};
use crate::value::Value;
2020-03-29 13:28:17 -04:00
use crate::{Expr, Token};
/// A style: `color: red`
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) struct Style {
pub property: String,
2020-04-12 19:37:12 -04:00
pub value: Spanned<Value>,
}
impl Style {
2020-02-01 21:59:23 -05:00
pub fn parse_property<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
2020-02-01 21:59:23 -05:00
scope: &Scope,
super_selector: &Selector,
super_property: String,
span_before: Span,
) -> SassResult<String> {
StyleParser::new(scope, super_selector).parse_property(toks, super_property, span_before)
2020-02-01 21:59:23 -05:00
}
2020-04-12 19:37:12 -04:00
pub fn to_string(&self) -> SassResult<String> {
Ok(format!(
"{}: {};",
self.property,
self.value.node.to_css_string(self.value.span)?
))
}
pub(crate) fn eval(self) -> SassResult<Self> {
Ok(Style {
property: self.property,
value: Spanned {
span: self.value.span,
node: self.value.node.eval(self.value.span)?.node,
},
})
}
2020-02-01 21:59:23 -05:00
pub fn parse_value<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
2020-02-01 21:59:23 -05:00
scope: &Scope,
super_selector: &Selector,
span_before: Span,
2020-04-12 19:37:12 -04:00
) -> SassResult<Spanned<Value>> {
StyleParser::new(scope, super_selector).parse_style_value(toks, scope, span_before)
2020-02-01 21:59:23 -05:00
}
pub fn from_tokens<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
2020-02-01 21:59:23 -05:00
scope: &Scope,
super_selector: &Selector,
super_property: String,
2020-02-16 10:54:25 -05:00
) -> SassResult<Expr> {
StyleParser::new(scope, super_selector).eat_style_group(toks, super_property, scope)
}
}
struct StyleParser<'a> {
scope: &'a Scope,
2020-02-01 21:59:23 -05:00
super_selector: &'a Selector,
}
impl<'a> StyleParser<'a> {
2020-02-09 19:07:44 -05:00
const fn new(scope: &'a Scope, super_selector: &'a Selector) -> Self {
2020-02-01 21:59:23 -05:00
StyleParser {
scope,
super_selector,
}
}
pub(crate) fn parse_style_value<I: Iterator<Item = Token>>(
&self,
toks: &mut PeekMoreIterator<I>,
scope: &Scope,
span_before: Span,
2020-04-12 19:37:12 -04:00
) -> SassResult<Spanned<Value>> {
2020-02-01 21:59:23 -05:00
devour_whitespace(toks);
Value::from_tokens(toks, scope, self.super_selector, span_before)
}
2020-02-01 21:59:23 -05:00
pub(crate) fn eat_style_group<I: Iterator<Item = Token>>(
&self,
toks: &mut PeekMoreIterator<I>,
super_property: String,
scope: &Scope,
2020-02-16 10:54:25 -05:00
) -> SassResult<Expr> {
2020-02-01 21:59:23 -05:00
let mut styles = Vec::new();
devour_whitespace(toks);
while let Some(tok) = toks.peek().cloned() {
2020-02-01 21:59:23 -05:00
match tok.kind {
2020-03-29 13:28:17 -04:00
'{' => {
2020-05-24 16:49:49 -04:00
toks.next();
2020-02-01 21:59:23 -05:00
devour_whitespace(toks);
loop {
let property =
2020-05-24 16:49:49 -04:00
self.parse_property(toks, super_property.clone(), tok.pos)?;
2020-02-01 21:59:23 -05:00
if let Some(tok) = toks.peek() {
2020-03-29 13:28:17 -04:00
if tok.kind == '{' {
match self.eat_style_group(toks, property, scope)? {
Expr::Styles(s) => styles.extend(s),
2020-02-22 17:57:13 -05:00
Expr::Style(s) => styles.push(*s),
_ => unreachable!(),
}
devour_whitespace(toks);
if let Some(tok) = toks.peek() {
2020-03-29 13:28:17 -04:00
if tok.kind == '}' {
toks.next();
devour_whitespace(toks);
return Ok(Expr::Styles(styles));
} else {
continue;
2020-02-01 21:59:23 -05:00
}
}
continue;
2020-02-01 21:59:23 -05:00
}
}
2020-05-24 16:49:49 -04:00
let value = self.parse_style_value(toks, scope, tok.pos)?;
match toks.peek() {
Some(Token { kind: '}', .. }) => {
styles.push(Style { property, value });
}
2020-05-24 16:49:49 -04:00
Some(Token { kind: ';', .. }) => {
toks.next();
devour_whitespace(toks);
styles.push(Style { property, value });
}
2020-05-24 16:49:49 -04:00
Some(Token { kind: '{', .. }) => {
styles.push(Style {
property: property.clone(),
value,
});
match self.eat_style_group(toks, property, scope)? {
2020-02-22 17:57:13 -05:00
Expr::Style(s) => styles.push(*s),
Expr::Styles(s) => styles.extend(s),
_ => unreachable!(),
}
}
2020-05-24 16:49:49 -04:00
Some(..) | None => {
devour_whitespace(toks);
styles.push(Style { property, value });
}
}
2020-02-01 21:59:23 -05:00
if let Some(tok) = toks.peek() {
match tok.kind {
2020-03-29 13:28:17 -04:00
'}' => {
2020-02-01 21:59:23 -05:00
toks.next();
devour_whitespace(toks);
return Ok(Expr::Styles(styles));
}
_ => continue,
}
}
}
}
_ => {
let value = self.parse_style_value(toks, scope, tok.pos)?;
2020-04-12 19:37:12 -04:00
let t = toks.peek().ok_or(("expected more input.", value.span))?;
match t.kind {
2020-03-29 13:28:17 -04:00
';' => {
toks.next();
devour_whitespace(toks);
}
2020-03-29 13:28:17 -04:00
'{' => {
let mut v = vec![Style {
property: super_property.clone(),
2020-04-12 19:37:12 -04:00
value,
}];
match self.eat_style_group(toks, super_property, scope)? {
2020-02-22 17:57:13 -05:00
Expr::Style(s) => v.push(*s),
Expr::Styles(s) => v.extend(s),
_ => unreachable!(),
}
return Ok(Expr::Styles(v));
}
_ => {}
}
2020-02-22 17:57:13 -05:00
return Ok(Expr::Style(Box::new(Style {
2020-02-01 21:59:23 -05:00
property: super_property,
2020-04-12 19:37:12 -04:00
value,
2020-02-22 17:57:13 -05:00
})));
2020-02-01 21:59:23 -05:00
}
}
}
Ok(Expr::Styles(styles))
}
pub(crate) fn parse_property<I: Iterator<Item = Token>>(
&self,
toks: &mut PeekMoreIterator<I>,
mut super_property: String,
span_before: Span,
) -> SassResult<String> {
2020-02-01 21:59:23 -05:00
devour_whitespace(toks);
2020-05-17 11:09:21 -04:00
let property = eat_ident(toks, self.scope, self.super_selector, span_before)?;
devour_whitespace_or_comment(toks)?;
2020-05-24 16:49:49 -04:00
if let Some(Token { kind: ':', .. }) = toks.peek() {
2020-03-29 13:28:17 -04:00
toks.next();
devour_whitespace_or_comment(toks)?;
2020-05-17 11:09:21 -04:00
} else {
return Err(("Expected \":\".", property.span).into());
2020-03-29 13:28:17 -04:00
}
2020-02-02 10:27:08 -05:00
if super_property.is_empty() {
Ok(property.node)
2020-02-02 10:27:08 -05:00
} else {
2020-05-17 11:09:21 -04:00
super_property.reserve(1 + property.node.len());
2020-02-01 21:59:23 -05:00
super_property.push('-');
2020-05-17 11:09:21 -04:00
super_property.push_str(&property.node);
Ok(super_property)
2020-02-01 21:59:23 -05:00
}
}
}