diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 33f6907..9ae2305 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -39,6 +39,7 @@ mod keyframes; mod media; mod mixin; mod style; +mod throw_away; mod value; mod variable; @@ -508,23 +509,6 @@ impl<'a> Parser<'a> { } impl<'a> Parser<'a> { - fn throw_away_until_closing_curly_brace(&mut self) { - let mut scope = 0; - while let Some(tok) = self.toks.next() { - match tok.kind { - '}' => { - if scope == 0 { - break; - } else { - scope -= 1; - } - } - '{' => scope += 1, - _ => continue, - } - } - } - fn parse_if(&mut self) -> SassResult> { self.whitespace_or_comment(); @@ -536,7 +520,7 @@ impl<'a> Parser<'a> { // consume the open curly brace let span_before = match self.toks.next() { Some(Token { kind: '{', pos }) => pos, - Some(..) | None => return Err(("expected \"}\".", self.span_before).into()), + Some(..) | None => return Err(("expected \"{\".", self.span_before).into()), }; if self.toks.peek().is_none() { @@ -563,7 +547,7 @@ impl<'a> Parser<'a> { } .parse_stmt()?; } else { - self.throw_away_until_closing_curly_brace(); + self.throw_away_until_closing_curly_brace()?; } self.whitespace_or_comment(); @@ -590,10 +574,20 @@ impl<'a> Parser<'a> { { self.toks.next(); self.toks.next(); - let cond = read_until_open_curly_brace(self.toks)?; - // todo: ensure there is a `{` - self.toks.next(); - if !found_true && self.parse_value_from_vec(cond)?.is_true() { + let cond = if !found_true { + let v = self.parse_value()?.node.is_true(); + match self.toks.next() { + Some(Token { kind: '{', .. }) => {} + Some(..) | None => { + return Err(("expected \"{\".", self.span_before).into()) + } + } + v + } else { + self.throw_away_until_open_curly_brace()?; + false + }; + if cond { found_true = true; body = Parser { toks: self.toks, @@ -610,17 +604,15 @@ impl<'a> Parser<'a> { extender: self.extender, } .parse_stmt()?; - // todo: ensure there is a `{` - self.toks.next(); } else { - self.throw_away_until_closing_curly_brace(); + self.throw_away_until_closing_curly_brace()?; } self.whitespace(); } '{' => { self.toks.next(); if found_true { - self.throw_away_until_closing_curly_brace(); + self.throw_away_until_closing_curly_brace()?; break; } else { return Parser { diff --git a/src/parse/throw_away.rs b/src/parse/throw_away.rs new file mode 100644 index 0000000..e6bf9fe --- /dev/null +++ b/src/parse/throw_away.rs @@ -0,0 +1,125 @@ +//! Consume tokens without allocating + +use crate::{error::SassResult, Token}; + +use super::Parser; + +impl<'a> Parser<'a> { + pub(super) fn throw_away_until_newline(&mut self) { + while let Some(tok) = self.toks.next() { + if tok.kind == '\n' { + break; + } + } + } + + pub(super) fn throw_away_quoted_string(&mut self, q: char) -> SassResult<()> { + while let Some(tok) = self.toks.next() { + match tok.kind { + '"' if q == '"' => { + return Ok(()); + } + '\'' if q == '\'' => { + return Ok(()); + } + '\\' => { + if self.toks.next().is_none() { + return Err((format!("Expected {}.", q), tok.pos).into()); + } + } + '#' => { + self.toks.next(); + self.throw_away_until_closing_curly_brace()?; + } + _ => {} + } + } + return Err((format!("Expected {}.", q), self.span_before).into()); + } + + pub(super) fn throw_away_until_open_curly_brace(&mut self) -> SassResult<()> { + while let Some(tok) = self.toks.next() { + match tok.kind { + '{' => return Ok(()), + '/' => { + match self.toks.peek() { + Some(Token { kind: '/', .. }) => self.throw_away_until_newline(), + _ => {} + }; + continue; + } + '\\' | '#' => { + self.toks.next(); + } + q @ '"' | q @ '\'' => { + self.throw_away_quoted_string(q)?; + continue; + } + _ => {} + } + } + Err(("expected \"{\".", self.span_before).into()) + } + + pub(super) fn throw_away_until_closing_curly_brace(&mut self) -> SassResult<()> { + let mut nesting = 0; + while let Some(tok) = self.toks.next() { + match tok.kind { + q @ '"' | q @ '\'' => { + self.throw_away_quoted_string(q)?; + } + '{' => { + nesting += 1; + } + '}' => { + if nesting == 0 { + return Ok(()); + } else { + nesting -= 1; + } + } + '/' => match self.toks.peek() { + Some(Token { kind: '/', .. }) => { + self.throw_away_until_newline(); + } + Some(..) | None => continue, + }, + '(' => { + self.throw_away_until_closing_paren()?; + } + '\\' => { + self.toks.next(); + } + _ => {} + } + } + Err(("expected \"}\".", self.span_before).into()) + } + + pub(super) fn throw_away_until_closing_paren(&mut self) -> SassResult<()> { + let mut scope = 0; + while let Some(tok) = self.toks.next() { + match tok.kind { + ')' => { + if scope < 1 { + return Ok(()); + } else { + scope -= 1; + } + } + '(' => scope += 1, + '"' | '\'' => { + self.throw_away_quoted_string(tok.kind)?; + } + '\\' => { + match self.toks.next() { + Some(tok) => tok, + None => continue, + }; + } + _ => {} + } + } + Err(("expected \")\".", self.span_before).into()) + } +} diff --git a/src/utils/read_until.rs b/src/utils/read_until.rs index 9a18054..bbdbb4a 100644 --- a/src/utils/read_until.rs +++ b/src/utils/read_until.rs @@ -224,7 +224,6 @@ pub(crate) fn read_until_closing_paren>( continue; } '\\' => { - t.push(toks.next().unwrap()); t.push(match toks.next() { Some(tok) => tok, None => continue,