Properly handle & in most contexts
This commit is contained in:
parent
6ebadd7869
commit
d7b22a41a6
14
src/args.rs
14
src/args.rs
@ -3,6 +3,7 @@ use std::iter::Peekable;
|
||||
|
||||
use crate::common::{Scope, Symbol};
|
||||
use crate::error::SassResult;
|
||||
use crate::selector::Selector;
|
||||
use crate::utils::{devour_whitespace, devour_whitespace_or_comment};
|
||||
use crate::value::Value;
|
||||
use crate::{Token, TokenKind};
|
||||
@ -46,6 +47,7 @@ impl CallArgs {
|
||||
pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<FuncArgs> {
|
||||
let mut args: Vec<FuncArg> = Vec::new();
|
||||
|
||||
@ -74,6 +76,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
||||
default: Some(Value::from_tokens(
|
||||
&mut default.into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)?),
|
||||
});
|
||||
break;
|
||||
@ -84,6 +87,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
||||
default: Some(Value::from_tokens(
|
||||
&mut default.into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)?),
|
||||
});
|
||||
break;
|
||||
@ -105,6 +109,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
||||
Some(Value::from_tokens(
|
||||
&mut default.into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)?)
|
||||
},
|
||||
});
|
||||
@ -133,6 +138,7 @@ pub(crate) fn eat_func_args<I: Iterator<Item = Token>>(
|
||||
pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<CallArgs> {
|
||||
let mut args: BTreeMap<String, Value> = BTreeMap::new();
|
||||
devour_whitespace_or_comment(toks);
|
||||
@ -164,7 +170,7 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
|
||||
TokenKind::Symbol(Symbol::CloseParen) => {
|
||||
args.insert(
|
||||
name,
|
||||
Value::from_tokens(&mut val.into_iter().peekable(), scope)?,
|
||||
Value::from_tokens(&mut val.into_iter().peekable(), scope, super_selector)?,
|
||||
);
|
||||
return Ok(CallArgs(args));
|
||||
}
|
||||
@ -179,7 +185,11 @@ pub(crate) fn eat_call_args<I: Iterator<Item = Token>>(
|
||||
|
||||
args.insert(
|
||||
name,
|
||||
Value::from_tokens(&mut val.clone().into_iter().peekable(), scope)?,
|
||||
Value::from_tokens(
|
||||
&mut val.clone().into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)?,
|
||||
);
|
||||
val.clear();
|
||||
devour_whitespace_or_comment(toks);
|
||||
|
@ -67,11 +67,11 @@ impl AtRule {
|
||||
AtRule::Debug(pos, message)
|
||||
}
|
||||
AtRuleKind::Mixin => {
|
||||
let (name, mixin) = Mixin::decl_from_tokens(toks, scope)?;
|
||||
let (name, mixin) = Mixin::decl_from_tokens(toks, scope, super_selector)?;
|
||||
AtRule::Mixin(name, Box::new(mixin))
|
||||
}
|
||||
AtRuleKind::Function => {
|
||||
let (name, func) = Function::decl_from_tokens(toks, scope.clone())?;
|
||||
let (name, func) = Function::decl_from_tokens(toks, scope.clone(), super_selector)?;
|
||||
AtRule::Function(name, Box::new(func))
|
||||
}
|
||||
AtRuleKind::Return => {
|
||||
@ -137,7 +137,11 @@ impl AtRule {
|
||||
_ => from_toks.push(tok),
|
||||
}
|
||||
}
|
||||
let from = match Value::from_tokens(&mut from_toks.into_iter().peekable(), scope)? {
|
||||
let from = match Value::from_tokens(
|
||||
&mut from_toks.into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)? {
|
||||
Value::Dimension(n, _) => match n.to_integer().to_usize() {
|
||||
Some(v) => v,
|
||||
None => return Err(format!("{} is not a int.", n).into()),
|
||||
@ -152,7 +156,11 @@ impl AtRule {
|
||||
_ => to_toks.push(tok),
|
||||
}
|
||||
}
|
||||
let to = match Value::from_tokens(&mut to_toks.into_iter().peekable(), scope)? {
|
||||
let to = match Value::from_tokens(
|
||||
&mut to_toks.into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)? {
|
||||
Value::Dimension(n, _) => match n.to_integer().to_usize() {
|
||||
Some(v) => v,
|
||||
None => return Err(format!("{} is not a int.", n).into()),
|
||||
|
@ -27,12 +27,7 @@ impl UnknownAtRule {
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::OpenCurlyBrace) => break,
|
||||
TokenKind::Interpolation => {
|
||||
params.push_str(
|
||||
&parse_interpolation(toks, scope)?
|
||||
.into_iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
);
|
||||
params.push_str(&parse_interpolation(toks, scope, super_selector)?.to_string());
|
||||
continue;
|
||||
}
|
||||
TokenKind::Variable(..) => params.push('$'),
|
||||
|
@ -24,6 +24,7 @@ impl Function {
|
||||
pub fn decl_from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
mut scope: Scope,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<(String, Function)> {
|
||||
let Token { kind, .. } = toks
|
||||
.next()
|
||||
@ -38,7 +39,7 @@ impl Function {
|
||||
Some(Token {
|
||||
kind: TokenKind::Symbol(Symbol::OpenParen),
|
||||
..
|
||||
}) => eat_func_args(toks, &scope)?,
|
||||
}) => eat_func_args(toks, &scope, super_selector)?,
|
||||
_ => return Err("expected \"(\".".into()),
|
||||
};
|
||||
|
||||
@ -83,13 +84,14 @@ impl Function {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn call(&self) -> SassResult<Value> {
|
||||
pub fn call(&self, super_selector: &Selector) -> SassResult<Value> {
|
||||
for rule in &self.body {
|
||||
match rule {
|
||||
AtRule::Return(toks) => {
|
||||
return Value::from_tokens(
|
||||
&mut toks.clone().into_iter().peekable(),
|
||||
&self.scope,
|
||||
super_selector,
|
||||
)
|
||||
}
|
||||
_ => todo!("unimplemented at rule in function body"),
|
||||
|
@ -417,7 +417,7 @@ impl<'a> StyleSheetParser<'a> {
|
||||
self.error(pos, "unexpected variable use at toplevel");
|
||||
}
|
||||
let VariableDecl { val, default } =
|
||||
eat_variable_value(&mut self.lexer, &self.global_scope)?;
|
||||
eat_variable_value(&mut self.lexer, &self.global_scope, &Selector::new())?;
|
||||
if !default || self.global_scope.get_var(&name).is_err() {
|
||||
self.global_scope.insert_var(&name, val)?;
|
||||
}
|
||||
@ -629,6 +629,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
return Ok(Some(Expr::Selector(Selector::from_tokens(
|
||||
&mut values.into_iter().peekable(),
|
||||
scope,
|
||||
super_selector,
|
||||
)?)));
|
||||
}
|
||||
TokenKind::Variable(_) => {
|
||||
@ -644,7 +645,8 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
{
|
||||
toks.next();
|
||||
devour_whitespace(toks);
|
||||
let VariableDecl { val, default } = eat_variable_value(toks, scope)?;
|
||||
let VariableDecl { val, default } =
|
||||
eat_variable_value(toks, scope, super_selector)?;
|
||||
if !default || scope.get_var(&name).is_err() {
|
||||
return Ok(Some(Expr::VariableDecl(name, Box::new(val))));
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ impl Mixin {
|
||||
pub fn decl_from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<(String, Mixin)> {
|
||||
let Token { kind, .. } = toks
|
||||
.next()
|
||||
@ -38,7 +39,7 @@ impl Mixin {
|
||||
Some(Token {
|
||||
kind: TokenKind::Symbol(Symbol::OpenParen),
|
||||
..
|
||||
}) => eat_func_args(toks, scope)?,
|
||||
}) => eat_func_args(toks, scope, super_selector)?,
|
||||
Some(Token {
|
||||
kind: TokenKind::Symbol(Symbol::OpenCurlyBrace),
|
||||
..
|
||||
@ -144,7 +145,7 @@ pub(crate) fn eat_include<I: Iterator<Item = Token>>(
|
||||
match tok.kind {
|
||||
TokenKind::Symbol(Symbol::SemiColon) => CallArgs::new(),
|
||||
TokenKind::Symbol(Symbol::OpenParen) => {
|
||||
let tmp = eat_call_args(toks, scope)?;
|
||||
let tmp = eat_call_args(toks, scope, super_selector)?;
|
||||
devour_whitespace(toks);
|
||||
if let Some(tok) = toks.next() {
|
||||
assert_eq!(tok.kind, TokenKind::Symbol(Symbol::SemiColon));
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::common::{Scope, Symbol, Whitespace};
|
||||
use crate::error::SassResult;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::utils::{
|
||||
devour_whitespace, devour_whitespace_or_comment, flatten_ident, parse_interpolation,
|
||||
parse_quoted_string, IsWhitespace,
|
||||
@ -8,7 +9,6 @@ use crate::{Token, TokenKind};
|
||||
use std::fmt::{self, Display, Write};
|
||||
use std::iter::Peekable;
|
||||
use std::string::ToString;
|
||||
use std::vec::IntoIter;
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Selector(pub Vec<SelectorKind>);
|
||||
@ -160,20 +160,25 @@ impl Display for SelectorKind {
|
||||
|
||||
struct SelectorParser<'a> {
|
||||
scope: &'a Scope,
|
||||
super_selector: &'a Selector,
|
||||
selectors: Vec<SelectorKind>,
|
||||
is_interpolated: bool,
|
||||
}
|
||||
|
||||
impl<'a> SelectorParser<'a> {
|
||||
const fn new(scope: &'a Scope) -> SelectorParser<'a> {
|
||||
const fn new(scope: &'a Scope, super_selector: &'a Selector) -> SelectorParser<'a> {
|
||||
SelectorParser {
|
||||
scope,
|
||||
super_selector,
|
||||
selectors: Vec::new(),
|
||||
is_interpolated: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn all_selectors(mut self, tokens: &'a mut Peekable<IntoIter<Token>>) -> SassResult<Selector> {
|
||||
fn all_selectors<I: Iterator<Item = Token>>(
|
||||
mut self,
|
||||
tokens: &'a mut Peekable<I>,
|
||||
) -> SassResult<Selector> {
|
||||
self.tokens_to_selectors(tokens)?;
|
||||
// remove trailing whitespace
|
||||
while let Some(x) = self.selectors.pop() {
|
||||
@ -185,9 +190,9 @@ impl<'a> SelectorParser<'a> {
|
||||
Ok(Selector(self.selectors))
|
||||
}
|
||||
|
||||
fn consume_pseudo_selector(
|
||||
fn consume_pseudo_selector<I: Iterator<Item = Token>>(
|
||||
&mut self,
|
||||
tokens: &'_ mut Peekable<IntoIter<Token>>,
|
||||
tokens: &'_ mut Peekable<I>,
|
||||
) -> SassResult<()> {
|
||||
if let Some(tok) = tokens.next() {
|
||||
match tok.kind {
|
||||
@ -232,14 +237,20 @@ impl<'a> SelectorParser<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn tokens_to_selectors(&mut self, tokens: &'_ mut Peekable<IntoIter<Token>>) -> SassResult<()> {
|
||||
fn tokens_to_selectors<I: Iterator<Item = Token>>(
|
||||
&mut self,
|
||||
tokens: &'_ mut Peekable<I>,
|
||||
) -> SassResult<()> {
|
||||
while tokens.peek().is_some() {
|
||||
self.consume_selector(tokens)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn consume_selector(&mut self, tokens: &'_ mut Peekable<IntoIter<Token>>) -> SassResult<()> {
|
||||
fn consume_selector<I: Iterator<Item = Token>>(
|
||||
&mut self,
|
||||
tokens: &'_ mut Peekable<I>,
|
||||
) -> SassResult<()> {
|
||||
if devour_whitespace_or_comment(tokens) {
|
||||
if let Some(Token {
|
||||
kind: TokenKind::Symbol(Symbol::Comma),
|
||||
@ -283,15 +294,21 @@ impl<'a> SelectorParser<'a> {
|
||||
TokenKind::Interpolation => {
|
||||
self.is_interpolated = true;
|
||||
self.tokens_to_selectors(
|
||||
&mut parse_interpolation(tokens, self.scope)?
|
||||
.into_iter()
|
||||
.peekable(),
|
||||
&mut Lexer::new(
|
||||
&parse_interpolation(
|
||||
tokens,
|
||||
self.scope,
|
||||
&Selector(vec![SelectorKind::Element(String::from("&"))]),
|
||||
)?
|
||||
.to_string(),
|
||||
)
|
||||
.peekable(),
|
||||
)?;
|
||||
self.is_interpolated = false;
|
||||
}
|
||||
TokenKind::Symbol(Symbol::OpenSquareBrace) => self
|
||||
.selectors
|
||||
.push(Attribute::from_tokens(tokens, self.scope)?),
|
||||
TokenKind::Symbol(Symbol::OpenSquareBrace) => self.selectors.push(
|
||||
Attribute::from_tokens(tokens, self.scope, self.super_selector)?,
|
||||
),
|
||||
_ => todo!("unimplemented selector"),
|
||||
};
|
||||
}
|
||||
@ -300,11 +317,12 @@ impl<'a> SelectorParser<'a> {
|
||||
}
|
||||
|
||||
impl Selector {
|
||||
pub fn from_tokens<'a>(
|
||||
tokens: &'a mut Peekable<IntoIter<Token>>,
|
||||
pub fn from_tokens<'a, I: Iterator<Item = Token>>(
|
||||
tokens: &'a mut Peekable<I>,
|
||||
scope: &'a Scope,
|
||||
super_selector: &'a Selector,
|
||||
) -> SassResult<Selector> {
|
||||
SelectorParser::new(scope).all_selectors(tokens)
|
||||
SelectorParser::new(scope, super_selector).all_selectors(tokens)
|
||||
}
|
||||
|
||||
pub fn zip(&self, other: &Selector) -> Selector {
|
||||
@ -390,20 +408,24 @@ pub(crate) struct Attribute {
|
||||
}
|
||||
|
||||
impl Attribute {
|
||||
pub fn from_tokens(
|
||||
toks: &mut Peekable<IntoIter<Token>>,
|
||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<SelectorKind> {
|
||||
devour_whitespace(toks);
|
||||
let attr = if let Some(t) = toks.next() {
|
||||
match t.kind {
|
||||
TokenKind::Ident(mut s) => {
|
||||
s.push_str(&flatten_ident(toks, scope)?);
|
||||
s.push_str(&flatten_ident(toks, scope, super_selector)?);
|
||||
s
|
||||
}
|
||||
TokenKind::Interpolation => {
|
||||
parse_interpolation(toks, scope, super_selector)?.to_string()
|
||||
}
|
||||
q @ TokenKind::Symbol(Symbol::DoubleQuote)
|
||||
| q @ TokenKind::Symbol(Symbol::SingleQuote) => {
|
||||
parse_quoted_string(toks, scope, &q)?.to_string()
|
||||
parse_quoted_string(toks, scope, &q, super_selector)?.to_string()
|
||||
}
|
||||
_ => return Err("Expected identifier.".into()),
|
||||
}
|
||||
@ -460,12 +482,12 @@ impl Attribute {
|
||||
let value = if let Some(t) = toks.next() {
|
||||
match t.kind {
|
||||
TokenKind::Ident(mut s) => {
|
||||
s.push_str(&flatten_ident(toks, scope)?);
|
||||
s.push_str(&flatten_ident(toks, scope, super_selector)?);
|
||||
s
|
||||
}
|
||||
q @ TokenKind::Symbol(Symbol::DoubleQuote)
|
||||
| q @ TokenKind::Symbol(Symbol::SingleQuote) => {
|
||||
parse_quoted_string(toks, scope, &q)?.to_string()
|
||||
parse_quoted_string(toks, scope, &q, super_selector)?.to_string()
|
||||
}
|
||||
_ => return Err("Expected identifier.".into()),
|
||||
}
|
||||
|
17
src/style.rs
17
src/style.rs
@ -88,7 +88,9 @@ impl<'a> StyleParser<'a> {
|
||||
| ref q @ TokenKind::Symbol(Symbol::SingleQuote) => {
|
||||
let q = q.clone();
|
||||
toks.next();
|
||||
let (s, q) = if let Value::Ident(s, q) = parse_quoted_string(toks, scope, &q)? {
|
||||
let (s, q) = if let Value::Ident(s, q) =
|
||||
parse_quoted_string(toks, scope, &q, self.super_selector)?
|
||||
{
|
||||
(s, q)
|
||||
} else {
|
||||
unreachable!()
|
||||
@ -107,7 +109,7 @@ impl<'a> StyleParser<'a> {
|
||||
| TokenKind::Symbol(Symbol::SemiColon) => break,
|
||||
TokenKind::Symbol(Symbol::BitAnd) => {
|
||||
style.push(Token {
|
||||
kind: TokenKind::Ident(self.super_selector.to_string()),
|
||||
kind: TokenKind::Symbol(Symbol::BitAnd),
|
||||
pos: Pos::new(),
|
||||
});
|
||||
toks.next();
|
||||
@ -117,7 +119,11 @@ impl<'a> StyleParser<'a> {
|
||||
};
|
||||
style.push(toks.next().unwrap());
|
||||
}
|
||||
Value::from_tokens(&mut style.into_iter().peekable(), self.scope)
|
||||
Value::from_tokens(
|
||||
&mut style.into_iter().peekable(),
|
||||
self.scope,
|
||||
self.super_selector,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn eat_style_group<I: Iterator<Item = Token>>(
|
||||
@ -236,10 +242,7 @@ impl<'a> StyleParser<'a> {
|
||||
TokenKind::Whitespace(_) | TokenKind::MultilineComment(_) => continue,
|
||||
TokenKind::Ident(ref s) => property.push_str(s),
|
||||
TokenKind::Interpolation => property.push_str(
|
||||
&parse_interpolation(toks, self.scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
&parse_interpolation(toks, self.scope, self.super_selector)?.to_string(),
|
||||
),
|
||||
TokenKind::Symbol(Symbol::Colon) => break,
|
||||
TokenKind::Symbol(Symbol::BitAnd) => {
|
||||
|
39
src/utils.rs
39
src/utils.rs
@ -1,6 +1,7 @@
|
||||
use crate::common::{Keyword, QuoteKind, Symbol};
|
||||
use crate::error::SassResult;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::selector::Selector;
|
||||
use crate::value::Value;
|
||||
use crate::{Scope, Token, TokenKind};
|
||||
use std::iter::{Iterator, Peekable};
|
||||
@ -44,7 +45,8 @@ pub(crate) fn devour_whitespace_or_comment<I: Iterator<Item = W>, W: IsWhitespac
|
||||
pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
||||
tokens: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
) -> SassResult<Vec<Token>> {
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<Value> {
|
||||
let mut val = String::new();
|
||||
while let Some(tok) = tokens.next() {
|
||||
match tok.kind {
|
||||
@ -54,27 +56,27 @@ pub(crate) fn parse_interpolation<I: Iterator<Item = Token>>(
|
||||
}
|
||||
q @ TokenKind::Symbol(Symbol::DoubleQuote)
|
||||
| q @ TokenKind::Symbol(Symbol::SingleQuote) => {
|
||||
val.push_str(&parse_quoted_string(tokens, scope, &q)?.to_string())
|
||||
val.push_str(&parse_quoted_string(tokens, scope, &q, super_selector)?.to_string())
|
||||
}
|
||||
TokenKind::Variable(ref v) => {
|
||||
val.push_str(&scope.get_var(v)?.clone().unquote().to_string())
|
||||
}
|
||||
TokenKind::Interpolation => val.push_str(
|
||||
&parse_interpolation(tokens, scope)?
|
||||
.iter()
|
||||
&Lexer::new(&parse_interpolation(tokens, scope, super_selector)?.to_string())
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
),
|
||||
_ => val.push_str(&tok.kind.to_string()),
|
||||
}
|
||||
}
|
||||
Ok(Lexer::new(
|
||||
&Value::from_tokens(&mut Lexer::new(&val).peekable(), scope)?
|
||||
if val.trim().is_empty() {
|
||||
return Ok(Value::Ident(String::new(), QuoteKind::None));
|
||||
}
|
||||
Ok(
|
||||
Value::from_tokens(&mut Lexer::new(&val).peekable(), scope, super_selector)?
|
||||
.eval()?
|
||||
.unquote()
|
||||
.to_string(),
|
||||
.unquote(),
|
||||
)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub(crate) struct VariableDecl {
|
||||
@ -91,6 +93,7 @@ impl VariableDecl {
|
||||
pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<VariableDecl> {
|
||||
devour_whitespace(toks);
|
||||
let mut default = false;
|
||||
@ -122,25 +125,21 @@ pub(crate) fn eat_variable_value<I: Iterator<Item = Token>>(
|
||||
}
|
||||
}
|
||||
devour_whitespace(toks);
|
||||
let val = Value::from_tokens(&mut raw.into_iter().peekable(), scope).unwrap();
|
||||
let val = Value::from_tokens(&mut raw.into_iter().peekable(), scope, super_selector).unwrap();
|
||||
Ok(VariableDecl::new(val, default))
|
||||
}
|
||||
|
||||
pub(crate) fn flatten_ident<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> 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>(),
|
||||
)
|
||||
s.push_str(&parse_interpolation(toks, scope, super_selector)?.to_string())
|
||||
}
|
||||
TokenKind::Ident(ref i) => {
|
||||
toks.next();
|
||||
@ -160,6 +159,7 @@ pub(crate) fn parse_quoted_string<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
q: &TokenKind,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<Value> {
|
||||
let mut s = String::new();
|
||||
let mut is_escaped = false;
|
||||
@ -194,12 +194,7 @@ pub(crate) fn parse_quoted_string<I: Iterator<Item = Token>>(
|
||||
}
|
||||
TokenKind::Interpolation if !is_escaped => {
|
||||
found_interpolation = true;
|
||||
s.push_str(
|
||||
&parse_interpolation(toks, scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
);
|
||||
s.push_str(&parse_interpolation(toks, scope, super_selector)?.to_string());
|
||||
continue;
|
||||
}
|
||||
TokenKind::Interpolation => {
|
||||
|
@ -10,8 +10,11 @@ use crate::builtin::GLOBAL_FUNCTIONS;
|
||||
use crate::color::Color;
|
||||
use crate::common::{Keyword, ListSeparator, Op, QuoteKind, Scope, Symbol};
|
||||
use crate::error::SassResult;
|
||||
use crate::selector::Selector;
|
||||
use crate::units::Unit;
|
||||
use crate::utils::{devour_whitespace_or_comment, parse_interpolation, parse_quoted_string};
|
||||
use crate::utils::{
|
||||
devour_whitespace_or_comment, flatten_ident, parse_interpolation, parse_quoted_string,
|
||||
};
|
||||
use crate::value::Value;
|
||||
use crate::{Token, TokenKind};
|
||||
|
||||
@ -65,42 +68,13 @@ fn parse_hex(s: &str) -> Value {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<Self> {
|
||||
let left = Self::_from_tokens(toks, scope)?;
|
||||
let left = Self::_from_tokens(toks, scope, super_selector)?;
|
||||
let whitespace = devour_whitespace_or_comment(toks);
|
||||
let next = match toks.peek() {
|
||||
Some(x) => x,
|
||||
@ -113,7 +87,7 @@ impl Value {
|
||||
TokenKind::Symbol(Symbol::Comma) => {
|
||||
toks.next();
|
||||
devour_whitespace_or_comment(toks);
|
||||
let right = match Self::from_tokens(toks, scope) {
|
||||
let right = match Self::from_tokens(toks, scope, super_selector) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Ok(left),
|
||||
};
|
||||
@ -136,7 +110,7 @@ impl Value {
|
||||
};
|
||||
toks.next();
|
||||
devour_whitespace_or_comment(toks);
|
||||
let right = match Self::from_tokens(toks, scope) {
|
||||
let right = match Self::from_tokens(toks, scope, super_selector) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Ok(left),
|
||||
};
|
||||
@ -144,7 +118,7 @@ impl Value {
|
||||
}
|
||||
_ => {
|
||||
devour_whitespace_or_comment(toks);
|
||||
let right = match Self::from_tokens(toks, scope) {
|
||||
let right = match Self::from_tokens(toks, scope, super_selector) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Ok(left),
|
||||
};
|
||||
@ -156,6 +130,7 @@ impl Value {
|
||||
fn _from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
super_selector: &Selector,
|
||||
) -> SassResult<Self> {
|
||||
let kind = if let Some(tok) = toks.next() {
|
||||
tok.kind
|
||||
@ -203,7 +178,7 @@ impl Value {
|
||||
}
|
||||
TokenKind::Symbol(Symbol::OpenParen) => {
|
||||
devour_whitespace_or_comment(toks);
|
||||
let val = Self::from_tokens(toks, scope)?;
|
||||
let val = Self::from_tokens(toks, scope, super_selector)?;
|
||||
assert_eq!(
|
||||
toks.next().unwrap().kind,
|
||||
TokenKind::Symbol(Symbol::CloseParen)
|
||||
@ -211,20 +186,13 @@ impl Value {
|
||||
Ok(Value::Paren(Box::new(val)))
|
||||
}
|
||||
TokenKind::Symbol(Symbol::BitAnd) => {
|
||||
Ok(Value::Ident(String::from("&"), QuoteKind::None))
|
||||
Ok(Value::Ident(super_selector.to_string(), QuoteKind::None))
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Hash) => {
|
||||
Ok(parse_hex(&flatten_ident(toks, scope, super_selector)?))
|
||||
}
|
||||
TokenKind::Symbol(Symbol::Hash) => Ok(parse_hex(&flatten_ident(toks, scope)?)),
|
||||
// TokenKind::Interpolation => {
|
||||
// Ok(Value::Ident(
|
||||
// parse_interpolation(toks, scope)
|
||||
// .iter()
|
||||
// .map(|x| x.kind.to_string())
|
||||
// .collect::<String>(),
|
||||
// QuoteKind::None
|
||||
// ))
|
||||
// }
|
||||
TokenKind::Ident(mut s) => {
|
||||
s.push_str(&flatten_ident(toks, scope)?);
|
||||
s.push_str(&flatten_ident(toks, scope, super_selector)?);
|
||||
match toks.peek() {
|
||||
Some(Token {
|
||||
kind: TokenKind::Symbol(Symbol::OpenParen),
|
||||
@ -234,7 +202,12 @@ impl Value {
|
||||
let func = match scope.get_fn(&s) {
|
||||
Ok(f) => f,
|
||||
Err(_) => match GLOBAL_FUNCTIONS.get(&s) {
|
||||
Some(f) => return f(&mut eat_call_args(toks, scope)?, scope),
|
||||
Some(f) => {
|
||||
return f(
|
||||
&mut eat_call_args(toks, scope, super_selector)?,
|
||||
scope,
|
||||
)
|
||||
}
|
||||
None => {
|
||||
s.push('(');
|
||||
let mut unclosed_parens = 0;
|
||||
@ -244,10 +217,8 @@ impl Value {
|
||||
unclosed_parens += 1;
|
||||
}
|
||||
TokenKind::Interpolation => s.push_str(
|
||||
&parse_interpolation(toks, scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>(),
|
||||
&parse_interpolation(toks, scope, super_selector)?
|
||||
.to_string(),
|
||||
),
|
||||
TokenKind::Variable(v) => {
|
||||
s.push_str(&scope.get_var(v)?.to_string())
|
||||
@ -270,8 +241,8 @@ impl Value {
|
||||
};
|
||||
Ok(func
|
||||
.clone()
|
||||
.args(&mut eat_call_args(toks, scope)?)?
|
||||
.call()?)
|
||||
.args(&mut eat_call_args(toks, scope, super_selector)?)?
|
||||
.call(super_selector)?)
|
||||
}
|
||||
_ => {
|
||||
if let Ok(c) = crate::color::ColorName::try_from(s.as_ref()) {
|
||||
@ -283,22 +254,18 @@ impl Value {
|
||||
}
|
||||
}
|
||||
q @ TokenKind::Symbol(Symbol::DoubleQuote)
|
||||
| q @ TokenKind::Symbol(Symbol::SingleQuote) => parse_quoted_string(toks, scope, &q),
|
||||
| q @ TokenKind::Symbol(Symbol::SingleQuote) => {
|
||||
parse_quoted_string(toks, scope, &q, super_selector)
|
||||
}
|
||||
TokenKind::Variable(ref v) => Ok(scope.get_var(v)?.clone()),
|
||||
TokenKind::Interpolation => {
|
||||
let mut s = parse_interpolation(toks, scope)?
|
||||
.iter()
|
||||
.map(|x| x.kind.to_string())
|
||||
.collect::<String>();
|
||||
let mut s = parse_interpolation(toks, scope, super_selector)?.to_string();
|
||||
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>(),
|
||||
&parse_interpolation(toks, scope, super_selector)?.to_string(),
|
||||
)
|
||||
}
|
||||
TokenKind::Ident(ref i) => {
|
||||
@ -318,7 +285,10 @@ impl Value {
|
||||
TokenKind::Keyword(Keyword::Through(s)) => Ok(Value::Ident(s, QuoteKind::None)),
|
||||
TokenKind::Keyword(Keyword::To(s)) => Ok(Value::Ident(s, QuoteKind::None)),
|
||||
TokenKind::Unknown(c) => Ok(Value::Ident(c.to_string(), QuoteKind::None)),
|
||||
_ => Err("Unexpected token in value parsing".into()),
|
||||
v => {
|
||||
dbg!(v);
|
||||
panic!("Unexpected token in value parsing")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,11 +25,11 @@ test!(
|
||||
);
|
||||
test!(
|
||||
preserves_inner_single_quotes,
|
||||
"a {\n color: #{\"''\"};\n}\n",
|
||||
"a {\n color: #{\"''\"};\n}\n",
|
||||
"a {\n color: '';\n}\n"
|
||||
);
|
||||
test!(
|
||||
single_quotes_converted_to_double_when_interpolated,
|
||||
"a {\n color: '#{foo}';\n}\n",
|
||||
"a {\n color: '#{foo}';\n}\n",
|
||||
"a {\n color: \"foo\";\n}\n"
|
||||
);
|
||||
|
@ -95,6 +95,11 @@ test!(
|
||||
selector_el_attribute_descendant,
|
||||
"a [attr] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
selector_attribute_interpolated,
|
||||
"a {\n [#{&}] {\n color: red;\n }\n}\n",
|
||||
"a [a] {\n color: red;\n}\n"
|
||||
);
|
||||
test!(selector_el_mul_el, "a, b {\n color: red;\n}\n");
|
||||
test!(
|
||||
selector_el_immediate_child_el,
|
||||
@ -293,3 +298,8 @@ test!(
|
||||
"a, %b, c {\n color: red;\n}\n",
|
||||
"a, c {\n color: red;\n}\n"
|
||||
);
|
||||
// test!(
|
||||
// removes_leading_space,
|
||||
// "#{&} a {\n color: red;\n}\n",
|
||||
// "a {\n color: red;\n}\n"
|
||||
// );
|
||||
|
@ -155,3 +155,33 @@ test!(
|
||||
styles_after_quoted,
|
||||
"a {\n color: \"red\";\n color: blue;\n}\n"
|
||||
);
|
||||
test!(
|
||||
interpolated_super_selector_in_style,
|
||||
"a {\n color: #{&};\n}\n",
|
||||
"a {\n color: a;\n}\n"
|
||||
);
|
||||
test!(
|
||||
interpolated_super_selector_in_style_symbols,
|
||||
"* .a #b:foo() {\n color: #{&};\n}\n",
|
||||
"* .a #b:foo() {\n color: * .a #b:foo();\n}\n"
|
||||
);
|
||||
test!(
|
||||
uninterpolated_super_selector,
|
||||
"* .a #b:foo() {\n color: &;\n}\n",
|
||||
"* .a #b:foo() {\n color: * .a #b:foo();\n}\n"
|
||||
);
|
||||
test!(
|
||||
interpolated_super_selector_in_selector_and_style,
|
||||
"a {\n b #{&} {\n color: &;\n }\n}\n",
|
||||
"a b a {\n color: a b a;\n}\n"
|
||||
);
|
||||
test!(
|
||||
emits_leading_whitespace,
|
||||
"a {\n color: unquote(\" foo\");\n}\n",
|
||||
"a {\n color: foo;\n}\n"
|
||||
);
|
||||
test!(
|
||||
emits_trailing_whitespace,
|
||||
"a {\n color: unquote(\"foo \");\n}\n",
|
||||
"a {\n color: foo ;\n}\n"
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user