refactor how url peeking is implemented

This commit is contained in:
Connor Skees 2021-07-12 02:59:20 -04:00
parent 03f48cfd22
commit 91aaa70446
3 changed files with 20 additions and 185 deletions

View File

@ -1,17 +1,6 @@
use std::{borrow::Borrow, iter::Iterator};
use codemap::Spanned;
use crate::{
error::SassResult,
parse::common::Comment,
utils::{
as_hex, hex_char_for, is_name, peek_until_closing_curly_brace, peek_whitespace,
IsWhitespace,
},
value::Value,
Token,
};
use crate::{error::SassResult, parse::common::Comment, utils::IsWhitespace, value::Value, Token};
use super::super::Parser;
@ -89,10 +78,14 @@ impl<'a> Parser<'a> {
pub(super) fn try_parse_url(&mut self) -> SassResult<Option<String>> {
let mut buf = String::from("url(");
peek_whitespace(self.toks);
while let Some(tok) = self.toks.peek() {
let start = self.toks.cursor();
self.whitespace();
while let Some(tok) = self.toks.next() {
let kind = tok.kind;
self.toks.advance_cursor();
if kind == '!'
|| kind == '%'
|| kind == '&'
@ -101,11 +94,11 @@ impl<'a> Parser<'a> {
{
buf.push(kind);
} else if kind == '\\' {
buf.push_str(&self.peek_escape()?);
buf.push_str(&self.parse_escape(false)?);
} else if kind == '#' {
if let Some(Token { kind: '{', .. }) = self.toks.peek() {
self.toks.advance_cursor();
let interpolation = self.peek_interpolation()?;
self.toks.next();
let interpolation = self.parse_interpolation()?;
match interpolation.node {
Value::String(ref s, ..) => buf.push_str(s),
v => buf.push_str(v.to_css_string(interpolation.span)?.borrow()),
@ -115,14 +108,15 @@ impl<'a> Parser<'a> {
}
} else if kind == ')' {
buf.push(')');
self.toks.truncate_iterator_to_cursor();
return Ok(Some(buf));
} else if kind.is_whitespace() {
peek_whitespace(self.toks);
self.whitespace();
if let Some(Token { kind: ')', .. }) = self.toks.peek() {
self.toks.advance_cursor();
self.toks.next();
buf.push(')');
self.toks.truncate_iterator_to_cursor();
return Ok(Some(buf));
}
@ -131,7 +125,9 @@ impl<'a> Parser<'a> {
break;
}
}
self.toks.reset_cursor();
self.toks.set_cursor(start);
Ok(None)
}
@ -465,68 +461,3 @@ impl<'a> Parser<'a> {
Ok(buffer)
}
}
/// Methods required to do arbitrary lookahead
impl<'a> Parser<'a> {
fn peek_interpolation(&mut self) -> SassResult<Spanned<Value>> {
let vec = peek_until_closing_curly_brace(self.toks)?;
self.toks.advance_cursor();
let val = self.parse_value_from_vec(vec, false)?;
Ok(Spanned {
node: val.node.unquote(),
span: val.span,
})
}
fn peek_escape(&mut self) -> SassResult<String> {
let mut value = 0;
let first = match self.toks.peek() {
Some(t) => t,
None => return Ok(String::new()),
};
let mut span = first.pos;
if first.kind == '\n' {
return Err(("Expected escape sequence.", first.pos()).into());
} else if first.kind.is_ascii_hexdigit() {
for _ in 0..6 {
let next = match self.toks.peek() {
Some(t) => t,
None => break,
};
if !next.kind.is_ascii_hexdigit() {
break;
}
value *= 16;
value += as_hex(next.kind);
span = span.merge(next.pos);
self.toks.peek_forward(1);
}
if matches!(
self.toks.peek(),
Some(Token { kind: ' ', .. })
| Some(Token { kind: '\n', .. })
| Some(Token { kind: '\t', .. })
) {
self.toks.peek_forward(1);
}
} else {
value = self.toks.peek_forward(1).unwrap().kind as u32;
}
let c = std::char::from_u32(value).ok_or(("Invalid escape sequence.", span))?;
if is_name(c) {
Ok(c.to_string())
} else if value <= 0x1F || value == 0x7F {
let mut buf = String::with_capacity(4);
buf.push('\\');
if value > 0xF {
buf.push(hex_char_for(value >> 4));
}
buf.push(hex_char_for(value & 0xF));
buf.push(' ');
Ok(buf)
} else {
Ok(format!("\\{}", c))
}
}
}

View File

@ -24,18 +24,6 @@ pub(crate) fn devour_whitespace(s: &mut Lexer) -> bool {
found_whitespace
}
pub(crate) fn peek_whitespace(s: &mut Lexer) -> bool {
let mut found_whitespace = false;
while let Some(w) = s.peek() {
if !w.is_whitespace() {
break;
}
found_whitespace = true;
s.advance_cursor();
}
found_whitespace
}
pub(crate) fn peek_whitespace_or_comment(s: &mut Lexer) -> bool {
let mut found_whitespace = false;
while let Some(w) = s.peek() {

View File

@ -2,91 +2,7 @@ use codemap::{Span, Spanned};
use crate::{error::SassResult, lexer::Lexer, Token};
use super::{as_hex, hex_char_for, is_name, is_name_start, peek_whitespace};
pub(crate) fn peek_until_closing_curly_brace(toks: &mut Lexer) -> SassResult<Vec<Token>> {
let mut t = Vec::new();
let mut nesting = 0;
while let Some(tok) = toks.peek() {
match tok.kind {
q @ '"' | q @ '\'' => {
t.push(tok);
toks.advance_cursor();
t.extend(peek_until_closing_quote(toks, q)?);
}
'{' => {
nesting += 1;
t.push(tok);
toks.advance_cursor();
}
'}' => {
if nesting == 0 {
break;
}
nesting -= 1;
t.push(tok);
toks.advance_cursor();
}
'/' => {
let next = toks
.peek_forward(1)
.ok_or(("Expected expression.", tok.pos))?;
match toks.peek() {
Some(Token { kind: '/', .. }) => peek_until_newline(toks),
_ => t.push(next),
};
continue;
}
_ => {
t.push(tok);
toks.advance_cursor();
}
}
}
peek_whitespace(toks);
Ok(t)
}
fn peek_until_closing_quote(toks: &mut Lexer, q: char) -> SassResult<Vec<Token>> {
let mut t = Vec::new();
while let Some(tok) = toks.peek() {
match tok.kind {
'"' if q == '"' => {
t.push(tok);
toks.advance_cursor();
break;
}
'\'' if q == '\'' => {
t.push(tok);
toks.advance_cursor();
break;
}
'\\' => {
t.push(tok);
t.push(match toks.peek_forward(1) {
Some(tok) => tok,
None => return Err((format!("Expected {}.", q), tok.pos).into()),
});
}
'#' => {
t.push(tok);
let next = match toks.peek() {
Some(tok) => tok,
None => return Err((format!("Expected {}.", q), tok.pos).into()),
};
if next.kind == '{' {
t.push(next);
toks.peek_forward(1);
t.append(&mut peek_until_closing_curly_brace(toks)?);
}
}
_ => t.push(tok),
}
toks.advance_cursor();
}
Ok(t)
}
use super::{as_hex, hex_char_for, is_name, is_name_start};
pub(crate) fn peek_until_newline(toks: &mut Lexer) {
while let Some(tok) = toks.peek() {