Properly handle & in most contexts

This commit is contained in:
ConnorSkees 2020-03-01 12:03:14 -05:00
parent 6ebadd7869
commit d7b22a41a6
13 changed files with 184 additions and 136 deletions

View File

@ -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);

View File

@ -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()),

View File

@ -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('$'),

View File

@ -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"),

View File

@ -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))));
}

View File

@ -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));

View File

@ -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()
&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()),
}

View File

@ -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) => {

View File

@ -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 => {

View File

@ -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")
}
}
}
}

View File

@ -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"
// );

View File

@ -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"
);