refactor how url peeking is implemented
This commit is contained in:
parent
03f48cfd22
commit
91aaa70446
@ -1,17 +1,6 @@
|
|||||||
use std::{borrow::Borrow, iter::Iterator};
|
use std::{borrow::Borrow, iter::Iterator};
|
||||||
|
|
||||||
use codemap::Spanned;
|
use crate::{error::SassResult, parse::common::Comment, utils::IsWhitespace, value::Value, Token};
|
||||||
|
|
||||||
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 super::super::Parser;
|
use super::super::Parser;
|
||||||
|
|
||||||
@ -89,10 +78,14 @@ impl<'a> Parser<'a> {
|
|||||||
|
|
||||||
pub(super) fn try_parse_url(&mut self) -> SassResult<Option<String>> {
|
pub(super) fn try_parse_url(&mut self) -> SassResult<Option<String>> {
|
||||||
let mut buf = String::from("url(");
|
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;
|
let kind = tok.kind;
|
||||||
self.toks.advance_cursor();
|
|
||||||
if kind == '!'
|
if kind == '!'
|
||||||
|| kind == '%'
|
|| kind == '%'
|
||||||
|| kind == '&'
|
|| kind == '&'
|
||||||
@ -101,11 +94,11 @@ impl<'a> Parser<'a> {
|
|||||||
{
|
{
|
||||||
buf.push(kind);
|
buf.push(kind);
|
||||||
} else if kind == '\\' {
|
} else if kind == '\\' {
|
||||||
buf.push_str(&self.peek_escape()?);
|
buf.push_str(&self.parse_escape(false)?);
|
||||||
} else if kind == '#' {
|
} else if kind == '#' {
|
||||||
if let Some(Token { kind: '{', .. }) = self.toks.peek() {
|
if let Some(Token { kind: '{', .. }) = self.toks.peek() {
|
||||||
self.toks.advance_cursor();
|
self.toks.next();
|
||||||
let interpolation = self.peek_interpolation()?;
|
let interpolation = self.parse_interpolation()?;
|
||||||
match interpolation.node {
|
match interpolation.node {
|
||||||
Value::String(ref s, ..) => buf.push_str(s),
|
Value::String(ref s, ..) => buf.push_str(s),
|
||||||
v => buf.push_str(v.to_css_string(interpolation.span)?.borrow()),
|
v => buf.push_str(v.to_css_string(interpolation.span)?.borrow()),
|
||||||
@ -115,14 +108,15 @@ impl<'a> Parser<'a> {
|
|||||||
}
|
}
|
||||||
} else if kind == ')' {
|
} else if kind == ')' {
|
||||||
buf.push(')');
|
buf.push(')');
|
||||||
self.toks.truncate_iterator_to_cursor();
|
|
||||||
return Ok(Some(buf));
|
return Ok(Some(buf));
|
||||||
} else if kind.is_whitespace() {
|
} else if kind.is_whitespace() {
|
||||||
peek_whitespace(self.toks);
|
self.whitespace();
|
||||||
|
|
||||||
if let Some(Token { kind: ')', .. }) = self.toks.peek() {
|
if let Some(Token { kind: ')', .. }) = self.toks.peek() {
|
||||||
self.toks.advance_cursor();
|
self.toks.next();
|
||||||
buf.push(')');
|
buf.push(')');
|
||||||
self.toks.truncate_iterator_to_cursor();
|
|
||||||
return Ok(Some(buf));
|
return Ok(Some(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +125,9 @@ impl<'a> Parser<'a> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.toks.reset_cursor();
|
|
||||||
|
self.toks.set_cursor(start);
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,68 +461,3 @@ impl<'a> Parser<'a> {
|
|||||||
Ok(buffer)
|
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -24,18 +24,6 @@ pub(crate) fn devour_whitespace(s: &mut Lexer) -> bool {
|
|||||||
found_whitespace
|
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 {
|
pub(crate) fn peek_whitespace_or_comment(s: &mut Lexer) -> bool {
|
||||||
let mut found_whitespace = false;
|
let mut found_whitespace = false;
|
||||||
while let Some(w) = s.peek() {
|
while let Some(w) = s.peek() {
|
||||||
|
@ -2,91 +2,7 @@ use codemap::{Span, Spanned};
|
|||||||
|
|
||||||
use crate::{error::SassResult, lexer::Lexer, Token};
|
use crate::{error::SassResult, lexer::Lexer, Token};
|
||||||
|
|
||||||
use super::{as_hex, hex_char_for, is_name, is_name_start, peek_whitespace};
|
use super::{as_hex, hex_char_for, is_name, is_name_start};
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn peek_until_newline(toks: &mut Lexer) {
|
pub(crate) fn peek_until_newline(toks: &mut Lexer) {
|
||||||
while let Some(tok) = toks.peek() {
|
while let Some(tok) = toks.peek() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user