diff --git a/src/builtin/color/hsl.rs b/src/builtin/color/hsl.rs index b1982f6..03a9c5e 100644 --- a/src/builtin/color/hsl.rs +++ b/src/builtin/color/hsl.rs @@ -9,15 +9,15 @@ pub(crate) fn register(f: &mut BTreeMap) { decl!(f "hsl", |args, _| { let hue = match arg!(args, 0, "hue").eval() { Value::Dimension(n, _) => n, - _ => todo!("$hue: ____ is not a number."), + v => return Err(format!("$hue: {} is not a number.", v).into()), }; let saturation = match arg!(args, 1, "saturation").eval() { Value::Dimension(n, _) => n / Number::from(100), - _ => todo!("$saturation: ____ is not a number."), + v => return Err(format!("$saturation: {} is not a number.", v).into()), }; let luminance = match arg!(args, 2, "luminance").eval() { Value::Dimension(n, _) => n / Number::from(100), - _ => todo!("$luminance: ____ is not a number."), + v => return Err(format!("$luminance: {} is not a number.", v).into()), }; let alpha = match arg!(args, 3, "alpha"=Value::Dimension(Number::from(1), Unit::None)) { Value::Dimension(n, Unit::None) => n, diff --git a/src/error.rs b/src/error.rs index 96d4e15..caeba44 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,6 +61,26 @@ impl From for String { } } +impl From<&str> for SassError { + #[inline] + fn from(error: &str) -> SassError { + SassError { + pos: Pos::new(), + message: error.to_string(), + } + } +} + +impl From for SassError { + #[inline] + fn from(error: String) -> SassError { + SassError { + pos: Pos::new(), + message: error, + } + } +} + impl Error for SassError { fn description(&self) -> &'static str { "SASS parsing error" diff --git a/src/lib.rs b/src/lib.rs index 4f735d1..1aa79ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -324,7 +324,7 @@ impl<'a> StyleSheetParser<'a> { | TokenKind::Symbol(Symbol::Mul) | TokenKind::Symbol(Symbol::Percent) | TokenKind::Symbol(Symbol::Period) => rules - .extend(self.eat_rules(&Selector(Vec::new()), &mut self.global_scope.clone())), + .extend(self.eat_rules(&Selector(Vec::new()), &mut self.global_scope.clone())?), TokenKind::Whitespace(_) => { self.lexer.next(); continue; @@ -348,8 +348,7 @@ impl<'a> StyleSheetParser<'a> { { self.error(pos, "unexpected variable use at toplevel"); } - let VariableDecl { val, default } = eat_variable_value(&mut self.lexer, &self.global_scope) - .unwrap_or_else(|err| self.error(err.0, &err.1)); + let VariableDecl { val, default } = eat_variable_value(&mut self.lexer, &self.global_scope)?; if !default || self.global_scope.get_var(&name).is_err() { self.global_scope.insert_var(&name, val); } @@ -367,7 +366,7 @@ impl<'a> StyleSheetParser<'a> { rules.push(Stmt::MultilineComment(comment)); } TokenKind::AtRule(AtRuleKind::Include) => { - rules.extend(eat_include(&mut self.lexer, &self.global_scope, &Selector(Vec::new())).unwrap_or_else(|e| self.error(e.0, &e.1))) + rules.extend(eat_include(&mut self.lexer, &self.global_scope, &Selector(Vec::new()))?) } TokenKind::AtRule(AtRuleKind::Import) => { let Token { pos, .. } = self @@ -445,11 +444,9 @@ impl<'a> StyleSheetParser<'a> { Ok((rules, self.global_scope)) } - fn eat_rules(&mut self, super_selector: &Selector, scope: &mut Scope) -> Vec { + fn eat_rules(&mut self, super_selector: &Selector, scope: &mut Scope) -> SassResult> { let mut stmts = Vec::new(); - while let Some(expr) = eat_expr(&mut self.lexer, scope, super_selector) - .unwrap_or_else(|error| self.error(error.0, &error.1)) - { + while let Some(expr) = eat_expr(&mut self.lexer, scope, super_selector)? { match expr { Expr::Style(s) => stmts.push(Stmt::Style(s)), #[allow(clippy::redundant_closure)] @@ -462,7 +459,7 @@ impl<'a> StyleSheetParser<'a> { } Expr::Selector(s) => { self.scope += 1; - let rules = self.eat_rules(&super_selector.zip(&s), scope); + let rules = self.eat_rules(&super_selector.zip(&s), scope)?; stmts.push(Stmt::RuleSet(RuleSet { super_selector: super_selector.clone(), selector: s, @@ -470,7 +467,7 @@ impl<'a> StyleSheetParser<'a> { })); self.scope -= 1; if self.scope == 0 { - return stmts; + return Ok(stmts); } } Expr::VariableDecl(name, val) => { @@ -487,7 +484,7 @@ impl<'a> StyleSheetParser<'a> { Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)), } } - stmts + Ok(stmts) } } @@ -495,7 +492,7 @@ pub(crate) fn eat_expr>( toks: &mut Peekable, scope: &Scope, super_selector: &Selector, -) -> Result, (Pos, String)> { +) -> SassResult> { let mut values = Vec::with_capacity(5); while let Some(tok) = toks.peek() { match &tok.kind { @@ -520,7 +517,7 @@ pub(crate) fn eat_expr>( // in a style `color:red`. todo: refactor let mut v = values.into_iter().peekable(); let property = Style::parse_property(&mut v, scope, super_selector, String::new()); - let value = Style::parse_value(&mut v, scope, super_selector); + let value = Style::parse_value(&mut v, scope, super_selector)?; return Ok(Some(Expr::Style(Style { property, value }))); } TokenKind::Symbol(Symbol::CloseCurlyBrace) => { @@ -535,7 +532,7 @@ pub(crate) fn eat_expr>( let mut v = values.into_iter().peekable(); let property = Style::parse_property(&mut v, scope, super_selector, String::new()); - let value = Style::parse_value(&mut v, scope, super_selector); + let value = Style::parse_value(&mut v, scope, super_selector)?; return Ok(Some(Expr::Style(Style { property, value }))); } } @@ -605,7 +602,7 @@ pub(crate) fn eat_expr>( AtRule::Charset(_) => todo!("@charset as expr"), AtRule::Debug(a, b) => Ok(Some(Expr::Debug(a, b))), AtRule::Warn(a, b) => Ok(Some(Expr::Warn(a, b))), - AtRule::Error(a, b) => Err((a, b)), + AtRule::Error(pos, err) => Err(SassError::new(err, pos)), AtRule::Return(_) => todo!("@return in unexpected location!"), }; } diff --git a/src/mixin.rs b/src/mixin.rs index 21900d5..5e4633b 100644 --- a/src/mixin.rs +++ b/src/mixin.rs @@ -3,6 +3,7 @@ use std::vec::IntoIter; use crate::args::{eat_call_args, eat_func_args, CallArgs, FuncArgs}; use crate::common::{Pos, Scope, Symbol}; +use crate::error::{SassError, SassResult}; use crate::selector::Selector; use crate::utils::devour_whitespace; use crate::{eat_expr, Expr, RuleSet, Stmt, Token, TokenKind}; @@ -87,11 +88,11 @@ impl Mixin { self } - pub fn call(mut self, super_selector: &Selector) -> Result, (Pos, String)> { + pub fn call(mut self, super_selector: &Selector) -> SassResult> { self.eval(super_selector) } - fn eval(&mut self, super_selector: &Selector) -> Result, (Pos, String)> { + fn eval(&mut self, super_selector: &Selector) -> SassResult> { let mut stmts = Vec::new(); while let Some(expr) = eat_expr(&mut self.body, &self.scope, super_selector)? { match expr { @@ -123,7 +124,7 @@ pub(crate) fn eat_include>( toks: &mut Peekable, scope: &Scope, super_selector: &Selector, -) -> Result, (Pos, String)> { +) -> SassResult> { toks.next(); devour_whitespace(toks); let Token { kind, pos } = toks @@ -131,7 +132,7 @@ pub(crate) fn eat_include>( .expect("this must exist because we have already peeked"); let name = match kind { TokenKind::Ident(s) => s, - _ => return Err((pos, String::from("Expected identifier."))), + _ => return Err(SassError::new("Expected identifier.", pos)), }; devour_whitespace(toks); @@ -147,17 +148,17 @@ pub(crate) fn eat_include>( } tmp } - _ => return Err((pos, String::from("expected `(` or `;`"))), + _ => return Err(SassError::new("expected `(` or `;`", pos)), } } else { - return Err((pos, String::from("unexpected EOF"))); + return Err(SassError::new("unexpected EOF", pos)); }; devour_whitespace(toks); let mixin = match scope.get_mixin(&name) { Ok(m) => m.clone(), - _ => return Err((pos, String::from("Expected identifier."))), + _ => return Err(SassError::new("Expected identifier.", pos)), }; let rules = mixin.args(&args).call(super_selector)?; diff --git a/src/style.rs b/src/style.rs index c40ad57..1cc98a6 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,4 +1,5 @@ use crate::common::{Pos, Scope, Symbol}; +use crate::error::SassResult; use crate::selector::Selector; use crate::utils::{devour_whitespace, parse_interpolation}; use crate::value::Value; @@ -33,7 +34,7 @@ impl Style { toks: &mut Peekable, scope: &Scope, super_selector: &Selector, - ) -> Value { + ) -> SassResult { StyleParser::new(scope, super_selector).parse_style_value(toks) } @@ -42,7 +43,7 @@ impl Style { scope: &Scope, super_selector: &Selector, super_property: String, - ) -> Result { + ) -> SassResult { StyleParser::new(scope, super_selector).eat_style_group(toks, super_property) } } @@ -63,7 +64,7 @@ impl<'a> StyleParser<'a> { pub(crate) fn parse_style_value>( &self, toks: &mut Peekable, - ) -> Value { + ) -> SassResult { let mut style = Vec::new(); let mut n = 0; devour_whitespace(toks); @@ -99,14 +100,14 @@ impl<'a> StyleParser<'a> { style.push(toks.next().unwrap()); } devour_whitespace(toks); - Value::from_tokens(&mut style.into_iter().peekable(), self.scope).unwrap() + Value::from_tokens(&mut style.into_iter().peekable(), self.scope) } pub(crate) fn eat_style_group>( &self, toks: &mut Peekable, super_property: String, - ) -> Result { + ) -> SassResult { let mut styles = Vec::new(); devour_whitespace(toks); while let Some(tok) = toks.peek() { @@ -138,7 +139,7 @@ impl<'a> StyleParser<'a> { _ => {} } } - let value = self.parse_style_value(toks); + let value = self.parse_style_value(toks)?; styles.push(Style { property, value }); if let Some(tok) = toks.peek() { match tok.kind { @@ -153,7 +154,7 @@ impl<'a> StyleParser<'a> { } } _ => { - let val = self.parse_style_value(toks); + let val = self.parse_style_value(toks)?; return Ok(Expr::Style(Style { property: super_property, value: val, diff --git a/src/utils.rs b/src/utils.rs index ddeb82f..109798e 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,4 +1,5 @@ -use crate::common::{Keyword, Pos, Symbol}; +use crate::common::{Keyword, Symbol}; +use crate::error::SassResult; use crate::lexer::Lexer; use crate::value::Value; use crate::{Scope, Token, TokenKind}; @@ -81,7 +82,7 @@ impl VariableDecl { pub(crate) fn eat_variable_value>( toks: &mut Peekable, scope: &Scope, -) -> Result { +) -> SassResult { devour_whitespace(toks); let mut default = false; let mut raw: Vec = Vec::new(); diff --git a/src/value/parse.rs b/src/value/parse.rs index 0611f7b..d33b0ee 100644 --- a/src/value/parse.rs +++ b/src/value/parse.rs @@ -9,6 +9,7 @@ use crate::args::eat_call_args; use crate::builtin::GLOBAL_FUNCTIONS; use crate::color::Color; use crate::common::{Keyword, ListSeparator, Op, QuoteKind, Scope, Symbol}; +use crate::error::SassResult; use crate::units::Unit; use crate::utils::{devour_whitespace_or_comment, parse_interpolation}; use crate::value::Value; @@ -83,25 +84,25 @@ impl Value { pub fn from_tokens>( toks: &mut Peekable, scope: &Scope, - ) -> Option { + ) -> SassResult { let left = Self::_from_tokens(toks, scope)?; let whitespace = devour_whitespace_or_comment(toks); let next = match toks.peek() { Some(x) => x, - None => return Some(left), + None => return Ok(left), }; match next.kind { TokenKind::Symbol(Symbol::SemiColon) | TokenKind::Symbol(Symbol::CloseParen) => { - Some(left) + Ok(left) } TokenKind::Symbol(Symbol::Comma) => { toks.next(); devour_whitespace_or_comment(toks); let right = match Self::from_tokens(toks, scope) { - Some(x) => x, - None => return Some(left), + Ok(x) => x, + Err(_) => return Ok(left), }; - Some(Value::List(vec![left, right], ListSeparator::Comma)) + Ok(Value::List(vec![left, right], ListSeparator::Comma)) } TokenKind::Symbol(Symbol::Plus) | TokenKind::Symbol(Symbol::Minus) @@ -121,18 +122,18 @@ impl Value { toks.next(); devour_whitespace_or_comment(toks); let right = match Self::from_tokens(toks, scope) { - Some(x) => x, - None => return Some(left), + Ok(x) => x, + Err(_) => return Ok(left), }; - Some(Value::BinaryOp(Box::new(left), op, Box::new(right))) + Ok(Value::BinaryOp(Box::new(left), op, Box::new(right))) } _ if whitespace => { devour_whitespace_or_comment(toks); let right = match Self::from_tokens(toks, scope) { - Some(x) => x, - None => return Some(left), + Ok(x) => x, + Err(_) => return Ok(left), }; - Some(Value::List(vec![left, right], ListSeparator::Space)) + Ok(Value::List(vec![left, right], ListSeparator::Space)) } _ => { dbg!(&next.kind); @@ -144,11 +145,11 @@ impl Value { fn _from_tokens>( toks: &mut Peekable, scope: &Scope, - ) -> Option { + ) -> SassResult { let kind = if let Some(tok) = toks.next() { tok.kind } else { - return None; + return Err("Unexpected EOF".into()); }; match kind { TokenKind::Number(val) => { @@ -187,7 +188,7 @@ impl Value { } BigRational::new(num.parse().unwrap(), pow(BigInt::from(10), num_dec)) }; - Some(Value::Dimension(Number::new(n), unit)) + Ok(Value::Dimension(Number::new(n), unit)) } TokenKind::Symbol(Symbol::OpenParen) => { devour_whitespace_or_comment(toks); @@ -196,14 +197,14 @@ impl Value { toks.next().unwrap().kind, TokenKind::Symbol(Symbol::CloseParen) ); - Some(Value::Paren(Box::new(val))) + Ok(Value::Paren(Box::new(val))) } TokenKind::Symbol(Symbol::BitAnd) => { - Some(Value::Ident(String::from("&"), QuoteKind::None)) + Ok(Value::Ident(String::from("&"), QuoteKind::None)) } - TokenKind::Symbol(Symbol::Hash) => Some(parse_hex(flatten_ident(toks, scope))), + TokenKind::Symbol(Symbol::Hash) => Ok(parse_hex(flatten_ident(toks, scope))), // TokenKind::Interpolation => { - // Some(Value::Ident( + // Ok(Value::Ident( // parse_interpolation(toks, scope) // .iter() // .map(|x| x.kind.to_string()) @@ -222,7 +223,7 @@ impl Value { let func = match scope.get_fn(&s) { Ok(f) => f, Err(_) => match GLOBAL_FUNCTIONS.get(&s) { - Some(f) => return f(&eat_call_args(toks, scope), scope).ok(), + Some(f) => return f(&eat_call_args(toks, scope), scope), None => { s.push('('); let mut unclosed_parens = 0; @@ -255,17 +256,17 @@ impl Value { } s.push_str(&t.kind.to_string()); } - return Some(Value::Ident(s, QuoteKind::None)); + return Ok(Value::Ident(s, QuoteKind::None)); } }, }; - Some(func.clone().args(&eat_call_args(toks, scope)).call()) + Ok(func.clone().args(&eat_call_args(toks, scope)).call()) } _ => { if let Ok(c) = crate::color::ColorName::try_from(s.as_ref()) { - Some(Value::Color(c.into_color(s))) + Ok(Value::Color(c.into_color(s))) } else { - Some(Value::Ident(s, QuoteKind::None)) + Ok(Value::Ident(s, QuoteKind::None)) } } } @@ -285,7 +286,7 @@ impl Value { } s.push_str(&tok.kind.to_string()); } - Some(Value::Ident(s, QuoteKind::Double)) + Ok(Value::Ident(s, QuoteKind::Double)) } TokenKind::Symbol(Symbol::SingleQuote) => { let mut s = String::new(); @@ -295,10 +296,10 @@ impl Value { } s.push_str(&tok.kind.to_string()); } - Some(Value::Ident(s, QuoteKind::Single)) + Ok(Value::Ident(s, QuoteKind::Single)) } TokenKind::Variable(ref v) => { - Some(scope.get_var(v).expect("expected variable").clone()) + Ok(scope.get_var(v).expect("expected variable").clone()) } TokenKind::Interpolation => { let mut s = parse_interpolation(toks, scope) @@ -323,13 +324,13 @@ impl Value { _ => break, } } - Some(Value::Ident(s, QuoteKind::None)) + Ok(Value::Ident(s, QuoteKind::None)) } - TokenKind::Keyword(Keyword::Important) => Some(Value::Important), - TokenKind::Keyword(Keyword::True) => Some(Value::True), - TokenKind::Keyword(Keyword::False) => Some(Value::False), - TokenKind::Keyword(Keyword::Null) => Some(Value::Null), - _ => None, + TokenKind::Keyword(Keyword::Important) => Ok(Value::Important), + TokenKind::Keyword(Keyword::True) => Ok(Value::True), + TokenKind::Keyword(Keyword::False) => Ok(Value::False), + TokenKind::Keyword(Keyword::Null) => Ok(Value::Null), + _ => Err("Unexpected token in value parsing".into()), } } }