diff --git a/src/atrule/mod.rs b/src/atrule/mod.rs index 0ab2c53..a8e9841 100644 --- a/src/atrule/mod.rs +++ b/src/atrule/mod.rs @@ -9,9 +9,15 @@ use crate::function::Function; use crate::mixin::Mixin; use crate::selector::Selector; use crate::units::Unit; -use crate::utils::{devour_whitespace, devour_whitespace_or_comment, parse_interpolation}; +use crate::utils::{devour_whitespace, devour_whitespace_or_comment}; use crate::value::{Number, Value}; -use crate::{eat_expr, Expr, RuleSet, Stmt, Token, TokenKind}; +use crate::{Stmt, Token, TokenKind}; + +use parse::eat_stmts; +use unknown::UnknownAtRule; + +mod parse; +mod unknown; #[derive(Debug, Clone)] pub(crate) enum AtRule { @@ -26,14 +32,6 @@ pub(crate) enum AtRule { For(Vec), } -#[derive(Debug, Clone)] -pub(crate) struct UnknownAtRule { - pub name: String, - pub super_selector: Selector, - pub params: String, - pub body: Vec, -} - impl AtRule { pub fn from_tokens>( rule: &AtRuleKind, @@ -182,7 +180,7 @@ impl AtRule { if from < to { for i in from..(to + through) { scope.insert_var(&var, Value::Dimension(Number::from(i), Unit::None))?; - stmts.extend(eat_unknown_atrule_body( + stmts.extend(eat_stmts( &mut body.clone().into_iter().peekable(), &scope, super_selector, @@ -191,7 +189,7 @@ impl AtRule { } else if from > to { for i in ((to - through)..(from + 1)).skip(1).rev() { scope.insert_var(&var, Value::Dimension(Number::from(i), Unit::None))?; - stmts.extend(eat_unknown_atrule_body( + stmts.extend(eat_stmts( &mut body.clone().into_iter().peekable(), &scope, super_selector, @@ -202,95 +200,17 @@ impl AtRule { } AtRuleKind::While => todo!("@while not yet implemented"), AtRuleKind::Keyframes => todo!("@keyframes not yet implemented"), - AtRuleKind::Unknown(name) => { - let mut params = String::new(); - while let Some(tok) = toks.next() { - match tok.kind { - TokenKind::Symbol(Symbol::OpenCurlyBrace) => break, - TokenKind::Interpolation => { - params.push_str( - &parse_interpolation(toks, scope)? - .into_iter() - .map(|x| x.kind.to_string()) - .collect::(), - ); - continue; - } - TokenKind::Variable(..) => params.push('$'), - TokenKind::Whitespace(..) => { - devour_whitespace(toks); - params.push(' '); - continue; - } - _ => {} - } - params.push_str(&tok.kind.to_string()); - } - - let raw_body = eat_unknown_atrule_body(toks, scope, super_selector)?; - let mut body = Vec::with_capacity(raw_body.len()); - body.push(Stmt::RuleSet(RuleSet::new())); - let mut rules = Vec::new(); - for stmt in raw_body { - match stmt { - s @ Stmt::Style(..) => rules.push(s), - s => body.push(s), - } - } - - body[0] = Stmt::RuleSet(RuleSet { - selector: super_selector.clone(), - rules, - super_selector: Selector::new(), - }); - - let u = UnknownAtRule { - name: name.clone(), - super_selector: Selector::new(), - params: params.trim().to_owned(), - body, - }; - - AtRule::Unknown(u) - } + AtRuleKind::Unknown(name) => AtRule::Unknown(UnknownAtRule::from_tokens( + toks, + name, + scope, + super_selector, + )?), _ => todo!("encountered unimplemented at rule"), }) } } -fn eat_unknown_atrule_body>( - toks: &mut Peekable, - scope: &Scope, - super_selector: &Selector, -) -> SassResult> { - let mut stmts = Vec::new(); - let mut scope = scope.clone(); - while let Some(expr) = eat_expr(toks, &scope, super_selector)? { - match expr { - Expr::AtRule(a) => stmts.push(Stmt::AtRule(a)), - Expr::Style(s) => stmts.push(Stmt::Style(s)), - Expr::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)), - Expr::Include(s) => stmts.extend(s), - Expr::MixinDecl(..) | Expr::FunctionDecl(..) | Expr::Debug(..) | Expr::Warn(..) => { - todo!() - } - Expr::Selector(selector) => { - let rules = eat_unknown_atrule_body(toks, &scope, &super_selector.zip(&selector))?; - stmts.push(Stmt::RuleSet(RuleSet { - super_selector: super_selector.clone(), - selector, - rules, - })); - } - Expr::VariableDecl(name, val) => { - scope.insert_var(&name, *val)?; - } - Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)), - } - } - Ok(stmts) -} - #[derive(Clone, Debug, Eq, PartialEq)] pub enum AtRuleKind { // SASS specific @rules diff --git a/src/atrule/parse.rs b/src/atrule/parse.rs new file mode 100644 index 0000000..3328c5a --- /dev/null +++ b/src/atrule/parse.rs @@ -0,0 +1,39 @@ +use std::iter::Peekable; + +use crate::common::Scope; +use crate::error::SassResult; +use crate::selector::Selector; +use crate::{eat_expr, Expr, RuleSet, Stmt, Token}; + +pub(crate) fn eat_stmts>( + toks: &mut Peekable, + scope: &Scope, + super_selector: &Selector, +) -> SassResult> { + let mut stmts = Vec::new(); + let mut scope = scope.clone(); + while let Some(expr) = eat_expr(toks, &scope, super_selector)? { + match expr { + Expr::AtRule(a) => stmts.push(Stmt::AtRule(a)), + Expr::Style(s) => stmts.push(Stmt::Style(s)), + Expr::Styles(s) => stmts.extend(s.into_iter().map(Box::new).map(Stmt::Style)), + Expr::Include(s) => stmts.extend(s), + Expr::MixinDecl(..) | Expr::FunctionDecl(..) | Expr::Debug(..) | Expr::Warn(..) => { + todo!() + } + Expr::Selector(selector) => { + let rules = eat_stmts(toks, &scope, &super_selector.zip(&selector))?; + stmts.push(Stmt::RuleSet(RuleSet { + super_selector: super_selector.clone(), + selector, + rules, + })); + } + Expr::VariableDecl(name, val) => { + scope.insert_var(&name, *val)?; + } + Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)), + } + } + Ok(stmts) +} diff --git a/src/atrule/unknown.rs b/src/atrule/unknown.rs new file mode 100644 index 0000000..997dcf7 --- /dev/null +++ b/src/atrule/unknown.rs @@ -0,0 +1,73 @@ +use std::iter::Peekable; + +use super::parse::eat_stmts; +use crate::common::{Scope, Symbol}; +use crate::error::SassResult; +use crate::selector::Selector; +use crate::utils::{devour_whitespace, parse_interpolation}; +use crate::{RuleSet, Stmt, Token, TokenKind}; + +#[derive(Debug, Clone)] +pub(crate) struct UnknownAtRule { + pub name: String, + pub super_selector: Selector, + pub params: String, + pub body: Vec, +} + +impl UnknownAtRule { + pub fn from_tokens>( + toks: &mut Peekable, + name: &str, + scope: &Scope, + super_selector: &Selector, + ) -> SassResult { + let mut params = String::new(); + while let Some(tok) = toks.next() { + match tok.kind { + TokenKind::Symbol(Symbol::OpenCurlyBrace) => break, + TokenKind::Interpolation => { + params.push_str( + &parse_interpolation(toks, scope)? + .into_iter() + .map(|x| x.kind.to_string()) + .collect::(), + ); + continue; + } + TokenKind::Variable(..) => params.push('$'), + TokenKind::Whitespace(..) => { + devour_whitespace(toks); + params.push(' '); + continue; + } + _ => {} + } + params.push_str(&tok.kind.to_string()); + } + + let raw_body = eat_stmts(toks, scope, super_selector)?; + let mut body = Vec::with_capacity(raw_body.len()); + body.push(Stmt::RuleSet(RuleSet::new())); + let mut rules = Vec::new(); + for stmt in raw_body { + match stmt { + s @ Stmt::Style(..) => rules.push(s), + s => body.push(s), + } + } + + body[0] = Stmt::RuleSet(RuleSet { + selector: super_selector.clone(), + rules, + super_selector: Selector::new(), + }); + + Ok(UnknownAtRule { + name: name.to_owned(), + super_selector: Selector::new(), + params: params.trim().to_owned(), + body, + }) + } +} diff --git a/src/value/ops.rs b/src/value/ops.rs index b0600a6..c4c0b24 100644 --- a/src/value/ops.rs +++ b/src/value/ops.rs @@ -82,7 +82,9 @@ impl Add for Value { }; Value::Ident(format!("{}{}", s1, c), quotes) } - Self::BinaryOp(..) | Self::Paren(..) => return Self::Ident(s1, quotes1) + other.eval()?, + Self::BinaryOp(..) | Self::Paren(..) => { + return Self::Ident(s1, quotes1) + other.eval()? + } Self::List(..) => todo!(), }, _ => todo!(),