grass/src/parse/mixin.rs

222 lines
6.9 KiB
Rust
Raw Normal View History

2020-06-16 22:34:01 -04:00
use codemap::Spanned;
2020-06-30 06:53:17 -04:00
use peekmore::PeekMore;
2020-06-16 22:34:01 -04:00
use crate::{
args::{CallArgs, FuncArgs},
2020-07-02 10:31:32 -04:00
atrule::{Content, Mixin},
2020-06-16 22:34:01 -04:00
error::SassResult,
2020-07-08 14:51:04 -04:00
scope::Scopes,
2020-06-16 22:34:01 -04:00
utils::read_until_closing_curly_brace,
Token,
};
2020-07-08 14:51:04 -04:00
use super::{common::ContextFlags, Parser, Stmt};
2020-06-16 22:34:01 -04:00
impl<'a> Parser<'a> {
pub(super) fn parse_mixin(&mut self) -> SassResult<()> {
self.whitespace();
let Spanned { node: name, span } = self.parse_identifier()?;
self.whitespace();
let args = match self.toks.next() {
Some(Token { kind: '(', .. }) => self.parse_func_args()?,
Some(Token { kind: '{', .. }) => FuncArgs::new(),
Some(t) => return Err(("expected \"{\".", t.pos()).into()),
None => return Err(("expected \"{\".", span).into()),
};
self.whitespace();
let mut body = read_until_closing_curly_brace(self.toks)?;
2020-06-18 03:09:24 -04:00
body.push(match self.toks.next() {
Some(tok) => tok,
None => return Err(("expected \"}\".", self.span_before).into()),
});
2020-06-16 22:34:01 -04:00
// todo: `@include` can only give content when `@content` is present within the body
// if `@content` is *not* present and `@include` attempts to give a body, we throw an error
// `Error: Mixin doesn't accept a content block.`
//
// this is blocked on figuring out just how to check for this. presumably we could have a check
// not when parsing initially, but rather when `@include`ing to see if an `@content` was found.
2020-07-08 14:51:04 -04:00
let mixin = Mixin::new(args, body, false, self.at_root);
2020-06-16 22:34:01 -04:00
if self.at_root {
self.global_scope.insert_mixin(name, mixin);
} else {
2020-07-08 14:51:04 -04:00
self.scopes.insert_mixin(name.into(), mixin);
2020-06-16 22:34:01 -04:00
}
Ok(())
}
pub(super) fn parse_include(&mut self) -> SassResult<Vec<Stmt>> {
self.whitespace_or_comment();
2020-07-08 14:51:04 -04:00
let name = self.parse_identifier()?.map_node(Into::into);
2020-06-16 22:34:01 -04:00
self.whitespace_or_comment();
2020-07-02 10:31:32 -04:00
let args = if let Some(Token { kind: '(', .. }) = self.toks.peek() {
self.toks.next();
self.parse_call_args()?
} else {
CallArgs::new(name.span)
};
2020-06-16 22:34:01 -04:00
2020-07-02 10:31:32 -04:00
self.whitespace_or_comment();
let content_args = if let Some(Token { kind: 'u', .. }) | Some(Token { kind: 'U', .. }) =
self.toks.peek()
{
let mut ident = self.parse_identifier_no_interpolation(false)?;
ident.node.make_ascii_lowercase();
if ident.node == "using" {
2020-06-16 22:34:01 -04:00
self.whitespace_or_comment();
2020-07-02 10:31:32 -04:00
if !matches!(self.toks.next(), Some(Token { kind: '(', .. })) {
return Err(("expected \"(\".", ident.span).into());
2020-06-16 22:34:01 -04:00
}
2020-07-02 10:31:32 -04:00
Some(self.parse_func_args()?)
} else {
return Err(("expected keyword \"using\".", ident.span).into());
2020-06-16 22:34:01 -04:00
}
2020-07-02 10:31:32 -04:00
} else {
None
2020-06-16 22:34:01 -04:00
};
2020-07-02 10:31:32 -04:00
self.whitespace_or_comment();
2020-06-16 22:34:01 -04:00
2020-07-02 10:31:32 -04:00
let content = if content_args.is_some()
|| matches!(self.toks.peek(), Some(Token { kind: '{', .. }))
{
if matches!(self.toks.peek(), Some(Token { kind: '{', .. })) {
self.toks.next();
}
let mut toks = read_until_closing_curly_brace(self.toks)?;
if let Some(tok) = self.toks.peek() {
toks.push(*tok);
self.toks.next();
}
Some(toks)
2020-06-16 22:34:01 -04:00
} else {
None
};
2020-07-02 10:31:32 -04:00
if let Some(Token { kind: ';', .. }) = self.toks.peek() {
self.toks.next();
}
let Mixin {
body,
args: fn_args,
2020-07-08 14:51:04 -04:00
declared_at_root,
2020-07-02 10:31:32 -04:00
..
2020-07-08 14:51:04 -04:00
} = self.scopes.get_mixin(
{
let Spanned { ref node, span } = name;
Spanned { node, span }
},
self.global_scope,
)?;
let scope = self.eval_args(fn_args, args)?;
let mut new_scope = Scopes::new();
let mut entered_scope = false;
if declared_at_root {
new_scope.enter_scope(scope);
} else {
entered_scope = true;
self.scopes.enter_scope(scope);
};
2020-06-16 22:34:01 -04:00
2020-07-02 15:22:15 -04:00
self.content.push(Content {
content,
content_args,
2020-07-08 14:51:04 -04:00
scopes: self.scopes.clone(),
2020-07-02 15:22:15 -04:00
});
2020-06-16 22:34:01 -04:00
let body = Parser {
2020-07-02 10:31:32 -04:00
toks: &mut body.into_iter().peekmore(),
2020-06-16 22:34:01 -04:00
map: self.map,
path: self.path,
2020-07-08 14:51:04 -04:00
scopes: if declared_at_root {
&mut new_scope
} else {
self.scopes
},
2020-06-16 22:34:01 -04:00
global_scope: self.global_scope,
super_selectors: self.super_selectors,
span_before: self.span_before,
2020-07-05 10:13:49 -04:00
flags: self.flags | ContextFlags::IN_MIXIN,
2020-07-02 15:22:15 -04:00
content: self.content,
2020-06-16 22:34:01 -04:00
at_root: false,
at_root_has_selector: self.at_root_has_selector,
2020-06-18 16:56:03 -04:00
extender: self.extender,
2020-06-16 22:34:01 -04:00
}
.parse()?;
2020-07-02 15:22:15 -04:00
self.content.pop();
2020-07-08 14:51:04 -04:00
if entered_scope {
self.scopes.exit_scope();
}
2020-07-02 15:22:15 -04:00
2020-06-16 22:34:01 -04:00
Ok(body)
}
2020-07-02 10:31:32 -04:00
pub(super) fn parse_content_rule(&mut self) -> SassResult<Vec<Stmt>> {
2020-07-08 15:32:04 -04:00
if !self.flags.in_mixin() {
return Err((
"@content is only allowed within mixin declarations.",
self.span_before,
)
.into());
}
let mut scope = self
.content
.last()
.cloned()
.unwrap_or_else(Content::new)
.scopes;
if let Some(Token { kind: '(', .. }) = self.toks.peek() {
self.toks.next();
let args = self.parse_call_args()?;
if let Some(Some(content_args)) = self.content.last().map(|v| v.content_args.clone()) {
args.max_args(content_args.len())?;
scope.merge(self.eval_args(content_args, args)?);
} else {
args.max_args(0)?;
2020-07-02 10:31:32 -04:00
}
2020-07-08 15:32:04 -04:00
}
2020-07-02 10:31:32 -04:00
2020-07-08 15:32:04 -04:00
Ok(if let Some(content) = &self.content.pop() {
let stmts = if let Some(body) = content.content.clone() {
Parser {
toks: &mut body.into_iter().peekmore(),
map: self.map,
path: self.path,
scopes: &mut scope,
global_scope: self.global_scope,
super_selectors: self.super_selectors,
span_before: self.span_before,
flags: self.flags,
content: self.content,
at_root: self.at_root,
at_root_has_selector: self.at_root_has_selector,
extender: self.extender,
}
.parse()?
2020-07-02 10:31:32 -04:00
} else {
Vec::new()
2020-07-08 15:32:04 -04:00
};
self.content.push(content.clone());
self.scopes.exit_scope();
stmts
2020-07-02 10:31:32 -04:00
} else {
2020-07-08 15:32:04 -04:00
Vec::new()
})
2020-06-16 22:34:01 -04:00
}
}