add more span information

this resolves a lot of panics that occurred when there was no more input
This commit is contained in:
ConnorSkees 2020-05-17 00:35:07 -04:00
parent 6e7f1cc319
commit b58ed29fd0
13 changed files with 93 additions and 52 deletions

View File

@ -221,7 +221,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
devour_whitespace(toks); devour_whitespace(toks);
while let Some(Token { kind, pos }) = toks.next() { while let Some(Token { kind, pos }) = toks.next() {
let name = match kind { let name = match kind {
'$' => eat_ident(toks, scope, super_selector)?, '$' => eat_ident(toks, scope, super_selector, pos)?,
')' => { ')' => {
close_paren_span = pos; close_paren_span = pos;
break; break;

View File

@ -98,7 +98,7 @@ pub(crate) fn parse_each<I: Iterator<Item = Token>>(
let next = toks.next().ok_or(("expected \"$\".", span))?; let next = toks.next().ok_or(("expected \"$\".", span))?;
span = next.pos(); span = next.pos();
match next.kind { match next.kind {
'$' => vars.push(eat_ident(toks, scope, super_selector)?), '$' => vars.push(eat_ident(toks, scope, super_selector, next.pos)?),
_ => return Err(("expected \"$\".", next.pos()).into()), _ => return Err(("expected \"$\".", next.pos()).into()),
} }
devour_whitespace(toks); devour_whitespace(toks);
@ -114,10 +114,7 @@ pub(crate) fn parse_each<I: Iterator<Item = Token>>(
break; break;
} }
} }
if toks.peek().is_none() { let i = eat_ident(toks, scope, super_selector, span)?;
todo!()
}
let i = eat_ident(toks, scope, super_selector)?;
if i.node.to_ascii_lowercase() != "in" { if i.node.to_ascii_lowercase() != "in" {
return Err(("Expected \"in\".", i.span).into()); return Err(("Expected \"in\".", i.span).into());
} }

View File

@ -89,14 +89,17 @@ pub(crate) fn parse_for<I: Iterator<Item = Token>>(
span: Span, span: Span,
) -> SassResult<AtRule> { ) -> SassResult<AtRule> {
devour_whitespace(toks); devour_whitespace(toks);
let var = match toks.next().ok_or(("expected \"$\".", span))?.kind { let next = toks.next().ok_or(("expected \"$\".", span))?;
'$' => eat_ident(toks, scope, super_selector)?, let var = match next.kind {
'$' => eat_ident(toks, scope, super_selector, next.pos)?,
_ => return Err(("expected \"$\".", span).into()), _ => return Err(("expected \"$\".", span).into()),
}; };
devour_whitespace(toks); devour_whitespace(toks);
if toks.peek().is_none() if toks.peek().is_none() {
|| eat_ident(toks, scope, super_selector)?.to_ascii_lowercase() != "from" return Err(("Expected \"from\".", var.span).into());
{ }
let span_before = toks.peek().unwrap().pos;
if eat_ident(toks, scope, super_selector, span_before)?.to_ascii_lowercase() != "from" {
return Err(("Expected \"from\".", var.span).into()); return Err(("Expected \"from\".", var.span).into());
} }
devour_whitespace(toks); devour_whitespace(toks);

View File

@ -44,8 +44,9 @@ impl Function {
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
scope: Scope, scope: Scope,
super_selector: &Selector, super_selector: &Selector,
span_before: Span,
) -> SassResult<(String, Function)> { ) -> SassResult<(String, Function)> {
let Spanned { node: name, span } = eat_ident(toks, &scope, super_selector)?; let Spanned { node: name, span } = eat_ident(toks, &scope, super_selector, span_before)?;
devour_whitespace(toks); devour_whitespace(toks);
let args = match toks.next() { let args = match toks.next() {
Some(Token { kind: '(', .. }) => eat_func_args(toks, &scope, super_selector)?, Some(Token { kind: '(', .. }) => eat_func_args(toks, &scope, super_selector)?,

View File

@ -57,11 +57,13 @@ impl If {
if first_char != 'e' && first_char != 'E' { if first_char != 'e' && first_char != 'E' {
break; break;
} }
toks.next();
} else { } else {
break; break;
} }
if eat_ident(toks, &Scope::new(), &Selector::new())?.to_ascii_lowercase() == "else" let span_before = toks.next().unwrap().pos;
if eat_ident(toks, &Scope::new(), &Selector::new(), span_before)?
.to_ascii_lowercase()
== "else"
{ {
devour_whitespace(toks); devour_whitespace(toks);
if let Some(tok) = toks.next() { if let Some(tok) = toks.next() {

View File

@ -1,6 +1,6 @@
use std::vec::IntoIter; use std::vec::IntoIter;
use codemap::Spanned; use codemap::{Span, Spanned};
use peekmore::{PeekMore, PeekMoreIterator}; use peekmore::{PeekMore, PeekMoreIterator};
@ -34,9 +34,10 @@ impl Mixin {
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
span_before: Span,
) -> SassResult<Spanned<(String, Mixin)>> { ) -> SassResult<Spanned<(String, Mixin)>> {
devour_whitespace(toks); devour_whitespace(toks);
let Spanned { node: name, span } = eat_ident(toks, scope, super_selector)?; let Spanned { node: name, span } = eat_ident(toks, scope, super_selector, span_before)?;
devour_whitespace(toks); devour_whitespace(toks);
let args = match toks.next() { let args = match toks.next() {
Some(Token { kind: '(', .. }) => eat_func_args(toks, scope, super_selector)?, Some(Token { kind: '(', .. }) => eat_func_args(toks, scope, super_selector)?,
@ -185,9 +186,10 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
content: Option<&[Spanned<Stmt>]>, content: Option<&[Spanned<Stmt>]>,
span_before: Span,
) -> SassResult<Vec<Spanned<Stmt>>> { ) -> SassResult<Vec<Spanned<Stmt>>> {
devour_whitespace_or_comment(toks)?; devour_whitespace_or_comment(toks)?;
let name = eat_ident(toks, scope, super_selector)?; let name = eat_ident(toks, scope, super_selector, span_before)?;
devour_whitespace_or_comment(toks)?; devour_whitespace_or_comment(toks)?;

View File

@ -121,14 +121,15 @@ impl AtRule {
let Spanned { let Spanned {
node: (name, mixin), node: (name, mixin),
span, span,
} = Mixin::decl_from_tokens(toks, scope, super_selector)?; } = Mixin::decl_from_tokens(toks, scope, super_selector, kind_span)?;
Spanned { Spanned {
node: AtRule::Mixin(name, Box::new(mixin)), node: AtRule::Mixin(name, Box::new(mixin)),
span, span,
} }
} }
AtRuleKind::Function => { AtRuleKind::Function => {
let (name, func) = Function::decl_from_tokens(toks, scope.clone(), super_selector)?; let (name, func) =
Function::decl_from_tokens(toks, scope.clone(), super_selector, kind_span)?;
Spanned { Spanned {
node: AtRule::Function(name, Box::new(func)), node: AtRule::Function(name, Box::new(func)),
span: kind_span, span: kind_span,
@ -236,7 +237,13 @@ impl AtRule {
span: kind_span, span: kind_span,
}, },
AtRuleKind::Include => Spanned { AtRuleKind::Include => Spanned {
node: AtRule::Include(eat_include(toks, scope, super_selector, content)?), node: AtRule::Include(eat_include(
toks,
scope,
super_selector,
content,
kind_span,
)?),
span: kind_span, span: kind_span,
}, },
AtRuleKind::Import => todo!("@import not yet implemented"), AtRuleKind::Import => todo!("@import not yet implemented"),

View File

@ -197,6 +197,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
scope, scope,
super_selector, super_selector,
String::new(), String::new(),
tok.unwrap().pos,
)?; )?;
return Ok(Some(Spanned { return Ok(Some(Spanned {
node: Style::from_tokens(toks, scope, super_selector, prop)?, node: Style::from_tokens(toks, scope, super_selector, prop)?,
@ -207,7 +208,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
} }
} }
';' => { ';' => {
toks.next(); let span_before = toks.next().unwrap().pos;
devour_whitespace(toks); devour_whitespace(toks);
// special edge case where there was no space between the colon // special edge case where there was no space between the colon
// in a style, e.g. `color:red`. todo: refactor // in a style, e.g. `color:red`. todo: refactor
@ -223,7 +224,13 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
span, span,
})); }));
} }
let property = Style::parse_property(&mut v, scope, super_selector, String::new())?; let property = Style::parse_property(
&mut v,
scope,
super_selector,
String::new(),
span_before,
)?;
let value = Style::parse_value(&mut v, scope, super_selector)?; let value = Style::parse_value(&mut v, scope, super_selector)?;
return Ok(Some(Spanned { return Ok(Some(Spanned {
node: Expr::Style(Box::new(Style { property, value })), node: Expr::Style(Box::new(Style { property, value })),
@ -244,8 +251,13 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
// and no semicolon following the style // and no semicolon following the style
// in a style `color:red`. todo: refactor // in a style `color:red`. todo: refactor
let mut v = values.into_iter().peekmore(); let mut v = values.into_iter().peekmore();
let property = let property = Style::parse_property(
Style::parse_property(&mut v, scope, super_selector, String::new())?; &mut v,
scope,
super_selector,
String::new(),
tok.pos,
)?;
let value = Style::parse_value(&mut v, scope, super_selector)?; let value = Style::parse_value(&mut v, scope, super_selector)?;
return Ok(Some(Spanned { return Ok(Some(Spanned {
node: Expr::Style(Box::new(Style { property, value })), node: Expr::Style(Box::new(Style { property, value })),
@ -327,10 +339,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
} }
'@' => { '@' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
if toks.peek().is_none() { let Spanned { node: ident, span } = eat_ident(toks, scope, super_selector, span)?;
return Err(("Expected identifier.", span).into());
}
let Spanned { node: ident, span } = eat_ident(toks, scope, super_selector)?;
devour_whitespace(toks); devour_whitespace(toks);
let rule = AtRule::from_tokens( let rule = AtRule::from_tokens(
&AtRuleKind::from(ident.as_str()), &AtRuleKind::from(ident.as_str()),

View File

@ -35,15 +35,16 @@ fn attribute_name<I: Iterator<Item = Token>>(
return Err(("expected \"|\".", pos).into()); return Err(("expected \"|\".", pos).into());
} }
toks.next(); let span_before = toks.next().unwrap().pos();
let ident = eat_ident(toks, scope, super_selector)?.node; let ident = eat_ident(toks, scope, super_selector, span_before)?.node;
return Ok(QualifiedName { return Ok(QualifiedName {
ident, ident,
namespace: Some('*'.to_string()), namespace: Some('*'.to_string()),
}); });
} }
let name_or_namespace = eat_ident(toks, scope, super_selector)?; let span_before = next.pos;
let name_or_namespace = eat_ident(toks, scope, super_selector, span_before)?;
match toks.peek() { match toks.peek() {
Some(v) if v.kind != '|' => { Some(v) if v.kind != '|' => {
return Ok(QualifiedName { return Ok(QualifiedName {
@ -67,8 +68,8 @@ fn attribute_name<I: Iterator<Item = Token>>(
} }
None => return Err(("expected more input.", name_or_namespace.span).into()), None => return Err(("expected more input.", name_or_namespace.span).into()),
} }
toks.next(); let span_before = toks.next().unwrap().pos();
let ident = eat_ident(toks, scope, super_selector)?.node; let ident = eat_ident(toks, scope, super_selector, span_before)?.node;
Ok(QualifiedName { Ok(QualifiedName {
ident, ident,
namespace: Some(name_or_namespace.node), namespace: Some(name_or_namespace.node),
@ -118,7 +119,7 @@ impl Attribute {
devour_whitespace(toks); devour_whitespace(toks);
let peek = toks.peek().ok_or(("expected more input.", start))?; let peek = toks.peek().ok_or(("expected more input.", start))?;
let span_before = peek.pos;
let value = match peek.kind { let value = match peek.kind {
q @ '\'' | q @ '"' => { q @ '\'' | q @ '"' => {
toks.next(); toks.next();
@ -127,7 +128,7 @@ impl Attribute {
_ => unreachable!(), _ => unreachable!(),
} }
} }
_ => eat_ident(toks, scope, super_selector)?.node, _ => eat_ident(toks, scope, super_selector, span_before)?.node,
}; };
devour_whitespace(toks); devour_whitespace(toks);

View File

@ -1,6 +1,6 @@
use peekmore::PeekMoreIterator; use peekmore::PeekMoreIterator;
use codemap::Spanned; use codemap::{Span, Spanned};
use crate::error::SassResult; use crate::error::SassResult;
use crate::scope::Scope; use crate::scope::Scope;
@ -22,8 +22,9 @@ impl Style {
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
super_property: String, super_property: String,
span_before: Span,
) -> SassResult<String> { ) -> SassResult<String> {
StyleParser::new(scope, super_selector).parse_property(toks, super_property) StyleParser::new(scope, super_selector).parse_property(toks, super_property, span_before)
} }
pub fn to_string(&self) -> SassResult<String> { pub fn to_string(&self) -> SassResult<String> {
@ -95,10 +96,11 @@ impl<'a> StyleParser<'a> {
while let Some(tok) = toks.peek() { while let Some(tok) = toks.peek() {
match tok.kind { match tok.kind {
'{' => { '{' => {
toks.next(); let span_before = toks.next().unwrap().pos;
devour_whitespace(toks); devour_whitespace(toks);
loop { loop {
let property = self.parse_property(toks, super_property.clone())?; let property =
self.parse_property(toks, super_property.clone(), span_before)?;
if let Some(tok) = toks.peek() { if let Some(tok) = toks.peek() {
if tok.kind == '{' { if tok.kind == '{' {
match self.eat_style_group(toks, property, scope)? { match self.eat_style_group(toks, property, scope)? {
@ -194,9 +196,10 @@ impl<'a> StyleParser<'a> {
&self, &self,
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
mut super_property: String, mut super_property: String,
span_before: Span,
) -> SassResult<String> { ) -> SassResult<String> {
devour_whitespace(toks); devour_whitespace(toks);
let property = eat_ident(toks, self.scope, self.super_selector)?.node; let property = eat_ident(toks, self.scope, self.super_selector, span_before)?.node;
devour_whitespace_or_comment(toks)?; devour_whitespace_or_comment(toks)?;
if toks.peek().is_some() && toks.peek().unwrap().kind == ':' { if toks.peek().is_some() && toks.peek().unwrap().kind == ':' {
toks.next(); toks.next();

View File

@ -164,8 +164,13 @@ impl<'a> StyleSheetParser<'a> {
continue; continue;
} }
'$' => { '$' => {
self.lexer.next(); let span_before = self.lexer.next().unwrap().pos();
let name = eat_ident(&mut self.lexer, &Scope::new(), &Selector::new())?; let name = eat_ident(
&mut self.lexer,
&Scope::new(),
&Selector::new(),
span_before
)?;
devour_whitespace(&mut self.lexer); devour_whitespace(&mut self.lexer);
let Token { kind, pos } = self let Token { kind, pos } = self
.lexer .lexer
@ -194,8 +199,13 @@ impl<'a> StyleSheetParser<'a> {
} }
} }
'@' => { '@' => {
self.lexer.next(); let span_before = self.lexer.next().unwrap().pos();
let Spanned { node: at_rule_kind, span } = eat_ident(&mut self.lexer, &Scope::new(), &Selector::new())?; let Spanned { node: at_rule_kind, span } = eat_ident(
&mut self.lexer,
&Scope::new(),
&Selector::new(),
span_before
)?;
if at_rule_kind.is_empty() { if at_rule_kind.is_empty() {
return Err(("Expected identifier.", span).into()); return Err(("Expected identifier.", span).into());
} }
@ -205,6 +215,7 @@ impl<'a> StyleSheetParser<'a> {
&Scope::new(), &Scope::new(),
&Selector::new(), &Selector::new(),
None, None,
span
)?), )?),
AtRuleKind::Import => { AtRuleKind::Import => {
devour_whitespace(&mut self.lexer); devour_whitespace(&mut self.lexer);

View File

@ -169,9 +169,12 @@ pub(crate) fn eat_ident<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<String>> { ) -> SassResult<Spanned<String>> {
// TODO: take span as param because we use unwrap here let mut span = toks
let mut span = toks.peek().unwrap().pos(); .peek()
.ok_or(("Expected identifier.", span_before))?
.pos();
let mut text = String::new(); let mut text = String::new();
if toks.peek().unwrap().kind == '-' { if toks.peek().unwrap().kind == '-' {
toks.next(); toks.next();

View File

@ -52,7 +52,7 @@ fn parse_hex<I: Iterator<Item = Token>>(
s.push(tok.kind); s.push(tok.kind);
} }
} else { } else {
let i = eat_ident(toks, scope, super_selector)?; let i = eat_ident(toks, scope, super_selector, span)?;
if i.node.chars().all(|c| c.is_ascii_hexdigit()) { if i.node.chars().all(|c| c.is_ascii_hexdigit()) {
s = i.node; s = i.node;
span = span.merge(i.span); span = span.merge(i.span);
@ -524,8 +524,9 @@ impl Value {
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
span_before: Span,
) -> SassResult<IntermediateValue> { ) -> SassResult<IntermediateValue> {
let Spanned { node: mut s, span } = eat_ident(toks, scope, super_selector)?; let Spanned { node: mut s, span } = eat_ident(toks, scope, super_selector, span_before)?;
let lower = s.to_ascii_lowercase(); let lower = s.to_ascii_lowercase();
@ -636,7 +637,7 @@ impl Value {
|| (!kind.is_ascii() && !kind.is_control()) || (!kind.is_ascii() && !kind.is_control())
|| (kind == '-' && next_is_hypen(toks)) => || (kind == '-' && next_is_hypen(toks)) =>
{ {
return Some(Self::ident(toks, scope, super_selector)); return Some(Self::ident(toks, scope, super_selector, span));
} }
'0'..='9' | '.' => { '0'..='9' | '.' => {
let Spanned { let Spanned {
@ -723,9 +724,10 @@ impl Value {
}) })
} }
'#' => { '#' => {
if let Some(Token { kind: '{', .. }) = toks.peek_forward(1) { if let Some(Token { kind: '{', pos }) = toks.peek_forward(1) {
let span_before = *pos;
toks.reset_view(); toks.reset_view();
return Some(Self::ident(toks, scope, super_selector)); return Some(Self::ident(toks, scope, super_selector, span_before));
} }
toks.reset_view(); toks.reset_view();
toks.next(); toks.next();
@ -847,7 +849,7 @@ impl Value {
}))); })));
} }
devour_whitespace(toks); devour_whitespace(toks);
let v = match eat_ident(toks, scope, super_selector) { let v = match eat_ident(toks, scope, super_selector, span) {
Ok(v) => v, Ok(v) => v,
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
}; };