Proper error message for undefined variables
This commit is contained in:
parent
f550d820b0
commit
e7de93bd38
@ -2,6 +2,7 @@ use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::{self, Display};
|
||||
|
||||
use crate::error::SassResult;
|
||||
use crate::function::Function;
|
||||
use crate::mixin::Mixin;
|
||||
use crate::value::Value;
|
||||
@ -357,10 +358,10 @@ impl Scope {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_var(&self, v: &str) -> Result<&Value, String> {
|
||||
pub fn get_var(&self, v: &str) -> SassResult<&Value> {
|
||||
match self.vars.get(&v.replace('_', "-")) {
|
||||
Some(v) => Ok(v),
|
||||
None => Err(format!("Undefined variable `{}`.", v)),
|
||||
None => Err(format!("Undefined variable: ${}.", v).into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -517,7 +517,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
scope,
|
||||
super_selector,
|
||||
String::new(),
|
||||
);
|
||||
)?;
|
||||
return Ok(Some(Style::from_tokens(toks, scope, super_selector, prop)?));
|
||||
} else {
|
||||
values.push(tok.unwrap());
|
||||
@ -529,7 +529,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
// special edge case where there was no space between the colon
|
||||
// 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 property = Style::parse_property(&mut v, scope, super_selector, String::new())?;
|
||||
let value = Style::parse_value(&mut v, scope, super_selector)?;
|
||||
return Ok(Some(Expr::Style(Style { property, value })));
|
||||
}
|
||||
@ -544,7 +544,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());
|
||||
Style::parse_property(&mut v, scope, super_selector, String::new())?;
|
||||
let value = Style::parse_value(&mut v, scope, super_selector)?;
|
||||
return Ok(Some(Expr::Style(Style { property, value })));
|
||||
}
|
||||
@ -555,7 +555,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
return Ok(Some(Expr::Selector(Selector::from_tokens(
|
||||
&mut values.into_iter().peekable(),
|
||||
scope,
|
||||
))));
|
||||
)?)));
|
||||
}
|
||||
TokenKind::Variable(_) => {
|
||||
let tok = toks
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::common::{Scope, Symbol};
|
||||
use crate::error::SassResult;
|
||||
use crate::utils::{
|
||||
devour_whitespace, devour_whitespace_or_comment, parse_interpolation, IsWhitespace,
|
||||
};
|
||||
@ -199,8 +200,8 @@ impl<'a> SelectorParser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn all_selectors(mut self, tokens: &'a mut Peekable<IntoIter<Token>>) -> Selector {
|
||||
self.tokens_to_selectors(tokens);
|
||||
fn all_selectors(mut self, tokens: &'a mut Peekable<IntoIter<Token>>) -> SassResult<Selector> {
|
||||
self.tokens_to_selectors(tokens)?;
|
||||
// remove trailing whitespace
|
||||
while let Some(x) = self.selectors.pop() {
|
||||
if x != SelectorKind::Whitespace {
|
||||
@ -208,7 +209,7 @@ impl<'a> SelectorParser<'a> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Selector(self.selectors)
|
||||
Ok(Selector(self.selectors))
|
||||
}
|
||||
|
||||
fn consume_pseudo_selector(&mut self, tokens: &'_ mut Peekable<IntoIter<Token>>) {
|
||||
@ -254,13 +255,14 @@ impl<'a> SelectorParser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn tokens_to_selectors(&mut self, tokens: &'_ mut Peekable<IntoIter<Token>>) {
|
||||
fn tokens_to_selectors(&mut self, tokens: &'_ mut Peekable<IntoIter<Token>>) -> SassResult<()> {
|
||||
while tokens.peek().is_some() {
|
||||
self.consume_selector(tokens)
|
||||
self.consume_selector(tokens)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn consume_selector(&mut self, tokens: &'_ mut Peekable<IntoIter<Token>>) {
|
||||
fn consume_selector(&mut self, tokens: &'_ mut Peekable<IntoIter<Token>>) -> SassResult<()> {
|
||||
if devour_whitespace_or_comment(tokens) {
|
||||
if let Some(Token {
|
||||
kind: TokenKind::Symbol(Symbol::Comma),
|
||||
@ -269,10 +271,10 @@ impl<'a> SelectorParser<'a> {
|
||||
{
|
||||
tokens.next();
|
||||
self.selectors.push(SelectorKind::Multiple);
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
self.selectors.push(SelectorKind::Whitespace);
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(Token { kind, .. }) = tokens.next() {
|
||||
match kind {
|
||||
@ -298,16 +300,17 @@ impl<'a> SelectorParser<'a> {
|
||||
TokenKind::Interpolation => {
|
||||
self.is_interpolated = true;
|
||||
self.tokens_to_selectors(
|
||||
&mut parse_interpolation(tokens, self.scope)
|
||||
&mut parse_interpolation(tokens, self.scope)?
|
||||
.into_iter()
|
||||
.peekable(),
|
||||
);
|
||||
)?;
|
||||
self.is_interpolated = false;
|
||||
}
|
||||
TokenKind::Attribute(attr) => self.selectors.push(SelectorKind::Attribute(attr)),
|
||||
_ => todo!("unimplemented selector"),
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -315,7 +318,7 @@ impl Selector {
|
||||
pub fn from_tokens<'a>(
|
||||
tokens: &'a mut Peekable<IntoIter<Token>>,
|
||||
scope: &'a Scope,
|
||||
) -> Selector {
|
||||
) -> SassResult<Selector> {
|
||||
SelectorParser::new(scope).all_selectors(tokens)
|
||||
}
|
||||
|
||||
|
12
src/style.rs
12
src/style.rs
@ -26,7 +26,7 @@ impl Style {
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
super_property: String,
|
||||
) -> String {
|
||||
) -> SassResult<String> {
|
||||
StyleParser::new(scope, super_selector).parse_property(toks, super_property)
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ impl<'a> StyleParser<'a> {
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
loop {
|
||||
let property = self.parse_property(toks, super_property.clone());
|
||||
let property = self.parse_property(toks, super_property.clone())?;
|
||||
if let Some(tok) = toks.peek() {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||
@ -169,14 +169,14 @@ impl<'a> StyleParser<'a> {
|
||||
&self,
|
||||
toks: &mut Peekable<I>,
|
||||
mut super_property: String,
|
||||
) -> String {
|
||||
) -> SassResult<String> {
|
||||
let mut property = String::new();
|
||||
while let Some(Token { kind, .. }) = toks.next() {
|
||||
match kind {
|
||||
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => continue,
|
||||
TokenKind::Ident(ref s) => property.push_str(s),
|
||||
TokenKind::Interpolation => property.push_str(
|
||||
&parse_interpolation(toks, self.scope)
|
||||
&parse_interpolation(toks, self.scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
@ -190,12 +190,12 @@ impl<'a> StyleParser<'a> {
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
if super_property.is_empty() {
|
||||
property
|
||||
Ok(property)
|
||||
} else {
|
||||
super_property.reserve(1 + property.len());
|
||||
super_property.push('-');
|
||||
super_property.push_str(&property);
|
||||
super_property
|
||||
Ok(super_property)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
src/utils.rs
13
src/utils.rs
@ -44,7 +44,7 @@ pub(crate) fn devour_whitespace_or_comment<I: Iterator<Item = W>, W: IsWhitespac
|
||||
pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
||||
tokens: &mut I,
|
||||
scope: &Scope,
|
||||
) -> Vec<Token> {
|
||||
) -> SassResult<Vec<Token>> {
|
||||
let mut val = Vec::new();
|
||||
while let Some(tok) = tokens.next() {
|
||||
match tok.kind {
|
||||
@ -52,20 +52,21 @@ pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
||||
todo!("invalid character in interpolation")
|
||||
}
|
||||
TokenKind::Variable(ref v) => val
|
||||
.extend(Lexer::new(&scope.get_var(v).unwrap().to_string()).collect::<Vec<Token>>()),
|
||||
TokenKind::Interpolation => val.extend(parse_interpolation(tokens, scope)),
|
||||
TokenKind::Variable(ref v) => {
|
||||
val.extend(Lexer::new(&scope.get_var(v)?.to_string()).collect::<Vec<Token>>())
|
||||
}
|
||||
TokenKind::Interpolation => val.extend(parse_interpolation(tokens, scope)?),
|
||||
_ => val.push(tok),
|
||||
}
|
||||
}
|
||||
Lexer::new(
|
||||
Ok(Lexer::new(
|
||||
&Value::from_tokens(&mut val.into_iter().peekable(), scope)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.replace("\"", "")
|
||||
.replace("'", ""),
|
||||
)
|
||||
.collect::<Vec<Token>>()
|
||||
.collect::<Vec<Token>>())
|
||||
}
|
||||
|
||||
pub(crate) struct VariableDecl {
|
||||
|
@ -53,14 +53,17 @@ fn parse_hex(s: String) -> Value {
|
||||
}
|
||||
}
|
||||
|
||||
fn flatten_ident<I: Iterator<Item = Token>>(toks: &mut Peekable<I>, scope: &Scope) -> String {
|
||||
fn flatten_ident<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
) -> SassResult<String> {
|
||||
let mut s = String::new();
|
||||
while let Some(tok) = toks.peek() {
|
||||
match tok.kind.clone() {
|
||||
TokenKind::Interpolation => {
|
||||
toks.next();
|
||||
s.push_str(
|
||||
&parse_interpolation(toks, scope)
|
||||
&parse_interpolation(toks, scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
@ -77,7 +80,7 @@ fn flatten_ident<I: Iterator<Item = Token>>(toks: &mut Peekable<I>, scope: &Scop
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
s
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
impl Value {
|
||||
@ -202,7 +205,7 @@ impl Value {
|
||||
TokenKind::Symbol(Symbol::BitAnd) => {
|
||||
Ok(Value::Ident(String::from("&"), QuoteKind::None))
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Hash) => Ok(parse_hex(flatten_ident(toks, scope))),
|
||||
TokenKind::Symbol(Symbol::Hash) => Ok(parse_hex(flatten_ident(toks, scope)?)),
|
||||
// TokenKind::Interpolation => {
|
||||
// Ok(Value::Ident(
|
||||
// parse_interpolation(toks, scope)
|
||||
@ -213,7 +216,7 @@ impl Value {
|
||||
// ))
|
||||
// }
|
||||
TokenKind::Ident(mut s) => {
|
||||
s.push_str(&flatten_ident(toks, scope));
|
||||
s.push_str(&flatten_ident(toks, scope)?);
|
||||
match toks.peek() {
|
||||
Some(Token {
|
||||
kind: TokenKind::Symbol(Symbol::OpenParen),
|
||||
@ -233,17 +236,14 @@ impl Value {
|
||||
unclosed_parens += 1;
|
||||
}
|
||||
TokenKind::Interpolation => s.push_str(
|
||||
&parse_interpolation(toks, scope)
|
||||
&parse_interpolation(toks, scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
),
|
||||
TokenKind::Variable(v) => s.push_str(
|
||||
&scope
|
||||
.get_var(v)
|
||||
.expect("expected variable to exist")
|
||||
.to_string(),
|
||||
),
|
||||
TokenKind::Variable(v) => {
|
||||
s.push_str(&scope.get_var(v)?.to_string())
|
||||
}
|
||||
TokenKind::Symbol(Symbol::CloseParen) => {
|
||||
if unclosed_parens <= 1 {
|
||||
s.push(')');
|
||||
@ -298,9 +298,9 @@ impl Value {
|
||||
}
|
||||
Ok(Value::Ident(s, QuoteKind::Single))
|
||||
}
|
||||
TokenKind::Variable(ref v) => Ok(scope.get_var(v).expect("expected variable").clone()),
|
||||
TokenKind::Variable(ref v) => Ok(scope.get_var(v)?.clone()),
|
||||
TokenKind::Interpolation => {
|
||||
let mut s = parse_interpolation(toks, scope)
|
||||
let mut s = parse_interpolation(toks, scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>();
|
||||
@ -309,7 +309,7 @@ impl Value {
|
||||
TokenKind::Interpolation => {
|
||||
toks.next();
|
||||
s.push_str(
|
||||
&parse_interpolation(toks, scope)
|
||||
&parse_interpolation(toks, scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
|
Loading…
x
Reference in New Issue
Block a user