Properly consume mixin declarations and add tests for mixins

This commit is contained in:
ConnorSkees 2020-01-17 10:44:16 -05:00
parent d4f50c8acc
commit e1191d7108

View File

@ -36,7 +36,7 @@ use crate::css::Css;
use crate::error::SassError; use crate::error::SassError;
use crate::format::PrettyPrinter; use crate::format::PrettyPrinter;
use crate::lexer::Lexer; use crate::lexer::Lexer;
use crate::mixin::{CallArgs, FuncArgs, Mixin}; use crate::mixin::{FuncArgs, Mixin};
use crate::selector::{Attribute, Selector}; use crate::selector::{Attribute, Selector};
use crate::style::Style; use crate::style::Style;
use crate::units::Unit; use crate::units::Unit;
@ -266,7 +266,8 @@ impl<'a> StyleSheetParser<'a> {
rules.push(Stmt::MultilineComment(comment)); rules.push(Stmt::MultilineComment(comment));
} }
TokenKind::AtRule(AtRule::Mixin) => { TokenKind::AtRule(AtRule::Mixin) => {
let (name, mixin) = parse_mixin(&mut self.lexer, self.global_scope.clone()).unwrap(); let (name, mixin) =
parse_mixin(&mut self.lexer, self.global_scope.clone()).unwrap();
self.global_scope.mixins.insert(name, mixin); self.global_scope.mixins.insert(name, mixin);
} }
TokenKind::AtRule(_) => { TokenKind::AtRule(_) => {
@ -276,7 +277,7 @@ impl<'a> StyleSheetParser<'a> {
}) = self.lexer.next() }) = self.lexer.next()
{ {
match eat_at_rule(rule, pos, &mut self.lexer, &mut self.global_scope) { match eat_at_rule(rule, pos, &mut self.lexer, &mut self.global_scope) {
Ok(a) => todo!(), Ok(_) => todo!(),
Err(Printer::Error(pos, message)) => self.error(pos, &message), Err(Printer::Error(pos, message)) => self.error(pos, &message),
Err(Printer::Warn(pos, message)) => self.warn(pos, &message), Err(Printer::Warn(pos, message)) => self.warn(pos, &message),
Err(Printer::Debug(pos, message)) => self.debug(pos, &message), Err(Printer::Debug(pos, message)) => self.debug(pos, &message),
@ -284,9 +285,8 @@ impl<'a> StyleSheetParser<'a> {
} }
} }
_ => { _ => {
if let Some(Token { pos, kind }) = self.lexer.next() { if let Some(Token { pos, .. }) = self.lexer.next() {
dbg!(kind); self.error(pos, "unexpected toplevel token")
self.error(pos.clone(), "unexpected toplevel token")
} else { } else {
unsafe { std::hint::unreachable_unchecked() } unsafe { std::hint::unreachable_unchecked() }
} }
@ -337,41 +337,45 @@ impl<'a> StyleSheetParser<'a> {
} }
} }
fn eat_include<I: std::iter::Iterator<Item = Token>>(toks: &mut Peekable<I>, scope: &Scope) -> Result<Vec<Stmt>, (Pos, &'static str)> { fn eat_include<I: std::iter::Iterator<Item = Token>>(
toks.next(); toks: &mut Peekable<I>,
devour_whitespace(toks); scope: &Scope,
let Token { kind, pos } = toks.next().unwrap(); super_selector: &Selector,
let name = if let TokenKind::Ident(s) = kind { ) -> Result<Vec<Stmt>, (Pos, &'static str)> {
s toks.next();
} else { devour_whitespace(toks);
return Err((pos, "expected identifier")); let Token { kind, pos } = toks.next().unwrap();
}; let name = if let TokenKind::Ident(s) = kind {
s
} else {
return Err((pos, "expected identifier"));
};
devour_whitespace(toks); devour_whitespace(toks);
match toks.next() { match toks.next() {
Some(Token { Some(Token {
kind: TokenKind::Symbol(Symbol::SemiColon), kind: TokenKind::Symbol(Symbol::SemiColon),
.. ..
}) => {} }) => {}
Some(Token { Some(Token {
kind: TokenKind::Symbol(Symbol::OpenParen), kind: TokenKind::Symbol(Symbol::OpenParen),
.. ..
}) => {} }) => {}
Some(Token { pos, .. }) => return Err((pos, "expected `(` or `;`")), Some(Token { pos, .. }) => return Err((pos, "expected `(` or `;`")),
None => return Err((pos, "unexpected EOF")), None => return Err((pos, "unexpected EOF")),
}
let mut mixin = if let Some(m) = scope.mixins.get(&name) {
m.clone()
} else {
return Err((pos, "expected identifier"));
};
let rules = mixin.eval(&Selector(Vec::new()), &mut scope.clone());
devour_whitespace(toks);
Ok(rules)
} }
let mut mixin = if let Some(m) = scope.mixins.get(&name) {
m.clone()
} else {
return Err((pos, "expected identifier"));
};
let rules = mixin.eval(super_selector, &mut scope.clone());
devour_whitespace(toks);
Ok(rules)
}
fn eat_func_args() -> FuncArgs { fn eat_func_args() -> FuncArgs {
todo!() todo!()
} }
@ -534,7 +538,14 @@ pub(crate) fn eat_expr<I: std::iter::Iterator<Item = Token>>(
} }
} }
TokenKind::AtRule(AtRule::Include) => { TokenKind::AtRule(AtRule::Include) => {
return Ok(Expr::Include(eat_include(toks, scope).unwrap())); return Ok(Expr::Include(
eat_include(toks, scope, super_selector).unwrap(),
));
}
TokenKind::AtRule(AtRule::Mixin) => {
toks.next();
let (name, mixin) = parse_mixin(toks, scope.clone()).unwrap();
return Ok(Expr::MixinDecl(name, mixin));
} }
TokenKind::AtRule(_) => { TokenKind::AtRule(_) => {
if let Some(Token { if let Some(Token {
@ -543,8 +554,7 @@ pub(crate) fn eat_expr<I: std::iter::Iterator<Item = Token>>(
}) = toks.next() }) = toks.next()
{ {
if let Ok(a) = eat_at_rule(rule, pos, toks, scope) { if let Ok(a) = eat_at_rule(rule, pos, toks, scope) {
dbg!("hi"); return Ok(a);
// return Ok(a);
} }
} }
} }
@ -561,8 +571,7 @@ pub(crate) fn eat_expr<I: std::iter::Iterator<Item = Token>>(
if let Some(tok) = toks.next() { if let Some(tok) = toks.next() {
values.push(tok.clone()) values.push(tok.clone())
} else { } else {
dbg!("hi"); unsafe { std::hint::unreachable_unchecked() }
// unsafe { std::hint::unreachable_unchecked() }
} }
} }
}; };
@ -1048,9 +1057,24 @@ mod css_misc {
#[cfg(test)] #[cfg(test)]
mod css_mixins { mod css_mixins {
use super::*; use super::*;
// test!( test!(
// basic_mixin, basic_mixin,
// "@mixin a {\n color: red;\n}\n\nb {\n @include a;\n}\n", "@mixin a {\n color: red;\n}\n\nb {\n @include a;\n}\n",
// "b {\n color: red;\n}\n" "b {\n color: red;\n}\n"
// ); );
test!(
mixin_two_styles,
"@mixin a {\n color: red;\n color: blue;\n}\n\nb {\n @include a;\n}\n",
"b {\n color: red;\n color: blue;\n}\n"
);
test!(
mixin_ruleset,
"@mixin a {\n b {\n color: red;\n }\n}\nb {\n @include a;\n}\n",
"b b {\n color: red;\n}\n"
);
test!(
mixin_two_rulesets,
"@mixin a {\n b {\n color: red;\n }\n c {\n color: blue;\n }\n}\nd {\n @include a;\n}\n",
"d b {\n color: red;\n}\nd c {\n color: blue;\n}\n"
);
} }