Properly consume mixin declarations and add tests for mixins
This commit is contained in:
parent
d4f50c8acc
commit
e1191d7108
118
src/main.rs
118
src/main.rs
@ -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"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user