Allow nested unknown @ rules

This commit is contained in:
ConnorSkees 2020-02-22 15:34:32 -05:00
parent a6cc86e4db
commit a3b260c0bd
7 changed files with 50 additions and 9 deletions

View File

@ -37,6 +37,7 @@ impl AtRule {
pos: Pos, pos: Pos,
toks: &mut Peekable<I>, toks: &mut Peekable<I>,
scope: &Scope, scope: &Scope,
super_selector: &Selector,
) -> SassResult<AtRule> { ) -> SassResult<AtRule> {
devour_whitespace(toks); devour_whitespace(toks);
Ok(match rule { Ok(match rule {
@ -116,7 +117,22 @@ impl AtRule {
params.push_str(&tok.kind.to_string()); params.push_str(&tok.kind.to_string());
} }
let body = eat_unknown_atrule_body(toks, scope, &Selector::new())?; let raw_body = eat_unknown_atrule_body(toks, scope, super_selector)?;
let mut body = Vec::with_capacity(raw_body.len());
body.push(Stmt::RuleSet(RuleSet::new()));
let mut rules = Vec::new();
for stmt in raw_body {
match stmt {
s @ Stmt::Style(..) => rules.push(s),
s => body.push(s),
}
}
body[0] = Stmt::RuleSet(RuleSet {
selector: super_selector.clone(),
rules,
super_selector: Selector::new(),
});
let u = UnknownAtRule { let u = UnknownAtRule {
name: name.clone(), name: name.clone(),
@ -140,6 +156,7 @@ fn eat_unknown_atrule_body<I: Iterator<Item = Token>>(
let mut stmts = Vec::new(); let mut stmts = Vec::new();
while let Some(expr) = eat_expr(toks, scope, super_selector)? { while let Some(expr) = eat_expr(toks, scope, super_selector)? {
match expr { match expr {
Expr::AtRule(a) => stmts.push(Stmt::AtRule(a)),
Expr::Style(s) => stmts.push(Stmt::Style(s)), Expr::Style(s) => stmts.push(Stmt::Style(s)),
Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)), Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)),
Expr::Include(s) => stmts.extend(s), Expr::Include(s) => stmts.extend(s),

View File

@ -82,7 +82,7 @@ impl Css {
.get_mut(0) .get_mut(0)
.expect("expected block to exist") .expect("expected block to exist")
.push_comment(s), .push_comment(s),
Stmt::AtRule(_) => todo!("at rule inside css block"), Stmt::AtRule(r) => vals.push(Toplevel::AtRule(r)),
}; };
} }
vals vals

View File

@ -4,6 +4,7 @@ use crate::args::{eat_func_args, CallArgs, FuncArgs};
use crate::atrule::AtRule; use crate::atrule::AtRule;
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; use crate::utils::devour_whitespace;
use crate::value::Value; use crate::value::Value;
use crate::{Token, TokenKind}; use crate::{Token, TokenKind};
@ -47,9 +48,13 @@ impl Function {
while nesting > 0 { while nesting > 0 {
if let Some(tok) = toks.next() { if let Some(tok) = toks.next() {
match &tok.kind { match &tok.kind {
TokenKind::AtRule(rule) => { TokenKind::AtRule(rule) => body.push(AtRule::from_tokens(
body.push(AtRule::from_tokens(rule, tok.pos, toks, scope)?) rule,
} tok.pos,
toks,
scope,
&Selector::new(),
)?),
TokenKind::Symbol(Symbol::CloseCurlyBrace) => nesting -= 1, TokenKind::Symbol(Symbol::CloseCurlyBrace) => nesting -= 1,
_ => {} _ => {}
} }

View File

@ -213,6 +213,16 @@ pub(crate) struct RuleSet {
super_selector: Selector, super_selector: Selector,
} }
impl RuleSet {
pub(crate) fn new() -> RuleSet {
RuleSet {
selector: Selector::new(),
rules: Vec::new(),
super_selector: Selector::new(),
}
}
}
/// An intermediate representation of what are essentially single lines /// An intermediate representation of what are essentially single lines
/// todo! rename this /// todo! rename this
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -236,6 +246,7 @@ enum Expr {
MultilineComment(String), MultilineComment(String),
Debug(Pos, String), Debug(Pos, String),
Warn(Pos, String), Warn(Pos, String),
AtRule(AtRule),
// /// Function call: `calc(10vw - 1px)` // /// Function call: `calc(10vw - 1px)`
// FuncCall(String, Vec<Token>), // FuncCall(String, Vec<Token>),
} }
@ -437,7 +448,7 @@ impl<'a> StyleSheetParser<'a> {
pos, pos,
}) = self.lexer.next() }) = self.lexer.next()
{ {
match AtRule::from_tokens(rule, pos, &mut self.lexer, &self.global_scope)? { match AtRule::from_tokens(rule, pos, &mut self.lexer, &self.global_scope, &Selector::new())? {
AtRule::Mixin(name, mixin) => { AtRule::Mixin(name, mixin) => {
self.global_scope.insert_mixin(&name, *mixin); self.global_scope.insert_mixin(&name, *mixin);
} }
@ -476,7 +487,7 @@ impl<'a> StyleSheetParser<'a> {
while let Some(expr) = eat_expr(&mut self.lexer, scope, super_selector)? { while let Some(expr) = eat_expr(&mut self.lexer, scope, super_selector)? {
match expr { match expr {
Expr::Style(s) => stmts.push(Stmt::Style(s)), Expr::Style(s) => stmts.push(Stmt::Style(s)),
#[allow(clippy::redundant_closure)] Expr::AtRule(s) => stmts.push(Stmt::AtRule(s)),
Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)), Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)),
Expr::MixinDecl(name, mixin) => { Expr::MixinDecl(name, mixin) => {
scope.insert_mixin(&name, *mixin); scope.insert_mixin(&name, *mixin);
@ -623,7 +634,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
pos, pos,
}) = toks.next() }) = toks.next()
{ {
return match AtRule::from_tokens(rule, pos, toks, scope)? { return match AtRule::from_tokens(rule, pos, toks, scope, &super_selector)? {
AtRule::Mixin(name, mixin) => Ok(Some(Expr::MixinDecl(name, mixin))), AtRule::Mixin(name, mixin) => Ok(Some(Expr::MixinDecl(name, mixin))),
AtRule::Function(name, func) => Ok(Some(Expr::FunctionDecl(name, func))), AtRule::Function(name, func) => Ok(Some(Expr::FunctionDecl(name, func))),
AtRule::Charset(_) => todo!("@charset as expr"), AtRule::Charset(_) => todo!("@charset as expr"),
@ -631,7 +642,7 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
AtRule::Warn(a, b) => Ok(Some(Expr::Warn(a, b))), AtRule::Warn(a, b) => Ok(Some(Expr::Warn(a, b))),
AtRule::Error(pos, err) => Err(SassError::new(err, pos)), AtRule::Error(pos, err) => Err(SassError::new(err, pos)),
AtRule::Return(_) => todo!("@return in unexpected location!"), AtRule::Return(_) => todo!("@return in unexpected location!"),
AtRule::Unknown(..) => todo!("nested media queries not yet implemented"), u @ AtRule::Unknown(..) => Ok(Some(Expr::AtRule(u))),
}; };
} }
} }

View File

@ -94,6 +94,7 @@ impl Mixin {
let mut stmts = Vec::new(); let mut stmts = Vec::new();
while let Some(expr) = eat_expr(&mut self.body, &self.scope, super_selector)? { while let Some(expr) = eat_expr(&mut self.body, &self.scope, super_selector)? {
match expr { match expr {
Expr::AtRule(a) => stmts.push(Stmt::AtRule(a)),
Expr::Style(s) => stmts.push(Stmt::Style(s)), Expr::Style(s) => stmts.push(Stmt::Style(s)),
Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)), Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)),
Expr::Include(s) => stmts.extend(s), Expr::Include(s) => stmts.extend(s),

View File

@ -335,6 +335,8 @@ impl Selector {
pub fn zip(&self, other: &Selector) -> Selector { pub fn zip(&self, other: &Selector) -> Selector {
if self.0.is_empty() { if self.0.is_empty() {
return Selector(other.0.clone()); return Selector(other.0.clone());
} else if other.0.is_empty() {
return self.clone();
} }
let mut rules: Vec<SelectorKind> = Vec::with_capacity(self.0.len() + other.0.len()); let mut rules: Vec<SelectorKind> = Vec::with_capacity(self.0.len() + other.0.len());
let sel1_split: Vec<&[SelectorKind]> = let sel1_split: Vec<&[SelectorKind]> =

View File

@ -7,3 +7,8 @@ test!(
basic_toplevel, basic_toplevel,
"@media foo {\n a {\n color: red;\n }\n}\n" "@media foo {\n a {\n color: red;\n }\n}\n"
); );
test!(
basic_nested,
"a {\n @media foo {\n color: red;\n }\n}\n",
"@media foo {\n a {\n color: red;\n }\n}\n"
);