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