track span_before when parsing values
this allows us to remove many panics on invalid input
This commit is contained in:
parent
812e9fec9c
commit
e5cceb60ec
13
src/args.rs
13
src/args.rs
@ -109,7 +109,10 @@ impl CallArgs {
|
||||
super_selector: &Selector,
|
||||
) -> Option<SassResult<Spanned<Value>>> {
|
||||
match self.0.remove(&CallArg::Named(val.into())) {
|
||||
Some(v) => Some(Value::from_vec(v, scope, super_selector)),
|
||||
Some(v) => {
|
||||
let span_before = v[0].pos;
|
||||
Some(Value::from_vec(v, scope, super_selector, span_before))
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
@ -124,7 +127,10 @@ impl CallArgs {
|
||||
super_selector: &Selector,
|
||||
) -> Option<SassResult<Spanned<Value>>> {
|
||||
match self.0.remove(&CallArg::Positional(val)) {
|
||||
Some(v) => Some(Value::from_vec(v, scope, super_selector)),
|
||||
Some(v) => {
|
||||
let span_before = v[0].pos;
|
||||
Some(Value::from_vec(v, scope, super_selector, span_before))
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
@ -159,7 +165,8 @@ impl CallArgs {
|
||||
};
|
||||
args.sort_by(|(a1, _), (a2, _)| a1.cmp(a2));
|
||||
for arg in args {
|
||||
vals.push(Value::from_vec(arg.1, scope, super_selector)?);
|
||||
let span_before = arg.1[0].pos;
|
||||
vals.push(Value::from_vec(arg.1, scope, super_selector, span_before)?);
|
||||
}
|
||||
Ok(vals)
|
||||
}
|
||||
|
@ -119,7 +119,12 @@ pub(crate) fn parse_each<I: Iterator<Item = Token>>(
|
||||
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)?;
|
||||
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
|
||||
|
@ -133,7 +133,7 @@ pub(crate) fn parse_for<I: Iterator<Item = Token>>(
|
||||
}
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
let from_val = Value::from_vec(from_toks, scope, super_selector)?;
|
||||
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,
|
||||
@ -150,7 +150,7 @@ pub(crate) fn parse_for<I: Iterator<Item = Token>>(
|
||||
|
||||
let to_toks = read_until_open_curly_brace(toks)?;
|
||||
toks.next();
|
||||
let to_val = Value::from_vec(to_toks, scope, super_selector)?;
|
||||
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,
|
||||
|
@ -88,11 +88,7 @@ impl Function {
|
||||
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_tokens(
|
||||
&mut mem::take(v).into_iter().peekmore(),
|
||||
&scope,
|
||||
super_selector,
|
||||
)?,
|
||||
Some(v) => Value::from_vec(mem::take(v), &scope, super_selector, args.span())?,
|
||||
None => {
|
||||
return Err(
|
||||
(format!("Missing argument ${}.", &arg.name), args.span()).into()
|
||||
@ -137,7 +133,7 @@ impl Function {
|
||||
match stmt.node {
|
||||
Stmt::AtRule(AtRule::Return(toks)) => {
|
||||
return Ok(Some(
|
||||
Value::from_vec(toks, &self.scope, super_selector)?.node,
|
||||
Value::from_vec(toks, &self.scope, super_selector, stmt.span)?.node,
|
||||
));
|
||||
}
|
||||
Stmt::AtRule(AtRule::For(f)) => {
|
||||
@ -169,7 +165,8 @@ impl Function {
|
||||
}
|
||||
Stmt::AtRule(AtRule::While(w)) => {
|
||||
let scope = &mut self.scope.clone();
|
||||
let mut val = Value::from_vec(w.cond.clone(), scope, super_selector)?;
|
||||
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(),
|
||||
@ -181,7 +178,7 @@ impl Function {
|
||||
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 = Value::from_vec(w.cond.clone(), scope, super_selector, val.span)?;
|
||||
}
|
||||
}
|
||||
Stmt::AtRule(AtRule::Each(..)) => todo!("@each in @function"),
|
||||
|
@ -42,10 +42,19 @@ impl If {
|
||||
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() || toks.next().is_none() {
|
||||
if init_cond_toks.is_empty() {
|
||||
return Err(("Expected expression.", span_before).into());
|
||||
}
|
||||
let init_cond = Value::from_vec(init_cond_toks, scope, super_selector)?;
|
||||
let span_before = match toks.next() {
|
||||
Some(t) => t.pos,
|
||||
None => return Err(("Expected expression.", span_before).into()),
|
||||
};
|
||||
let init_cond = Value::from_vec(
|
||||
init_cond_toks,
|
||||
scope,
|
||||
super_selector,
|
||||
span_before,
|
||||
)?;
|
||||
devour_whitespace_or_comment(toks)?;
|
||||
let mut init_toks = read_until_closing_curly_brace(toks)?;
|
||||
if let Some(tok) = toks.next() {
|
||||
@ -78,11 +87,12 @@ impl If {
|
||||
devour_whitespace(toks);
|
||||
match tok.kind.to_ascii_lowercase() {
|
||||
'i' if toks.next().unwrap().kind.to_ascii_lowercase() == 'f' => {
|
||||
toks.next();
|
||||
let pos = toks.next().unwrap().pos;
|
||||
let cond = Value::from_vec(
|
||||
read_until_open_curly_brace(toks)?,
|
||||
scope,
|
||||
super_selector,
|
||||
pos,
|
||||
)?;
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
|
@ -29,9 +29,9 @@ impl Media {
|
||||
match tok.kind {
|
||||
'{' => break,
|
||||
'#' => {
|
||||
if toks.peek().unwrap().kind == '{' {
|
||||
if let Some(Token { kind: '{', pos }) = toks.peek().cloned() {
|
||||
toks.next();
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector)?;
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector, pos)?;
|
||||
params.push_str(&interpolation.node.to_css_string(interpolation.span)?);
|
||||
continue;
|
||||
} else {
|
||||
|
@ -79,11 +79,7 @@ impl Mixin {
|
||||
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_tokens(
|
||||
&mut std::mem::take(v).into_iter().peekmore(),
|
||||
scope,
|
||||
super_selector,
|
||||
)?,
|
||||
Some(v) => Value::from_vec(mem::take(v), scope, super_selector, args.span())?,
|
||||
None => {
|
||||
return Err(
|
||||
(format!("Missing argument ${}.", &arg.name), args.span()).into()
|
||||
|
@ -72,6 +72,7 @@ impl AtRule {
|
||||
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());
|
||||
@ -84,6 +85,7 @@ impl AtRule {
|
||||
read_until_semicolon_or_closing_curly_brace(toks)?,
|
||||
scope,
|
||||
super_selector,
|
||||
kind_span,
|
||||
)?;
|
||||
span.merge(kind_span);
|
||||
if toks.peek().unwrap().kind == ';' {
|
||||
@ -106,6 +108,7 @@ impl AtRule {
|
||||
read_until_semicolon_or_closing_curly_brace(toks)?,
|
||||
scope,
|
||||
super_selector,
|
||||
kind_span,
|
||||
)?;
|
||||
span.merge(kind_span);
|
||||
if toks.peek().unwrap().kind == ';' {
|
||||
|
@ -41,9 +41,9 @@ impl UnknownAtRule {
|
||||
match tok.kind {
|
||||
'{' => break,
|
||||
'#' => {
|
||||
if let Some(Token { kind: '{', .. }) = toks.peek() {
|
||||
if let Some(Token { kind: '{', pos }) = toks.peek().cloned() {
|
||||
toks.next();
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector)?;
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector, pos)?;
|
||||
params.push_str(&interpolation.node.to_css_string(interpolation.span)?);
|
||||
} else {
|
||||
params.push(tok.kind);
|
||||
|
@ -28,7 +28,7 @@ impl While {
|
||||
content: Option<&[Spanned<Stmt>]>,
|
||||
) -> SassResult<Vec<Spanned<Stmt>>> {
|
||||
let mut stmts = Vec::new();
|
||||
let mut val = Value::from_vec(self.cond.clone(), scope, super_selector)?;
|
||||
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(
|
||||
@ -39,7 +39,7 @@ impl While {
|
||||
content,
|
||||
&mut stmts,
|
||||
)?;
|
||||
val = Value::from_vec(self.cond.clone(), scope, super_selector)?;
|
||||
val = Value::from_vec(self.cond.clone(), scope, super_selector, self.cond[0].pos)?;
|
||||
}
|
||||
Ok(stmts)
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
String::new(),
|
||||
span_before,
|
||||
)?;
|
||||
let value = Style::parse_value(&mut v, scope, super_selector)?;
|
||||
let value = Style::parse_value(&mut v, scope, super_selector, span_before)?;
|
||||
return Ok(Some(Spanned {
|
||||
node: Expr::Style(Box::new(Style { property, value })),
|
||||
span,
|
||||
@ -259,7 +259,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
String::new(),
|
||||
tok.pos,
|
||||
)?;
|
||||
let value = Style::parse_value(&mut v, scope, super_selector)?;
|
||||
let value = Style::parse_value(&mut v, scope, super_selector, tok.pos)?;
|
||||
return Ok(Some(Spanned {
|
||||
node: Expr::Style(Box::new(Style { property, value })),
|
||||
span,
|
||||
@ -298,7 +298,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
val,
|
||||
default,
|
||||
global,
|
||||
} = eat_variable_value(toks, scope, super_selector)?;
|
||||
} = eat_variable_value(toks, scope, super_selector, name.span)?;
|
||||
if global {
|
||||
insert_global_var(&name.node, val.clone())?;
|
||||
}
|
||||
|
@ -233,10 +233,10 @@ impl Selector {
|
||||
span = span.merge(tok.pos());
|
||||
match tok.kind {
|
||||
'#' => {
|
||||
if toks.peek().is_some() && toks.peek().unwrap().kind == '{' {
|
||||
if let Some(Token { kind: '{', pos }) = toks.peek().cloned() {
|
||||
toks.next();
|
||||
string.push_str(
|
||||
&parse_interpolation(toks, scope, super_selector)?
|
||||
&parse_interpolation(toks, scope, super_selector, pos)?
|
||||
.to_css_string(span)?,
|
||||
);
|
||||
} else {
|
||||
|
12
src/style.rs
12
src/style.rs
@ -49,8 +49,9 @@ impl Style {
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
span_before: Span,
|
||||
) -> SassResult<Spanned<Value>> {
|
||||
StyleParser::new(scope, super_selector).parse_style_value(toks, scope)
|
||||
StyleParser::new(scope, super_selector).parse_style_value(toks, scope, span_before)
|
||||
}
|
||||
|
||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||
@ -80,9 +81,10 @@ impl<'a> StyleParser<'a> {
|
||||
&self,
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
scope: &Scope,
|
||||
span_before: Span,
|
||||
) -> SassResult<Spanned<Value>> {
|
||||
devour_whitespace(toks);
|
||||
Value::from_tokens(toks, scope, self.super_selector)
|
||||
Value::from_tokens(toks, scope, self.super_selector, span_before)
|
||||
}
|
||||
|
||||
pub(crate) fn eat_style_group<I: Iterator<Item = Token>>(
|
||||
@ -93,7 +95,7 @@ impl<'a> StyleParser<'a> {
|
||||
) -> SassResult<Expr> {
|
||||
let mut styles = Vec::new();
|
||||
devour_whitespace(toks);
|
||||
while let Some(tok) = toks.peek() {
|
||||
while let Some(tok) = toks.peek().cloned() {
|
||||
match tok.kind {
|
||||
'{' => {
|
||||
let span_before = toks.next().unwrap().pos;
|
||||
@ -121,7 +123,7 @@ impl<'a> StyleParser<'a> {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let value = self.parse_style_value(toks, scope)?;
|
||||
let value = self.parse_style_value(toks, scope, span_before)?;
|
||||
match toks.peek().unwrap().kind {
|
||||
'}' => {
|
||||
styles.push(Style { property, value });
|
||||
@ -160,7 +162,7 @@ impl<'a> StyleParser<'a> {
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let value = self.parse_style_value(toks, scope)?;
|
||||
let value = self.parse_style_value(toks, scope, tok.pos)?;
|
||||
let t = toks.peek().ok_or(("expected more input.", value.span))?;
|
||||
match t.kind {
|
||||
'}' => {}
|
||||
|
@ -195,14 +195,15 @@ impl<'a> StyleSheetParser<'a> {
|
||||
let whitespace = peek_whitespace(self.lexer);
|
||||
|
||||
match self.lexer.peek() {
|
||||
Some(Token { kind: ':', .. }) => {
|
||||
Some(Token { kind: ':', pos }) => {
|
||||
let pos = *pos;
|
||||
self.lexer
|
||||
.take(name.node.chars().count() + whitespace + 1)
|
||||
.for_each(drop);
|
||||
devour_whitespace(self.lexer);
|
||||
|
||||
let VariableDecl { val, default, .. } =
|
||||
eat_variable_value(self.lexer, &Scope::new(), &Selector::new())?;
|
||||
eat_variable_value(self.lexer, &Scope::new(), &Selector::new(), pos)?;
|
||||
|
||||
if !(default && global_var_exists(&name.node)) {
|
||||
insert_global_var(&name.node, val)?;
|
||||
|
@ -100,16 +100,20 @@ pub(crate) fn eat_comment<I: Iterator<Item = Token>>(
|
||||
};
|
||||
while let Some(tok) = toks.next() {
|
||||
span = span.merge(tok.pos());
|
||||
if tok.kind == '*' && toks.peek().unwrap().kind == '/' {
|
||||
toks.next();
|
||||
break;
|
||||
} else if tok.kind == '#' && toks.peek().unwrap().kind == '{' {
|
||||
toks.next();
|
||||
comment
|
||||
.push_str(&parse_interpolation(toks, scope, super_selector)?.to_css_string(span)?);
|
||||
continue;
|
||||
match (tok.kind, toks.peek()) {
|
||||
('*', Some(Token { kind: '/', .. })) => {
|
||||
toks.next();
|
||||
break;
|
||||
}
|
||||
('#', Some(Token { kind: '{', .. })) => {
|
||||
toks.next();
|
||||
comment.push_str(
|
||||
&parse_interpolation(toks, scope, super_selector, span)?.to_css_string(span)?,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
(..) => comment.push(tok.kind),
|
||||
}
|
||||
comment.push(tok.kind);
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
Ok(Spanned {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::iter::Iterator;
|
||||
|
||||
use codemap::Spanned;
|
||||
use codemap::{Span, Spanned};
|
||||
|
||||
use peekmore::PeekMoreIterator;
|
||||
|
||||
@ -15,8 +15,14 @@ pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
span_before: Span,
|
||||
) -> SassResult<Spanned<Value>> {
|
||||
let val = Value::from_vec(read_until_closing_curly_brace(toks)?, scope, super_selector)?;
|
||||
let val = Value::from_vec(
|
||||
read_until_closing_curly_brace(toks)?,
|
||||
scope,
|
||||
super_selector,
|
||||
span_before,
|
||||
)?;
|
||||
toks.next();
|
||||
Ok(Spanned {
|
||||
node: val.node.eval(val.span)?.node.unquote(),
|
||||
|
@ -97,11 +97,11 @@ fn interpolated_ident_body<I: Iterator<Item = Token>>(
|
||||
buf.push_str(&escape(toks, false)?);
|
||||
}
|
||||
'#' => {
|
||||
if let Some(Token { kind: '{', .. }) = toks.peek_forward(1) {
|
||||
if let Some(Token { kind: '{', pos }) = toks.peek_forward(1).cloned() {
|
||||
toks.next();
|
||||
toks.next();
|
||||
// TODO: if ident, interpolate literally
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector)?;
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector, pos)?;
|
||||
buf.push_str(&interpolation.node.to_css_string(interpolation.span)?);
|
||||
} else {
|
||||
toks.reset_view();
|
||||
@ -207,20 +207,21 @@ pub(crate) fn eat_ident<I: Iterator<Item = Token>>(
|
||||
// (first == '#' && scanner.peekChar(1) == $lbrace)
|
||||
} else if first == '#' {
|
||||
toks.next();
|
||||
if toks.peek().is_none() {
|
||||
let Token { kind, pos } = if let Some(tok) = toks.peek() {
|
||||
*tok
|
||||
} else {
|
||||
return Err(("Expected identifier.", pos).into());
|
||||
}
|
||||
let Token { kind, pos } = toks.peek().unwrap();
|
||||
if kind == &'{' {
|
||||
};
|
||||
if kind == '{' {
|
||||
toks.next();
|
||||
text.push_str(
|
||||
&match parse_interpolation(toks, scope, super_selector)?.node {
|
||||
&match parse_interpolation(toks, scope, super_selector, pos)?.node {
|
||||
Value::String(s, ..) => s,
|
||||
v => v.to_css_string(span)?.into(),
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return Err(("Expected identifier.", *pos).into());
|
||||
return Err(("Expected identifier.", pos).into());
|
||||
}
|
||||
} else {
|
||||
return Err(("Expected identifier.", pos).into());
|
||||
@ -293,9 +294,9 @@ pub(crate) fn parse_quoted_string<I: Iterator<Item = Token>>(
|
||||
'"' if q == '"' => break,
|
||||
'\'' if q == '\'' => break,
|
||||
'#' => {
|
||||
if toks.peek().unwrap().kind == '{' {
|
||||
if let Some(Token { kind: '{', pos }) = toks.peek().cloned() {
|
||||
toks.next();
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector)?;
|
||||
let interpolation = parse_interpolation(toks, scope, super_selector, pos)?;
|
||||
s.push_str(&match interpolation.node {
|
||||
Value::String(s, ..) => s,
|
||||
v => v.to_css_string(interpolation.span)?.into(),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::iter::Iterator;
|
||||
|
||||
use codemap::Spanned;
|
||||
use codemap::{Span, Spanned};
|
||||
|
||||
use peekmore::PeekMoreIterator;
|
||||
|
||||
@ -34,6 +34,7 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
span_before: Span,
|
||||
) -> SassResult<VariableDecl> {
|
||||
devour_whitespace(toks);
|
||||
let mut default = false;
|
||||
@ -126,6 +127,6 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
||||
}
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
let val = Value::from_vec(val_toks, scope, super_selector)?;
|
||||
let val = Value::from_vec(val_toks, scope, super_selector, span_before)?;
|
||||
Ok(VariableDecl::new(val, default, global))
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use codemap::{Span, Spanned};
|
||||
|
||||
use peekmore::PeekMoreIterator;
|
||||
|
||||
use crate::error::SassResult;
|
||||
@ -7,6 +9,7 @@ use crate::utils::{
|
||||
devour_whitespace, parse_interpolation, peek_escape, peek_until_closing_curly_brace,
|
||||
peek_whitespace,
|
||||
};
|
||||
use crate::value::Value;
|
||||
use crate::Token;
|
||||
|
||||
pub(crate) fn eat_calc_args<I: Iterator<Item = Token>>(
|
||||
@ -26,10 +29,10 @@ pub(crate) fn eat_calc_args<I: Iterator<Item = Token>>(
|
||||
}
|
||||
'#' => {
|
||||
if toks.peek().is_some() && toks.peek().unwrap().kind == '{' {
|
||||
let span = toks.next().unwrap().pos();
|
||||
buf.push_str(
|
||||
&parse_interpolation(toks, scope, super_selector)?.to_css_string(span)?,
|
||||
);
|
||||
let span_before = toks.next().unwrap().pos();
|
||||
let interpolation =
|
||||
parse_interpolation(toks, scope, super_selector, span_before)?;
|
||||
buf.push_str(&interpolation.node.to_css_string(interpolation.span)?);
|
||||
} else {
|
||||
buf.push('#');
|
||||
}
|
||||
@ -106,11 +109,11 @@ pub(crate) fn try_eat_url<I: Iterator<Item = Token>>(
|
||||
} else if kind == '\\' {
|
||||
buf.push_str(&peek_escape(toks)?);
|
||||
} else if kind == '#' {
|
||||
let next = toks.peek();
|
||||
if next.is_some() && next.unwrap().kind == '{' {
|
||||
if let Some(Token { kind: '{', pos }) = toks.peek() {
|
||||
let pos = *pos;
|
||||
toks.move_forward(1);
|
||||
peek_counter += 1;
|
||||
let (interpolation, count) = peek_interpolation(toks, scope, super_selector)?;
|
||||
let (interpolation, count) = peek_interpolation(toks, scope, super_selector, pos)?;
|
||||
peek_counter += count;
|
||||
buf.push_str(&match interpolation.node {
|
||||
Value::String(s, ..) => s,
|
||||
@ -144,18 +147,16 @@ pub(crate) fn try_eat_url<I: Iterator<Item = Token>>(
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
use crate::value::Value;
|
||||
use codemap::Spanned;
|
||||
|
||||
fn peek_interpolation<I: Iterator<Item = Token>>(
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
span_before: Span,
|
||||
) -> SassResult<(Spanned<Value>, usize)> {
|
||||
let vec = peek_until_closing_curly_brace(toks);
|
||||
let peek_counter = vec.len();
|
||||
toks.move_forward(1);
|
||||
let val = Value::from_vec(vec, scope, super_selector)?;
|
||||
let val = Value::from_vec(vec, scope, super_selector, span_before)?;
|
||||
Ok((
|
||||
Spanned {
|
||||
node: val.node.eval(val.span)?.node.unquote(),
|
||||
|
@ -189,7 +189,12 @@ fn parse_paren(
|
||||
let paren_toks = &mut t.node.into_iter().peekmore();
|
||||
|
||||
let mut map = SassMap::new();
|
||||
let key = Value::from_vec(read_until_char(paren_toks, ':')?, scope, super_selector)?;
|
||||
let key = Value::from_vec(
|
||||
read_until_char(paren_toks, ':')?,
|
||||
scope,
|
||||
super_selector,
|
||||
t.span,
|
||||
)?;
|
||||
|
||||
if paren_toks.peek().is_none() {
|
||||
return Ok(Spanned {
|
||||
@ -198,7 +203,12 @@ fn parse_paren(
|
||||
});
|
||||
}
|
||||
|
||||
let val = Value::from_vec(read_until_char(paren_toks, ',')?, scope, super_selector)?;
|
||||
let val = Value::from_vec(
|
||||
read_until_char(paren_toks, ',')?,
|
||||
scope,
|
||||
super_selector,
|
||||
key.span,
|
||||
)?;
|
||||
|
||||
map.insert(key.node, val.node);
|
||||
|
||||
@ -212,9 +222,19 @@ fn parse_paren(
|
||||
let mut span = key.span;
|
||||
|
||||
loop {
|
||||
let key = Value::from_vec(read_until_char(paren_toks, ':')?, scope, super_selector)?;
|
||||
let key = Value::from_vec(
|
||||
read_until_char(paren_toks, ':')?,
|
||||
scope,
|
||||
super_selector,
|
||||
span,
|
||||
)?;
|
||||
devour_whitespace(paren_toks);
|
||||
let val = Value::from_vec(read_until_char(paren_toks, ',')?, scope, super_selector)?;
|
||||
let val = Value::from_vec(
|
||||
read_until_char(paren_toks, ',')?,
|
||||
scope,
|
||||
super_selector,
|
||||
key.span,
|
||||
)?;
|
||||
span = span.merge(val.span);
|
||||
devour_whitespace(paren_toks);
|
||||
if map.insert(key.node, val.node) {
|
||||
@ -387,7 +407,7 @@ fn single_value<I: Iterator<Item = Token>>(
|
||||
IntermediateValue::Whitespace => unreachable!(),
|
||||
IntermediateValue::Comma => return Err(("Expected expression.", span).into()),
|
||||
IntermediateValue::Bracketed(t) => {
|
||||
let v = Value::from_vec(t, scope, super_selector)?;
|
||||
let v = Value::from_vec(t, scope, super_selector, span)?;
|
||||
match v.node {
|
||||
Value::List(v, sep, Brackets::None) => Value::List(v, sep, Brackets::Bracketed),
|
||||
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed),
|
||||
@ -416,10 +436,11 @@ impl Value {
|
||||
toks: &mut PeekMoreIterator<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
span_before: Span,
|
||||
) -> SassResult<Spanned<Self>> {
|
||||
let span = match toks.peek() {
|
||||
Some(Token { pos, .. }) => *pos,
|
||||
None => todo!("Expected expression."),
|
||||
None => return Err(("Expected expression.", span_before).into()),
|
||||
};
|
||||
devour_whitespace(toks);
|
||||
let mut last_was_whitespace = false;
|
||||
@ -490,13 +511,15 @@ impl Value {
|
||||
);
|
||||
continue;
|
||||
}
|
||||
space_separated.push(match Value::from_vec(t, scope, super_selector)?.node {
|
||||
Value::List(v, sep, Brackets::None) => {
|
||||
Value::List(v, sep, Brackets::Bracketed).span(val.span)
|
||||
}
|
||||
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed)
|
||||
.span(val.span),
|
||||
})
|
||||
space_separated.push(
|
||||
match Value::from_vec(t, scope, super_selector, val.span)?.node {
|
||||
Value::List(v, sep, Brackets::None) => {
|
||||
Value::List(v, sep, Brackets::Bracketed).span(val.span)
|
||||
}
|
||||
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed)
|
||||
.span(val.span),
|
||||
},
|
||||
)
|
||||
}
|
||||
IntermediateValue::Paren(t) => {
|
||||
last_was_whitespace = false;
|
||||
@ -547,8 +570,17 @@ impl Value {
|
||||
toks: Vec<Token>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
span_before: Span,
|
||||
) -> SassResult<Spanned<Value>> {
|
||||
Self::from_tokens(&mut toks.into_iter().peekmore(), scope, super_selector)
|
||||
if toks.is_empty() {
|
||||
return Err(("Expected expression.", span_before).into());
|
||||
}
|
||||
Self::from_tokens(
|
||||
&mut toks.into_iter().peekmore(),
|
||||
scope,
|
||||
super_selector,
|
||||
span_before,
|
||||
)
|
||||
}
|
||||
|
||||
fn ident<I: Iterator<Item = Token>>(
|
||||
|
@ -208,3 +208,19 @@ error!(
|
||||
at_else_alone,
|
||||
"@else {}", "Error: This at-rule is not allowed here."
|
||||
);
|
||||
error!(
|
||||
no_expression_for_variable,
|
||||
"a {$color: {ed;}", "Error: Expected expression."
|
||||
);
|
||||
error!(
|
||||
empty_style_value_no_semicolon,
|
||||
"a {color:}", "Error: Expected expression."
|
||||
);
|
||||
error!(
|
||||
empty_style_value_semicolon,
|
||||
"a {color:;}", "Error: Expected expression."
|
||||
);
|
||||
error!(
|
||||
ident_colon_closing_brace,
|
||||
"r:}", "Error: Expected expression."
|
||||
);
|
||||
|
@ -87,7 +87,7 @@ test!(
|
||||
test!(
|
||||
non_ascii_numeric_interpreted_as_unit,
|
||||
"a {\n color: 2߄;\n}\n",
|
||||
"@charset \"UTF-8\";\na {\n color: 2߄;\n}"
|
||||
"@charset \"UTF-8\";\na {\n color: 2߄;\n}\n"
|
||||
);
|
||||
error!(
|
||||
display_single_mul,
|
||||
|
Loading…
x
Reference in New Issue
Block a user