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