2020-02-24 15:07:18 -05:00
|
|
|
use crate::common::{Keyword, QuoteKind, Symbol};
|
2020-02-16 10:54:25 -05:00
|
|
|
use crate::error::SassResult;
|
2020-01-26 18:43:07 -05:00
|
|
|
use crate::lexer::Lexer;
|
2020-01-26 09:28:44 -05:00
|
|
|
use crate::value::Value;
|
2020-01-12 20:56:07 -05:00
|
|
|
use crate::{Scope, Token, TokenKind};
|
2020-01-14 17:39:19 -05:00
|
|
|
use std::iter::{Iterator, Peekable};
|
2020-01-12 20:15:27 -05:00
|
|
|
|
2020-01-20 13:15:47 -05:00
|
|
|
pub(crate) trait IsWhitespace {
|
2020-01-12 20:15:27 -05:00
|
|
|
fn is_whitespace(&self) -> bool;
|
|
|
|
}
|
|
|
|
|
2020-01-20 16:00:37 -05:00
|
|
|
pub(crate) fn devour_whitespace<I: Iterator<Item = W>, W: IsWhitespace>(
|
|
|
|
s: &mut Peekable<I>,
|
|
|
|
) -> bool {
|
2020-01-12 20:15:27 -05:00
|
|
|
let mut found_whitespace = false;
|
|
|
|
while let Some(w) = s.peek() {
|
|
|
|
if !w.is_whitespace() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
found_whitespace = true;
|
|
|
|
s.next();
|
|
|
|
}
|
|
|
|
found_whitespace
|
|
|
|
}
|
2020-01-12 20:56:07 -05:00
|
|
|
|
2020-01-25 09:54:38 -05:00
|
|
|
pub(crate) trait IsComment {
|
|
|
|
fn is_comment(&self) -> bool;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn devour_whitespace_or_comment<I: Iterator<Item = W>, W: IsWhitespace + IsComment>(
|
|
|
|
s: &mut Peekable<I>,
|
|
|
|
) -> bool {
|
|
|
|
let mut found_whitespace = false;
|
|
|
|
while let Some(w) = s.peek() {
|
|
|
|
if !w.is_whitespace() && !w.is_comment() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
found_whitespace = true;
|
|
|
|
s.next();
|
|
|
|
}
|
|
|
|
found_whitespace
|
|
|
|
}
|
|
|
|
|
2020-02-24 17:08:49 -05:00
|
|
|
pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
2020-02-24 16:58:48 -05:00
|
|
|
tokens: &mut Peekable<I>,
|
2020-01-14 17:39:19 -05:00
|
|
|
scope: &Scope,
|
2020-02-17 07:18:54 -05:00
|
|
|
) -> SassResult<Vec<Token>> {
|
2020-02-24 16:58:48 -05:00
|
|
|
let mut val = String::new();
|
2020-01-20 17:01:25 -05:00
|
|
|
while let Some(tok) = tokens.next() {
|
2020-01-18 20:24:28 -05:00
|
|
|
match tok.kind {
|
2020-01-14 17:39:19 -05:00
|
|
|
TokenKind::Symbol(Symbol::CloseCurlyBrace) => break,
|
|
|
|
TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
|
|
|
todo!("invalid character in interpolation")
|
|
|
|
}
|
2020-02-24 16:58:48 -05:00
|
|
|
q @ TokenKind::Symbol(Symbol::DoubleQuote)
|
|
|
|
| q @ TokenKind::Symbol(Symbol::SingleQuote) => {
|
|
|
|
val.push_str(&parse_quoted_string(tokens, scope, q)?.to_string())
|
|
|
|
}
|
2020-02-17 07:18:54 -05:00
|
|
|
TokenKind::Variable(ref v) => {
|
2020-02-24 16:58:48 -05:00
|
|
|
val.push_str(&scope.get_var(v)?.clone().unquote().to_string())
|
2020-02-17 07:18:54 -05:00
|
|
|
}
|
2020-02-24 16:58:48 -05:00
|
|
|
TokenKind::Interpolation => val.push_str(
|
2020-02-24 17:08:49 -05:00
|
|
|
&parse_interpolation(tokens, scope)?
|
2020-02-24 16:58:48 -05:00
|
|
|
.iter()
|
|
|
|
.map(|x| x.kind.to_string())
|
|
|
|
.collect::<String>(),
|
|
|
|
),
|
|
|
|
_ => val.push_str(&tok.kind.to_string()),
|
2020-01-14 17:39:19 -05:00
|
|
|
}
|
|
|
|
}
|
2020-02-17 07:18:54 -05:00
|
|
|
Ok(Lexer::new(
|
2020-02-24 16:58:48 -05:00
|
|
|
&Value::from_tokens(&mut Lexer::new(&val).peekable(), scope)?
|
|
|
|
.eval()?
|
|
|
|
.unquote()
|
|
|
|
.to_string(),
|
2020-02-02 21:11:22 -05:00
|
|
|
)
|
2020-02-24 16:58:48 -05:00
|
|
|
.collect())
|
|
|
|
}
|
|
|
|
|
2020-01-29 21:02:32 -05:00
|
|
|
pub(crate) struct VariableDecl {
|
|
|
|
pub val: Value,
|
|
|
|
pub default: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl VariableDecl {
|
2020-02-08 17:03:43 -05:00
|
|
|
pub const fn new(val: Value, default: bool) -> VariableDecl {
|
2020-01-29 21:02:32 -05:00
|
|
|
VariableDecl { val, default }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-20 13:15:47 -05:00
|
|
|
pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
2020-01-14 19:34:13 -05:00
|
|
|
toks: &mut Peekable<I>,
|
|
|
|
scope: &Scope,
|
2020-02-16 10:54:25 -05:00
|
|
|
) -> SassResult<VariableDecl> {
|
2020-01-14 19:34:13 -05:00
|
|
|
devour_whitespace(toks);
|
2020-01-29 21:02:32 -05:00
|
|
|
let mut default = false;
|
|
|
|
let mut raw: Vec<Token> = Vec::new();
|
|
|
|
let mut nesting = 0;
|
|
|
|
while let Some(tok) = toks.peek() {
|
|
|
|
match tok.kind {
|
|
|
|
TokenKind::Symbol(Symbol::SemiColon) => {
|
|
|
|
toks.next();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
TokenKind::Keyword(Keyword::Default) => {
|
|
|
|
toks.next();
|
|
|
|
default = true
|
|
|
|
}
|
|
|
|
TokenKind::Interpolation | TokenKind::Symbol(Symbol::OpenCurlyBrace) => {
|
|
|
|
nesting += 1;
|
|
|
|
raw.push(toks.next().unwrap());
|
|
|
|
}
|
|
|
|
TokenKind::Symbol(Symbol::CloseCurlyBrace) => {
|
|
|
|
if nesting == 0 {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
nesting -= 1;
|
|
|
|
raw.push(toks.next().unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => raw.push(toks.next().unwrap()),
|
|
|
|
}
|
|
|
|
}
|
2020-01-14 19:34:13 -05:00
|
|
|
devour_whitespace(toks);
|
2020-01-29 21:02:32 -05:00
|
|
|
let val = Value::from_tokens(&mut raw.into_iter().peekable(), scope).unwrap();
|
|
|
|
Ok(VariableDecl::new(val, default))
|
2020-01-14 19:34:13 -05:00
|
|
|
}
|
2020-02-24 15:07:18 -05:00
|
|
|
|
|
|
|
pub(crate) fn flatten_ident<I: Iterator<Item = Token>>(
|
|
|
|
toks: &mut Peekable<I>,
|
|
|
|
scope: &Scope,
|
|
|
|
) -> SassResult<String> {
|
|
|
|
let mut s = String::new();
|
|
|
|
while let Some(tok) = toks.peek() {
|
|
|
|
match tok.kind.clone() {
|
|
|
|
TokenKind::Interpolation => {
|
|
|
|
toks.next();
|
|
|
|
s.push_str(
|
|
|
|
&parse_interpolation(toks, scope)?
|
|
|
|
.iter()
|
|
|
|
.map(|x| x.kind.to_string())
|
|
|
|
.collect::<String>(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
TokenKind::Ident(ref i) => {
|
|
|
|
toks.next();
|
|
|
|
s.push_str(i)
|
|
|
|
}
|
|
|
|
TokenKind::Number(ref n) => {
|
|
|
|
toks.next();
|
|
|
|
s.push_str(n)
|
|
|
|
}
|
|
|
|
_ => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn parse_quoted_string<I: Iterator<Item = Token>>(
|
|
|
|
toks: &mut Peekable<I>,
|
|
|
|
scope: &Scope,
|
|
|
|
q: TokenKind,
|
2020-02-24 15:18:53 -05:00
|
|
|
) -> SassResult<Value> {
|
2020-02-24 15:07:18 -05:00
|
|
|
let mut s = String::new();
|
|
|
|
let mut is_escaped = false;
|
2020-02-24 15:18:53 -05:00
|
|
|
let mut found_interpolation = false;
|
2020-02-24 15:07:18 -05:00
|
|
|
while let Some(tok) = toks.next() {
|
|
|
|
match tok.kind {
|
|
|
|
TokenKind::Symbol(Symbol::DoubleQuote)
|
|
|
|
if !is_escaped && q == TokenKind::Symbol(Symbol::DoubleQuote) =>
|
|
|
|
{
|
|
|
|
break
|
|
|
|
}
|
2020-02-24 17:47:32 -05:00
|
|
|
TokenKind::Symbol(Symbol::DoubleQuote) if is_escaped => {
|
|
|
|
s.push('\\');
|
|
|
|
s.push('"');
|
|
|
|
is_escaped = false;
|
|
|
|
continue;
|
|
|
|
}
|
2020-02-24 15:07:18 -05:00
|
|
|
TokenKind::Symbol(Symbol::SingleQuote)
|
|
|
|
if !is_escaped && q == TokenKind::Symbol(Symbol::SingleQuote) =>
|
|
|
|
{
|
|
|
|
break
|
|
|
|
}
|
2020-02-24 17:47:32 -05:00
|
|
|
TokenKind::Symbol(Symbol::SingleQuote) if is_escaped => {
|
|
|
|
s.push('\\');
|
|
|
|
s.push('\'');
|
|
|
|
is_escaped = false;
|
|
|
|
continue;
|
|
|
|
}
|
2020-02-24 15:07:18 -05:00
|
|
|
TokenKind::Symbol(Symbol::BackSlash) if !is_escaped => is_escaped = true,
|
|
|
|
TokenKind::Symbol(Symbol::BackSlash) => s.push('\\'),
|
2020-02-24 17:47:32 -05:00
|
|
|
TokenKind::Interpolation if !is_escaped => {
|
2020-02-24 15:18:53 -05:00
|
|
|
found_interpolation = true;
|
2020-02-24 15:07:18 -05:00
|
|
|
s.push_str(
|
|
|
|
&parse_interpolation(toks, scope)?
|
|
|
|
.iter()
|
|
|
|
.map(|x| x.kind.to_string())
|
|
|
|
.collect::<String>(),
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
2020-02-24 17:47:32 -05:00
|
|
|
TokenKind::Interpolation => {
|
|
|
|
s.push('#');
|
|
|
|
s.push('{');
|
|
|
|
is_escaped = false;
|
|
|
|
continue;
|
|
|
|
}
|
2020-02-24 15:07:18 -05:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
if is_escaped && tok.kind != TokenKind::Symbol(Symbol::BackSlash) {
|
|
|
|
is_escaped = false;
|
|
|
|
}
|
2020-02-24 17:47:32 -05:00
|
|
|
if tok.kind != TokenKind::Symbol(Symbol::BackSlash) {
|
|
|
|
s.push_str(&tok.kind.to_string());
|
|
|
|
}
|
2020-02-24 15:07:18 -05:00
|
|
|
}
|
2020-02-24 15:18:53 -05:00
|
|
|
let quotes = if found_interpolation {
|
|
|
|
QuoteKind::Double
|
|
|
|
} else {
|
|
|
|
match q {
|
|
|
|
TokenKind::Symbol(Symbol::DoubleQuote) => QuoteKind::Double,
|
|
|
|
TokenKind::Symbol(Symbol::SingleQuote) => QuoteKind::Single,
|
|
|
|
_ => unreachable!(),
|
|
|
|
}
|
2020-02-24 15:07:18 -05:00
|
|
|
};
|
2020-02-24 15:18:53 -05:00
|
|
|
Ok(Value::Ident(s, quotes))
|
2020-02-24 15:07:18 -05:00
|
|
|
}
|