improve span information in values
This commit is contained in:
parent
0c144e22ba
commit
709b95d035
@ -96,9 +96,9 @@ use crate::style::Style;
|
|||||||
pub use crate::stylesheet::StyleSheet;
|
pub use crate::stylesheet::StyleSheet;
|
||||||
pub(crate) use crate::token::Token;
|
pub(crate) use crate::token::Token;
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
devour_whitespace, eat_comment, eat_ident, eat_variable_value,
|
devour_whitespace, eat_comment, eat_ident, eat_variable_value, peek_ident_no_interpolation,
|
||||||
read_until_closing_curly_brace, read_until_closing_paren, read_until_newline, VariableDecl,
|
peek_whitespace, read_until_closing_curly_brace, read_until_closing_paren, read_until_newline,
|
||||||
peek_whitespace, peek_ident_no_interpolation
|
VariableDecl,
|
||||||
};
|
};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
|
||||||
@ -290,7 +290,8 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
|||||||
let whitespace = peek_whitespace(toks);
|
let whitespace = peek_whitespace(toks);
|
||||||
|
|
||||||
if toks.peek().ok_or(("expected \":\".", name.span))?.kind == ':' {
|
if toks.peek().ok_or(("expected \":\".", name.span))?.kind == ':' {
|
||||||
toks.take(name.node.chars().count() + whitespace + 1).for_each(drop);
|
toks.take(name.node.chars().count() + whitespace + 1)
|
||||||
|
.for_each(drop);
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
let VariableDecl {
|
let VariableDecl {
|
||||||
val,
|
val,
|
||||||
|
@ -137,16 +137,16 @@ struct IntermediateValueIterator<'a, I: Iterator<Item = Token>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I: Iterator<Item = Token>> Iterator for IntermediateValueIterator<'a, I> {
|
impl<'a, I: Iterator<Item = Token>> Iterator for IntermediateValueIterator<'a, I> {
|
||||||
type Item = SassResult<IntermediateValue>;
|
type Item = SassResult<Spanned<IntermediateValue>>;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
Value::parse_intermediate_value(self.toks, self.scope, self.super_selector)
|
Value::parse_intermediate_value(self.toks, self.scope, self.super_selector)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsWhitespace for SassResult<IntermediateValue> {
|
impl IsWhitespace for SassResult<Spanned<IntermediateValue>> {
|
||||||
fn is_whitespace(&self) -> bool {
|
fn is_whitespace(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Ok(v) => v.is_whitespace(),
|
Ok(v) => v.node.is_whitespace(),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,14 +154,20 @@ impl IsWhitespace for SassResult<IntermediateValue> {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
enum IntermediateValue {
|
enum IntermediateValue {
|
||||||
Value(Spanned<Value>),
|
Value(Value),
|
||||||
Op(Spanned<Op>),
|
Op(Op),
|
||||||
Bracketed(Spanned<Vec<Token>>),
|
Bracketed(Vec<Token>),
|
||||||
Paren(Spanned<Vec<Token>>),
|
Paren(Vec<Token>),
|
||||||
Comma,
|
Comma,
|
||||||
Whitespace,
|
Whitespace,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntermediateValue {
|
||||||
|
fn span(self, span: Span) -> Spanned<Self> {
|
||||||
|
Spanned { node: self, span }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl IsWhitespace for IntermediateValue {
|
impl IsWhitespace for IntermediateValue {
|
||||||
fn is_whitespace(&self) -> bool {
|
fn is_whitespace(&self) -> bool {
|
||||||
if self == &IntermediateValue::Whitespace {
|
if self == &IntermediateValue::Whitespace {
|
||||||
@ -337,23 +343,24 @@ fn single_value<I: Iterator<Item = Token>>(
|
|||||||
super_selector: &Selector,
|
super_selector: &Selector,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> SassResult<Spanned<Value>> {
|
) -> SassResult<Spanned<Value>> {
|
||||||
Ok(match iter.next().ok_or(("Expected expression.", span))?? {
|
let next = iter.next().ok_or(("Expected expression.", span))??;
|
||||||
IntermediateValue::Value(v) => v,
|
Ok(match next.node {
|
||||||
IntermediateValue::Op(op) => match op.node {
|
IntermediateValue::Value(v) => v.span(next.span),
|
||||||
|
IntermediateValue::Op(op) => match op {
|
||||||
Op::Minus => {
|
Op::Minus => {
|
||||||
devour_whitespace(iter);
|
devour_whitespace(iter);
|
||||||
let val = single_value(iter, scope, super_selector, span)?;
|
let val = single_value(iter, scope, super_selector, span)?;
|
||||||
Spanned {
|
Spanned {
|
||||||
node: val.node.neg(val.span)?,
|
node: val.node.neg(val.span)?,
|
||||||
span: op.span.merge(val.span),
|
span: next.span.merge(val.span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Op::Not => {
|
Op::Not => {
|
||||||
devour_whitespace(iter);
|
devour_whitespace(iter);
|
||||||
let val = single_value(iter, scope, super_selector, span)?;
|
let val = single_value(iter, scope, super_selector, span)?;
|
||||||
Spanned {
|
Spanned {
|
||||||
node: Value::UnaryOp(op.node, Box::new(val.node)),
|
node: Value::UnaryOp(Op::Not, Box::new(val.node)),
|
||||||
span: op.span.merge(val.span),
|
span: next.span.merge(val.span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Op::Plus => {
|
Op::Plus => {
|
||||||
@ -368,7 +375,7 @@ fn single_value<I: Iterator<Item = Token>>(
|
|||||||
format!("/{}", val.node.to_css_string(val.span)?),
|
format!("/{}", val.node.to_css_string(val.span)?),
|
||||||
QuoteKind::None,
|
QuoteKind::None,
|
||||||
),
|
),
|
||||||
span: op.span.merge(val.span),
|
span: next.span.merge(val.span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
_ => todo!(),
|
||||||
@ -376,7 +383,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.node, scope, super_selector)?;
|
let v = Value::from_vec(t, scope, super_selector)?;
|
||||||
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),
|
||||||
@ -384,7 +391,7 @@ fn single_value<I: Iterator<Item = Token>>(
|
|||||||
.span(v.span)
|
.span(v.span)
|
||||||
}
|
}
|
||||||
IntermediateValue::Paren(t) => {
|
IntermediateValue::Paren(t) => {
|
||||||
let val = Value::from_vec(t.node, scope, super_selector)?;
|
let val = Value::from_vec(t, scope, super_selector)?;
|
||||||
Spanned {
|
Spanned {
|
||||||
node: Value::Paren(Box::new(val.node)),
|
node: Value::Paren(Box::new(val.node)),
|
||||||
span: val.span,
|
span: val.span,
|
||||||
@ -414,17 +421,21 @@ impl Value {
|
|||||||
}
|
}
|
||||||
.peekmore();
|
.peekmore();
|
||||||
while let Some(val) = iter.next() {
|
while let Some(val) = iter.next() {
|
||||||
match val? {
|
let val = val?;
|
||||||
|
match val.node {
|
||||||
IntermediateValue::Value(v) => {
|
IntermediateValue::Value(v) => {
|
||||||
last_was_whitespace = false;
|
last_was_whitespace = false;
|
||||||
space_separated.push(v)
|
space_separated.push(v.span(val.span))
|
||||||
}
|
}
|
||||||
IntermediateValue::Op(op) => {
|
IntermediateValue::Op(op) => {
|
||||||
eat_op(
|
eat_op(
|
||||||
&mut iter,
|
&mut iter,
|
||||||
scope,
|
scope,
|
||||||
super_selector,
|
super_selector,
|
||||||
op,
|
Spanned {
|
||||||
|
node: op,
|
||||||
|
span: val.span,
|
||||||
|
},
|
||||||
&mut space_separated,
|
&mut space_separated,
|
||||||
last_was_whitespace,
|
last_was_whitespace,
|
||||||
)?;
|
)?;
|
||||||
@ -435,10 +446,14 @@ impl Value {
|
|||||||
}
|
}
|
||||||
IntermediateValue::Comma => {
|
IntermediateValue::Comma => {
|
||||||
last_was_whitespace = false;
|
last_was_whitespace = false;
|
||||||
|
|
||||||
if space_separated.len() == 1 {
|
if space_separated.len() == 1 {
|
||||||
comma_separated.push(space_separated.pop().unwrap());
|
comma_separated.push(space_separated.pop().unwrap());
|
||||||
} else {
|
} else {
|
||||||
let mut span = space_separated.get(0).unwrap().span;
|
let mut span = space_separated
|
||||||
|
.get(0)
|
||||||
|
.ok_or(("Expected expression.", val.span))?
|
||||||
|
.span;
|
||||||
comma_separated.push(
|
comma_separated.push(
|
||||||
Value::List(
|
Value::List(
|
||||||
mem::take(&mut space_separated)
|
mem::take(&mut space_separated)
|
||||||
@ -457,26 +472,32 @@ impl Value {
|
|||||||
}
|
}
|
||||||
IntermediateValue::Bracketed(t) => {
|
IntermediateValue::Bracketed(t) => {
|
||||||
last_was_whitespace = false;
|
last_was_whitespace = false;
|
||||||
if t.node.is_empty() {
|
if t.is_empty() {
|
||||||
space_separated.push(
|
space_separated.push(
|
||||||
Value::List(Vec::new(), ListSeparator::Space, Brackets::Bracketed)
|
Value::List(Vec::new(), ListSeparator::Space, Brackets::Bracketed)
|
||||||
.span(t.span),
|
.span(val.span),
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
space_separated.push(
|
space_separated.push(match Value::from_vec(t, scope, super_selector)?.node {
|
||||||
match Value::from_vec(t.node, scope, super_selector)?.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(t.span)
|
}
|
||||||
}
|
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed)
|
||||||
v => Value::List(vec![v], ListSeparator::Space, Brackets::Bracketed)
|
.span(val.span),
|
||||||
.span(t.span),
|
})
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
IntermediateValue::Paren(t) => {
|
IntermediateValue::Paren(t) => {
|
||||||
last_was_whitespace = false;
|
last_was_whitespace = false;
|
||||||
parse_paren(t, scope, super_selector, &mut space_separated)?;
|
parse_paren(
|
||||||
|
Spanned {
|
||||||
|
node: t,
|
||||||
|
span: val.span,
|
||||||
|
},
|
||||||
|
scope,
|
||||||
|
super_selector,
|
||||||
|
&mut space_separated,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -525,7 +546,7 @@ impl Value {
|
|||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
super_selector: &Selector,
|
super_selector: &Selector,
|
||||||
span_before: Span,
|
span_before: Span,
|
||||||
) -> SassResult<IntermediateValue> {
|
) -> SassResult<Spanned<IntermediateValue>> {
|
||||||
let Spanned { node: mut s, span } = eat_ident(toks, scope, super_selector, span_before)?;
|
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();
|
||||||
@ -535,10 +556,10 @@ impl Value {
|
|||||||
toks.next();
|
toks.next();
|
||||||
s.push(':');
|
s.push(':');
|
||||||
s.push_str(&eat_progid(toks, scope, super_selector)?);
|
s.push_str(&eat_progid(toks, scope, super_selector)?);
|
||||||
return Ok(IntermediateValue::Value(Spanned {
|
return Ok(Spanned {
|
||||||
node: Value::Ident(s, QuoteKind::None),
|
node: IntermediateValue::Value(Value::Ident(s, QuoteKind::None)),
|
||||||
span,
|
span,
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(Token { kind: '(', .. }) = toks.peek() {
|
if let Some(Token { kind: '(', .. }) = toks.peek() {
|
||||||
@ -550,10 +571,12 @@ impl Value {
|
|||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(_) => match GLOBAL_FUNCTIONS.get(s.replace('_', "-").as_str()) {
|
Err(_) => match GLOBAL_FUNCTIONS.get(s.replace('_', "-").as_str()) {
|
||||||
Some(f) => {
|
Some(f) => {
|
||||||
return Ok(IntermediateValue::Value(Spanned {
|
return Ok(IntermediateValue::Value(f.0(
|
||||||
node: f.0(eat_call_args(toks)?, scope, super_selector)?,
|
eat_call_args(toks)?,
|
||||||
span,
|
scope,
|
||||||
}))
|
super_selector,
|
||||||
|
)?)
|
||||||
|
.span(span))
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
match lower.as_str() {
|
match lower.as_str() {
|
||||||
@ -573,59 +596,56 @@ impl Value {
|
|||||||
&eat_call_args(toks)?.to_css_string(scope, super_selector)?,
|
&eat_call_args(toks)?.to_css_string(scope, super_selector)?,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
return Ok(IntermediateValue::Value(Spanned {
|
return Ok(
|
||||||
node: Value::Ident(s, QuoteKind::None),
|
IntermediateValue::Value(Value::Ident(s, QuoteKind::None)).span(span)
|
||||||
span,
|
);
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return Ok(IntermediateValue::Value(
|
return Ok(IntermediateValue::Value(func.eval(
|
||||||
func.eval(eat_call_args(toks)?, scope, super_selector)?
|
eat_call_args(toks)?,
|
||||||
.span(span),
|
scope,
|
||||||
));
|
super_selector,
|
||||||
|
)?)
|
||||||
|
.span(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(c) = NAMED_COLORS.get_by_name(&lower.as_str()) {
|
if let Some(c) = NAMED_COLORS.get_by_name(&lower.as_str()) {
|
||||||
return Ok(IntermediateValue::Value(Spanned {
|
return Ok(IntermediateValue::Value(Value::Color(Box::new(Color::new(
|
||||||
node: Value::Color(Box::new(Color::new(c[0], c[1], c[2], c[3], s))),
|
c[0], c[1], c[2], c[3], s,
|
||||||
span,
|
))))
|
||||||
}));
|
.span(span));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(match lower.as_str() {
|
Ok(match lower.as_str() {
|
||||||
"true" => IntermediateValue::Value(Value::True.span(span)),
|
"true" => IntermediateValue::Value(Value::True),
|
||||||
"false" => IntermediateValue::Value(Value::False.span(span)),
|
"false" => IntermediateValue::Value(Value::False),
|
||||||
"null" => IntermediateValue::Value(Value::Null.span(span)),
|
"null" => IntermediateValue::Value(Value::Null),
|
||||||
"not" => IntermediateValue::Op(Spanned {
|
"not" => IntermediateValue::Op(Op::Not),
|
||||||
node: Op::Not,
|
"and" => IntermediateValue::Op(Op::And),
|
||||||
span,
|
"or" => IntermediateValue::Op(Op::Or),
|
||||||
}),
|
_ => IntermediateValue::Value(Value::Ident(s, QuoteKind::None)),
|
||||||
"and" => IntermediateValue::Op(Spanned {
|
}
|
||||||
node: Op::And,
|
.span(span))
|
||||||
span,
|
|
||||||
}),
|
|
||||||
"or" => IntermediateValue::Op(Spanned { node: Op::Or, span }),
|
|
||||||
_ => IntermediateValue::Value(Spanned {
|
|
||||||
node: Value::Ident(s, QuoteKind::None),
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_intermediate_value<I: Iterator<Item = Token>>(
|
fn parse_intermediate_value<I: Iterator<Item = Token>>(
|
||||||
toks: &mut PeekMoreIterator<I>,
|
toks: &mut PeekMoreIterator<I>,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
super_selector: &Selector,
|
super_selector: &Selector,
|
||||||
) -> Option<SassResult<IntermediateValue>> {
|
) -> Option<SassResult<Spanned<IntermediateValue>>> {
|
||||||
if devour_whitespace(toks) {
|
|
||||||
return Some(Ok(IntermediateValue::Whitespace));
|
|
||||||
}
|
|
||||||
let (kind, span) = match toks.peek() {
|
let (kind, span) = match toks.peek() {
|
||||||
Some(v) => (v.kind, v.pos()),
|
Some(v) => (v.kind, v.pos()),
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if devour_whitespace(toks) {
|
||||||
|
return Some(Ok(Spanned {
|
||||||
|
node: IntermediateValue::Whitespace,
|
||||||
|
span,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
let next_is_hypen = |toks: &mut PeekMoreIterator<I>| {
|
let next_is_hypen = |toks: &mut PeekMoreIterator<I>| {
|
||||||
toks.peek_forward(1).is_some()
|
toks.peek_forward(1).is_some()
|
||||||
&& matches!(toks.peek().unwrap().kind, '-' | '_' | 'a'..='z' | 'A'..='Z')
|
&& matches!(toks.peek().unwrap().kind, '-' | '_' | 'a'..='z' | 'A'..='Z')
|
||||||
@ -670,26 +690,32 @@ impl Value {
|
|||||||
let n = if val.dec_len == 0 {
|
let n = if val.dec_len == 0 {
|
||||||
if val.num.len() <= 18 && val.times_ten.is_empty() {
|
if val.num.len() <= 18 && val.times_ten.is_empty() {
|
||||||
let n = Rational64::new_raw(val.num.parse::<i64>().unwrap(), 1);
|
let n = Rational64::new_raw(val.num.parse::<i64>().unwrap(), 1);
|
||||||
return Some(Ok(IntermediateValue::Value(
|
return Some(Ok(IntermediateValue::Value(Value::Dimension(
|
||||||
Value::Dimension(Number::new_machine(n), unit).span(span),
|
Number::new_machine(n),
|
||||||
)));
|
unit,
|
||||||
|
))
|
||||||
|
.span(span)));
|
||||||
}
|
}
|
||||||
BigRational::new_raw(val.num.parse::<BigInt>().unwrap(), BigInt::one())
|
BigRational::new_raw(val.num.parse::<BigInt>().unwrap(), BigInt::one())
|
||||||
} else {
|
} else {
|
||||||
if val.num.len() <= 18 && val.times_ten.is_empty() {
|
if val.num.len() <= 18 && val.times_ten.is_empty() {
|
||||||
let n =
|
let n =
|
||||||
Rational64::new(val.num.parse::<i64>().unwrap(), pow(10, val.dec_len));
|
Rational64::new(val.num.parse::<i64>().unwrap(), pow(10, val.dec_len));
|
||||||
return Some(Ok(IntermediateValue::Value(
|
return Some(Ok(IntermediateValue::Value(Value::Dimension(
|
||||||
Value::Dimension(Number::new_machine(n), unit).span(span),
|
Number::new_machine(n),
|
||||||
)));
|
unit,
|
||||||
|
))
|
||||||
|
.span(span)));
|
||||||
}
|
}
|
||||||
BigRational::new(val.num.parse().unwrap(), pow(BigInt::from(10), val.dec_len))
|
BigRational::new(val.num.parse().unwrap(), pow(BigInt::from(10), val.dec_len))
|
||||||
};
|
};
|
||||||
|
|
||||||
if val.times_ten.is_empty() {
|
if val.times_ten.is_empty() {
|
||||||
return Some(Ok(IntermediateValue::Value(
|
return Some(Ok(IntermediateValue::Value(Value::Dimension(
|
||||||
Value::Dimension(Number::new_big(n), unit).span(span),
|
Number::new_big(n),
|
||||||
)));
|
unit,
|
||||||
|
))
|
||||||
|
.span(span)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let times_ten = pow(
|
let times_ten = pow(
|
||||||
@ -712,9 +738,8 @@ impl Value {
|
|||||||
BigRational::new(BigInt::one(), times_ten)
|
BigRational::new(BigInt::one(), times_ten)
|
||||||
};
|
};
|
||||||
|
|
||||||
IntermediateValue::Value(
|
IntermediateValue::Value(Value::Dimension(Number::new_big(n * times_ten), unit))
|
||||||
Value::Dimension(Number::new_big(n * times_ten), unit).span(span),
|
.span(span)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
'(' => {
|
'(' => {
|
||||||
let mut span = toks.next().unwrap().pos();
|
let mut span = toks.next().unwrap().pos();
|
||||||
@ -727,14 +752,11 @@ impl Value {
|
|||||||
}
|
}
|
||||||
span = span.merge(last_tok.pos());
|
span = span.merge(last_tok.pos());
|
||||||
}
|
}
|
||||||
IntermediateValue::Paren(Spanned { node: inner, span })
|
IntermediateValue::Paren(inner).span(span)
|
||||||
}
|
}
|
||||||
'&' => {
|
'&' => {
|
||||||
let span = toks.next().unwrap().pos();
|
let span = toks.next().unwrap().pos();
|
||||||
IntermediateValue::Value(Spanned {
|
IntermediateValue::Value(super_selector.into_value()).span(span)
|
||||||
node: super_selector.into_value(),
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
'#' => {
|
'#' => {
|
||||||
if let Some(Token { kind: '{', pos }) = toks.peek_forward(1) {
|
if let Some(Token { kind: '{', pos }) = toks.peek_forward(1) {
|
||||||
@ -744,10 +766,11 @@ impl Value {
|
|||||||
}
|
}
|
||||||
toks.reset_view();
|
toks.reset_view();
|
||||||
toks.next();
|
toks.next();
|
||||||
IntermediateValue::Value(match parse_hex(toks, scope, super_selector, span) {
|
let hex = match parse_hex(toks, scope, super_selector, span) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
})
|
};
|
||||||
|
IntermediateValue::Value(hex.node).span(hex.span)
|
||||||
}
|
}
|
||||||
q @ '"' | q @ '\'' => {
|
q @ '"' | q @ '\'' => {
|
||||||
let span_start = toks.next().unwrap().pos();
|
let span_start = toks.next().unwrap().pos();
|
||||||
@ -756,10 +779,7 @@ impl Value {
|
|||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
};
|
};
|
||||||
IntermediateValue::Value(Spanned {
|
IntermediateValue::Value(node).span(span_start.merge(span))
|
||||||
node,
|
|
||||||
span: span_start.merge(span),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
'[' => {
|
'[' => {
|
||||||
let mut span = toks.next().unwrap().pos();
|
let mut span = toks.next().unwrap().pos();
|
||||||
@ -771,7 +791,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
span = span.merge(last_tok.pos());
|
span = span.merge(last_tok.pos());
|
||||||
}
|
}
|
||||||
IntermediateValue::Bracketed(Spanned { node: inner, span })
|
IntermediateValue::Bracketed(inner).span(span)
|
||||||
}
|
}
|
||||||
'$' => {
|
'$' => {
|
||||||
toks.next();
|
toks.next();
|
||||||
@ -779,75 +799,58 @@ impl Value {
|
|||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
};
|
};
|
||||||
IntermediateValue::Value(Spanned {
|
IntermediateValue::Value(
|
||||||
node: match scope.get_var(val.clone()) {
|
match scope.get_var(val.clone()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
}
|
}
|
||||||
.node,
|
.node,
|
||||||
span: val.span,
|
)
|
||||||
})
|
.span(val.span)
|
||||||
}
|
}
|
||||||
'+' => {
|
'+' => {
|
||||||
let span = toks.next().unwrap().pos();
|
let span = toks.next().unwrap().pos();
|
||||||
IntermediateValue::Op(Spanned {
|
IntermediateValue::Op(Op::Plus).span(span)
|
||||||
node: Op::Plus,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
'-' => {
|
'-' => {
|
||||||
let span = toks.next().unwrap().pos();
|
let span = toks.next().unwrap().pos();
|
||||||
IntermediateValue::Op(Spanned {
|
IntermediateValue::Op(Op::Minus).span(span)
|
||||||
node: Op::Minus,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
'*' => {
|
'*' => {
|
||||||
let span = toks.next().unwrap().pos();
|
let span = toks.next().unwrap().pos();
|
||||||
IntermediateValue::Op(Spanned {
|
IntermediateValue::Op(Op::Mul).span(span)
|
||||||
node: Op::Mul,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
'%' => {
|
'%' => {
|
||||||
let span = toks.next().unwrap().pos();
|
let span = toks.next().unwrap().pos();
|
||||||
IntermediateValue::Op(Spanned {
|
IntermediateValue::Op(Op::Rem).span(span)
|
||||||
node: Op::Rem,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
',' => {
|
',' => {
|
||||||
toks.next();
|
toks.next();
|
||||||
IntermediateValue::Comma
|
IntermediateValue::Comma.span(span)
|
||||||
}
|
}
|
||||||
q @ '>' | q @ '<' => {
|
q @ '>' | q @ '<' => {
|
||||||
let mut span = toks.next().unwrap().pos();
|
let mut span = toks.next().unwrap().pos();
|
||||||
IntermediateValue::Op(Spanned {
|
IntermediateValue::Op(if toks.peek().unwrap().kind == '=' {
|
||||||
node: if toks.peek().unwrap().kind == '=' {
|
span = span.merge(toks.next().unwrap().pos());
|
||||||
span = span.merge(toks.next().unwrap().pos());
|
match q {
|
||||||
match q {
|
'>' => Op::GreaterThanEqual,
|
||||||
'>' => Op::GreaterThanEqual,
|
'<' => Op::LessThanEqual,
|
||||||
'<' => Op::LessThanEqual,
|
_ => unreachable!(),
|
||||||
_ => unreachable!(),
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
match q {
|
||||||
match q {
|
'>' => Op::GreaterThan,
|
||||||
'>' => Op::GreaterThan,
|
'<' => Op::LessThan,
|
||||||
'<' => Op::LessThan,
|
_ => unreachable!(),
|
||||||
_ => unreachable!(),
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
})
|
})
|
||||||
|
.span(span)
|
||||||
}
|
}
|
||||||
'=' => {
|
'=' => {
|
||||||
let mut span = toks.next().unwrap().pos();
|
let mut span = toks.next().unwrap().pos();
|
||||||
if let Token { kind: '=', pos } = toks.next().unwrap() {
|
if let Token { kind: '=', pos } = toks.next().unwrap() {
|
||||||
span = span.merge(pos);
|
span = span.merge(pos);
|
||||||
IntermediateValue::Op(Spanned {
|
IntermediateValue::Op(Op::Equal).span(span)
|
||||||
node: Op::Equal,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
return Some(Err(("expected \"=\".", span).into()));
|
return Some(Err(("expected \"=\".", span).into()));
|
||||||
}
|
}
|
||||||
@ -856,10 +859,7 @@ impl Value {
|
|||||||
let mut span = toks.next().unwrap().pos();
|
let mut span = toks.next().unwrap().pos();
|
||||||
if toks.peek().is_some() && toks.peek().unwrap().kind == '=' {
|
if toks.peek().is_some() && toks.peek().unwrap().kind == '=' {
|
||||||
span = span.merge(toks.next().unwrap().pos());
|
span = span.merge(toks.next().unwrap().pos());
|
||||||
return Some(Ok(IntermediateValue::Op(Spanned {
|
return Some(Ok(IntermediateValue::Op(Op::NotEqual).span(span)));
|
||||||
node: Op::NotEqual,
|
|
||||||
span,
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
let v = match eat_ident(toks, scope, super_selector, span) {
|
let v = match eat_ident(toks, scope, super_selector, span) {
|
||||||
@ -868,10 +868,7 @@ impl Value {
|
|||||||
};
|
};
|
||||||
span = span.merge(v.span);
|
span = span.merge(v.span);
|
||||||
if v.node.to_ascii_lowercase().as_str() == "important" {
|
if v.node.to_ascii_lowercase().as_str() == "important" {
|
||||||
IntermediateValue::Value(Spanned {
|
IntermediateValue::Value(Value::Important).span(span)
|
||||||
node: Value::Important,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
return Some(Err(("Expected \"important\".", span).into()));
|
return Some(Err(("Expected \"important\".", span).into()));
|
||||||
}
|
}
|
||||||
@ -887,20 +884,19 @@ impl Value {
|
|||||||
Ok(..) => {}
|
Ok(..) => {}
|
||||||
Err(e) => return Some(Err(e)),
|
Err(e) => return Some(Err(e)),
|
||||||
}
|
}
|
||||||
IntermediateValue::Whitespace
|
IntermediateValue::Whitespace.span(span)
|
||||||
} else if '/' == toks.peek().unwrap().kind {
|
} else if '/' == toks.peek().unwrap().kind {
|
||||||
read_until_newline(toks);
|
read_until_newline(toks);
|
||||||
devour_whitespace(toks);
|
devour_whitespace(toks);
|
||||||
IntermediateValue::Whitespace
|
IntermediateValue::Whitespace.span(span)
|
||||||
} else {
|
} else {
|
||||||
IntermediateValue::Op(Spanned {
|
IntermediateValue::Op(Op::Div).span(span)
|
||||||
node: Op::Div,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
';' | '}' | '{' => return None,
|
';' | '}' | '{' => return None,
|
||||||
':' | '?' | ')' | '@' | '^' | ']' => return Some(Err(("expected \";\".", span).into())),
|
':' | '?' | ')' | '@' | '^' | ']' => {
|
||||||
|
return Some(Err(("expected \";\".", span).into()))
|
||||||
|
}
|
||||||
v if v as u32 >= 0x80 || v.is_control() || v == '`' => {
|
v if v as u32 >= 0x80 || v.is_control() || v == '`' => {
|
||||||
return Some(Err(("Expected expression.", span).into()))
|
return Some(Err(("Expected expression.", span).into()))
|
||||||
}
|
}
|
||||||
|
@ -82,19 +82,14 @@ error!(
|
|||||||
nothing_after_variable_in_style,
|
nothing_after_variable_in_style,
|
||||||
"a {$a", "Error: expected \":\"."
|
"a {$a", "Error: expected \":\"."
|
||||||
);
|
);
|
||||||
error!(
|
error!(toplevel_comma, "a {},", "Error: expected \"{\".");
|
||||||
toplevel_comma,
|
error!(toplevel_exclamation, "! {}", "Error: expected \"{\".");
|
||||||
"a {},", "Error: expected \"{\"."
|
error!(toplevel_backtick, "` {}", "Error: expected selector.");
|
||||||
);
|
|
||||||
error!(
|
|
||||||
toplevel_exclamation,
|
|
||||||
"! {}", "Error: expected \"{\"."
|
|
||||||
);
|
|
||||||
error!(
|
|
||||||
toplevel_backtick,
|
|
||||||
"` {}", "Error: expected selector."
|
|
||||||
);
|
|
||||||
error!(
|
error!(
|
||||||
backtick_in_value,
|
backtick_in_value,
|
||||||
"a {color:`red;}", "Error: Expected expression."
|
"a {color:`red;}", "Error: Expected expression."
|
||||||
);
|
);
|
||||||
|
error!(
|
||||||
|
comma_begins_value,
|
||||||
|
"a {color:,red;}", "Error: Expected expression."
|
||||||
|
);
|
||||||
|
@ -15,13 +15,11 @@ test!(
|
|||||||
);
|
);
|
||||||
error!(
|
error!(
|
||||||
uppercase_non_ident,
|
uppercase_non_ident,
|
||||||
"a {\n color: to-upper-case(123);\n}\n",
|
"a {\n color: to-upper-case(123);\n}\n", "Error: $string: 123 is not a string."
|
||||||
"Error: $string: 123 is not a string."
|
|
||||||
);
|
);
|
||||||
error!(
|
error!(
|
||||||
lowercase_non_ident,
|
lowercase_non_ident,
|
||||||
"a {\n color: to-lower-case(123);\n}\n",
|
"a {\n color: to-lower-case(123);\n}\n", "Error: $string: 123 is not a string."
|
||||||
"Error: $string: 123 is not a string."
|
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
uppercase_named_arg,
|
uppercase_named_arg,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user