Refactor error handling
This commit is contained in:
parent
6d0686866e
commit
f817598a9d
@ -9,15 +9,15 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
|
||||
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,
|
||||
|
20
src/error.rs
20
src/error.rs
@ -61,6 +61,26 @@ impl From<SassError> for String {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for SassError {
|
||||
#[inline]
|
||||
fn from(error: &str) -> SassError {
|
||||
SassError {
|
||||
pos: Pos::new(),
|
||||
message: error.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> 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"
|
||||
|
27
src/lib.rs
27
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<Stmt> {
|
||||
fn eat_rules(&mut self, super_selector: &Selector, scope: &mut Scope) -> SassResult<Vec<Stmt>> {
|
||||
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<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> Result<Option<Expr>, (Pos, String)> {
|
||||
) -> SassResult<Option<Expr>> {
|
||||
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<I: Iterator<Item = Token>>(
|
||||
// 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<I: Iterator<Item = Token>>(
|
||||
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<I: Iterator<Item = Token>>(
|
||||
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!"),
|
||||
};
|
||||
}
|
||||
|
15
src/mixin.rs
15
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<Vec<Stmt>, (Pos, String)> {
|
||||
pub fn call(mut self, super_selector: &Selector) -> SassResult<Vec<Stmt>> {
|
||||
self.eval(super_selector)
|
||||
}
|
||||
|
||||
fn eval(&mut self, super_selector: &Selector) -> Result<Vec<Stmt>, (Pos, String)> {
|
||||
fn eval(&mut self, super_selector: &Selector) -> SassResult<Vec<Stmt>> {
|
||||
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<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> Result<Vec<Stmt>, (Pos, String)> {
|
||||
) -> SassResult<Vec<Stmt>> {
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
let Token { kind, pos } = toks
|
||||
@ -131,7 +132,7 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
|
||||
.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<I: Iterator<Item = Token>>(
|
||||
}
|
||||
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)?;
|
||||
|
15
src/style.rs
15
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<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> Value {
|
||||
) -> SassResult<Value> {
|
||||
StyleParser::new(scope, super_selector).parse_style_value(toks)
|
||||
}
|
||||
|
||||
@ -42,7 +43,7 @@ impl Style {
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
super_property: String,
|
||||
) -> Result<Expr, (Pos, String)> {
|
||||
) -> SassResult<Expr> {
|
||||
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<I: Iterator<Item = Token>>(
|
||||
&self,
|
||||
toks: &mut Peekable<I>,
|
||||
) -> Value {
|
||||
) -> SassResult<Value> {
|
||||
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<I: Iterator<Item = Token>>(
|
||||
&self,
|
||||
toks: &mut Peekable<I>,
|
||||
super_property: String,
|
||||
) -> Result<Expr, (Pos, String)> {
|
||||
) -> SassResult<Expr> {
|
||||
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,
|
||||
|
@ -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<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
) -> Result<VariableDecl, (Pos, String)> {
|
||||
) -> SassResult<VariableDecl> {
|
||||
devour_whitespace(toks);
|
||||
let mut default = false;
|
||||
let mut raw: Vec<Token> = Vec::new();
|
||||
|
@ -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<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
) -> Option<Self> {
|
||||
) -> SassResult<Self> {
|
||||
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<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
) -> Option<Self> {
|
||||
) -> SassResult<Self> {
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user