2020-01-17 16:23:21 -05:00
|
|
|
use std::iter::Peekable;
|
|
|
|
use std::vec::IntoIter;
|
|
|
|
|
2020-01-25 11:00:29 -05:00
|
|
|
use crate::args::{eat_call_args, eat_func_args, CallArgs, FuncArgs};
|
2020-01-25 20:53:51 -05:00
|
|
|
use crate::common::{Pos, Scope, Symbol};
|
2020-02-16 10:54:25 -05:00
|
|
|
use crate::error::{SassError, SassResult};
|
2020-01-17 08:14:10 -05:00
|
|
|
use crate::selector::Selector;
|
2020-01-18 19:54:47 -05:00
|
|
|
use crate::utils::devour_whitespace;
|
|
|
|
use crate::{eat_expr, Expr, RuleSet, Stmt, Token, TokenKind};
|
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-01-17 08:14:10 -05:00
|
|
|
body: Peekable<IntoIter<Token>>,
|
2020-01-12 20:15:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Mixin {
|
|
|
|
pub fn new(scope: Scope, args: FuncArgs, body: Vec<Token>) -> Self {
|
2020-01-17 21:20:56 -05:00
|
|
|
let body = body.into_iter().peekable();
|
2020-01-18 21:15:09 -05:00
|
|
|
Mixin { scope, args, body }
|
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-01-18 19:54:47 -05:00
|
|
|
toks: &mut Peekable<I>,
|
|
|
|
scope: &Scope,
|
2020-01-20 09:39:06 -05:00
|
|
|
) -> Result<(String, Mixin), (Pos, String)> {
|
2020-01-25 13:07:55 -05:00
|
|
|
let Token { pos, kind } = toks
|
2020-01-18 19:54:47 -05:00
|
|
|
.next()
|
|
|
|
.expect("this must exist because we have already peeked");
|
|
|
|
devour_whitespace(toks);
|
2020-01-25 13:07:55 -05:00
|
|
|
let name = match kind {
|
|
|
|
TokenKind::Ident(s) => s,
|
2020-01-19 01:01:02 -05:00
|
|
|
_ => {
|
2020-01-20 09:39:06 -05:00
|
|
|
return Err((
|
2020-01-19 01:01:02 -05:00
|
|
|
pos,
|
|
|
|
String::from("expected identifier after mixin declaration"),
|
|
|
|
))
|
|
|
|
}
|
2020-01-18 19:54:47 -05:00
|
|
|
};
|
|
|
|
devour_whitespace(toks);
|
|
|
|
let args = match toks.next() {
|
|
|
|
Some(Token {
|
|
|
|
kind: TokenKind::Symbol(Symbol::OpenParen),
|
|
|
|
..
|
2020-01-26 09:13:39 -05:00
|
|
|
}) => eat_func_args(toks, scope),
|
2020-01-18 19:54:47 -05:00
|
|
|
Some(Token {
|
|
|
|
kind: TokenKind::Symbol(Symbol::OpenCurlyBrace),
|
|
|
|
..
|
|
|
|
}) => FuncArgs::new(),
|
2020-01-20 09:39:06 -05:00
|
|
|
_ => return Err((pos, String::from("expected `(` or `{`"))),
|
2020-01-18 19:54:47 -05:00
|
|
|
};
|
|
|
|
|
2020-02-02 18:01:09 -05:00
|
|
|
devour_whitespace(toks);
|
|
|
|
|
2020-01-18 19:54:47 -05:00
|
|
|
let mut nesting = 1;
|
|
|
|
let mut body = Vec::new();
|
|
|
|
|
|
|
|
while nesting > 0 {
|
|
|
|
if let Some(tok) = toks.next() {
|
|
|
|
match &tok.kind {
|
2020-01-19 01:01:02 -05:00
|
|
|
TokenKind::Symbol(Symbol::OpenCurlyBrace)
|
|
|
|
// interpolation token eats the opening brace but not the closing
|
|
|
|
| TokenKind::Interpolation => nesting += 1,
|
|
|
|
TokenKind::Symbol(Symbol::CloseCurlyBrace) => nesting -= 1,
|
|
|
|
_ => {}
|
|
|
|
}
|
2020-01-18 19:54:47 -05:00
|
|
|
body.push(tok)
|
|
|
|
} else {
|
2020-01-20 09:39:06 -05:00
|
|
|
return Err((pos, String::from("unexpected EOF")));
|
2020-01-18 19:54:47 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok((name, Mixin::new(scope.clone(), args, body)))
|
|
|
|
}
|
|
|
|
|
2020-01-18 20:24:28 -05:00
|
|
|
pub fn args(mut self, args: &CallArgs) -> Mixin {
|
2020-01-19 10:58:38 -05:00
|
|
|
for (idx, arg) in self.args.0.iter().enumerate() {
|
|
|
|
let val = match args.get(&format!("{}", idx)) {
|
2020-01-19 12:10:35 -05:00
|
|
|
Some(v) => v.clone(),
|
2020-01-19 10:58:38 -05:00
|
|
|
None => match args.get(&arg.name) {
|
2020-01-19 12:10:35 -05:00
|
|
|
Some(v) => v.clone(),
|
2020-01-19 10:58:38 -05:00
|
|
|
None => arg.default.clone().expect("missing variable!"),
|
|
|
|
},
|
|
|
|
};
|
2020-02-08 17:26:01 -05:00
|
|
|
self.scope.insert_var(&arg.name, val);
|
2020-01-17 14:44:55 -05:00
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2020-02-16 10:54:25 -05:00
|
|
|
pub fn call(mut self, super_selector: &Selector) -> SassResult<Vec<Stmt>> {
|
2020-01-18 21:15:09 -05:00
|
|
|
self.eval(super_selector)
|
2020-01-18 09:03:38 -05:00
|
|
|
}
|
|
|
|
|
2020-02-16 10:54:25 -05:00
|
|
|
fn eval(&mut self, super_selector: &Selector) -> SassResult<Vec<Stmt>> {
|
2020-01-17 08:14:10 -05:00
|
|
|
let mut stmts = Vec::new();
|
2020-01-18 23:55:11 -05:00
|
|
|
while let Some(expr) = eat_expr(&mut self.body, &self.scope, super_selector)? {
|
2020-01-17 08:14:10 -05:00
|
|
|
match expr {
|
|
|
|
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
2020-02-09 19:07:44 -05:00
|
|
|
Expr::Styles(s) => stmts.extend(s.into_iter().map(Stmt::Style)),
|
2020-01-26 13:52:47 -05:00
|
|
|
Expr::Include(s) => stmts.extend(s),
|
2020-01-26 15:26:45 -05:00
|
|
|
Expr::MixinDecl(..) | Expr::FunctionDecl(..) | Expr::Debug(..) | Expr::Warn(..) => {
|
|
|
|
todo!()
|
|
|
|
}
|
2020-01-18 21:15:09 -05:00
|
|
|
Expr::Selector(selector) => {
|
|
|
|
let rules = self.eval(&super_selector.zip(&selector))?;
|
2020-01-17 08:14:10 -05:00
|
|
|
stmts.push(Stmt::RuleSet(RuleSet {
|
|
|
|
super_selector: super_selector.clone(),
|
2020-01-18 21:15:09 -05:00
|
|
|
selector,
|
2020-01-17 08:14:10 -05:00
|
|
|
rules,
|
|
|
|
}));
|
2020-01-12 20:15:40 -05:00
|
|
|
}
|
2020-01-17 08:14:10 -05:00
|
|
|
Expr::VariableDecl(name, val) => {
|
2020-02-16 18:03:19 -05:00
|
|
|
self.scope.insert_var(&name, *val);
|
2020-01-14 19:34:13 -05:00
|
|
|
}
|
2020-01-17 08:14:10 -05:00
|
|
|
Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)),
|
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-01-18 23:55:11 -05:00
|
|
|
toks: &mut Peekable<I>,
|
|
|
|
scope: &Scope,
|
|
|
|
super_selector: &Selector,
|
2020-02-16 10:54:25 -05:00
|
|
|
) -> SassResult<Vec<Stmt>> {
|
2020-01-18 23:55:11 -05:00
|
|
|
toks.next();
|
|
|
|
devour_whitespace(toks);
|
|
|
|
let Token { kind, pos } = toks
|
|
|
|
.next()
|
|
|
|
.expect("this must exist because we have already peeked");
|
|
|
|
let name = match kind {
|
|
|
|
TokenKind::Ident(s) => s,
|
2020-02-16 10:54:25 -05:00
|
|
|
_ => return Err(SassError::new("Expected identifier.", pos)),
|
2020-01-18 23:55:11 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
devour_whitespace(toks);
|
|
|
|
|
|
|
|
let args = if let Some(tok) = toks.next() {
|
|
|
|
match tok.kind {
|
|
|
|
TokenKind::Symbol(Symbol::SemiColon) => CallArgs::new(),
|
2020-01-26 13:52:47 -05:00
|
|
|
TokenKind::Symbol(Symbol::OpenParen) => {
|
2020-02-16 21:34:52 -05:00
|
|
|
let tmp = eat_call_args(toks, scope)?;
|
2020-01-26 13:52:47 -05:00
|
|
|
devour_whitespace(toks);
|
|
|
|
if let Some(tok) = toks.next() {
|
|
|
|
assert_eq!(tok.kind, TokenKind::Symbol(Symbol::SemiColon));
|
|
|
|
}
|
|
|
|
tmp
|
2020-01-26 15:26:45 -05:00
|
|
|
}
|
2020-02-16 10:54:25 -05:00
|
|
|
_ => return Err(SassError::new("expected `(` or `;`", pos)),
|
2020-01-18 23:55:11 -05:00
|
|
|
}
|
|
|
|
} else {
|
2020-02-16 10:54:25 -05:00
|
|
|
return Err(SassError::new("unexpected EOF", pos));
|
2020-01-18 23:55:11 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
devour_whitespace(toks);
|
|
|
|
|
2020-02-08 17:26:01 -05:00
|
|
|
let mixin = match scope.get_mixin(&name) {
|
|
|
|
Ok(m) => m.clone(),
|
2020-02-16 10:54:25 -05:00
|
|
|
_ => return Err(SassError::new("Expected identifier.", pos)),
|
2020-01-18 23:55:11 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
let rules = mixin.args(&args).call(super_selector)?;
|
|
|
|
Ok(rules)
|
|
|
|
}
|