Very basic @function implementation
This commit is contained in:
parent
d4e99051c7
commit
b0e1826449
@ -0,0 +1,70 @@
|
||||
use std::iter::Peekable;
|
||||
use std::vec::IntoIter;
|
||||
|
||||
use crate::common::{Pos, Scope, Symbol};
|
||||
use crate::args::{eat_func_args, FuncArgs};
|
||||
use crate::utils::devour_whitespace;
|
||||
use crate::{Token, TokenKind};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Function {
|
||||
scope: Scope,
|
||||
args: FuncArgs,
|
||||
body: Peekable<IntoIter<Token>>,
|
||||
}
|
||||
|
||||
|
||||
impl Function {
|
||||
pub fn new(scope: Scope, args: FuncArgs, body: Vec<Token>) -> Self {
|
||||
let body = body.into_iter().peekable();
|
||||
Function { scope, args, body }
|
||||
}
|
||||
|
||||
pub fn decl_from_tokens<I: Iterator<Item = Token>>(
|
||||
toks: &mut Peekable<I>,
|
||||
scope: &Scope,
|
||||
) -> Result<(String, Function), (Pos, String)> {
|
||||
let Token { pos, kind } = toks
|
||||
.next()
|
||||
.expect("this must exist because we have already peeked");
|
||||
devour_whitespace(toks);
|
||||
let name = match kind {
|
||||
TokenKind::Ident(s) => s,
|
||||
_ => {
|
||||
return Err((
|
||||
pos,
|
||||
String::from("expected identifier after function declaration"),
|
||||
))
|
||||
}
|
||||
};
|
||||
devour_whitespace(toks);
|
||||
let args = match toks.next() {
|
||||
Some(Token {
|
||||
kind: TokenKind::Symbol(Symbol::OpenParen),
|
||||
..
|
||||
}) => eat_func_args(toks),
|
||||
_ => return Err((pos, String::from("expected `(` after function declaration"))),
|
||||
};
|
||||
|
||||
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((pos, String::from("unexpected EOF")));
|
||||
}
|
||||
}
|
||||
|
||||
Ok((name, Function::new(scope.clone(), args, body)))
|
||||
}
|
||||
|
||||
}
|
17
src/lib.rs
17
src/lib.rs
@ -52,6 +52,7 @@ use crate::common::{Keyword, Op, Pos, Scope, Symbol, Whitespace};
|
||||
use crate::css::Css;
|
||||
use crate::error::SassError;
|
||||
use crate::format::PrettyPrinter;
|
||||
use crate::function::Function;
|
||||
use crate::imports::import;
|
||||
use crate::lexer::Lexer;
|
||||
use crate::mixin::{eat_include, Mixin};
|
||||
@ -201,6 +202,7 @@ enum Expr {
|
||||
VariableDecl(String, Vec<Token>),
|
||||
/// A mixin declaration `@mixin foo {}`
|
||||
MixinDecl(String, Mixin),
|
||||
FunctionDecl(String, Function),
|
||||
/// An include statement `@include foo;`
|
||||
Include(Vec<Stmt>),
|
||||
/// A multiline comment: `/* foobar */`
|
||||
@ -398,11 +400,6 @@ impl<'a> StyleSheetParser<'a> {
|
||||
rules.extend(new_rules);
|
||||
self.global_scope.merge(new_scope);
|
||||
}
|
||||
TokenKind::AtRule(AtRuleKind::Mixin) => {
|
||||
let (name, mixin) =
|
||||
Mixin::decl_from_tokens(&mut self.lexer, &self.global_scope).unwrap();
|
||||
self.global_scope.mixins.insert(name, mixin);
|
||||
}
|
||||
TokenKind::AtRule(_) => {
|
||||
if let Some(Token {
|
||||
kind: TokenKind::AtRule(ref rule),
|
||||
@ -411,10 +408,10 @@ impl<'a> StyleSheetParser<'a> {
|
||||
{
|
||||
match eat_at_rule(rule, pos, &mut self.lexer, &self.global_scope) {
|
||||
AtRule::Mixin(name, mixin) => {self.global_scope.mixins.insert(name, mixin);},
|
||||
AtRule::Function(name, func) => {self.global_scope.functions.insert(name, func);},
|
||||
AtRule::Error(pos, message) => self.error(pos, &message),
|
||||
AtRule::Warn(pos, message) => self.warn(pos, &message),
|
||||
AtRule::Debug(pos, message) => self.debug(pos, &message),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -437,6 +434,9 @@ impl<'a> StyleSheetParser<'a> {
|
||||
Expr::MixinDecl(name, mixin) => {
|
||||
scope.mixins.insert(name, mixin);
|
||||
}
|
||||
Expr::FunctionDecl(name, func) => {
|
||||
scope.functions.insert(name, func);
|
||||
}
|
||||
Expr::Selector(s) => {
|
||||
self.scope += 1;
|
||||
let rules = self.eat_rules(&super_selector.zip(&s), scope);
|
||||
@ -512,7 +512,7 @@ fn eat_at_rule<I: Iterator<Item = Token>>(
|
||||
Ok(m) => m,
|
||||
Err(e) => return AtRule::Error(e.0, e.1),
|
||||
};
|
||||
Ok(Expr::MixinDecl(name, mixin))
|
||||
AtRule::Function(name, mixin)
|
||||
}
|
||||
AtRuleKind::Use => todo!("@use not yet implemented"),
|
||||
AtRuleKind::Annotation => todo!("@annotation not yet implemented"),
|
||||
@ -525,7 +525,6 @@ fn eat_at_rule<I: Iterator<Item = Token>>(
|
||||
AtRuleKind::For => todo!("@for not yet implemented"),
|
||||
AtRuleKind::While => todo!("@while not yet implemented"),
|
||||
AtRuleKind::Media => todo!("@media not yet implemented"),
|
||||
AtRuleKind::Function => todo!("@function not yet implemented"),
|
||||
AtRuleKind::Keyframes => todo!("@keyframes not yet implemented"),
|
||||
_ => todo!("encountered unimplemented at rule"),
|
||||
}
|
||||
@ -620,10 +619,10 @@ pub(crate) fn eat_expr<I: Iterator<Item = Token>>(
|
||||
{
|
||||
return match eat_at_rule(rule, pos, toks, scope) {
|
||||
AtRule::Mixin(name, mixin) => Ok(Some(Expr::MixinDecl(name, mixin))),
|
||||
AtRule::Function(name, func) => Ok(Some(Expr::FunctionDecl(name, func))),
|
||||
AtRule::Debug(a, b) => Ok(Some(Expr::Debug(a, b))),
|
||||
AtRule::Warn(a, b) => Ok(Some(Expr::Warn(a, b))),
|
||||
AtRule::Error(a, b) => Err((a, b)),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ impl Mixin {
|
||||
while let Some(expr) = eat_expr(&mut self.body, &self.scope, super_selector)? {
|
||||
match expr {
|
||||
Expr::Style(s) => stmts.push(Stmt::Style(s)),
|
||||
Expr::Include(..) | Expr::MixinDecl(..) | Expr::Debug(..) | Expr::Warn(..) => {
|
||||
Expr::Include(..) | Expr::MixinDecl(..) | Expr::FunctionDecl(..) | Expr::Debug(..) | Expr::Warn(..) => {
|
||||
todo!()
|
||||
}
|
||||
Expr::Selector(selector) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user