grass/src/mixin.rs

123 lines
4.0 KiB
Rust
Raw Normal View History

use std::iter::Peekable;
use std::vec::IntoIter;
2020-01-18 19:54:47 -05:00
use crate::common::{Pos, Printer, Scope, Symbol};
use crate::function::{eat_func_args, CallArgs, FuncArgs};
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)]
pub struct Mixin {
scope: Scope,
args: FuncArgs,
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-18 19:54:47 -05:00
pub fn from_tokens<I: Iterator<Item = Token>>(
toks: &mut Peekable<I>,
scope: &Scope,
) -> Result<(String, Mixin), Printer> {
let Token { pos, .. } = toks
.next()
.expect("this must exist because we have already peeked");
devour_whitespace(toks);
let name = if let Some(Token {
kind: TokenKind::Ident(s),
..
}) = toks.next()
{
s
} else {
return Err(Printer::Error(
pos,
String::from("expected identifier after mixin declaration"),
));
};
devour_whitespace(toks);
let args = match toks.next() {
Some(Token {
kind: TokenKind::Symbol(Symbol::OpenParen),
..
}) => eat_func_args(toks),
Some(Token {
kind: TokenKind::Symbol(Symbol::OpenCurlyBrace),
..
}) => FuncArgs::new(),
_ => return Err(Printer::Error(pos, String::from("expected `(` or `{`"))),
};
let mut nesting = 1;
let mut body = Vec::new();
while nesting > 0 {
if let Some(tok) = toks.next() {
match &tok.kind {
TokenKind::Symbol(Symbol::OpenCurlyBrace)
// interpolation token eats the opening brace but not the closing
| TokenKind::Interpolation => nesting += 1,
TokenKind::Symbol(Symbol::CloseCurlyBrace) => nesting -= 1,
_ => {}
}
body.push(tok)
} else {
return Err(Printer::Error(pos, String::from("unexpected EOF")));
}
}
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-17 14:44:55 -05:00
for (idx, arg) in args.0.iter().enumerate() {
if arg.is_named() {
todo!("keyword args")
} else {
2020-01-18 19:00:49 -05:00
self.scope.vars.insert(
self.args
.0
.get(idx)
.expect("too many args passed to mixin")
.name
.clone(),
arg.val.clone(),
);
2020-01-17 14:44:55 -05:00
}
}
self
}
2020-01-18 20:24:28 -05:00
pub fn call(mut self, super_selector: &Selector) -> Result<Vec<Stmt>, (Pos, &'static str)> {
2020-01-18 21:15:09 -05:00
self.eval(super_selector)
2020-01-18 09:03:38 -05:00
}
2020-01-18 21:15:09 -05:00
pub fn eval(&mut self, super_selector: &Selector) -> Result<Vec<Stmt>, (Pos, &'static str)> {
let mut stmts = Vec::new();
2020-01-18 21:15:09 -05:00
while let Some(expr) = eat_expr(&mut self.body, &mut self.scope, super_selector)? {
match expr {
Expr::Style(s) => stmts.push(Stmt::Style(s)),
2020-01-18 19:54:47 -05:00
Expr::Include(_) | Expr::MixinDecl(_, _) => todo!(),
2020-01-18 21:15:09 -05:00
Expr::Selector(selector) => {
let rules = self.eval(&super_selector.zip(&selector))?;
stmts.push(Stmt::RuleSet(RuleSet {
super_selector: super_selector.clone(),
2020-01-18 21:15:09 -05:00
selector,
rules,
}));
2020-01-12 20:15:40 -05:00
}
Expr::VariableDecl(name, val) => {
2020-01-18 21:15:09 -05:00
self.scope.vars.insert(name, val);
2020-01-14 19:34:13 -05:00
}
Expr::MultilineComment(s) => stmts.push(Stmt::MultilineComment(s)),
2020-01-12 20:15:40 -05:00
}
}
Ok(stmts)
2020-01-12 20:15:40 -05:00
}
}