diff --git a/README.md b/README.md index 54e1d8f..b90f390 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ for this version will be provided when the library becomes more stable. The large features remaining are ``` -builtin functions content-exists, min, max +builtin functions min, max @extend (~600 tests) indented syntax (27 tests) css imports @@ -39,7 +39,6 @@ css imports @forward (~400 tests) @keyframes (~30 tests) @supports (~128 tests) -@each inside @function ``` ## Features @@ -74,6 +73,13 @@ cargo b --release These numbers come from a default run of the sass specification as shown above. +``` +2020-06-16 +PASSING: 2489 +FAILING: 2604 +TOTAL: 5093 +``` + ``` 2020-06-07 PASSING: 2442 diff --git a/src/args.rs b/src/args.rs index 7cf5e38..498f4f7 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,21 +1,13 @@ use std::collections::HashMap; -use std::mem; use codemap::{Span, Spanned}; -use peekmore::PeekMoreIterator; - -use crate::common::Identifier; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::utils::{ - devour_whitespace, devour_whitespace_or_comment, eat_ident, eat_ident_no_interpolation, - read_until_closing_paren, read_until_closing_quote, read_until_closing_square_brace, +use crate::{ + common::Identifier, + error::SassResult, + parse::Parser, + {Cow, Token}, }; -use crate::value::Value; -use crate::Cow; -use crate::Token; #[derive(Debug, Clone, Eq, PartialEq)] pub(crate) struct FuncArgs(pub Vec); @@ -34,10 +26,10 @@ impl FuncArgs { } #[derive(Debug, Clone)] -pub(crate) struct CallArgs(HashMap>, Span); +pub(crate) struct CallArgs(pub HashMap>, pub Span); #[derive(Debug, Clone, Hash, Eq, PartialEq)] -enum CallArg { +pub(crate) enum CallArg { Named(Identifier), Positional(usize), } @@ -63,11 +55,7 @@ impl CallArgs { CallArgs(HashMap::new(), span) } - pub fn to_css_string( - self, - scope: &Scope, - super_selector: &Selector, - ) -> SassResult> { + pub fn to_css_string(self, parser: &mut Parser<'_>) -> SassResult> { let mut string = String::with_capacity(2 + self.len() * 10); string.push('('); let mut span = self.1; @@ -79,7 +67,7 @@ impl CallArgs { }); } - let args = match self.get_variadic(scope, super_selector) { + let args = match parser.variadic_args(self) { Ok(v) => v, Err(..) => { return Err(("Plain CSS functions don't support keyword arguments.", span).into()) @@ -103,73 +91,32 @@ impl CallArgs { /// Get argument by name /// /// Removes the argument - pub fn get_named>( - &mut self, - val: T, - scope: &Scope, - super_selector: &Selector, - ) -> Option>> { - match self.0.remove(&CallArg::Named(val.into())) { - Some(v) => { - let span_before = v[0].pos; - Some(Value::from_vec(v, scope, super_selector, span_before)) - } - None => None, - } + pub fn get_named>(&mut self, val: T) -> Option> { + self.0.remove(&CallArg::Named(val.into())) } /// Get a positional argument by 0-indexed position /// /// Removes the argument - pub fn get_positional( - &mut self, - val: usize, - scope: &Scope, - super_selector: &Selector, - ) -> Option>> { - match self.0.remove(&CallArg::Positional(val)) { - Some(v) => { - let span_before = v[0].pos; - Some(Value::from_vec(v, scope, super_selector, span_before)) - } - None => None, - } + pub fn get_positional(&mut self, val: usize) -> Option> { + self.0.remove(&CallArg::Positional(val)) } - pub fn get>( - &mut self, - position: usize, - name: T, - scope: &Scope, - super_selector: &Selector, - ) -> Option>> { - match self.get_named(name, scope, super_selector) { + pub fn get>(&mut self, position: usize, name: T) -> Option> { + match self.get_named(name) { Some(v) => Some(v), - None => self.get_positional(position, scope, super_selector), + None => self.get_positional(position), } } - pub fn get_variadic( - self, - scope: &Scope, - super_selector: &Selector, - ) -> SassResult>> { - let mut vals = Vec::new(); - let mut args = match self - .0 - .into_iter() - .map(|(a, v)| Ok((a.position()?, v))) - .collect::)>, String>>() - { - Ok(v) => v, - Err(e) => return Err((format!("No argument named ${}.", e), self.1).into()), - }; - args.sort_by(|(a1, _), (a2, _)| a1.cmp(a2)); - for arg in args { - let span_before = arg.1[0].pos; - vals.push(Value::from_vec(arg.1, scope, super_selector, span_before)?); + pub fn get_err(&mut self, position: usize, name: &'static str) -> SassResult> { + match self.get_named(name) { + Some(v) => Ok(v), + None => match self.get_positional(position) { + Some(v) => Ok(v), + None => Err((format!("Missing argument ${}.", name), self.span()).into()), + }, } - Ok(vals) } /// Decrement all positional arguments by 1 @@ -219,201 +166,3 @@ impl CallArgs { Ok(()) } } - -pub(crate) fn eat_func_args>( - toks: &mut PeekMoreIterator, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { - let mut args: Vec = Vec::new(); - let mut close_paren_span: Span = toks.peek().unwrap().pos(); - - devour_whitespace(toks); - while let Some(Token { kind, pos }) = toks.next() { - let name = match kind { - '$' => eat_ident(toks, scope, super_selector, pos)?, - ')' => { - close_paren_span = pos; - break; - } - _ => return Err(("expected \")\".", pos).into()), - }; - let mut default: Vec = Vec::new(); - let mut is_variadic = false; - devour_whitespace(toks); - let (kind, span) = match toks.next() { - Some(Token { kind, pos }) => (kind, pos), - _ => todo!("unexpected eof"), - }; - match kind { - ':' => { - devour_whitespace(toks); - while let Some(tok) = toks.peek() { - match &tok.kind { - ',' => { - toks.next(); - args.push(FuncArg { - name: name.node.into(), - default: Some(default), - is_variadic, - }); - break; - } - ')' => { - args.push(FuncArg { - name: name.node.into(), - default: Some(default), - is_variadic, - }); - close_paren_span = tok.pos(); - break; - } - '(' => { - default.push(toks.next().unwrap()); - default.extend(read_until_closing_paren(toks)?); - } - _ => default.push(toks.next().unwrap()), - } - } - } - '.' => { - let next = toks.next().ok_or(("expected \".\".", span))?; - if next.kind != '.' { - return Err(("expected \".\".", next.pos()).into()); - } - let next = toks.next().ok_or(("expected \".\".", next.pos()))?; - if next.kind != '.' { - return Err(("expected \".\".", next.pos()).into()); - } - devour_whitespace(toks); - let next = toks.next().ok_or(("expected \")\".", next.pos()))?; - if next.kind != ')' { - return Err(("expected \")\".", next.pos()).into()); - } - - is_variadic = true; - - args.push(FuncArg { - name: name.node.into(), - default: Some(default), - is_variadic, - }); - break; - } - ')' => { - close_paren_span = span; - args.push(FuncArg { - name: name.node.into(), - default: if default.is_empty() { - None - } else { - Some(default) - }, - is_variadic, - }); - break; - } - ',' => args.push(FuncArg { - name: name.node.into(), - default: None, - is_variadic, - }), - _ => {} - } - devour_whitespace(toks); - } - devour_whitespace(toks); - match toks.next() { - Some(v) if v.kind == '{' => {} - Some(..) | None => return Err(("expected \"{\".", close_paren_span).into()), - }; - Ok(FuncArgs(args)) -} - -pub(crate) fn eat_call_args>( - toks: &mut PeekMoreIterator, - span_before: Span, -) -> SassResult { - let mut args: HashMap> = HashMap::new(); - devour_whitespace_or_comment(toks)?; - let mut name = String::new(); - let mut val: Vec = Vec::new(); - let mut span = toks.peek().ok_or(("expected \")\".", span_before))?.pos(); - loop { - match toks.peek() { - Some(Token { kind: '$', .. }) => { - let Token { pos, .. } = toks.next().unwrap(); - let v = eat_ident_no_interpolation(toks, false, pos)?; - let whitespace = devour_whitespace_or_comment(toks)?; - if let Some(Token { kind: ':', .. }) = toks.peek() { - toks.next(); - name = v.node; - } else { - val.push(Token::new(pos, '$')); - let mut current_pos = 0; - val.extend(v.chars().map(|x| { - let len = x.len_utf8() as u64; - let tok = Token::new(v.span.subspan(current_pos, current_pos + len), x); - current_pos += len; - tok - })); - if whitespace { - val.push(Token::new(pos, ' ')); - } - name.clear(); - } - } - Some(Token { kind: ')', .. }) => { - toks.next(); - return Ok(CallArgs(args, span)); - } - Some(..) | None => name.clear(), - } - devour_whitespace_or_comment(toks)?; - - while let Some(tok) = toks.next() { - match tok.kind { - ')' => { - args.insert( - if name.is_empty() { - CallArg::Positional(args.len()) - } else { - CallArg::Named(name.into()) - }, - val, - ); - span = span.merge(tok.pos()); - return Ok(CallArgs(args, span)); - } - ',' => break, - '[' => { - val.push(tok); - val.extend(read_until_closing_square_brace(toks)?); - } - '(' => { - val.push(tok); - val.extend(read_until_closing_paren(toks)?); - } - '"' | '\'' => { - val.push(tok); - val.extend(read_until_closing_quote(toks, tok.kind)?); - } - _ => val.push(tok), - } - } - - args.insert( - if name.is_empty() { - CallArg::Positional(args.len()) - } else { - CallArg::Named(name.as_str().into()) - }, - mem::take(&mut val), - ); - devour_whitespace(toks); - - if toks.peek().is_none() { - return Ok(CallArgs(args, span)); - } - } -} diff --git a/src/atrule/each_rule.rs b/src/atrule/each_rule.rs deleted file mode 100644 index 15ed6a4..0000000 --- a/src/atrule/each_rule.rs +++ /dev/null @@ -1,143 +0,0 @@ -use codemap::{Span, Spanned}; - -use peekmore::{PeekMore, PeekMoreIterator}; - -use crate::common::{Brackets, ListSeparator}; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::utils::{ - devour_whitespace, eat_ident, read_until_closing_curly_brace, read_until_open_curly_brace, -}; -use crate::value::Value; -use crate::{Stmt, Token}; - -use super::{ruleset_eval, AtRule}; - -#[derive(Debug, Clone)] -pub(crate) struct Each { - vars: Vec>, - iter: Vec, - body: Vec, -} - -impl Each { - pub fn ruleset_eval( - self, - scope: &mut Scope, - super_selector: &Selector, - content: Option<&[Spanned]>, - ) -> SassResult>> { - let mut stmts = Vec::new(); - for row in self.iter { - let this_iterator = match row { - Value::List(v, ..) => v, - Value::Map(m) => m - .into_iter() - .map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None)) - .collect(), - v => vec![v], - }; - - if self.vars.len() == 1 { - if this_iterator.len() == 1 { - scope.insert_var( - &self.vars[0].node, - Spanned { - node: this_iterator[0].clone(), - span: self.vars[0].span, - }, - )?; - } else { - scope.insert_var( - &self.vars[0].node, - Spanned { - node: Value::List(this_iterator, ListSeparator::Space, Brackets::None), - span: self.vars[0].span, - }, - )?; - } - } else { - for (var, val) in self.vars.clone().into_iter().zip( - this_iterator - .into_iter() - .chain(std::iter::once(Value::Null).cycle()), - ) { - scope.insert_var( - &var.node, - Spanned { - node: val, - span: var.span, - }, - )?; - } - } - - ruleset_eval( - &mut self.body.clone().into_iter().peekmore(), - scope, - super_selector, - false, - content, - &mut stmts, - )?; - } - Ok(stmts) - } -} - -pub(crate) fn parse_each>( - toks: &mut PeekMoreIterator, - scope: &mut Scope, - super_selector: &Selector, - mut span: Span, -) -> SassResult { - devour_whitespace(toks); - let mut vars = Vec::new(); - loop { - let next = toks.next().ok_or(("expected \"$\".", span))?; - span = next.pos(); - match next.kind { - '$' => vars.push(eat_ident(toks, scope, super_selector, next.pos)?), - _ => return Err(("expected \"$\".", next.pos()).into()), - } - devour_whitespace(toks); - if toks - .peek() - .ok_or(("expected \"$\".", vars[vars.len() - 1].span))? - .kind - == ',' - { - toks.next(); - devour_whitespace(toks); - } else { - break; - } - } - let i = eat_ident(toks, scope, super_selector, span)?; - if i.node.to_ascii_lowercase() != "in" { - return Err(("Expected \"in\".", i.span).into()); - } - devour_whitespace(toks); - let iter_val = Value::from_vec( - read_until_open_curly_brace(toks)?, - scope, - super_selector, - i.span, - )?; - let iter = match iter_val.node.eval(iter_val.span)?.node { - Value::List(v, ..) => v, - Value::Map(m) => m - .into_iter() - .map(|(k, v)| Value::List(vec![k, v], ListSeparator::Space, Brackets::None)) - .collect(), - v => vec![v], - }; - toks.next(); - devour_whitespace(toks); - let mut body = read_until_closing_curly_brace(toks)?; - body.push(toks.next().unwrap()); - devour_whitespace(toks); - - Ok(AtRule::Each(Each { vars, iter, body })) -} diff --git a/src/atrule/for_rule.rs b/src/atrule/for_rule.rs deleted file mode 100644 index 2d024b3..0000000 --- a/src/atrule/for_rule.rs +++ /dev/null @@ -1,180 +0,0 @@ -use std::iter::{Iterator, Rev, Skip}; -use std::ops::Range; - -use codemap::{Span, Spanned}; - -use peekmore::{PeekMore, PeekMoreIterator}; - -use num_traits::cast::ToPrimitive; - -use super::parse::ruleset_eval; -use super::AtRule; - -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::utils::{ - devour_whitespace, eat_ident, peek_ident_no_interpolation, read_until_closing_curly_brace, - read_until_open_curly_brace, -}; -use crate::value::{Number, Value}; -use crate::{Stmt, Token}; - -pub(crate) enum ForIterator { - Forward(Range), - Backward(Rev>>), -} - -impl Iterator for ForIterator { - type Item = isize; - fn next(&mut self) -> Option { - match self { - Self::Forward(i) => i.next(), - Self::Backward(i) => i.next(), - } - } -} - -#[derive(Debug, Clone)] -pub(crate) struct For { - pub var: Spanned, - from: isize, - to: isize, - through: isize, - pub body: Vec, -} - -impl For { - pub fn ruleset_eval( - self, - scope: &mut Scope, - super_selector: &Selector, - content: Option<&[Spanned]>, - ) -> SassResult>> { - let mut stmts = Vec::new(); - for i in self.iter() { - scope.insert_var( - &self.var.node, - Spanned { - node: Value::Dimension(Number::from(i), Unit::None), - span: self.var.span, - }, - )?; - ruleset_eval( - &mut self.body.iter().cloned().peekmore(), - scope, - super_selector, - false, - content, - &mut stmts, - )?; - } - Ok(stmts) - } - - #[allow(clippy::range_plus_one)] - pub fn iter(&self) -> ForIterator { - if self.from < self.to { - ForIterator::Forward(self.from..(self.to + self.through)) - } else { - ForIterator::Backward(((self.to - self.through)..(self.from + 1)).skip(1).rev()) - } - } -} - -pub(crate) fn parse_for>( - toks: &mut PeekMoreIterator, - scope: &mut Scope, - super_selector: &Selector, - span: Span, -) -> SassResult { - devour_whitespace(toks); - let next = toks.next().ok_or(("expected \"$\".", span))?; - let var = match next.kind { - '$' => eat_ident(toks, scope, super_selector, next.pos)?, - _ => return Err(("expected \"$\".", span).into()), - }; - devour_whitespace(toks); - if toks.peek().is_none() { - return Err(("Expected \"from\".", var.span).into()); - } - let span_before = toks.peek().unwrap().pos; - if eat_ident(toks, scope, super_selector, span_before)?.to_ascii_lowercase() != "from" { - return Err(("Expected \"from\".", var.span).into()); - } - devour_whitespace(toks); - let mut from_toks = Vec::new(); - let mut through = 0; - while let Some(tok) = toks.peek().cloned() { - match tok.kind { - 't' | 'T' | '\\' => { - let ident = peek_ident_no_interpolation(toks, false, tok.pos)?; - match ident.node.to_ascii_lowercase().as_str() { - "through" => { - through = 1; - // todo: it should take more if there were escapes - toks.take(7).for_each(drop); - break; - } - "to" => { - // todo: it should take more if there were escapes - toks.take(2).for_each(drop); - break; - } - _ => { - return Err(("Invalid flag name.", ident.span).into()); - } - } - } - '{' => { - return Err(("Expected \"to\" or \"through\".", tok.pos()).into()); - } - _ => from_toks.push(toks.next().unwrap()), - } - } - devour_whitespace(toks); - let from_val = Value::from_vec(from_toks, scope, super_selector, span_before)?; - let from = match from_val.node.eval(from_val.span)?.node { - Value::Dimension(n, _) => match n.to_integer().to_isize() { - Some(v) => v, - None => return Err((format!("{} is not a int.", n), from_val.span).into()), - }, - v => { - return Err(( - format!("{} is not an integer.", v.inspect(from_val.span)?), - from_val.span, - ) - .into()) - } - }; - - let to_toks = read_until_open_curly_brace(toks)?; - toks.next(); - let to_val = Value::from_vec(to_toks, scope, super_selector, from_val.span)?; - let to = match to_val.node.eval(to_val.span)?.node { - Value::Dimension(n, _) => match n.to_integer().to_isize() { - Some(v) => v, - None => return Err((format!("{} is not a int.", n), to_val.span).into()), - }, - v => { - return Err(( - format!("{} is not an integer.", v.to_css_string(to_val.span)?), - to_val.span, - ) - .into()) - } - }; - let body = read_until_closing_curly_brace(toks)?; - toks.next(); - - devour_whitespace(toks); - - Ok(AtRule::For(For { - from, - to, - through, - body, - var, - })) -} diff --git a/src/atrule/function.rs b/src/atrule/function.rs index 0271674..5a451e2 100644 --- a/src/atrule/function.rs +++ b/src/atrule/function.rs @@ -1,26 +1,12 @@ -use std::mem; +use codemap::Span; -use super::eat_stmts; - -use codemap::{Span, Spanned}; - -use peekmore::{PeekMore, PeekMoreIterator}; - -use crate::args::{eat_func_args, CallArgs, FuncArgs}; -use crate::atrule::AtRule; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::utils::{devour_whitespace, eat_ident, read_until_closing_curly_brace}; -use crate::value::{Number, Value}; -use crate::{Stmt, Token}; +use crate::{args::FuncArgs, scope::Scope, Token}; #[derive(Debug, Clone)] pub(crate) struct Function { - scope: Scope, - args: FuncArgs, - body: Vec, + pub scope: Scope, + pub args: FuncArgs, + pub body: Vec, pos: Span, } @@ -41,151 +27,4 @@ impl Function { pos, } } - - pub fn decl_from_tokens>( - toks: &mut PeekMoreIterator, - scope: Scope, - super_selector: &Selector, - span_before: Span, - ) -> SassResult<(String, Function)> { - let Spanned { node: name, span } = eat_ident(toks, &scope, super_selector, span_before)?; - devour_whitespace(toks); - let args = match toks.next() { - Some(Token { kind: '(', .. }) => eat_func_args(toks, &scope, super_selector)?, - Some(Token { pos, .. }) => return Err(("expected \"(\".", pos).into()), - None => return Err(("expected \"(\".", span).into()), - }; - - devour_whitespace(toks); - - let mut body = read_until_closing_curly_brace(toks)?; //eat_stmts(toks, &mut scope.clone(), super_selector)?; - body.push(toks.next().unwrap()); - devour_whitespace(toks); - - Ok((name, Function::new(scope, args, body, span))) - } - - pub fn args( - &mut self, - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, - ) -> SassResult<()> { - let mut scope = scope.clone(); - for (idx, arg) in self.args.0.iter_mut().enumerate() { - if arg.is_variadic { - let span = args.span(); - let arg_list = Value::ArgList(args.get_variadic(&scope, super_selector)?); - self.scope.insert_var( - arg.name.clone(), - Spanned { - node: arg_list, - span, - }, - )?; - break; - } - let val = match args.get(idx, arg.name.clone(), &scope, super_selector) { - Some(v) => v?, - None => match arg.default.as_mut() { - Some(v) => Value::from_vec(mem::take(v), &scope, super_selector, args.span())?, - None => { - return Err( - (format!("Missing argument ${}.", &arg.name), args.span()).into() - ) - } - }, - }; - scope.insert_var(arg.name.clone(), val.clone())?; - self.scope.insert_var(mem::take(&mut arg.name), val)?; - } - Ok(()) - } - - pub fn eval_body(&mut self, super_selector: &Selector) -> SassResult>> { - eat_stmts( - &mut std::mem::take(&mut self.body).into_iter().peekmore(), - &mut self.scope, - super_selector, - false, - None, - ) - } - - pub fn eval( - mut self, - args: CallArgs, - scope: &Scope, - super_selector: &Selector, - ) -> SassResult { - self.args(args, scope, super_selector)?; - let stmts = self.eval_body(super_selector)?; - self.call(super_selector, stmts)? - .ok_or_else(|| ("Function finished without @return.", self.pos).into()) - } - - pub fn call( - &mut self, - super_selector: &Selector, - stmts: Vec>, - ) -> SassResult> { - for stmt in stmts { - match stmt.node { - Stmt::AtRule(AtRule::Return(toks)) => { - return Ok(Some( - Value::from_vec(toks, &self.scope, super_selector, stmt.span)?.node, - )); - } - Stmt::AtRule(AtRule::For(f)) => { - for i in f.iter() { - self.scope.insert_var( - &f.var.node, - Spanned { - node: Value::Dimension(Number::from(i), Unit::None), - span: f.var.span, - }, - )?; - let for_stmts = eat_stmts( - &mut f.body.clone().into_iter().peekmore(), - &mut self.scope, - super_selector, - false, - None, - )?; - if let Some(v) = self.call(super_selector, for_stmts)? { - return Ok(Some(v)); - } - } - } - Stmt::AtRule(AtRule::If(i)) => { - let if_stmts = i.eval(&mut self.scope, super_selector, None)?; - if let Some(v) = self.call(super_selector, if_stmts)? { - return Ok(Some(v)); - } - } - Stmt::AtRule(AtRule::While(w)) => { - let scope = &mut self.scope.clone(); - let mut val = - Value::from_vec(w.cond.clone(), scope, super_selector, stmt.span)?; - while val.node.is_true(val.span)? { - let while_stmts = eat_stmts( - &mut w.body.clone().into_iter().peekmore(), - scope, - super_selector, - false, - None, - )?; - if let Some(v) = self.call(super_selector, while_stmts)? { - return Ok(Some(v)); - } - val = Value::from_vec(w.cond.clone(), scope, super_selector, val.span)?; - } - } - Stmt::AtRule(AtRule::Each(..)) => todo!("@each in @function"), - // todo: multiline comments - _ => return Err(("This at-rule is not allowed here.", stmt.span).into()), - } - } - Ok(None) - } } diff --git a/src/atrule/if_rule.rs b/src/atrule/if_rule.rs deleted file mode 100644 index d0023da..0000000 --- a/src/atrule/if_rule.rs +++ /dev/null @@ -1,137 +0,0 @@ -use codemap::{Span, Spanned}; - -use peekmore::{PeekMore, PeekMoreIterator}; - -use super::ruleset_eval; - -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::utils::{ - devour_whitespace, devour_whitespace_or_comment, peek_ident_no_interpolation, - read_until_closing_curly_brace, read_until_open_curly_brace, -}; -use crate::value::Value; -use crate::{Stmt, Token}; - -#[derive(Debug, Clone)] -pub(crate) struct If { - pub branches: Vec, - pub else_: Vec, -} - -#[derive(Debug, Clone)] -pub(crate) struct Branch { - pub cond: Vec, - pub toks: Vec, -} - -impl Branch { - pub fn new(cond: Vec, toks: Vec) -> Branch { - Branch { cond, toks } - } -} - -impl If { - pub fn from_tokens>( - toks: &mut PeekMoreIterator, - span_before: Span, - ) -> SassResult { - devour_whitespace_or_comment(toks)?; - let mut branches = Vec::new(); - let init_cond_toks = read_until_open_curly_brace(toks)?; - if init_cond_toks.is_empty() { - return Err(("Expected expression.", span_before).into()); - } - let span_before = match toks.next() { - Some(t) => t.pos, - None => return Err(("Expected expression.", span_before).into()), - }; - devour_whitespace_or_comment(toks)?; - let mut init_toks = read_until_closing_curly_brace(toks)?; - if let Some(tok) = toks.next() { - init_toks.push(tok); - } else { - return Err(("expected \"}\".", span_before).into()); - } - devour_whitespace(toks); - - branches.push(Branch::new(init_cond_toks, init_toks)); - - let mut else_ = Vec::new(); - - loop { - if let Some(Token { kind: '@', pos }) = toks.peek().cloned() { - toks.peek_forward(1); - let ident = peek_ident_no_interpolation(toks, false, pos)?; - if ident.as_str() != "else" { - toks.reset_view(); - break; - } - toks.take(4).for_each(drop); - } else { - break; - } - devour_whitespace(toks); - if let Some(tok) = toks.next() { - devour_whitespace(toks); - match tok.kind.to_ascii_lowercase() { - 'i' if toks.next().unwrap().kind.to_ascii_lowercase() == 'f' => { - toks.next(); - let cond = read_until_open_curly_brace(toks)?; - toks.next(); - devour_whitespace(toks); - branches.push(Branch::new(cond, read_until_closing_curly_brace(toks)?)); - toks.next(); - devour_whitespace(toks); - } - '{' => { - else_ = read_until_closing_curly_brace(toks)?; - toks.next(); - break; - } - _ => { - return Err(("expected \"{\".", tok.pos()).into()); - } - } - } else { - break; - } - } - devour_whitespace(toks); - - Ok(If { branches, else_ }) - } - - pub fn eval( - self, - scope: &mut Scope, - super_selector: &Selector, - content: Option<&[Spanned]>, - ) -> SassResult>> { - let mut stmts = Vec::new(); - let mut toks = Vec::new(); - let mut found_true = false; - for branch in self.branches { - let span_before = branch.cond.first().unwrap().pos; - let cond = Value::from_vec(branch.cond, scope, super_selector, span_before)?; - if cond.node.is_true(cond.span)? { - toks = branch.toks; - found_true = true; - break; - } - } - if !found_true { - toks = self.else_; - } - ruleset_eval( - &mut toks.into_iter().peekmore(), - scope, - super_selector, - false, - content, - &mut stmts, - )?; - Ok(stmts) - } -} diff --git a/src/atrule/media.rs b/src/atrule/media.rs deleted file mode 100644 index 99e3f10..0000000 --- a/src/atrule/media.rs +++ /dev/null @@ -1,85 +0,0 @@ -use codemap::{Span, Spanned}; - -use peekmore::PeekMoreIterator; - -use super::parse::ruleset_eval; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::utils::{devour_whitespace, parse_interpolation}; -use crate::{RuleSet, Stmt, Token}; - -#[derive(Debug, Clone)] -pub(crate) struct Media { - pub super_selector: Selector, - pub params: String, - pub body: Vec>, -} - -impl Media { - pub fn from_tokens>( - toks: &mut PeekMoreIterator, - scope: &mut Scope, - super_selector: &Selector, - kind_span: Span, - content: Option<&[Spanned]>, - ) -> SassResult { - let mut params = String::new(); - while let Some(tok) = toks.next() { - match tok.kind { - '{' => break, - '#' => { - if let Some(Token { kind: '{', pos }) = toks.peek().cloned() { - toks.next(); - let interpolation = parse_interpolation(toks, scope, super_selector, pos)?; - params.push_str(&interpolation.node.to_css_string(interpolation.span)?); - continue; - } else { - params.push(tok.kind); - } - } - '\n' | ' ' | '\t' => { - devour_whitespace(toks); - params.push(' '); - continue; - } - _ => {} - } - params.push(tok.kind); - } - - if params.is_empty() { - return Err(("Expected identifier.", kind_span).into()); - } - - let mut raw_body = Vec::new(); - ruleset_eval(toks, scope, super_selector, false, content, &mut raw_body)?; - let mut rules = Vec::with_capacity(raw_body.len()); - let mut body = Vec::new(); - - for stmt in raw_body { - match stmt.node { - Stmt::Style(..) => body.push(stmt), - _ => rules.push(stmt), - } - } - - if !super_selector.is_empty() { - body = vec![Spanned { - node: Stmt::RuleSet(RuleSet { - selector: super_selector.clone(), - rules: body, - super_selector: Selector::new(), - }), - span: kind_span, - }]; - } - body.append(&mut rules); - - Ok(Media { - super_selector: Selector::new(), - params: params.trim().to_owned(), - body, - }) - } -} diff --git a/src/atrule/mixin.rs b/src/atrule/mixin.rs index d8df725..678447d 100644 --- a/src/atrule/mixin.rs +++ b/src/atrule/mixin.rs @@ -1,261 +1,30 @@ -use std::mem; use std::vec::IntoIter; -use codemap::{Span, Spanned}; - use peekmore::{PeekMore, PeekMoreIterator}; -use super::ruleset_eval; - -use crate::args::{eat_call_args, eat_func_args, CallArgs, FuncArgs}; -use crate::atrule::AtRule; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::utils::{ - devour_whitespace, devour_whitespace_or_comment, eat_ident, read_until_closing_curly_brace, -}; -use crate::value::Value; -use crate::{eat_expr, Expr, RuleSet, Stmt, Token}; +use crate::{args::FuncArgs, scope::Scope, Token}; #[derive(Debug, Clone)] pub(crate) struct Mixin { - scope: Scope, - args: FuncArgs, - body: PeekMoreIterator>, + pub scope: Scope, + pub args: FuncArgs, + pub body: PeekMoreIterator>, + pub accepts_content_block: bool, } impl Mixin { - pub fn new(scope: Scope, args: FuncArgs, body: Vec) -> Self { + pub fn new( + scope: Scope, + args: FuncArgs, + body: Vec, + accepts_content_block: bool, + ) -> Self { let body = body.into_iter().peekmore(); - Mixin { scope, args, body } - } - - pub fn decl_from_tokens>( - toks: &mut PeekMoreIterator, - scope: &Scope, - super_selector: &Selector, - span_before: Span, - ) -> SassResult> { - devour_whitespace(toks); - let Spanned { node: name, span } = eat_ident(toks, scope, super_selector, span_before)?; - devour_whitespace(toks); - let args = match toks.next() { - Some(Token { kind: '(', .. }) => eat_func_args(toks, scope, super_selector)?, - Some(Token { kind: '{', .. }) => FuncArgs::new(), - Some(t) => return Err(("expected \"{\".", t.pos()).into()), - None => return Err(("expected \"{\".", span).into()), - }; - - devour_whitespace(toks); - - let mut body = read_until_closing_curly_brace(toks)?; - body.push(toks.next().unwrap()); - - Ok(Spanned { - node: (name, Mixin::new(scope.clone(), args, body)), - span, - }) - } - - pub fn args( - mut self, - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, - ) -> SassResult { - for (idx, arg) in self.args.0.iter_mut().enumerate() { - if arg.is_variadic { - let span = args.span(); - self.scope.insert_var( - mem::take(&mut arg.name), - Spanned { - node: Value::ArgList(args.get_variadic(scope, super_selector)?), - span, - }, - )?; - break; - } - let val = match args.get(idx, arg.name.clone(), scope, super_selector) { - Some(v) => v?, - None => match arg.default.as_mut() { - Some(v) => Value::from_vec(mem::take(v), scope, super_selector, args.span())?, - None => { - return Err( - (format!("Missing argument ${}.", &arg.name), args.span()).into() - ) - } - }, - }; - self.scope.insert_var(mem::take(&mut arg.name), val)?; + Mixin { + scope, + args, + body, + accepts_content_block, } - Ok(self) - } - - pub fn call( - mut self, - super_selector: &Selector, - content: Option<&[Spanned]>, - ) -> SassResult>> { - self.eval(super_selector, content) - } - - fn eval( - &mut self, - super_selector: &Selector, - content: Option<&[Spanned]>, - ) -> SassResult>> { - let mut stmts = Vec::new(); - while let Some(expr) = eat_expr(&mut self.body, &mut self.scope, super_selector, content)? { - let span = expr.span; - match expr.node { - Expr::AtRule(a) => match a { - AtRule::For(f) => { - stmts.extend(f.ruleset_eval(&mut self.scope, super_selector, content)?) - } - AtRule::Each(e) => { - stmts.extend(e.ruleset_eval(&mut self.scope, super_selector, content)?) - } - AtRule::While(w) => stmts.extend(w.ruleset_eval( - &mut self.scope, - super_selector, - false, - content, - )?), - AtRule::Include(s) => stmts.extend(s), - AtRule::If(i) => { - stmts.extend(i.eval(&mut self.scope.clone(), super_selector, content)?) - } - AtRule::Content => { - stmts.extend(content.unwrap_or_default().iter().cloned()); - } - AtRule::Return(..) => { - return Err(("This at-rule is not allowed here.", span).into()) - } - AtRule::Debug(..) | AtRule::Warn(..) => todo!(), - r => stmts.push(Spanned { - node: Stmt::AtRule(r), - span, - }), - }, - Expr::Style(s) => stmts.push(Spanned { - node: Stmt::Style(s), - span, - }), - Expr::Styles(s) => stmts.extend( - s.into_iter() - .map(Box::new) - .map(Stmt::Style) - .map(|style| Spanned { node: style, span }), - ), - Expr::FunctionDecl(..) => { - return Err(("Mixins may not contain function declarations.", span).into()) - } - Expr::MixinDecl(..) => { - return Err(("Mixins may not contain mixin declarations.", span).into()) - } - Expr::Selector(selector) => { - let rules = self.eval( - &selector.resolve_parent_selectors(super_selector, true), - content, - )?; - stmts.push(Spanned { - node: Stmt::RuleSet(RuleSet { - super_selector: super_selector.clone(), - selector, - rules, - }), - span, - }); - } - Expr::VariableDecl(name, val) => { - self.scope.insert_var(&name, *val)?; - } - Expr::MultilineComment(s) => stmts.push(Spanned { - node: Stmt::MultilineComment(s), - span, - }), - } - } - Ok(stmts) } } - -pub(crate) fn eat_include>( - toks: &mut PeekMoreIterator, - scope: &Scope, - super_selector: &Selector, - content: Option<&[Spanned]>, - span_before: Span, -) -> SassResult>> { - devour_whitespace_or_comment(toks)?; - let name = eat_ident(toks, scope, super_selector, span_before)?; - - devour_whitespace_or_comment(toks)?; - - let mut has_content = false; - - let args = if let Some(tok) = toks.next() { - match tok.kind { - ';' => CallArgs::new(name.span), - '(' => { - let tmp = eat_call_args(toks, tok.pos)?; - devour_whitespace_or_comment(toks)?; - if let Some(tok) = toks.peek() { - match tok.kind { - ';' => { - toks.next(); - } - '{' => { - toks.next(); - has_content = true - } - _ => {} - } - } - tmp - } - '{' => { - has_content = true; - CallArgs::new(name.span) - } - _ => return Err(("expected \"{\".", tok.pos()).into()), - } - } else { - return Err(("unexpected EOF", name.span).into()); - }; - - devour_whitespace(toks); - - let mut this_content = Vec::new(); - - if let Some(tok) = toks.peek() { - if tok.kind == '{' { - toks.next(); - ruleset_eval( - toks, - &mut scope.clone(), - super_selector, - false, - content, - &mut this_content, - )?; - } else if has_content { - ruleset_eval( - toks, - &mut scope.clone(), - super_selector, - false, - content, - &mut this_content, - )?; - } - } - - let mixin = scope.get_mixin(name)?; - - let rules = mixin - .args(args, scope, super_selector)? - .call(super_selector, Some(&this_content))?; - Ok(rules) -} diff --git a/src/atrule/mod.rs b/src/atrule/mod.rs index 05c549d..5c42d66 100644 --- a/src/atrule/mod.rs +++ b/src/atrule/mod.rs @@ -1,273 +1,7 @@ -use codemap::{Span, Spanned}; - -use peekmore::{PeekMore, PeekMoreIterator}; - -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::utils::{ - devour_whitespace, read_until_closing_curly_brace, read_until_open_curly_brace, - read_until_semicolon_or_closing_curly_brace, -}; -use crate::value::Value; -use crate::{Cow, RuleSet, Stmt, Token}; - -use each_rule::{parse_each, Each}; -use for_rule::For; pub(crate) use function::Function; -pub(crate) use if_rule::If; pub(crate) use kind::AtRuleKind; -use media::Media; -pub(crate) use mixin::{eat_include, Mixin}; -use parse::{eat_stmts, eat_stmts_at_root, ruleset_eval}; -use unknown::UnknownAtRule; -use while_rule::{parse_while, While}; +pub(crate) use mixin::Mixin; -mod each_rule; -mod for_rule; mod function; -mod if_rule; mod kind; -mod media; mod mixin; -mod parse; -mod unknown; -mod while_rule; - -#[derive(Debug, Clone)] -pub(crate) enum AtRule { - Warn(Spanned>), - Debug(Spanned>), - Mixin(String, Box), - Function(String, Box), - Return(Vec), - Charset, - Content, - Unknown(UnknownAtRule), - For(For), - Each(Each), - While(While), - Include(Vec>), - If(If), - Media(Media), - AtRoot(Vec>), -} - -impl AtRule { - pub fn from_tokens>( - rule: AtRuleKind, - kind_span: Span, - toks: &mut PeekMoreIterator, - scope: &mut Scope, - super_selector: &Selector, - content: Option<&[Spanned]>, - ) -> SassResult> { - devour_whitespace(toks); - Ok(match rule { - AtRuleKind::Error => { - let Spanned { - node: message, - span, - } = Value::from_vec( - read_until_semicolon_or_closing_curly_brace(toks)?, - scope, - super_selector, - kind_span, - )?; - - return Err((message.inspect(span)?.to_string(), span.merge(kind_span)).into()); - } - AtRuleKind::Warn => { - let Spanned { - node: message, - span, - } = Value::from_vec( - read_until_semicolon_or_closing_curly_brace(toks)?, - scope, - super_selector, - kind_span, - )?; - span.merge(kind_span); - if let Some(Token { kind: ';', .. }) = toks.peek() { - kind_span.merge(toks.next().unwrap().pos()); - } - devour_whitespace(toks); - Spanned { - node: AtRule::Warn(Spanned { - node: message.to_css_string(span)?, - span, - }), - span, - } - } - AtRuleKind::Debug => { - let Spanned { - node: message, - span, - } = Value::from_vec( - read_until_semicolon_or_closing_curly_brace(toks)?, - scope, - super_selector, - kind_span, - )?; - span.merge(kind_span); - if let Some(Token { kind: ';', .. }) = toks.peek() { - kind_span.merge(toks.next().unwrap().pos()); - } - devour_whitespace(toks); - Spanned { - node: AtRule::Debug(Spanned { - node: message.inspect(span)?, - span, - }), - span, - } - } - AtRuleKind::Mixin => { - let Spanned { - node: (name, mixin), - span, - } = Mixin::decl_from_tokens(toks, scope, super_selector, kind_span)?; - Spanned { - node: AtRule::Mixin(name, Box::new(mixin)), - span, - } - } - AtRuleKind::Function => { - let (name, func) = - Function::decl_from_tokens(toks, scope.clone(), super_selector, kind_span)?; - Spanned { - node: AtRule::Function(name, Box::new(func)), - span: kind_span, - } - } - AtRuleKind::Return => { - let v = read_until_semicolon_or_closing_curly_brace(toks)?; - if let Some(Token { kind: ';', .. }) = toks.peek() { - toks.next(); - } - devour_whitespace(toks); - Spanned { - node: AtRule::Return(v), - span: kind_span, - } - } - AtRuleKind::AtRoot => { - let mut selector = &Selector::from_tokens( - &mut read_until_open_curly_brace(toks)?.into_iter().peekmore(), - scope, - super_selector, - true, - )? - .resolve_parent_selectors(super_selector, false); - let mut is_some = true; - if selector.is_empty() { - is_some = false; - selector = super_selector; - } - toks.next(); - devour_whitespace(toks); - let mut body = read_until_closing_curly_brace(toks)?; - body.push(toks.next().unwrap()); - devour_whitespace(toks); - let mut styles = Vec::new(); - #[allow(clippy::unnecessary_filter_map)] - let raw_stmts = eat_stmts_at_root( - &mut body.into_iter().peekmore(), - scope, - selector, - 0, - is_some, - content, - )? - .into_iter() - .filter_map(|s| match s.node { - Stmt::Style(..) => { - styles.push(s); - None - } - _ => Some(s), - }) - .collect::>>(); - let mut stmts = vec![Spanned { - node: Stmt::RuleSet(RuleSet { - selector: selector.clone(), - rules: styles, - super_selector: Selector::new(), - }), - span: kind_span, - }]; - stmts.extend(raw_stmts); - Spanned { - node: AtRule::AtRoot(stmts), - span: kind_span, - } - } - AtRuleKind::Charset => { - read_until_semicolon_or_closing_curly_brace(toks)?; - if let Some(Token { kind: ';', .. }) = toks.peek() { - toks.next(); - } - devour_whitespace(toks); - Spanned { - node: AtRule::Charset, - span: kind_span, - } - } - AtRuleKind::Each => Spanned { - node: parse_each(toks, scope, super_selector, kind_span)?, - span: kind_span, - }, - AtRuleKind::If => Spanned { - node: AtRule::If(If::from_tokens(toks, kind_span)?), - span: kind_span, - }, - AtRuleKind::For => Spanned { - node: for_rule::parse_for(toks, scope, super_selector, kind_span)?, - span: kind_span, - }, - AtRuleKind::While => parse_while(toks, kind_span)?, - AtRuleKind::Unknown(name) => Spanned { - node: AtRule::Unknown(UnknownAtRule::from_tokens( - toks, - name, - scope, - super_selector, - kind_span, - content, - )?), - span: kind_span, - }, - AtRuleKind::Content => Spanned { - node: AtRule::Content, - span: kind_span, - }, - AtRuleKind::Include => Spanned { - node: AtRule::Include(eat_include( - toks, - scope, - super_selector, - content, - kind_span, - )?), - span: kind_span, - }, - AtRuleKind::Media => Spanned { - node: AtRule::Media(Media::from_tokens( - toks, - scope, - super_selector, - kind_span, - content, - )?), - span: kind_span, - }, - AtRuleKind::Import => todo!("@import not yet implemented"), - AtRuleKind::Forward => todo!("@forward not yet implemented"), - AtRuleKind::Supports => todo!("@supports not yet implemented"), - AtRuleKind::Keyframes => todo!("@keyframes not yet implemented"), - AtRuleKind::Extend => todo!("@extend not yet implemented"), - AtRuleKind::Use => todo!("@use not yet implemented"), - }) - } -} diff --git a/src/atrule/parse.rs b/src/atrule/parse.rs deleted file mode 100644 index af54c93..0000000 --- a/src/atrule/parse.rs +++ /dev/null @@ -1,148 +0,0 @@ -use codemap::Spanned; - -use peekmore::PeekMoreIterator; - -use super::AtRule; - -use crate::error::SassResult; -use crate::scope::{global_var_exists, insert_global_var, Scope}; -use crate::selector::Selector; -use crate::{eat_expr, Expr, RuleSet, Stmt, Token}; - -pub(crate) fn eat_stmts>( - toks: &mut PeekMoreIterator, - scope: &mut Scope, - super_selector: &Selector, - at_root: bool, - content: Option<&[Spanned]>, -) -> SassResult>> { - let mut stmts = Vec::new(); - while let Some(expr) = eat_expr(toks, scope, super_selector, content)? { - let span = expr.span; - match expr.node { - Expr::AtRule(a) => stmts.push(Stmt::AtRule(a).span(span)), - Expr::Style(s) => stmts.push(Stmt::Style(s).span(span)), - Expr::Styles(s) => stmts.extend( - s.into_iter() - .map(Box::new) - .map(Stmt::Style) - .map(|style| Spanned { node: style, span }), - ), - Expr::MixinDecl(..) | Expr::FunctionDecl(..) => todo!(), - Expr::Selector(selector) => { - let rules = eat_stmts( - toks, - scope, - &selector.resolve_parent_selectors(super_selector, true), - at_root, - content, - )?; - stmts.push( - Stmt::RuleSet(RuleSet { - super_selector: super_selector.clone(), - selector, - rules, - }) - .span(span), - ); - } - //TODO: refactor handling of `Expr::VariableDecl`, as most is already handled in `eat_expr` - Expr::VariableDecl(name, val) => { - if at_root && global_var_exists(&name) { - insert_global_var(&name, *val.clone())?; - } - scope.insert_var(&name, *val)?; - } - Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s).span(span)), - } - } - Ok(stmts) -} - -pub(crate) fn eat_stmts_at_root>( - toks: &mut PeekMoreIterator, - scope: &mut Scope, - super_selector: &Selector, - mut nesting: usize, - is_some: bool, - content: Option<&[Spanned]>, -) -> SassResult>> { - let mut stmts = Vec::new(); - while let Some(expr) = eat_expr(toks, scope, super_selector, content)? { - let span = expr.span; - match expr.node { - Expr::AtRule(a) => stmts.push(Stmt::AtRule(a).span(span)), - Expr::Style(s) => stmts.push(Stmt::Style(s).span(span)), - Expr::Styles(s) => stmts.extend( - s.into_iter() - .map(Box::new) - .map(Stmt::Style) - .map(|style| Spanned { node: style, span }), - ), - Expr::MixinDecl(..) | Expr::FunctionDecl(..) => todo!(), - Expr::Selector(mut selector) => { - selector = - selector.resolve_parent_selectors(super_selector, nesting > 1 || is_some); - nesting += 1; - let rules = eat_stmts_at_root(toks, scope, &selector, nesting, true, content)?; - nesting -= 1; - stmts.push( - Stmt::RuleSet(RuleSet { - super_selector: if nesting > 1 { - super_selector.clone() - } else { - Selector::new() - }, - selector, - rules, - }) - .span(span), - ); - } - Expr::VariableDecl(name, val) => { - scope.insert_var(&name, *val)?; - } - Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s).span(span)), - } - } - Ok(stmts) -} - -pub(crate) fn ruleset_eval>( - toks: &mut PeekMoreIterator, - scope: &mut Scope, - super_selector: &Selector, - at_root: bool, - content: Option<&[Spanned]>, - stmts: &mut Vec>, -) -> SassResult<()> { - for stmt in eat_stmts(toks, scope, super_selector, at_root, content)? { - match stmt.node { - Stmt::AtRule(AtRule::For(f)) => { - stmts.extend(f.ruleset_eval(scope, super_selector, content)?) - } - Stmt::AtRule(AtRule::Each(e)) => { - stmts.extend(e.ruleset_eval(scope, super_selector, content)?) - } - Stmt::AtRule(AtRule::While(w)) => { - // TODO: should at_root be false? scoping - stmts.extend(w.ruleset_eval(scope, super_selector, at_root, content)?) - } - Stmt::AtRule(AtRule::Include(s)) => stmts.extend(s), - Stmt::AtRule(AtRule::If(i)) => stmts.extend(i.eval(scope, super_selector, content)?), - Stmt::AtRule(AtRule::Content) => { - if let Some(c) = content { - stmts.extend(c.iter().cloned()); - } else { - return Err(( - "@content is only allowed within mixin declarations.", - stmt.span, - ) - .into()); - } - } - _ => stmts.push(stmt), - } - } - Ok(()) -} diff --git a/src/atrule/unknown.rs b/src/atrule/unknown.rs deleted file mode 100644 index 0fc4c0e..0000000 --- a/src/atrule/unknown.rs +++ /dev/null @@ -1,96 +0,0 @@ -use codemap::{Span, Spanned}; - -use peekmore::PeekMoreIterator; - -use super::parse::ruleset_eval; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::utils::{devour_whitespace, parse_interpolation}; -use crate::{RuleSet, Stmt, Token}; - -#[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 PeekMoreIterator, - name: String, - scope: &mut Scope, - super_selector: &Selector, - kind_span: Span, - content: Option<&[Spanned]>, - ) -> SassResult { - let mut params = String::new(); - devour_whitespace(toks); - if let Some(Token { kind: ';', .. }) | None = toks.peek() { - toks.next(); - return Ok(UnknownAtRule { - name, - super_selector: Selector::new(), - params: String::new(), - body: Vec::new(), - }); - } - while let Some(tok) = toks.next() { - match tok.kind { - '{' => break, - '#' => { - if let Some(Token { kind: '{', pos }) = toks.peek().cloned() { - toks.next(); - let interpolation = parse_interpolation(toks, scope, super_selector, pos)?; - params.push_str(&interpolation.node.to_css_string(interpolation.span)?); - } else { - params.push(tok.kind); - } - continue; - } - '\n' | ' ' | '\t' => { - devour_whitespace(toks); - params.push(' '); - continue; - } - _ => {} - } - params.push(tok.kind); - } - - let mut raw_body = Vec::new(); - ruleset_eval(toks, scope, super_selector, false, content, &mut raw_body)?; - let mut rules = Vec::with_capacity(raw_body.len()); - let mut body = Vec::new(); - - for stmt in raw_body { - match stmt.node { - Stmt::Style(..) => body.push(stmt), - _ => rules.push(stmt), - } - } - - if super_selector.is_empty() { - body.append(&mut rules); - } else { - body = vec![Spanned { - node: Stmt::RuleSet(RuleSet { - selector: super_selector.clone(), - rules: body, - super_selector: Selector::new(), - }), - span: kind_span, - }]; - body.append(&mut rules); - } - - Ok(UnknownAtRule { - name, - super_selector: Selector::new(), - params: params.trim().to_owned(), - body, - }) - } -} diff --git a/src/atrule/while_rule.rs b/src/atrule/while_rule.rs deleted file mode 100644 index 59e4eb6..0000000 --- a/src/atrule/while_rule.rs +++ /dev/null @@ -1,70 +0,0 @@ -use codemap::{Span, Spanned}; - -use peekmore::{PeekMore, PeekMoreIterator}; - -use super::{ruleset_eval, AtRule}; - -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::utils::{ - devour_whitespace, read_until_closing_curly_brace, read_until_open_curly_brace, -}; -use crate::value::Value; -use crate::{Stmt, Token}; - -#[derive(Debug, Clone)] -pub(crate) struct While { - pub cond: Vec, - pub body: Vec, -} - -impl While { - pub fn ruleset_eval( - self, - scope: &mut Scope, - super_selector: &Selector, - at_root: bool, - content: Option<&[Spanned]>, - ) -> SassResult>> { - let mut stmts = Vec::new(); - let mut val = Value::from_vec(self.cond.clone(), scope, super_selector, self.cond[0].pos)?; - let scope = &mut scope.clone(); - while val.node.is_true(val.span)? { - ruleset_eval( - &mut self.body.clone().into_iter().peekmore(), - scope, - super_selector, - at_root, - content, - &mut stmts, - )?; - val = Value::from_vec(self.cond.clone(), scope, super_selector, self.cond[0].pos)?; - } - Ok(stmts) - } -} - -pub(crate) fn parse_while>( - toks: &mut PeekMoreIterator, - span: Span, -) -> SassResult> { - devour_whitespace(toks); - let cond = read_until_open_curly_brace(toks)?; - - if cond.is_empty() { - return Err(("Expected expression.", span).into()); - } - - toks.next(); - - let mut body = read_until_closing_curly_brace(toks)?; - - body.push(toks.next().unwrap()); - - devour_whitespace(toks); - Ok(Spanned { - node: AtRule::While(While { cond, body }), - span, - }) -} diff --git a/src/builtin/color/hsl.rs b/src/builtin/color/hsl.rs index 550623d..d4ab7ec 100644 --- a/src/builtin/color/hsl.rs +++ b/src/builtin/color/hsl.rs @@ -2,27 +2,23 @@ use super::{Builtin, GlobalFunctionMap}; use num_traits::One; -use crate::args::CallArgs; -use crate::color::Color; -use crate::common::QuoteKind; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::value::{Number, Value}; +use crate::{ + args::CallArgs, + color::Color, + common::QuoteKind, + error::SassResult, + parse::Parser, + unit::Unit, + value::{Number, Value}, +}; -fn inner_hsl( - name: &'static str, - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn inner_hsl(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { if args.is_empty() { return Err(("Missing argument $channels.", args.span()).into()); } if args.len() == 1 { - let mut channels = match arg!(args, scope, super_selector, 0, "channels") { + let mut channels = match parser.arg(&mut args, 0, "channels")? { Value::List(v, ..) => v, _ => return Err(("Missing argument $channels.", args.span()).into()), }; @@ -87,11 +83,11 @@ fn inner_hsl( Number::one(), )))) } else { - let hue = match arg!(args, scope, super_selector, 0, "hue") { + let hue = match parser.arg(&mut args, 0, "hue")? { Value::Dimension(n, _) => n, v if v.is_special_function() => { - let saturation = arg!(args, scope, super_selector, 1, "saturation"); - let lightness = arg!(args, scope, super_selector, 2, "lightness"); + let saturation = parser.arg(&mut args, 1, "saturation")?; + let lightness = parser.arg(&mut args, 2, "lightness")?; let mut string = format!( "{}({}, {}, {}", name, @@ -102,7 +98,8 @@ fn inner_hsl( if !args.is_empty() { string.push_str(", "); string.push_str( - &arg!(args, scope, super_selector, 3, "alpha") + &parser + .arg(&mut args, 3, "alpha")? .to_css_string(args.span())?, ); } @@ -117,10 +114,10 @@ fn inner_hsl( .into()) } }; - let saturation = match arg!(args, scope, super_selector, 1, "saturation") { + let saturation = match parser.arg(&mut args, 1, "saturation")? { Value::Dimension(n, _) => n / Number::from(100), v if v.is_special_function() => { - let lightness = arg!(args, scope, super_selector, 2, "lightness"); + let lightness = parser.arg(&mut args, 2, "lightness")?; let mut string = format!( "{}({}, {}, {}", name, @@ -131,7 +128,8 @@ fn inner_hsl( if !args.is_empty() { string.push_str(", "); string.push_str( - &arg!(args, scope, super_selector, 3, "alpha") + &parser + .arg(&mut args, 3, "alpha")? .to_css_string(args.span())?, ); } @@ -149,7 +147,7 @@ fn inner_hsl( .into()) } }; - let lightness = match arg!(args, scope, super_selector, 2, "lightness") { + let lightness = match parser.arg(&mut args, 2, "lightness")? { Value::Dimension(n, _) => n / Number::from(100), v if v.is_special_function() => { let mut string = format!( @@ -162,7 +160,8 @@ fn inner_hsl( if !args.is_empty() { string.push_str(", "); string.push_str( - &arg!(args, scope, super_selector, 3, "alpha") + &parser + .arg(&mut args, 3, "alpha")? .to_css_string(args.span())?, ); } @@ -180,13 +179,12 @@ fn inner_hsl( .into()) } }; - let alpha = match arg!( - args, - scope, - super_selector, + let alpha = match parser.default_arg( + &mut args, 3, - "alpha" = Value::Dimension(Number::one(), Unit::None) - ) { + "alpha", + Value::Dimension(Number::one(), Unit::None), + )? { Value::Dimension(n, Unit::None) => n, Value::Dimension(n, Unit::Percent) => n / Number::from(100), v @ Value::Dimension(..) => { @@ -226,17 +224,17 @@ fn inner_hsl( } } -fn hsl(args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - inner_hsl("hsl", args, scope, super_selector) +fn hsl(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + inner_hsl("hsl", args, parser) } -fn hsla(args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - inner_hsl("hsla", args, scope, super_selector) +fn hsla(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + inner_hsl("hsla", args, parser) } -fn hue(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Dimension(c.hue(), Unit::Deg)), v => Err(( format!("$color: {} is not a color.", v.to_css_string(args.span())?), @@ -246,9 +244,9 @@ fn hue(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResu } } -fn saturation(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn saturation(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Dimension(c.saturation(), Unit::Percent)), v => Err(( format!("$color: {} is not a color.", v.to_css_string(args.span())?), @@ -258,9 +256,9 @@ fn saturation(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S } } -fn lightness(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn lightness(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Dimension(c.lightness(), Unit::Percent)), v => Err(( format!("$color: {} is not a color.", v.to_css_string(args.span())?), @@ -270,9 +268,9 @@ fn lightness(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa } } -fn adjust_hue(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn adjust_hue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -282,7 +280,7 @@ fn adjust_hue(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S .into()) } }; - let degrees = match arg!(args, scope, super_selector, 1, "degrees") { + let degrees = match parser.arg(&mut args, 1, "degrees")? { Value::Dimension(n, _) => n, v => { return Err(( @@ -298,9 +296,9 @@ fn adjust_hue(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S Ok(Value::Color(Box::new(color.adjust_hue(degrees)))) } -fn lighten(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn lighten(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -310,7 +308,7 @@ fn lighten(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass .into()) } }; - let amount = match arg!(args, scope, super_selector, 1, "amount") { + let amount = match parser.arg(&mut args, 1, "amount")? { Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), v => { return Err(( @@ -326,9 +324,9 @@ fn lighten(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass Ok(Value::Color(Box::new(color.lighten(amount)))) } -fn darken(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn darken(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -338,7 +336,7 @@ fn darken(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassR .into()) } }; - let amount = match arg!(args, scope, super_selector, 1, "amount") { + let amount = match parser.arg(&mut args, 1, "amount")? { Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), v => { return Err(( @@ -354,19 +352,21 @@ fn darken(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassR Ok(Value::Color(Box::new(color.darken(amount)))) } -fn saturate(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn saturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; if args.len() == 1 { return Ok(Value::String( format!( "saturate({})", - arg!(args, scope, super_selector, 0, "amount").to_css_string(args.span())? + parser + .arg(&mut args, 0, "amount")? + .to_css_string(args.span())? ), QuoteKind::None, )); } - let amount = match arg!(args, scope, super_selector, 1, "amount") { + let amount = match parser.arg(&mut args, 1, "amount")? { Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), v => { return Err(( @@ -379,7 +379,7 @@ fn saturate(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sas .into()) } }; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, Value::Dimension(n, u) => { return Ok(Value::String( @@ -398,9 +398,9 @@ fn saturate(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sas Ok(Value::Color(Box::new(color.saturate(amount)))) } -fn desaturate(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn desaturate(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -410,7 +410,7 @@ fn desaturate(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S .into()) } }; - let amount = match arg!(args, scope, super_selector, 1, "amount") { + let amount = match parser.arg(&mut args, 1, "amount")? { Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 100) / Number::from(100), v => { return Err(( @@ -426,9 +426,9 @@ fn desaturate(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S Ok(Value::Color(Box::new(color.desaturate(amount)))) } -fn grayscale(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn grayscale(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, Value::Dimension(n, u) => { return Ok(Value::String( @@ -447,9 +447,9 @@ fn grayscale(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa Ok(Value::Color(Box::new(color.desaturate(Number::one())))) } -fn complement(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn complement(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -462,15 +462,14 @@ fn complement(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S Ok(Value::Color(Box::new(color.complement()))) } -fn invert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn invert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let weight = match arg!( - args, - scope, - super_selector, + let weight = match parser.default_arg( + &mut args, 1, - "weight" = Value::Dimension(Number::from(100), Unit::Percent) - ) { + "weight", + Value::Dimension(Number::from(100), Unit::Percent), + )? { Value::Dimension(n, u) => bound!(args, "weight", n, u, 0, 100) / Number::from(100), v => { return Err(( @@ -483,7 +482,7 @@ fn invert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassR .into()) } }; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Color(Box::new(c.invert(weight)))), Value::Dimension(n, Unit::Percent) => { Ok(Value::String(format!("invert({}%)", n), QuoteKind::None)) diff --git a/src/builtin/color/opacity.rs b/src/builtin/color/opacity.rs index 93c2bd4..45923d7 100644 --- a/src/builtin/color/opacity.rs +++ b/src/builtin/color/opacity.rs @@ -1,17 +1,13 @@ use super::{Builtin, GlobalFunctionMap}; -use crate::args::CallArgs; -use crate::common::QuoteKind; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::value::Number; -use crate::value::Value; +use crate::{ + args::CallArgs, common::QuoteKind, error::SassResult, parse::Parser, unit::Unit, value::Number, + value::Value, +}; -fn alpha(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn alpha(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)), v => Err(( format!("$color: {} is not a color.", v.to_css_string(args.span())?), @@ -21,9 +17,9 @@ fn alpha(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRe } } -fn opacity(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn opacity(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Dimension(c.alpha(), Unit::None)), Value::Dimension(num, unit) => Ok(Value::String( format!("opacity({}{})", num, unit), @@ -37,9 +33,9 @@ fn opacity(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass } } -fn opacify(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn opacify(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -49,7 +45,7 @@ fn opacify(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass .into()) } }; - let amount = match arg!(args, scope, super_selector, 1, "amount") { + let amount = match parser.arg(&mut args, 1, "amount")? { Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 1), v => { return Err(( @@ -65,9 +61,9 @@ fn opacify(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass Ok(Value::Color(Box::new(color.fade_in(amount)))) } -fn fade_in(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn fade_in(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -77,7 +73,7 @@ fn fade_in(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass .into()) } }; - let amount = match arg!(args, scope, super_selector, 1, "amount") { + let amount = match parser.arg(&mut args, 1, "amount")? { Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 1), v => { return Err(( @@ -93,13 +89,9 @@ fn fade_in(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass Ok(Value::Color(Box::new(color.fade_in(amount)))) } -fn transparentize( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn transparentize(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -109,7 +101,7 @@ fn transparentize( .into()) } }; - let amount = match arg!(args, scope, super_selector, 1, "amount") { + let amount = match parser.arg(&mut args, 1, "amount")? { Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 1), v => { return Err(( @@ -125,9 +117,9 @@ fn transparentize( Ok(Value::Color(Box::new(color.fade_out(amount)))) } -fn fade_out(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn fade_out(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -137,7 +129,7 @@ fn fade_out(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sas .into()) } }; - let amount = match arg!(args, scope, super_selector, 1, "amount") { + let amount = match parser.arg(&mut args, 1, "amount")? { Value::Dimension(n, u) => bound!(args, "amount", n, u, 0, 1), v => { return Err(( diff --git a/src/builtin/color/other.rs b/src/builtin/color/other.rs index 0cd4f3c..115c50d 100644 --- a/src/builtin/color/other.rs +++ b/src/builtin/color/other.rs @@ -2,18 +2,19 @@ use super::{Builtin, GlobalFunctionMap}; use num_traits::{One, Signed, Zero}; -use crate::args::CallArgs; -use crate::color::Color; -use crate::common::QuoteKind; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::value::{Number, Value}; +use crate::{ + args::CallArgs, + color::Color, + common::QuoteKind, + error::SassResult, + parse::Parser, + unit::Unit, + value::{Number, Value}, +}; macro_rules! opt_rgba { - ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $scope:ident, $super_selector:ident) => { - let $name = match named_arg!($args, $scope, $super_selector, $arg = Value::Null) { + ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $parser:ident) => { + let $name = match $parser.default_named_arg(&mut $args, $arg, Value::Null)? { Value::Dimension(n, u) => Some(bound!($args, $arg, n, u, $low, $high)), Value::Null => None, v => { @@ -32,8 +33,8 @@ macro_rules! opt_rgba { } macro_rules! opt_hsl { - ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $scope:ident, $super_selector:ident) => { - let $name = match named_arg!($args, $scope, $super_selector, $arg = Value::Null) { + ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $parser:ident) => { + let $name = match $parser.default_named_arg(&mut $args, $arg, Value::Null)? { Value::Dimension(n, u) => { Some(bound!($args, $arg, n, u, $low, $high) / Number::from(100)) } @@ -53,8 +54,8 @@ macro_rules! opt_hsl { }; } -fn change_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - if args.get_positional(1, scope, super_selector).is_some() { +fn change_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + if parser.positional_arg(&mut args, 1).is_some() { return Err(( "Only one positional argument is allowed. All other arguments must be passed by name.", args.span(), @@ -62,7 +63,7 @@ fn change_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> .into()); } - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -73,10 +74,10 @@ fn change_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> } }; - opt_rgba!(args, alpha, "alpha", 0, 1, scope, super_selector); - opt_rgba!(args, red, "red", 0, 255, scope, super_selector); - opt_rgba!(args, green, "green", 0, 255, scope, super_selector); - opt_rgba!(args, blue, "blue", 0, 255, scope, super_selector); + opt_rgba!(args, alpha, "alpha", 0, 1, parser); + opt_rgba!(args, red, "red", 0, 255, parser); + opt_rgba!(args, green, "green", 0, 255, parser); + opt_rgba!(args, blue, "blue", 0, 255, parser); if red.is_some() || green.is_some() || blue.is_some() { return Ok(Value::Color(Box::new(Color::from_rgba( @@ -87,7 +88,7 @@ fn change_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> )))); } - let hue = match named_arg!(args, scope, super_selector, "hue" = Value::Null) { + let hue = match parser.default_named_arg(&mut args, "hue", Value::Null)? { Value::Dimension(n, _) => Some(n), Value::Null => None, v => { @@ -99,16 +100,8 @@ fn change_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> } }; - opt_hsl!( - args, - saturation, - "saturation", - 0, - 100, - scope, - super_selector - ); - opt_hsl!(args, luminance, "lightness", 0, 100, scope, super_selector); + opt_hsl!(args, saturation, "saturation", 0, 100, parser); + opt_hsl!(args, luminance, "lightness", 0, 100, parser); if hue.is_some() || saturation.is_some() || luminance.is_some() { // Color::as_hsla() returns more exact values than Color::hue(), etc. @@ -128,8 +121,8 @@ fn change_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> })) } -fn adjust_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - let color = match arg!(args, scope, super_selector, 0, "color") { +fn adjust_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -140,10 +133,10 @@ fn adjust_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> } }; - opt_rgba!(args, alpha, "alpha", -1, 1, scope, super_selector); - opt_rgba!(args, red, "red", -255, 255, scope, super_selector); - opt_rgba!(args, green, "green", -255, 255, scope, super_selector); - opt_rgba!(args, blue, "blue", -255, 255, scope, super_selector); + opt_rgba!(args, alpha, "alpha", -1, 1, parser); + opt_rgba!(args, red, "red", -255, 255, parser); + opt_rgba!(args, green, "green", -255, 255, parser); + opt_rgba!(args, blue, "blue", -255, 255, parser); if red.is_some() || green.is_some() || blue.is_some() { return Ok(Value::Color(Box::new(Color::from_rgba( @@ -154,7 +147,7 @@ fn adjust_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> )))); } - let hue = match named_arg!(args, scope, super_selector, "hue" = Value::Null) { + let hue = match parser.default_named_arg(&mut args, "hue", Value::Null)? { Value::Dimension(n, _) => Some(n), Value::Null => None, v => { @@ -166,24 +159,8 @@ fn adjust_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> } }; - opt_hsl!( - args, - saturation, - "saturation", - -100, - 100, - scope, - super_selector - ); - opt_hsl!( - args, - luminance, - "lightness", - -100, - 100, - scope, - super_selector - ); + opt_hsl!(args, saturation, "saturation", -100, 100, parser); + opt_hsl!(args, luminance, "lightness", -100, 100, parser); if hue.is_some() || saturation.is_some() || luminance.is_some() { // Color::as_hsla() returns more exact values than Color::hue(), etc. @@ -204,7 +181,9 @@ fn adjust_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> })) } -fn scale_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +#[allow(clippy::cognitive_complexity)] +// todo: refactor into rgb and hsl? +fn scale_color(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { fn scale(val: Number, by: Number, max: Number) -> Number { if by.is_zero() { return val; @@ -213,7 +192,7 @@ fn scale_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> } let span = args.span(); - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( @@ -225,8 +204,8 @@ fn scale_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> }; macro_rules! opt_scale_arg { - ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $scope:ident, $super_selector:ident) => { - let $name = match named_arg!($args, $scope, $super_selector, $arg = Value::Null) { + ($args:ident, $name:ident, $arg:literal, $low:literal, $high:literal, $parser:ident) => { + let $name = match $parser.default_named_arg(&mut $args, $arg, Value::Null)? { Value::Dimension(n, Unit::Percent) => { Some(bound!($args, $arg, n, Unit::Percent, $low, $high) / Number::from(100)) } @@ -257,10 +236,10 @@ fn scale_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> }; } - opt_scale_arg!(args, alpha, "alpha", -100, 100, scope, super_selector); - opt_scale_arg!(args, red, "red", -100, 100, scope, super_selector); - opt_scale_arg!(args, green, "green", -100, 100, scope, super_selector); - opt_scale_arg!(args, blue, "blue", -100, 100, scope, super_selector); + opt_scale_arg!(args, alpha, "alpha", -100, 100, parser); + opt_scale_arg!(args, red, "red", -100, 100, parser); + opt_scale_arg!(args, green, "green", -100, 100, parser); + opt_scale_arg!(args, blue, "blue", -100, 100, parser); if red.is_some() || green.is_some() || blue.is_some() { return Ok(Value::Color(Box::new(Color::from_rgba( @@ -287,24 +266,8 @@ fn scale_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> )))); } - opt_scale_arg!( - args, - saturation, - "saturation", - -100, - 100, - scope, - super_selector - ); - opt_scale_arg!( - args, - luminance, - "lightness", - -100, - 100, - scope, - super_selector - ); + opt_scale_arg!(args, saturation, "saturation", -100, 100, parser); + opt_scale_arg!(args, luminance, "lightness", -100, 100, parser); if saturation.is_some() || luminance.is_some() { // Color::as_hsla() returns more exact values than Color::hue(), etc. @@ -337,9 +300,9 @@ fn scale_color(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> })) } -fn ie_hex_str(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn ie_hex_str(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v => { return Err(( diff --git a/src/builtin/color/rgb.rs b/src/builtin/color/rgb.rs index 8f8c5a1..110c5e7 100644 --- a/src/builtin/color/rgb.rs +++ b/src/builtin/color/rgb.rs @@ -2,28 +2,26 @@ use super::{Builtin, GlobalFunctionMap}; use num_traits::One; -use crate::args::CallArgs; -use crate::color::Color; -use crate::common::QuoteKind; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::value::{Number, Value}; +use crate::{ + args::CallArgs, + color::Color, + common::QuoteKind, + error::SassResult, + parse::Parser, + unit::Unit, + value::{Number, Value}, +}; /// name: Either `rgb` or `rgba` depending on the caller -fn inner_rgb( - name: &'static str, - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +// todo: refactor into smaller functions +#[allow(clippy::cognitive_complexity)] +fn inner_rgb(name: &'static str, mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { if args.is_empty() { return Err(("Missing argument $channels.", args.span()).into()); } if args.len() == 1 { - let mut channels = match arg!(args, scope, super_selector, 0, "channels") { + let mut channels = match parser.arg(&mut args, 0, "channels")? { Value::List(v, ..) => v, _ => return Err(("Missing argument $channels.", args.span()).into()), }; @@ -121,10 +119,10 @@ fn inner_rgb( Ok(Value::Color(Box::new(color))) } else if args.len() == 2 { - let color = match arg!(args, scope, super_selector, 0, "color") { + let color = match parser.arg(&mut args, 0, "color")? { Value::Color(c) => c, v if v.is_special_function() => { - let alpha = arg!(args, scope, super_selector, 1, "alpha"); + let alpha = parser.arg(&mut args, 1, "alpha")?; return Ok(Value::String( format!( "{}({}, {})", @@ -143,7 +141,7 @@ fn inner_rgb( .into()) } }; - let alpha = match arg!(args, scope, super_selector, 1, "alpha") { + let alpha = match parser.arg(&mut args, 1, "alpha")? { Value::Dimension(n, Unit::None) => n, Value::Dimension(n, Unit::Percent) => n / Number::from(100), v @ Value::Dimension(..) => { @@ -179,7 +177,7 @@ fn inner_rgb( }; Ok(Value::Color(Box::new(color.with_alpha(alpha)))) } else { - let red = match arg!(args, scope, super_selector, 0, "red") { + let red = match parser.arg(&mut args, 0, "red")? { Value::Dimension(n, Unit::None) => n, Value::Dimension(n, Unit::Percent) => (n / Number::from(100)) * Number::from(255), v @ Value::Dimension(..) => { @@ -193,8 +191,8 @@ fn inner_rgb( .into()) } v if v.is_special_function() => { - let green = arg!(args, scope, super_selector, 1, "green"); - let blue = arg!(args, scope, super_selector, 2, "blue"); + let green = parser.arg(&mut args, 1, "green")?; + let blue = parser.arg(&mut args, 2, "blue")?; let mut string = format!( "{}({}, {}, {}", name, @@ -205,7 +203,8 @@ fn inner_rgb( if !args.is_empty() { string.push_str(", "); string.push_str( - &arg!(args, scope, super_selector, 3, "alpha") + &parser + .arg(&mut args, 3, "alpha")? .to_css_string(args.span())?, ); } @@ -220,7 +219,7 @@ fn inner_rgb( .into()) } }; - let green = match arg!(args, scope, super_selector, 1, "green") { + let green = match parser.arg(&mut args, 1, "green")? { Value::Dimension(n, Unit::None) => n, Value::Dimension(n, Unit::Percent) => (n / Number::from(100)) * Number::from(255), v @ Value::Dimension(..) => { @@ -234,7 +233,7 @@ fn inner_rgb( .into()) } v if v.is_special_function() => { - let blue = arg!(args, scope, super_selector, 2, "blue"); + let blue = parser.arg(&mut args, 2, "blue")?; let mut string = format!( "{}({}, {}, {}", name, @@ -245,7 +244,8 @@ fn inner_rgb( if !args.is_empty() { string.push_str(", "); string.push_str( - &arg!(args, scope, super_selector, 3, "alpha") + &parser + .arg(&mut args, 3, "alpha")? .to_css_string(args.span())?, ); } @@ -260,7 +260,7 @@ fn inner_rgb( .into()) } }; - let blue = match arg!(args, scope, super_selector, 2, "blue") { + let blue = match parser.arg(&mut args, 2, "blue")? { Value::Dimension(n, Unit::None) => n, Value::Dimension(n, Unit::Percent) => (n / Number::from(100)) * Number::from(255), v @ Value::Dimension(..) => { @@ -284,7 +284,8 @@ fn inner_rgb( if !args.is_empty() { string.push_str(", "); string.push_str( - &arg!(args, scope, super_selector, 3, "alpha") + &parser + .arg(&mut args, 3, "alpha")? .to_css_string(args.span())?, ); } @@ -299,13 +300,12 @@ fn inner_rgb( .into()) } }; - let alpha = match arg!( - args, - scope, - super_selector, + let alpha = match parser.default_arg( + &mut args, 3, - "alpha" = Value::Dimension(Number::one(), Unit::None) - ) { + "alpha", + Value::Dimension(Number::one(), Unit::None), + )? { Value::Dimension(n, Unit::None) => n, Value::Dimension(n, Unit::Percent) => n / Number::from(100), v @ Value::Dimension(..) => { @@ -343,17 +343,17 @@ fn inner_rgb( } } -fn rgb(args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - inner_rgb("rgb", args, scope, super_selector) +fn rgb(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + inner_rgb("rgb", args, parser) } -fn rgba(args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - inner_rgb("rgba", args, scope, super_selector) +fn rgba(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + inner_rgb("rgba", args, parser) } -fn red(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn red(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Dimension(c.red(), Unit::None)), v => Err(( format!("$color: {} is not a color.", v.to_css_string(args.span())?), @@ -363,9 +363,9 @@ fn red(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResu } } -fn green(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn green(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Dimension(c.green(), Unit::None)), v => Err(( format!("$color: {} is not a color.", v.to_css_string(args.span())?), @@ -375,9 +375,9 @@ fn green(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRe } } -fn blue(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn blue(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "color") { + match parser.arg(&mut args, 0, "color")? { Value::Color(c) => Ok(Value::Dimension(c.blue(), Unit::None)), v => Err(( format!("$color: {} is not a color.", v.to_css_string(args.span())?), @@ -387,9 +387,9 @@ fn blue(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes } } -fn mix(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn mix(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - let color1 = match arg!(args, scope, super_selector, 0, "color1") { + let color1 = match parser.arg(&mut args, 0, "color1")? { Value::Color(c) => c, v => { return Err(( @@ -400,7 +400,7 @@ fn mix(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResu } }; - let color2 = match arg!(args, scope, super_selector, 1, "color2") { + let color2 = match parser.arg(&mut args, 1, "color2")? { Value::Color(c) => c, v => { return Err(( @@ -411,13 +411,12 @@ fn mix(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResu } }; - let weight = match arg!( - args, - scope, - super_selector, + let weight = match parser.default_arg( + &mut args, 2, - "weight" = Value::Dimension(Number::from(50), Unit::None) - ) { + "weight", + Value::Dimension(Number::from(50), Unit::None), + )? { Value::Dimension(n, u) => bound!(args, "weight", n, u, 0, 100) / Number::from(100), v => { return Err(( diff --git a/src/builtin/list.rs b/src/builtin/list.rs index c9e17de..91cdf12 100644 --- a/src/builtin/list.rs +++ b/src/builtin/list.rs @@ -2,17 +2,18 @@ use super::{Builtin, GlobalFunctionMap}; use num_traits::{One, Signed, ToPrimitive, Zero}; -use crate::args::CallArgs; -use crate::common::{Brackets, ListSeparator, QuoteKind}; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::value::{Number, Value}; +use crate::{ + args::CallArgs, + common::{Brackets, ListSeparator, QuoteKind}, + error::SassResult, + parse::Parser, + unit::Unit, + value::{Number, Value}, +}; -fn length(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let len = match arg!(args, scope, super_selector, 0, "list") { + let len = match parser.arg(&mut args, 0, "list")? { Value::List(v, ..) => Number::from(v.len()), Value::Map(m) => Number::from(m.len()), _ => Number::one(), @@ -20,10 +21,10 @@ fn length(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassR Ok(Value::Dimension(len, Unit::None)) } -fn nth(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let mut list = arg!(args, scope, super_selector, 0, "list").as_list(); - let n = match arg!(args, scope, super_selector, 1, "n") { + let mut list = parser.arg(&mut args, 0, "list")?.as_list(); + let n = match parser.arg(&mut args, 1, "n")? { Value::Dimension(num, _) => num, v => { return Err(( @@ -61,14 +62,10 @@ fn nth(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResu })) } -fn list_separator( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn list_separator(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; Ok(Value::String( - match arg!(args, scope, super_selector, 0, "list") { + match parser.arg(&mut args, 0, "list")? { Value::List(_, sep, ..) => sep.name(), _ => ListSeparator::Space.name(), } @@ -77,14 +74,14 @@ fn list_separator( )) } -fn set_nth(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn set_nth(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - let (mut list, sep, brackets) = match arg!(args, scope, super_selector, 0, "list") { + let (mut list, sep, brackets) = match parser.arg(&mut args, 0, "list")? { Value::List(v, sep, b) => (v, sep, b), Value::Map(m) => (m.entries(), ListSeparator::Comma, Brackets::None), v => (vec![v], ListSeparator::Space, Brackets::None), }; - let n = match arg!(args, scope, super_selector, 1, "n") { + let n = match parser.arg(&mut args, 1, "n")? { Value::Dimension(num, _) => num, v => { return Err(( @@ -113,7 +110,7 @@ fn set_nth(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass return Err((format!("$n: {} is not an int.", n), args.span()).into()); } - let val = arg!(args, scope, super_selector, 2, "value"); + let val = parser.arg(&mut args, 2, "value")?; if n.is_positive() { list[n.to_integer().to_usize().unwrap_or(std::usize::MAX) - 1] = val; @@ -124,20 +121,19 @@ fn set_nth(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass Ok(Value::List(list, sep, brackets)) } -fn append(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn append(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - let (mut list, sep, brackets) = match arg!(args, scope, super_selector, 0, "list") { + let (mut list, sep, brackets) = match parser.arg(&mut args, 0, "list")? { Value::List(v, sep, b) => (v, sep, b), v => (vec![v], ListSeparator::Space, Brackets::None), }; - let val = arg!(args, scope, super_selector, 1, "val"); - let sep = match arg!( - args, - scope, - super_selector, + let val = parser.arg(&mut args, 1, "val")?; + let sep = match parser.default_arg( + &mut args, 2, - "separator" = Value::String("auto".to_owned(), QuoteKind::None) - ) { + "separator", + Value::String("auto".to_owned(), QuoteKind::None), + )? { Value::String(s, ..) => match s.as_str() { "auto" => sep, "comma" => ListSeparator::Comma, @@ -167,25 +163,24 @@ fn append(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassR Ok(Value::List(list, sep, brackets)) } -fn join(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn join(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(4)?; - let (mut list1, sep1, brackets) = match arg!(args, scope, super_selector, 0, "list1") { + let (mut list1, sep1, brackets) = match parser.arg(&mut args, 0, "list1")? { Value::List(v, sep, brackets) => (v, sep, brackets), Value::Map(m) => (m.entries(), ListSeparator::Comma, Brackets::None), v => (vec![v], ListSeparator::Space, Brackets::None), }; - let (list2, sep2) = match arg!(args, scope, super_selector, 1, "list2") { + let (list2, sep2) = match parser.arg(&mut args, 1, "list2")? { Value::List(v, sep, ..) => (v, sep), Value::Map(m) => (m.entries(), ListSeparator::Comma), v => (vec![v], ListSeparator::Space), }; - let sep = match arg!( - args, - scope, - super_selector, + let sep = match parser.default_arg( + &mut args, 2, - "separator" = Value::String("auto".to_owned(), QuoteKind::None) - ) { + "separator", + Value::String("auto".to_owned(), QuoteKind::None), + )? { Value::String(s, ..) => match s.as_str() { "auto" => { if list1.is_empty() || (list1.len() == 1 && sep1 == ListSeparator::Space) { @@ -216,13 +211,12 @@ fn join(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes } }; - let brackets = match arg!( - args, - scope, - super_selector, + let brackets = match parser.default_arg( + &mut args, 3, - "bracketed" = Value::String("auto".to_owned(), QuoteKind::None) - ) { + "bracketed", + Value::String("auto".to_owned(), QuoteKind::None), + )? { Value::String(s, ..) => match s.as_str() { "auto" => brackets, _ => Brackets::Bracketed, @@ -241,23 +235,21 @@ fn join(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes Ok(Value::List(list1, sep, brackets)) } -fn is_bracketed(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn is_bracketed(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - Ok(Value::bool( - match arg!(args, scope, super_selector, 0, "list") { - Value::List(.., brackets) => match brackets { - Brackets::Bracketed => true, - Brackets::None => false, - }, - _ => false, + Ok(Value::bool(match parser.arg(&mut args, 0, "list")? { + Value::List(.., brackets) => match brackets { + Brackets::Bracketed => true, + Brackets::None => false, }, - )) + _ => false, + })) } -fn index(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn index(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let list = arg!(args, scope, super_selector, 0, "list").as_list(); - let value = arg!(args, scope, super_selector, 1, "value"); + let list = parser.arg(&mut args, 0, "list")?.as_list(); + let value = parser.arg(&mut args, 1, "value")?; // TODO: find a way around this unwrap. // It should be impossible to hit as the arg is // evaluated prior to checking equality, but @@ -275,10 +267,10 @@ fn index(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRe Ok(Value::Dimension(index, Unit::None)) } -fn zip(args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn zip(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { let span = args.span(); - let lists = args - .get_variadic(scope, super_selector)? + let lists = parser + .variadic_args(args)? .into_iter() .map(|x| Ok(x.node.eval(span)?.node.as_list())) .collect::>>>()?; diff --git a/src/builtin/macros.rs b/src/builtin/macros.rs index b0bca29..8bc83ee 100644 --- a/src/builtin/macros.rs +++ b/src/builtin/macros.rs @@ -1,41 +1,3 @@ -macro_rules! arg { - ($args:ident, $scope:ident, $super_selector:ident, $idx:literal, $name:literal) => { - match $args.get_positional($idx, $scope, $super_selector) { - Some(v) => v?.node.eval($args.span())?.node, - None => match $args.get_named($name.to_owned(), $scope, $super_selector) { - Some(v) => v?.node.eval($args.span())?.node, - None => { - return Err((concat!("Missing argument $", $name, "."), $args.span()).into()) - } - }, - }; - }; - ($args:ident, $scope:ident, $super_selector:ident, $idx:literal, $name:literal=$default:expr) => { - match $args.get_positional($idx, $scope, $super_selector) { - Some(v) => v?.node.eval($args.span())?.node, - None => match $args.get_named($name.to_owned(), $scope, $super_selector) { - Some(v) => v?.node.eval($args.span())?.node, - None => $default, - }, - }; - }; -} - -macro_rules! named_arg { - ($args:ident, $scope:ident, $super_selector:ident, $name:literal) => { - match $args.get_named($name.to_owned(), $scope, $super_selector) { - Some(v) => v?.node.eval($args.span())?.node, - None => return Err((concat!("Missing argument $", $name, "."), $args.span()).into()), - }; - }; - ($args:ident, $scope:ident, $super_selector:ident, $name:literal=$default:expr) => { - match $args.get_named($name.to_owned(), $scope, $super_selector) { - Some(v) => v?.node.eval($args.span())?.node, - None => $default, - }; - }; -} - macro_rules! bound { ($args:ident, $name:literal, $arg:ident, $unit:ident, $low:literal, $high:literal) => { if $arg > Number::from($high) || $arg < Number::from($low) { diff --git a/src/builtin/map.rs b/src/builtin/map.rs index 7ddd514..0b10fd8 100644 --- a/src/builtin/map.rs +++ b/src/builtin/map.rs @@ -1,16 +1,17 @@ use super::{Builtin, GlobalFunctionMap}; -use crate::args::CallArgs; -use crate::common::{Brackets, ListSeparator}; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::value::{SassMap, Value}; +use crate::{ + args::CallArgs, + common::{Brackets, ListSeparator}, + error::SassResult, + parse::Parser, + value::{SassMap, Value}, +}; -fn map_get(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn map_get(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let key = arg!(args, scope, super_selector, 1, "key"); - let map = match arg!(args, scope, super_selector, 0, "map") { + let key = parser.arg(&mut args, 1, "key")?; + let map = match parser.arg(&mut args, 0, "map")? { Value::Map(m) => m, Value::List(v, ..) if v.is_empty() => SassMap::new(), v => { @@ -24,10 +25,10 @@ fn map_get(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass Ok(map.get(&key, args.span())?.unwrap_or(Value::Null)) } -fn map_has_key(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn map_has_key(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let key = arg!(args, scope, super_selector, 1, "key"); - let map = match arg!(args, scope, super_selector, 0, "map") { + let key = parser.arg(&mut args, 1, "key")?; + let map = match parser.arg(&mut args, 0, "map")? { Value::Map(m) => m, Value::List(v, ..) if v.is_empty() => SassMap::new(), v => { @@ -41,9 +42,9 @@ fn map_has_key(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Ok(Value::bool(map.get(&key, args.span())?.is_some())) } -fn map_keys(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn map_keys(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let map = match arg!(args, scope, super_selector, 0, "map") { + let map = match parser.arg(&mut args, 0, "map")? { Value::Map(m) => m, Value::List(v, ..) if v.is_empty() => SassMap::new(), v => { @@ -61,9 +62,9 @@ fn map_keys(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sas )) } -fn map_values(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn map_values(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let map = match arg!(args, scope, super_selector, 0, "map") { + let map = match parser.arg(&mut args, 0, "map")? { Value::Map(m) => m, Value::List(v, ..) if v.is_empty() => SassMap::new(), v => { @@ -81,9 +82,9 @@ fn map_values(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S )) } -fn map_merge(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn map_merge(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let mut map1 = match arg!(args, scope, super_selector, 0, "map1") { + let mut map1 = match parser.arg(&mut args, 0, "map1")? { Value::Map(m) => m, Value::List(v, ..) if v.is_empty() => SassMap::new(), v => { @@ -94,7 +95,7 @@ fn map_merge(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa .into()) } }; - let map2 = match arg!(args, scope, super_selector, 1, "map2") { + let map2 = match parser.arg(&mut args, 1, "map2")? { Value::Map(m) => m, Value::List(v, ..) if v.is_empty() => SassMap::new(), v => { @@ -109,8 +110,8 @@ fn map_merge(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa Ok(Value::Map(map1)) } -fn map_remove(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - let mut map = match arg!(args, scope, super_selector, 0, "map") { +fn map_remove(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + let mut map = match parser.arg(&mut args, 0, "map")? { Value::Map(m) => m, Value::List(v, ..) if v.is_empty() => SassMap::new(), v => { @@ -121,7 +122,7 @@ fn map_remove(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S .into()) } }; - let keys = args.get_variadic(scope, super_selector)?; + let keys = parser.variadic_args(args)?; for key in keys { map.remove(&key); } diff --git a/src/builtin/math.rs b/src/builtin/math.rs index 0992409..1ff1e75 100644 --- a/src/builtin/math.rs +++ b/src/builtin/math.rs @@ -5,16 +5,17 @@ use num_traits::{One, Signed, ToPrimitive, Zero}; #[cfg(feature = "random")] use rand::Rng; -use crate::args::CallArgs; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::value::{Number, Value}; +use crate::{ + args::CallArgs, + error::SassResult, + parse::Parser, + unit::Unit, + value::{Number, Value}, +}; -fn percentage(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn percentage(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let num = match arg!(args, scope, super_selector, 0, "number") { + let num = match parser.arg(&mut args, 0, "number")? { Value::Dimension(n, Unit::None) => n * Number::from(100), v @ Value::Dimension(..) => { return Err(( @@ -40,9 +41,9 @@ fn percentage(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S Ok(Value::Dimension(num, Unit::Percent)) } -fn round(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn round(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "number") { + match parser.arg(&mut args, 0, "number")? { Value::Dimension(n, u) => Ok(Value::Dimension(n.round(), u)), v => Err(( format!( @@ -55,9 +56,9 @@ fn round(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRe } } -fn ceil(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn ceil(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "number") { + match parser.arg(&mut args, 0, "number")? { Value::Dimension(n, u) => Ok(Value::Dimension(n.ceil(), u)), v => Err(( format!( @@ -70,9 +71,9 @@ fn ceil(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes } } -fn floor(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn floor(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "number") { + match parser.arg(&mut args, 0, "number")? { Value::Dimension(n, u) => Ok(Value::Dimension(n.floor(), u)), v => Err(( format!( @@ -85,9 +86,9 @@ fn floor(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRe } } -fn abs(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn abs(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "number") { + match parser.arg(&mut args, 0, "number")? { Value::Dimension(n, u) => Ok(Value::Dimension(n.abs(), u)), v => Err(( format!( @@ -100,9 +101,9 @@ fn abs(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResu } } -fn comparable(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn comparable(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let unit1 = match arg!(args, scope, super_selector, 0, "number1") { + let unit1 = match parser.arg(&mut args, 0, "number1")? { Value::Dimension(_, u) => u, v => { return Err(( @@ -115,7 +116,7 @@ fn comparable(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S .into()) } }; - let unit2 = match arg!(args, scope, super_selector, 1, "number2") { + let unit2 = match parser.arg(&mut args, 1, "number2")? { Value::Dimension(_, u) => u, v => { return Err(( @@ -134,9 +135,9 @@ fn comparable(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S // TODO: write tests for this #[cfg(feature = "random")] -fn random(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn random(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let limit = match arg!(args, scope, super_selector, 0, "limit" = Value::Null) { + let limit = match parser.default_arg(&mut args, 0, "limit", Value::Null)? { Value::Dimension(n, _) => n, Value::Null => { let mut rng = rand::thread_rng(); diff --git a/src/builtin/meta.rs b/src/builtin/meta.rs index 68a54db..97d8274 100644 --- a/src/builtin/meta.rs +++ b/src/builtin/meta.rs @@ -2,31 +2,30 @@ use super::{Builtin, GlobalFunctionMap, GLOBAL_FUNCTIONS}; use codemap::Spanned; -use crate::args::CallArgs; -use crate::common::QuoteKind; -use crate::error::SassResult; -use crate::scope::global_var_exists; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::value::{SassFunction, Value}; +use crate::{ + args::CallArgs, + common::QuoteKind, + error::SassResult, + parse::Parser, + unit::Unit, + value::{SassFunction, Value}, +}; -fn if_(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn if_(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - if arg!(args, scope, super_selector, 0, "condition").is_true(args.span())? { - Ok(arg!(args, scope, super_selector, 1, "if-true")) + if parser + .arg(&mut args, 0, "condition")? + .is_true(args.span())? + { + Ok(parser.arg(&mut args, 1, "if-true")?) } else { - Ok(arg!(args, scope, super_selector, 2, "if-false")) + Ok(parser.arg(&mut args, 2, "if-false")?) } } -fn feature_exists( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn feature_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "feature") { + match parser.arg(&mut args, 0, "feature")? { #[allow(clippy::match_same_arms)] Value::String(s, _) => Ok(match s.as_str() { // A local variable will shadow a global variable unless @@ -57,9 +56,9 @@ fn feature_exists( } } -fn unit(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn unit(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let unit = match arg!(args, scope, super_selector, 0, "number") { + let unit = match parser.arg(&mut args, 0, "number")? { Value::Dimension(_, u) => u.to_string(), v => { return Err(( @@ -75,88 +74,44 @@ fn unit(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes Ok(Value::String(unit, QuoteKind::Quoted)) } -fn type_of(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn type_of(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - let value = arg!(args, scope, super_selector, 0, "value"); + let value = parser.arg(&mut args, 0, "value")?; Ok(Value::String( value.kind(args.span())?.to_owned(), QuoteKind::None, )) } -fn unitless(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn unitless(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; #[allow(clippy::match_same_arms)] - Ok(match arg!(args, scope, super_selector, 0, "number") { + Ok(match parser.arg(&mut args, 0, "number")? { Value::Dimension(_, Unit::None) => Value::True, Value::Dimension(_, _) => Value::False, _ => Value::True, }) } -fn inspect(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn inspect(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; Ok(Value::String( - arg!(args, scope, super_selector, 0, "value") + parser + .arg(&mut args, 0, "value")? .inspect(args.span())? .into_owned(), QuoteKind::None, )) } -fn variable_exists( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn variable_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "name") { - Value::String(s, _) => Ok(Value::bool(scope.var_exists(&s))), - v => Err(( - format!("$name: {} is not a string.", v.to_css_string(args.span())?), - args.span(), - ) - .into()), - } -} - -fn global_variable_exists( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { - args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "name") { - Value::String(s, _) => Ok(Value::bool(global_var_exists(&s))), - v => Err(( - format!("$name: {} is not a string.", v.to_css_string(args.span())?), - args.span(), - ) - .into()), - } -} - -fn mixin_exists(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - args.max_args(2)?; - match arg!(args, scope, super_selector, 0, "name") { - Value::String(s, _) => Ok(Value::bool(scope.mixin_exists(&s))), - v => Err(( - format!("$name: {} is not a string.", v.to_css_string(args.span())?), - args.span(), - ) - .into()), - } -} - -fn function_exists( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { - args.max_args(2)?; - match arg!(args, scope, super_selector, 0, "name") { + match parser.arg(&mut args, 0, "name")? { Value::String(s, _) => Ok(Value::bool( - scope.fn_exists(&s) || GLOBAL_FUNCTIONS.contains_key(s.as_str()), + parser + .scopes + .last() + .var_exists(&s.into(), parser.global_scope), )), v => Err(( format!("$name: {} is not a string.", v.to_css_string(args.span())?), @@ -166,9 +121,51 @@ fn function_exists( } } -fn get_function(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn global_variable_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + args.max_args(1)?; + match parser.arg(&mut args, 0, "name")? { + Value::String(s, _) => Ok(Value::bool( + parser.global_scope.var_exists_no_global(&s.into()), + )), + v => Err(( + format!("$name: {} is not a string.", v.to_css_string(args.span())?), + args.span(), + ) + .into()), + } +} + +fn mixin_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + args.max_args(2)?; + match parser.arg(&mut args, 0, "name")? { + Value::String(s, _) => Ok(Value::bool( + parser.scopes.last().mixin_exists(&s, parser.global_scope), + )), + v => Err(( + format!("$name: {} is not a string.", v.to_css_string(args.span())?), + args.span(), + ) + .into()), + } +} + +fn function_exists(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + args.max_args(2)?; + match parser.arg(&mut args, 0, "name")? { + Value::String(s, _) => Ok(Value::bool( + parser.scopes.last().fn_exists(&s, parser.global_scope), + )), + v => Err(( + format!("$name: {} is not a string.", v.to_css_string(args.span())?), + args.span(), + ) + .into()), + } +} + +fn get_function(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - let name = match arg!(args, scope, super_selector, 0, "name") { + let name = match parser.arg(&mut args, 0, "name")? { Value::String(s, _) => s, v => { return Err(( @@ -178,8 +175,10 @@ fn get_function(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> .into()) } }; - let css = arg!(args, scope, super_selector, 1, "css" = Value::False).is_true(args.span())?; - let module = match arg!(args, scope, super_selector, 2, "module" = Value::Null) { + let css = parser + .default_arg(&mut args, 1, "css", Value::False)? + .is_true(args.span())?; + let module = match parser.default_arg(&mut args, 2, "module", Value::Null)? { Value::String(s, ..) => Some(s), Value::Null => None, v => { @@ -202,10 +201,13 @@ fn get_function(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> .into()); } - let func = match scope.get_fn(Spanned { - node: &name, - span: args.span(), - }) { + let func = match parser.scopes.last().get_fn( + Spanned { + node: &name, + span: args.span(), + }, + parser.global_scope, + ) { Ok(f) => SassFunction::UserDefined(Box::new(f), name.into()), Err(..) => match GLOBAL_FUNCTIONS.get(name.as_str()) { Some(f) => SassFunction::Builtin(f.clone(), name.into()), @@ -216,8 +218,8 @@ fn get_function(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Ok(Value::Function(func)) } -fn call(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { - let func = match arg!(args, scope, super_selector, 0, "function") { +fn call(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + let func = match parser.arg(&mut args, 0, "function")? { Value::Function(f) => f, v => { return Err(( @@ -230,7 +232,20 @@ fn call(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRes .into()) } }; - func.call(args.decrement(), scope, super_selector) + func.call(args.decrement(), parser) +} + +#[allow(clippy::needless_pass_by_value)] +fn content_exists(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { + args.max_args(0)?; + if !parser.in_mixin { + return Err(( + "content-exists() may only be called within a mixin.", + parser.span_before, + ) + .into()); + } + Ok(Value::bool(parser.content.is_some())) } pub(crate) fn declare(f: &mut GlobalFunctionMap) { @@ -249,4 +264,5 @@ pub(crate) fn declare(f: &mut GlobalFunctionMap) { f.insert("function-exists", Builtin::new(function_exists)); f.insert("get-function", Builtin::new(get_function)); f.insert("call", Builtin::new(call)); + f.insert("content-exists", Builtin::new(content_exists)); } diff --git a/src/builtin/mod.rs b/src/builtin/mod.rs index 8e37009..2e8d8e7 100644 --- a/src/builtin/mod.rs +++ b/src/builtin/mod.rs @@ -1,12 +1,11 @@ -use once_cell::sync::Lazy; -use std::collections::HashMap; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::{ + collections::HashMap, + sync::atomic::{AtomicUsize, Ordering}, +}; -use crate::args::CallArgs; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::value::Value; +use once_cell::sync::Lazy; + +use crate::{args::CallArgs, error::SassResult, parse::Parser, value::Value}; #[macro_use] mod macros; @@ -26,12 +25,12 @@ static FUNCTION_COUNT: AtomicUsize = AtomicUsize::new(0); // TODO: impl Fn #[derive(Clone)] pub(crate) struct Builtin( - pub fn(CallArgs, &Scope, &Selector) -> SassResult, + pub fn(CallArgs, &mut Parser<'_>) -> SassResult, usize, ); impl Builtin { - pub fn new(body: fn(CallArgs, &Scope, &Selector) -> SassResult) -> Builtin { + pub fn new(body: fn(CallArgs, &mut Parser<'_>) -> SassResult) -> Builtin { let count = FUNCTION_COUNT.fetch_add(1, Ordering::Relaxed); Self(body, count) } diff --git a/src/builtin/selector.rs b/src/builtin/selector.rs index 6f99801..97a5c6d 100644 --- a/src/builtin/selector.rs +++ b/src/builtin/selector.rs @@ -1,54 +1,34 @@ use super::{Builtin, GlobalFunctionMap}; -use crate::args::CallArgs; -use crate::common::{Brackets, ListSeparator, QuoteKind}; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::{ - ComplexSelector, ComplexSelectorComponent, Extender, Selector, SelectorList, +use crate::{ + args::CallArgs, + common::{Brackets, ListSeparator, QuoteKind}, + error::SassResult, + parse::Parser, + selector::{ComplexSelector, ComplexSelectorComponent, Extender, Selector, SelectorList}, + value::Value, }; -use crate::value::Value; -fn is_superselector( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn is_superselector(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let parent_selector = arg!(args, scope, super_selector, 0, "super").to_selector( - args.span(), - scope, - super_selector, - "super", - false, - )?; - let child_selector = arg!(args, scope, super_selector, 1, "sub").to_selector( - args.span(), - scope, - super_selector, - "sub", - false, - )?; + let parent_selector = parser + .arg(&mut args, 0, "super")? + .to_selector(parser, "super", false)?; + let child_selector = parser + .arg(&mut args, 1, "sub")? + .to_selector(parser, "sub", false)?; Ok(Value::bool( parent_selector.is_super_selector(&child_selector), )) } -fn simple_selectors( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn simple_selectors(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; // todo: Value::to_compound_selector - let selector = arg!(args, scope, super_selector, 0, "selector").to_selector( - args.span(), - scope, - super_selector, - "selector", - false, - )?; + let selector = parser + .arg(&mut args, 0, "selector")? + .to_selector(parser, "selector", false)?; if selector.0.components.len() != 1 { return Err(("$selector: expected selector.", args.span()).into()); @@ -73,30 +53,24 @@ fn simple_selectors( )) } -fn selector_parse( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn selector_parse(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - Ok(arg!(args, scope, super_selector, 0, "selector") - .to_selector(args.span(), scope, super_selector, "selector", false)? + Ok(parser + .arg(&mut args, 0, "selector")? + .to_selector(parser, "selector", false)? .into_value()) } -fn selector_nest(args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn selector_nest(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { let span = args.span(); - let selectors = args.get_variadic(scope, super_selector)?; + let selectors = parser.variadic_args(args)?; if selectors.is_empty() { return Err(("$selectors: At least one selector must be passed.", span).into()); } Ok(selectors .into_iter() - .map(|sel| { - sel.node - .to_selector(span, scope, super_selector, "selectors", true) - }) + .map(|sel| sel.node.to_selector(parser, "selectors", true)) .collect::>>()? .into_iter() .fold(Selector::new(), |parent, child| { @@ -105,9 +79,9 @@ fn selector_nest(args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa .into_value()) } -fn selector_append(args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn selector_append(args: CallArgs, parser: &mut Parser<'_>) -> SassResult { let span = args.span(); - let selectors = args.get_variadic(scope, super_selector)?; + let selectors = parser.variadic_args(args)?; if selectors.is_empty() { return Err(("$selectors: At least one selector must be passed.", span).into()); } @@ -115,9 +89,7 @@ fn selector_append(args: CallArgs, scope: &Scope, super_selector: &Selector) -> let mut parsed_selectors = selectors .into_iter() .map(|s| { - let tmp = s - .node - .to_selector(span, scope, super_selector, "selectors", false)?; + let tmp = s.node.to_selector(parser, "selectors", false)?; if tmp.contains_parent_selector() { Err(("Parent selectors aren't allowed here.", span).into()) } else { @@ -164,80 +136,42 @@ fn selector_append(args: CallArgs, scope: &Scope, super_selector: &Selector) -> .into_value()) } -fn selector_extend( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn selector_extend(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - let selector = arg!(args, scope, super_selector, 0, "selector").to_selector( - args.span(), - scope, - super_selector, - "selector", - false, - )?; - let target = arg!(args, scope, super_selector, 1, "extendee").to_selector( - args.span(), - scope, - super_selector, - "extendee", - false, - )?; - let source = arg!(args, scope, super_selector, 2, "extender").to_selector( - args.span(), - scope, - super_selector, - "extender", - false, - )?; + let selector = parser + .arg(&mut args, 0, "selector")? + .to_selector(parser, "selector", false)?; + let target = parser + .arg(&mut args, 1, "extendee")? + .to_selector(parser, "extendee", false)?; + let source = parser + .arg(&mut args, 2, "extender")? + .to_selector(parser, "extender", false)?; Ok(Extender::extend(selector.0, source.0, target.0).to_sass_list()) } -fn selector_replace( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn selector_replace(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - let selector = arg!(args, scope, super_selector, 0, "selector").to_selector( - args.span(), - scope, - super_selector, - "selector", - false, - )?; - let target = arg!(args, scope, super_selector, 1, "original").to_selector( - args.span(), - scope, - super_selector, - "original", - false, - )?; - let source = arg!(args, scope, super_selector, 2, "replacement").to_selector( - args.span(), - scope, - super_selector, - "replacement", - false, - )?; + let selector = parser + .arg(&mut args, 0, "selector")? + .to_selector(parser, "selector", false)?; + let target = parser + .arg(&mut args, 1, "original")? + .to_selector(parser, "original", false)?; + let source = + parser + .arg(&mut args, 2, "replacement")? + .to_selector(parser, "replacement", false)?; Ok(Extender::replace(selector.0, source.0, target.0).to_sass_list()) } -fn selector_unify( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn selector_unify(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let selector1 = arg!(args, scope, super_selector, 0, "selector1").to_selector( - args.span(), - scope, - super_selector, - "selector1", - false, - )?; + let selector1 = + parser + .arg(&mut args, 0, "selector1")? + .to_selector(parser, "selector1", false)?; if selector1.contains_parent_selector() { return Err(( @@ -247,13 +181,10 @@ fn selector_unify( .into()); } - let selector2 = arg!(args, scope, super_selector, 1, "selector2").to_selector( - args.span(), - scope, - super_selector, - "selector2", - false, - )?; + let selector2 = + parser + .arg(&mut args, 1, "selector2")? + .to_selector(parser, "selector2", false)?; if selector2.contains_parent_selector() { return Err(( diff --git a/src/builtin/string.rs b/src/builtin/string.rs index b8d62c2..521ba54 100644 --- a/src/builtin/string.rs +++ b/src/builtin/string.rs @@ -6,21 +6,18 @@ use num_traits::{Signed, ToPrimitive, Zero}; #[cfg(feature = "random")] use rand::{distributions::Alphanumeric, thread_rng, Rng}; -use crate::args::CallArgs; -use crate::common::QuoteKind; -use crate::error::SassResult; -use crate::scope::Scope; -use crate::selector::Selector; -use crate::unit::Unit; -use crate::value::{Number, Value}; +use crate::{ + args::CallArgs, + common::QuoteKind, + error::SassResult, + parse::Parser, + unit::Unit, + value::{Number, Value}, +}; -fn to_upper_case( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn to_upper_case(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "string") { + match parser.arg(&mut args, 0, "string")? { Value::String(mut i, q) => { i.make_ascii_uppercase(); Ok(Value::String(i, q)) @@ -36,13 +33,9 @@ fn to_upper_case( } } -fn to_lower_case( - mut args: CallArgs, - scope: &Scope, - super_selector: &Selector, -) -> SassResult { +fn to_lower_case(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "string") { + match parser.arg(&mut args, 0, "string")? { Value::String(mut i, q) => { i.make_ascii_lowercase(); Ok(Value::String(i, q)) @@ -58,9 +51,9 @@ fn to_lower_case( } } -fn str_length(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn str_length(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "string") { + match parser.arg(&mut args, 0, "string")? { Value::String(i, _) => Ok(Value::Dimension( Number::from(i.chars().count()), Unit::None, @@ -76,9 +69,9 @@ fn str_length(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S } } -fn quote(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn quote(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "string") { + match parser.arg(&mut args, 0, "string")? { Value::String(i, _) => Ok(Value::String(i, QuoteKind::Quoted)), v => Err(( format!( @@ -91,9 +84,9 @@ fn quote(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassRe } } -fn unquote(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn unquote(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(1)?; - match arg!(args, scope, super_selector, 0, "string") { + match parser.arg(&mut args, 0, "string")? { i @ Value::String(..) => Ok(i.unquote()), v => Err(( format!( @@ -106,9 +99,9 @@ fn unquote(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sass } } -fn str_slice(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn str_slice(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - let (string, quotes) = match arg!(args, scope, super_selector, 0, "string") { + let (string, quotes) = match parser.arg(&mut args, 0, "string")? { Value::String(s, q) => (s, q), v => { return Err(( @@ -122,7 +115,7 @@ fn str_slice(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa } }; let str_len = string.chars().count(); - let start = match arg!(args, scope, super_selector, 1, "start-at") { + let start = match parser.arg(&mut args, 1, "start-at")? { Value::Dimension(n, Unit::None) if n.is_decimal() => { return Err((format!("{} is not an int.", n), args.span()).into()) } @@ -155,7 +148,7 @@ fn str_slice(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa .into()) } }; - let mut end = match arg!(args, scope, super_selector, 2, "end-at" = Value::Null) { + let mut end = match parser.default_arg(&mut args, 2, "end-at", Value::Null)? { Value::Dimension(n, Unit::None) if n.is_decimal() => { return Err((format!("{} is not an int.", n), args.span()).into()) } @@ -208,9 +201,9 @@ fn str_slice(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa } } -fn str_index(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn str_index(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(2)?; - let s1 = match arg!(args, scope, super_selector, 0, "string") { + let s1 = match parser.arg(&mut args, 0, "string")? { Value::String(i, _) => i, v => { return Err(( @@ -224,7 +217,7 @@ fn str_index(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa } }; - let substr = match arg!(args, scope, super_selector, 1, "substring") { + let substr = match parser.arg(&mut args, 1, "substring")? { Value::String(i, _) => i, v => { return Err(( @@ -244,9 +237,9 @@ fn str_index(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> Sa }) } -fn str_insert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> SassResult { +fn str_insert(mut args: CallArgs, parser: &mut Parser<'_>) -> SassResult { args.max_args(3)?; - let (s1, quotes) = match arg!(args, scope, super_selector, 0, "string") { + let (s1, quotes) = match parser.arg(&mut args, 0, "string")? { Value::String(i, q) => (i, q), v => { return Err(( @@ -260,7 +253,7 @@ fn str_insert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S } }; - let substr = match arg!(args, scope, super_selector, 1, "insert") { + let substr = match parser.arg(&mut args, 1, "insert")? { Value::String(i, _) => i, v => { return Err(( @@ -274,7 +267,7 @@ fn str_insert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S } }; - let index = match arg!(args, scope, super_selector, 2, "index") { + let index = match parser.arg(&mut args, 2, "index")? { Value::Dimension(n, Unit::None) if n.is_decimal() => { return Err((format!("$index: {} is not an int.", n), args.span()).into()) } @@ -347,7 +340,7 @@ fn str_insert(mut args: CallArgs, scope: &Scope, super_selector: &Selector) -> S #[cfg(feature = "random")] #[allow(clippy::needless_pass_by_value)] -fn unique_id(args: CallArgs, _: &Scope, _: &Selector) -> SassResult { +fn unique_id(args: CallArgs, _: &mut Parser<'_>) -> SassResult { args.max_args(0)?; let mut rng = thread_rng(); let string = std::iter::repeat(()) diff --git a/src/color/mod.rs b/src/color/mod.rs index 81b8806..aa9a1ac 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -15,8 +15,10 @@ //! Named colors retain their original casing, //! so `rEd` should be emitted as `rEd`. -use std::cmp::{max, min}; -use std::fmt::{self, Display}; +use std::{ + cmp::{max, min}, + fmt::{self, Display}, +}; use crate::value::Number; pub(crate) use name::NAMED_COLORS; diff --git a/src/error.rs b/src/error.rs index f1b9908..47b7f54 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,7 +1,9 @@ -use std::error::Error; -use std::fmt::{self, Display}; -use std::io; -use std::string::FromUtf8Error; +use std::{ + error::Error, + fmt::{self, Display}, + io, + string::FromUtf8Error, +}; use codemap::{Span, SpanLoc}; diff --git a/src/imports.rs b/src/imports.rs deleted file mode 100644 index eea987d..0000000 --- a/src/imports.rs +++ /dev/null @@ -1,46 +0,0 @@ -use std::ffi::{OsStr, OsString}; -use std::path::Path; - -use codemap::{CodeMap, Spanned}; - -use crate::error::SassResult; -use crate::scope::Scope; -use crate::{Stmt, StyleSheet}; - -pub(crate) fn import( - ctx: &Path, - path: &Path, - map: &mut CodeMap, -) -> SassResult<(Vec>, Scope)> { - let mut rules = Vec::new(); - let mut scope = Scope::new(); - if path.is_absolute() { - todo!("absolute import") - } - let path_buf = ctx.parent().unwrap_or_else(|| Path::new("")).join(path); - // "todo: will panic if path ended in `..`" - let name = path_buf.file_name().unwrap(); - if path_buf.extension() == Some(OsStr::new(".css")) { - // || name.starts_with("http://") || name.starts_with("https://") { - todo!("handle css imports") - } - let mut p1 = path_buf.clone(); - p1.push(OsString::from("index.scss")); - let mut p2 = path_buf.clone(); - p2.push(OsString::from("_index.scss")); - let paths = [ - path_buf.with_file_name(name).with_extension("scss"), - path_buf.with_file_name(format!("_{}.scss", name.to_str().unwrap())), - path_buf, - p1, - p2, - ]; - for name in &paths { - if name.is_file() { - let (rules2, scope2) = StyleSheet::export_from_path(&name.to_str().unwrap(), map)?; - rules.extend(rules2); - scope.extend(scope2); - } - } - Ok((rules, scope)) -} diff --git a/src/lexer.rs b/src/lexer.rs index 2f6f918..858a197 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,6 +1,4 @@ -use std::iter::Peekable; -use std::str::Chars; -use std::sync::Arc; +use std::{iter::Peekable, str::Chars, sync::Arc}; use codemap::File; diff --git a/src/lib.rs b/src/lib.rs index 92aa6c7..912d401 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,10 +11,8 @@ Spec progress as of 2020-05-01: ## Use as library ``` -use grass::{SassResult, StyleSheet}; - -fn main() -> SassResult<()> { - let sass = StyleSheet::new("a { b { color: &; } }".to_string())?; +fn main() -> Result<(), grass::Error> { + let sass = grass::from_string("a { b { color: &; } }".to_string())?; assert_eq!(sass, "a b {\n color: a b;\n}\n"); Ok(()) } @@ -88,32 +86,26 @@ grass input.scss )] #![cfg_attr(feature = "nightly", feature(track_caller))] #![cfg_attr(feature = "profiling", inline(never))] - -use std::convert::TryFrom; -use std::iter::Iterator; +use std::{fs, path::Path}; #[cfg(target_pointer_width = "64")] pub(crate) use beef::lean::Cow; #[cfg(not(target_pointer_width = "64"))] pub(crate) use beef::Cow; -use codemap::{Span, Spanned}; +use codemap::CodeMap; -use peekmore::{PeekMore, PeekMoreIterator}; +use peekmore::PeekMore; -use crate::atrule::{AtRule, AtRuleKind, Function, Mixin}; -pub use crate::error::{SassError, SassResult}; -use crate::scope::{insert_global_var, Scope}; -use crate::selector::Selector; -use crate::style::Style; -pub use crate::stylesheet::StyleSheet; +pub use crate::error::{SassError as Error, SassResult as Result}; pub(crate) use crate::token::Token; -use crate::utils::{ - devour_whitespace, eat_comment, eat_ident, eat_variable_value, peek_ident_no_interpolation, - peek_whitespace, read_until_closing_curly_brace, read_until_closing_paren, read_until_newline, - VariableDecl, +use crate::{ + lexer::Lexer, + output::Css, + parse::{common::NeverEmptyVec, Parser}, + scope::Scope, + selector::Selector, }; -use crate::value::Value; mod args; mod atrule; @@ -121,294 +113,101 @@ mod builtin; mod color; mod common; mod error; -mod imports; mod lexer; mod output; +mod parse; mod scope; mod selector; mod style; -mod stylesheet; mod token; mod unit; mod utils; mod value; -#[derive(Clone, Debug)] -pub(crate) enum Stmt { - /// A [`Style`](/grass/style/struct.Style) - Style(Box