grass/src/atrule/mixin.rs

241 lines
7.8 KiB
Rust
Raw Normal View History

use std::vec::IntoIter;
2020-04-12 19:37:12 -04:00
use codemap::Spanned;
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;
use crate::scope::Scope;
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)]
pub(crate) struct Mixin {
2020-01-12 20:15:40 -05:00
scope: Scope,
args: FuncArgs,
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 {
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
}
pub fn decl_from_tokens<I: Iterator<Item = Token>>(
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
}
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();
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-02 13:33:26 -04:00
break;
}
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?,
None => match &arg.default {
Some(v) => Value::from_tokens(
&mut v.iter().cloned().peekmore(),
scope,
super_selector,
)?,
2020-04-12 19:37:12 -04:00
None => {
return Err(
(format!("Missing argument ${}.", &arg.name), args.span()).into()
)
}
},
2020-01-19 10:58:38 -05:00
},
};
self.scope.insert_var(&arg.name, val)?;
2020-01-17 14:44:55 -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>>> {
let mut stmts = Vec::new();
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 {
AtRule::For(f) => {
stmts.extend(f.ruleset_eval(&mut self.scope, super_selector)?)
2020-04-12 19:37:12 -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
}
Expr::VariableDecl(name, val) => {
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
}
}
Ok(stmts)
2020-01-12 20:15:40 -05:00
}
}
pub(crate) fn eat_include<I: Iterator<Item = Token>>(
toks: &mut PeekMoreIterator<I>,
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-03-29 13:28:17 -04:00
devour_whitespace_or_comment(toks)?;
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() {
match tok.kind {
2020-04-12 19:37:12 -04:00
';' => CallArgs::new(name.span),
2020-03-29 13:28:17 -04:00
'(' => {
let tmp = eat_call_args(toks)?;
2020-03-29 13:28:17 -04:00
devour_whitespace_or_comment(toks)?;
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!(),
}
}
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()),
}
} else {
2020-04-12 19:37:12 -04:00
return Err(("unexpected EOF", name.span).into());
};
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)?;
let rules = mixin
.args(args, scope, super_selector)?
.content(content)
.call(super_selector)?;
Ok(rules)
}