2020-01-17 16:23:21 -05:00
|
|
|
use std::vec::IntoIter;
|
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
use codemap::Spanned;
|
|
|
|
|
2020-04-20 03:45:28 -04:00
|
|
|
use peekmore::{PeekMore, PeekMoreIterator};
|
|
|
|
|
2020-03-22 15:08:13 -04:00
|
|
|
use super::eat_stmts;
|
|
|
|
|
2020-01-25 11:00:29 -05:00
|
|
|
use crate::args::{eat_call_args, eat_func_args, CallArgs, FuncArgs};
|
2020-03-01 17:06:55 -05:00
|
|
|
use crate::atrule::AtRule;
|
2020-03-29 13:28:17 -04:00
|
|
|
use crate::error::SassResult;
|
2020-03-17 20:13:53 -04:00
|
|
|
use crate::scope::Scope;
|
2020-01-17 08:14:10 -05:00
|
|
|
use crate::selector::Selector;
|
2020-03-29 13:28:17 -04:00
|
|
|
use crate::utils::{
|
|
|
|
devour_whitespace, devour_whitespace_or_comment, eat_ident, read_until_closing_curly_brace,
|
|
|
|
};
|
2020-04-02 13:45:14 -04:00
|
|
|
use crate::value::Value;
|
2020-03-29 13:28:17 -04:00
|
|
|
use crate::{eat_expr, Expr, RuleSet, Stmt, Token};
|
2020-01-12 20:15:40 -05:00
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
2020-01-20 13:15:47 -05:00
|
|
|
pub(crate) struct Mixin {
|
2020-01-12 20:15:40 -05:00
|
|
|
scope: Scope,
|
|
|
|
args: FuncArgs,
|
2020-04-20 03:45:28 -04:00
|
|
|
body: PeekMoreIterator<IntoIter<Token>>,
|
2020-04-12 19:37:12 -04:00
|
|
|
content: Vec<Spanned<Stmt>>,
|
2020-01-12 20:15:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Mixin {
|
2020-04-12 19:37:12 -04:00
|
|
|
pub fn new(
|
|
|
|
scope: Scope,
|
|
|
|
args: FuncArgs,
|
|
|
|
body: Vec<Token>,
|
|
|
|
content: Vec<Spanned<Stmt>>,
|
|
|
|
) -> Self {
|
2020-04-20 03:45:28 -04:00
|
|
|
let body = body.into_iter().peekmore();
|
2020-03-22 15:08:13 -04:00
|
|
|
Mixin {
|
|
|
|
scope,
|
|
|
|
args,
|
|
|
|
body,
|
|
|
|
content,
|
|
|
|
}
|
2020-01-12 20:15:40 -05:00
|
|
|
}
|
|
|
|
|
2020-01-20 09:39:06 -05:00
|
|
|
pub fn decl_from_tokens<I: Iterator<Item = Token>>(
|
2020-04-20 03:45:28 -04:00
|
|
|
toks: &mut PeekMoreIterator<I>,
|
2020-01-18 19:54:47 -05:00
|
|
|
scope: &Scope,
|
2020-03-01 12:03:14 -05:00
|
|
|
super_selector: &Selector,
|
2020-04-12 19:37:12 -04:00
|
|
|
) -> SassResult<Spanned<(String, Mixin)>> {
|
2020-01-18 19:54:47 -05:00
|
|
|
devour_whitespace(toks);
|
2020-04-12 19:37:12 -04:00
|
|
|
let Spanned { node: name, span } = eat_ident(toks, scope, super_selector)?;
|
2020-01-18 19:54:47 -05:00
|
|
|
devour_whitespace(toks);
|
|
|
|
let args = match toks.next() {
|
2020-03-29 13:28:17 -04:00
|
|
|
Some(Token { kind: '(', .. }) => eat_func_args(toks, scope, super_selector)?,
|
|
|
|
Some(Token { kind: '{', .. }) => FuncArgs::new(),
|
2020-04-12 19:37:12 -04:00
|
|
|
Some(t) => return Err(("expected \"{\".", t.pos()).into()),
|
|
|
|
None => return Err(("expected \"{\".", span).into()),
|
2020-01-18 19:54:47 -05:00
|
|
|
};
|
|
|
|
|
2020-02-02 18:01:09 -05:00
|
|
|
devour_whitespace(toks);
|
|
|
|
|
2020-03-29 13:28:17 -04:00
|
|
|
let mut body = read_until_closing_curly_brace(toks);
|
|
|
|
body.push(toks.next().unwrap());
|
2020-01-18 19:54:47 -05:00
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
Ok(Spanned {
|
|
|
|
node: (name, Mixin::new(scope.clone(), args, body, Vec::new())),
|
|
|
|
span,
|
|
|
|
})
|
2020-03-22 15:08:13 -04:00
|
|
|
}
|
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
pub fn content(mut self, content: Vec<Spanned<Stmt>>) -> Mixin {
|
2020-03-22 15:08:13 -04:00
|
|
|
self.content = content;
|
|
|
|
self
|
2020-01-18 19:54:47 -05:00
|
|
|
}
|
|
|
|
|
2020-04-04 18:17:04 -04:00
|
|
|
pub fn args(
|
|
|
|
mut self,
|
|
|
|
mut args: CallArgs,
|
|
|
|
scope: &Scope,
|
|
|
|
super_selector: &Selector,
|
|
|
|
) -> SassResult<Mixin> {
|
2020-01-19 10:58:38 -05:00
|
|
|
for (idx, arg) in self.args.0.iter().enumerate() {
|
2020-04-02 13:33:26 -04:00
|
|
|
if arg.is_variadic {
|
2020-04-12 19:37:12 -04:00
|
|
|
let span = args.span();
|
2020-04-04 18:17:04 -04:00
|
|
|
self.scope.insert_var(
|
|
|
|
&arg.name,
|
2020-04-12 19:37:12 -04:00
|
|
|
Spanned {
|
|
|
|
node: Value::ArgList(args.get_variadic(scope, super_selector)?),
|
|
|
|
span,
|
|
|
|
},
|
2020-04-04 18:17:04 -04:00
|
|
|
)?;
|
2020-04-02 13:33:26 -04:00
|
|
|
break;
|
|
|
|
}
|
2020-04-04 18:17:04 -04:00
|
|
|
let val = match args.get_positional(idx, scope, super_selector) {
|
|
|
|
Some(v) => v?,
|
|
|
|
None => match args.get_named(arg.name.clone(), scope, super_selector) {
|
|
|
|
Some(v) => v?,
|
2020-02-17 09:33:52 -05:00
|
|
|
None => match &arg.default {
|
2020-04-04 18:17:04 -04:00
|
|
|
Some(v) => Value::from_tokens(
|
2020-04-20 03:45:28 -04:00
|
|
|
&mut v.iter().cloned().peekmore(),
|
2020-04-04 18:17:04 -04:00
|
|
|
scope,
|
|
|
|
super_selector,
|
|
|
|
)?,
|
2020-04-12 19:37:12 -04:00
|
|
|
None => {
|
|
|
|
return Err(
|
|
|
|
(format!("Missing argument ${}.", &arg.name), args.span()).into()
|
|
|
|
)
|
|
|
|
}
|
2020-02-17 09:33:52 -05:00
|
|
|
},
|
2020-01-19 10:58:38 -05:00
|
|
|
},
|
|
|
|
};
|
2020-02-29 20:09:41 -05:00
|
|
|
self.scope.insert_var(&arg.name, val)?;
|
2020-01-17 14:44:55 -05:00
|
|
|
}
|
2020-02-17 09:33:52 -05:00
|
|
|
Ok(self)
|
2020-01-17 14:44:55 -05:00
|
|
|
}
|
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
pub fn call(mut self, super_selector: &Selector) -> SassResult<Vec<Spanned<Stmt>>> {
|
2020-01-18 21:15:09 -05:00
|
|
|
self.eval(super_selector)
|
2020-01-18 09:03:38 -05:00
|
|
|
}
|
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
fn eval(&mut self, super_selector: &Selector) -> SassResult<Vec<Spanned<Stmt>>> {
|
2020-01-17 08:14:10 -05:00
|
|
|
let mut stmts = Vec::new();
|
2020-03-01 08:20:59 -05:00
|
|
|
while let Some(expr) = eat_expr(&mut self.body, &mut self.scope, super_selector)? {
|
2020-04-12 19:37:12 -04:00
|
|
|
let span = expr.span;
|
|
|
|
match expr.node {
|
2020-03-01 17:06:55 -05:00
|
|
|
Expr::AtRule(a) => match a {
|
2020-04-23 13:57:10 -04:00
|
|
|
AtRule::For(f) => {
|
|
|
|
stmts.extend(f.ruleset_eval(&mut self.scope, super_selector)?)
|
2020-04-12 19:37:12 -04:00
|
|
|
}
|
2020-04-23 13:57:10 -04:00
|
|
|
AtRule::Include(s) | AtRule::While(s) | AtRule::Each(s) => stmts.extend(s),
|
2020-04-05 17:34:30 -04:00
|
|
|
AtRule::If(i) => stmts.extend(i.eval(&mut self.scope.clone(), super_selector)?),
|
2020-03-22 15:08:13 -04:00
|
|
|
AtRule::Content => stmts.extend(self.content.clone()),
|
2020-04-12 19:37:12 -04:00
|
|
|
AtRule::Return(..) => {
|
|
|
|
return Err(("This at-rule is not allowed here.", span).into())
|
|
|
|
}
|
|
|
|
AtRule::Debug(..) | AtRule::Warn(..) => todo!(),
|
|
|
|
r => stmts.push(Spanned {
|
|
|
|
node: Stmt::AtRule(r),
|
|
|
|
span,
|
|
|
|
}),
|
2020-03-01 17:06:55 -05:00
|
|
|
},
|
2020-04-12 19:37:12 -04:00
|
|
|
Expr::Style(s) => stmts.push(Spanned {
|
|
|
|
node: Stmt::Style(s),
|
|
|
|
span,
|
|
|
|
}),
|
|
|
|
Expr::Styles(s) => stmts.extend(
|
|
|
|
s.into_iter()
|
|
|
|
.map(Box::new)
|
|
|
|
.map(Stmt::Style)
|
|
|
|
.map(|style| Spanned { node: style, span }),
|
|
|
|
),
|
2020-02-28 18:32:11 -05:00
|
|
|
Expr::FunctionDecl(..) => {
|
2020-04-12 19:37:12 -04:00
|
|
|
return Err(("Mixins may not contain function declarations.", span).into())
|
2020-01-26 15:26:45 -05:00
|
|
|
}
|
2020-02-28 18:32:11 -05:00
|
|
|
Expr::MixinDecl(..) => {
|
2020-04-12 19:37:12 -04:00
|
|
|
return Err(("Mixins may not contain mixin declarations.", span).into())
|
2020-02-28 18:32:11 -05:00
|
|
|
}
|
2020-01-18 21:15:09 -05:00
|
|
|
Expr::Selector(selector) => {
|
|
|
|
let rules = self.eval(&super_selector.zip(&selector))?;
|
2020-04-12 19:37:12 -04:00
|
|
|
stmts.push(Spanned {
|
|
|
|
node: Stmt::RuleSet(RuleSet {
|
|
|
|
super_selector: super_selector.clone(),
|
|
|
|
selector,
|
|
|
|
rules,
|
|
|
|
}),
|
|
|
|
span,
|
|
|
|
});
|
2020-01-12 20:15:40 -05:00
|
|
|
}
|
2020-01-17 08:14:10 -05:00
|
|
|
Expr::VariableDecl(name, val) => {
|
2020-02-29 20:09:41 -05:00
|
|
|
self.scope.insert_var(&name, *val)?;
|
2020-01-14 19:34:13 -05:00
|
|
|
}
|
2020-04-12 19:37:12 -04:00
|
|
|
Expr::MultilineComment(s) => stmts.push(Spanned {
|
|
|
|
node: Stmt::MultilineComment(s),
|
|
|
|
span,
|
|
|
|
}),
|
2020-01-12 20:15:40 -05:00
|
|
|
}
|
|
|
|
}
|
2020-01-17 21:42:51 -05:00
|
|
|
Ok(stmts)
|
2020-01-12 20:15:40 -05:00
|
|
|
}
|
|
|
|
}
|
2020-01-18 23:55:11 -05:00
|
|
|
|
2020-01-20 13:15:47 -05:00
|
|
|
pub(crate) fn eat_include<I: Iterator<Item = Token>>(
|
2020-04-20 03:45:28 -04:00
|
|
|
toks: &mut PeekMoreIterator<I>,
|
2020-01-18 23:55:11 -05:00
|
|
|
scope: &Scope,
|
|
|
|
super_selector: &Selector,
|
2020-04-12 19:37:12 -04:00
|
|
|
) -> SassResult<Vec<Spanned<Stmt>>> {
|
2020-03-29 13:28:17 -04:00
|
|
|
devour_whitespace_or_comment(toks)?;
|
|
|
|
let name = eat_ident(toks, scope, super_selector)?;
|
2020-01-18 23:55:11 -05:00
|
|
|
|
2020-03-29 13:28:17 -04:00
|
|
|
devour_whitespace_or_comment(toks)?;
|
2020-01-18 23:55:11 -05:00
|
|
|
|
2020-04-12 19:37:12 -04:00
|
|
|
let mut has_content = false;
|
2020-03-22 15:08:13 -04:00
|
|
|
|
2020-04-02 13:33:26 -04:00
|
|
|
let args = if let Some(tok) = toks.next() {
|
2020-01-18 23:55:11 -05:00
|
|
|
match tok.kind {
|
2020-04-12 19:37:12 -04:00
|
|
|
';' => CallArgs::new(name.span),
|
2020-03-29 13:28:17 -04:00
|
|
|
'(' => {
|
2020-04-23 13:57:10 -04:00
|
|
|
let tmp = eat_call_args(toks)?;
|
2020-03-29 13:28:17 -04:00
|
|
|
devour_whitespace_or_comment(toks)?;
|
2020-01-26 13:52:47 -05:00
|
|
|
if let Some(tok) = toks.next() {
|
2020-03-22 15:08:13 -04:00
|
|
|
match tok.kind {
|
2020-03-29 13:28:17 -04:00
|
|
|
';' => {}
|
2020-04-12 19:37:12 -04:00
|
|
|
'{' => has_content = true,
|
2020-03-22 15:08:13 -04:00
|
|
|
_ => todo!(),
|
|
|
|
}
|
2020-01-26 13:52:47 -05:00
|
|
|
}
|
|
|
|
tmp
|
2020-01-26 15:26:45 -05:00
|
|
|
}
|
2020-03-29 13:28:17 -04:00
|
|
|
'{' => {
|
2020-04-12 19:37:12 -04:00
|
|
|
has_content = true;
|
|
|
|
CallArgs::new(name.span)
|
2020-03-22 15:08:13 -04:00
|
|
|
}
|
2020-04-12 19:37:12 -04:00
|
|
|
_ => return Err(("expected \"{\".", tok.pos()).into()),
|
2020-01-18 23:55:11 -05:00
|
|
|
}
|
|
|
|
} else {
|
2020-04-12 19:37:12 -04:00
|
|
|
return Err(("unexpected EOF", name.span).into());
|
2020-01-18 23:55:11 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
devour_whitespace(toks);
|
|
|
|
|
2020-03-22 15:08:13 -04:00
|
|
|
let content = if let Some(tok) = toks.peek() {
|
2020-03-29 13:28:17 -04:00
|
|
|
if tok.kind == '{' {
|
2020-03-22 15:08:13 -04:00
|
|
|
toks.next();
|
|
|
|
eat_stmts(toks, &mut scope.clone(), super_selector)?
|
2020-04-12 19:37:12 -04:00
|
|
|
} else if has_content {
|
2020-03-22 15:08:13 -04:00
|
|
|
eat_stmts(toks, &mut scope.clone(), super_selector)?
|
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Vec::new()
|
|
|
|
};
|
|
|
|
|
2020-04-21 18:22:26 -04:00
|
|
|
let mixin = scope.get_mixin(name)?;
|
2020-01-18 23:55:11 -05:00
|
|
|
|
2020-04-04 18:17:04 -04:00
|
|
|
let rules = mixin
|
|
|
|
.args(args, scope, super_selector)?
|
|
|
|
.content(content)
|
|
|
|
.call(super_selector)?;
|
2020-01-18 23:55:11 -05:00
|
|
|
Ok(rules)
|
|
|
|
}
|