refactor iteration over intermediate values

This commit is contained in:
ConnorSkees 2020-05-01 03:13:20 -04:00
parent 4d17c24514
commit daf7f247cf
3 changed files with 126 additions and 148 deletions

View File

@ -5,10 +5,7 @@ use codemap::Spanned;
use crate::error::SassResult; use crate::error::SassResult;
use crate::scope::Scope; use crate::scope::Scope;
use crate::selector::Selector; use crate::selector::Selector;
use crate::utils::{ use crate::utils::{devour_whitespace, devour_whitespace_or_comment, eat_ident};
devour_whitespace, devour_whitespace_or_comment, eat_ident,
read_until_semicolon_or_open_or_closing_curly_brace,
};
use crate::value::Value; use crate::value::Value;
use crate::{Expr, Token}; use crate::{Expr, Token};
@ -84,11 +81,7 @@ impl<'a> StyleParser<'a> {
scope: &Scope, scope: &Scope,
) -> SassResult<Spanned<Value>> { ) -> SassResult<Spanned<Value>> {
devour_whitespace(toks); devour_whitespace(toks);
Value::from_vec( Value::from_tokens(toks, scope, self.super_selector)
read_until_semicolon_or_open_or_closing_curly_brace(toks),
scope,
self.super_selector,
)
} }
pub(crate) fn eat_style_group<I: Iterator<Item = Token>>( pub(crate) fn eat_style_group<I: Iterator<Item = Token>>(

View File

@ -174,71 +174,6 @@ pub(crate) fn read_until_semicolon_or_closing_curly_brace<I: Iterator<Item = Tok
t t
} }
pub(crate) fn read_until_semicolon_or_open_or_closing_curly_brace<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
) -> Vec<Token> {
let mut t = Vec::new();
let mut nesting = 0;
while let Some(tok) = toks.peek() {
match tok.kind {
';' => {
break;
}
'\\' => {
t.push(toks.next().unwrap());
if toks.peek().is_some() {
t.push(toks.next().unwrap());
}
}
'"' | '\'' => {
let quote = toks.next().unwrap();
t.push(quote);
t.extend(read_until_closing_quote(toks, quote.kind));
}
'#' => {
t.push(toks.next().unwrap());
match toks.peek().unwrap().kind {
'{' => nesting += 1,
';' => break,
'}' => {
if nesting == 0 {
break;
} else {
nesting -= 1;
}
}
_ => {}
}
t.push(toks.next().unwrap());
}
'{' => break,
'}' => {
if nesting == 0 {
break;
} else {
nesting -= 1;
t.push(toks.next().unwrap());
}
}
'/' => {
let next = toks.next().unwrap();
match toks.peek().unwrap().kind {
'/' => read_until_newline(toks),
_ => t.push(next),
};
continue;
}
'(' => {
t.push(toks.next().unwrap());
t.extend(read_until_closing_paren(toks));
}
_ => t.push(toks.next().unwrap()),
}
}
devour_whitespace(toks);
t
}
pub(crate) fn read_until_closing_paren<I: Iterator<Item = Token>>( pub(crate) fn read_until_closing_paren<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
) -> Vec<Token> { ) -> Vec<Token> {

View File

@ -130,6 +130,28 @@ fn parse_hex<I: Iterator<Item = Token>>(
} }
} }
struct IntermediateValueIterator<'a, I: Iterator<Item = Token>> {
toks: &'a mut PeekMoreIterator<I>,
scope: &'a Scope,
super_selector: &'a Selector,
}
impl<'a, I: Iterator<Item = Token>> Iterator for IntermediateValueIterator<'a, I> {
type Item = SassResult<IntermediateValue>;
fn next(&mut self) -> Option<Self::Item> {
Value::parse_intermediate_value(self.toks, self.scope, self.super_selector)
}
}
impl IsWhitespace for SassResult<IntermediateValue> {
fn is_whitespace(&self) -> bool {
match self {
Ok(v) => v.is_whitespace(),
_ => false,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
enum IntermediateValue { enum IntermediateValue {
Value(Spanned<Value>), Value(Spanned<Value>),
@ -208,8 +230,8 @@ fn parse_paren(
Ok(()) Ok(())
} }
fn eat_op<I: Iterator<Item = IntermediateValue>>( fn eat_op<I: Iterator<Item = Token>>(
iter: &mut PeekMoreIterator<I>, iter: &mut PeekMoreIterator<IntermediateValueIterator<I>>,
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
op: Spanned<Op>, op: Spanned<Op>,
@ -308,13 +330,13 @@ fn eat_op<I: Iterator<Item = IntermediateValue>>(
Ok(()) Ok(())
} }
fn single_value<I: Iterator<Item = IntermediateValue>>( fn single_value<I: Iterator<Item = Token>>(
iter: &mut PeekMoreIterator<I>, iter: &mut PeekMoreIterator<IntermediateValueIterator<I>>,
scope: &Scope, scope: &Scope,
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))? { Ok(match iter.next().ok_or(("Expected expression.", span))?? {
IntermediateValue::Value(v) => v, IntermediateValue::Value(v) => v,
IntermediateValue::Op(op) => match op.node { IntermediateValue::Op(op) => match op.node {
Op::Minus => { Op::Minus => {
@ -376,21 +398,22 @@ impl Value {
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
) -> SassResult<Spanned<Self>> { ) -> SassResult<Spanned<Self>> {
let mut intermediate_values = Vec::new();
let span = match toks.peek() { let span = match toks.peek() {
Some(Token { pos, .. }) => *pos, Some(Token { pos, .. }) => *pos,
None => todo!("Expected expression."), None => todo!("Expected expression."),
}; };
devour_whitespace(toks); devour_whitespace(toks);
while toks.peek().is_some() {
intermediate_values.push(Self::parse_intermediate_value(toks, scope, super_selector)?);
}
let mut last_was_whitespace = false; let mut last_was_whitespace = false;
let mut space_separated = Vec::new(); let mut space_separated = Vec::new();
let mut comma_separated = Vec::new(); let mut comma_separated = Vec::new();
let mut iter = intermediate_values.into_iter().peekmore(); let mut iter = IntermediateValueIterator {
toks,
scope,
super_selector,
}
.peekmore();
while let Some(val) = iter.next() { while let Some(val) = iter.next() {
match val { match val? {
IntermediateValue::Value(v) => { IntermediateValue::Value(v) => {
last_was_whitespace = false; last_was_whitespace = false;
space_separated.push(v) space_separated.push(v)
@ -590,37 +613,43 @@ impl Value {
toks: &mut PeekMoreIterator<I>, toks: &mut PeekMoreIterator<I>,
scope: &Scope, scope: &Scope,
super_selector: &Selector, super_selector: &Selector,
) -> SassResult<IntermediateValue> { ) -> Option<SassResult<IntermediateValue>> {
if devour_whitespace(toks) { if devour_whitespace(toks) {
return Ok(IntermediateValue::Whitespace); 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 => panic!("unexpected eof"), None => return None,
}; };
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')
}; };
match kind { Some(Ok(match kind {
_ if kind.is_ascii_alphabetic() _ if kind.is_ascii_alphabetic()
|| kind == '_' || kind == '_'
|| kind == '\\' || kind == '\\'
|| (!kind.is_ascii() && !kind.is_control()) || (!kind.is_ascii() && !kind.is_control())
|| (kind == '-' && next_is_hypen(toks)) => || (kind == '-' && next_is_hypen(toks)) =>
{ {
Self::ident(toks, scope, super_selector) return Some(Self::ident(toks, scope, super_selector));
} }
'0'..='9' | '.' => { '0'..='9' | '.' => {
let Spanned { let Spanned {
node: val, node: val,
mut span, mut span,
} = eat_number(toks)?; } = match eat_number(toks) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
let unit = if let Some(tok) = toks.peek() { let unit = if let Some(tok) = toks.peek() {
match tok.kind { match tok.kind {
'a'..='z' | 'A'..='Z' | '_' => { 'a'..='z' | 'A'..='Z' | '_' => {
let u = eat_ident_no_interpolation(toks, true)?; let u = match eat_ident_no_interpolation(toks, true) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
span = span.merge(u.span); span = span.merge(u.span);
Unit::from(&u.node) Unit::from(&u.node)
} }
@ -641,18 +670,23 @@ impl Value {
}; };
if val.times_ten.is_empty() { if val.times_ten.is_empty() {
return Ok(IntermediateValue::Value( return Some(Ok(IntermediateValue::Value(
Value::Dimension(Number::new(n), unit).span(span), Value::Dimension(Number::new(n), unit).span(span),
)); )));
} }
let times_ten = pow( let times_ten = pow(
BigInt::from(10), BigInt::from(10),
val.times_ten match val
.times_ten
.parse::<BigInt>() .parse::<BigInt>()
.unwrap() .unwrap()
.to_usize() .to_usize()
.ok_or(("Exponent too large (expected usize).", span))?, .ok_or(("Exponent too large (expected usize).", span))
{
Ok(v) => v,
Err(e) => return Some(Err(e.into())),
},
); );
let times_ten = if val.times_ten_is_postive { let times_ten = if val.times_ten_is_postive {
@ -661,9 +695,9 @@ impl Value {
BigRational::new(BigInt::one(), times_ten) BigRational::new(BigInt::one(), times_ten)
}; };
Ok(IntermediateValue::Value( IntermediateValue::Value(
Value::Dimension(Number::new(n * times_ten), unit).span(span), Value::Dimension(Number::new(n * times_ten), unit).span(span),
)) )
} }
'(' => { '(' => {
let mut span = toks.next().unwrap().pos(); let mut span = toks.next().unwrap().pos();
@ -672,41 +706,43 @@ impl Value {
if !inner.is_empty() { if !inner.is_empty() {
let last_tok = inner.pop().unwrap(); let last_tok = inner.pop().unwrap();
if last_tok.kind != ')' { if last_tok.kind != ')' {
return Err(("expected \")\".", span).into()); return Some(Err(("expected \")\".", span).into()));
} }
span = span.merge(last_tok.pos()); span = span.merge(last_tok.pos());
} }
Ok(IntermediateValue::Paren(Spanned { node: inner, span })) IntermediateValue::Paren(Spanned { node: inner, span })
} }
'&' => { '&' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
Ok(IntermediateValue::Value(Spanned { IntermediateValue::Value(Spanned {
node: super_selector.into_value(), node: super_selector.into_value(),
span, span,
})) })
} }
'#' => { '#' => {
if let Ok(s) = eat_ident(toks, scope, super_selector) { if let Ok(s) = eat_ident(toks, scope, super_selector) {
Ok(IntermediateValue::Value(Spanned { IntermediateValue::Value(Spanned {
node: Value::Ident(s.node, QuoteKind::None), node: Value::Ident(s.node, QuoteKind::None),
span: s.span, span: s.span,
})) })
} else { } else {
Ok(IntermediateValue::Value(parse_hex( IntermediateValue::Value(match parse_hex(toks, scope, super_selector, span) {
toks, Ok(v) => v,
scope, Err(e) => return Some(Err(e)),
super_selector, })
span,
)?))
} }
} }
q @ '"' | q @ '\'' => { q @ '"' | q @ '\'' => {
let span_start = toks.next().unwrap().pos(); let span_start = toks.next().unwrap().pos();
let Spanned { node, span } = parse_quoted_string(toks, scope, q, super_selector)?; let Spanned { node, span } =
Ok(IntermediateValue::Value(Spanned { match parse_quoted_string(toks, scope, q, super_selector) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
IntermediateValue::Value(Spanned {
node, node,
span: span_start.merge(span), span: span_start.merge(span),
})) })
} }
'[' => { '[' => {
let mut span = toks.next().unwrap().pos(); let mut span = toks.next().unwrap().pos();
@ -714,55 +750,62 @@ impl Value {
if !inner.is_empty() { if !inner.is_empty() {
let last_tok = inner.pop().unwrap(); let last_tok = inner.pop().unwrap();
if last_tok.kind != ']' { if last_tok.kind != ']' {
return Err(("expected \"]\".", span).into()); return Some(Err(("expected \"]\".", span).into()));
} }
span = span.merge(last_tok.pos()); span = span.merge(last_tok.pos());
} }
Ok(IntermediateValue::Bracketed(Spanned { node: inner, span })) IntermediateValue::Bracketed(Spanned { node: inner, span })
} }
'$' => { '$' => {
toks.next(); toks.next();
let val = eat_ident_no_interpolation(toks, false)?; let val = match eat_ident_no_interpolation(toks, false) {
Ok(IntermediateValue::Value(Spanned { Ok(v) => v,
node: scope.get_var(val.clone())?.node, Err(e) => return Some(Err(e)),
};
IntermediateValue::Value(Spanned {
node: match scope.get_var(val.clone()) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
}
.node,
span: val.span, span: val.span,
})) })
} }
'+' => { '+' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
Ok(IntermediateValue::Op(Spanned { IntermediateValue::Op(Spanned {
node: Op::Plus, node: Op::Plus,
span, span,
})) })
} }
'-' => { '-' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
Ok(IntermediateValue::Op(Spanned { IntermediateValue::Op(Spanned {
node: Op::Minus, node: Op::Minus,
span, span,
})) })
} }
'*' => { '*' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
Ok(IntermediateValue::Op(Spanned { IntermediateValue::Op(Spanned {
node: Op::Mul, node: Op::Mul,
span, span,
})) })
} }
'%' => { '%' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
Ok(IntermediateValue::Op(Spanned { IntermediateValue::Op(Spanned {
node: Op::Rem, node: Op::Rem,
span, span,
})) })
} }
',' => { ',' => {
toks.next(); toks.next();
Ok(IntermediateValue::Comma) IntermediateValue::Comma
} }
q @ '>' | q @ '<' => { q @ '>' | q @ '<' => {
let mut span = toks.next().unwrap().pos(); let mut span = toks.next().unwrap().pos();
Ok(IntermediateValue::Op(Spanned { IntermediateValue::Op(Spanned {
node: 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 {
@ -778,64 +821,71 @@ impl Value {
} }
}, },
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);
Ok(IntermediateValue::Op(Spanned { IntermediateValue::Op(Spanned {
node: Op::Equal, node: Op::Equal,
span, span,
})) })
} else { } else {
Err(("expected \"=\".", span).into()) return Some(Err(("expected \"=\".", span).into()));
} }
} }
'!' => { '!' => {
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 Ok(IntermediateValue::Op(Spanned { return Some(Ok(IntermediateValue::Op(Spanned {
node: Op::NotEqual, node: Op::NotEqual,
span, span,
})); })));
} }
devour_whitespace(toks); devour_whitespace(toks);
let v = eat_ident(toks, scope, super_selector)?; let v = match eat_ident(toks, scope, super_selector) {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
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" {
Ok(IntermediateValue::Value(Spanned { IntermediateValue::Value(Spanned {
node: Value::Important, node: Value::Important,
span, span,
})) })
} else { } else {
Err(("Expected \"important\".", span).into()) return Some(Err(("Expected \"important\".", span).into()));
} }
} }
'/' => { '/' => {
let span = toks.next().unwrap().pos(); let span = toks.next().unwrap().pos();
if toks.peek().is_none() { if toks.peek().is_none() {
return Err(("Expected expression.", span).into()); return Some(Err(("Expected expression.", span).into()));
} }
if '*' == toks.peek().unwrap().kind { if '*' == toks.peek().unwrap().kind {
toks.next(); toks.next();
eat_comment(toks, &Scope::new(), &Selector::new())?; match eat_comment(toks, &Scope::new(), &Selector::new()) {
Ok(IntermediateValue::Whitespace) Ok(..) => {}
Err(e) => return Some(Err(e)),
}
IntermediateValue::Whitespace
} 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);
Ok(IntermediateValue::Whitespace) IntermediateValue::Whitespace
} else { } else {
Ok(IntermediateValue::Op(Spanned { IntermediateValue::Op(Spanned {
node: Op::Div, node: Op::Div,
span, span,
})) })
} }
} }
':' | '?' | ')' | '@' => Err(("expected \";\".", span).into()), ';' | '}' | '{' => return None,
v if v.is_control() => Err(("Expected expression.", span).into()), ':' | '?' | ')' | '@' => return Some(Err(("expected \";\".", span).into())),
v if v.is_control() => return Some(Err(("Expected expression.", span).into())),
v => todo!("unexpected token in value parsing: {:?}", v), v => todo!("unexpected token in value parsing: {:?}", v),
} }))
} }
} }