grass/src/parse/function.rs

108 lines
3.4 KiB
Rust
Raw Normal View History

2020-06-16 22:34:01 -04:00
use codemap::Spanned;
use peekmore::PeekMore;
use crate::{
args::CallArgs,
atrule::Function,
common::unvendor,
2020-06-16 22:34:01 -04:00
error::SassResult,
utils::{read_until_closing_curly_brace, read_until_semicolon_or_closing_curly_brace},
value::Value,
Token,
};
2020-07-05 10:13:49 -04:00
use super::{common::ContextFlags, NeverEmptyVec, Parser, Stmt};
2020-06-18 18:14:35 -04:00
/// Names that functions are not allowed to have
const FORBIDDEN_IDENTIFIERS: [&str; 7] =
["calc", "element", "expression", "url", "and", "or", "not"];
2020-06-16 22:34:01 -04:00
impl<'a> Parser<'a> {
pub(super) fn parse_function(&mut self) -> SassResult<()> {
self.whitespace_or_comment();
let Spanned { node: name, span } = self.parse_identifier()?;
2020-07-05 10:13:49 -04:00
if self.flags.in_mixin() {
return Err(("Mixins may not contain function declarations.", span).into());
}
2020-07-05 10:13:49 -04:00
if self.flags.in_control_flow() {
return Err(("Functions may not be declared in control directives.", span).into());
}
if FORBIDDEN_IDENTIFIERS.contains(&unvendor(&name)) {
return Err(("Invalid function name.", span).into());
2020-06-16 22:34:01 -04:00
}
self.whitespace_or_comment();
let args = match self.toks.next() {
Some(Token { kind: '(', .. }) => self.parse_func_args()?,
Some(Token { pos, .. }) => return Err(("expected \"(\".", 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
self.whitespace();
let function = Function::new(self.scopes.last().clone(), args, body, span);
if self.at_root {
self.global_scope.insert_fn(name, function);
} else {
self.scopes.last_mut().insert_fn(name, function);
}
Ok(())
}
2020-06-25 00:27:24 -04:00
pub(super) fn parse_return(&mut self) -> SassResult<Box<Value>> {
2020-06-16 22:34:01 -04:00
let toks = read_until_semicolon_or_closing_curly_brace(self.toks)?;
let v = self.parse_value_from_vec(toks)?;
if let Some(Token { kind: ';', .. }) = self.toks.peek() {
self.toks.next();
}
2020-06-25 00:27:24 -04:00
Ok(Box::new(v.node))
2020-06-16 22:34:01 -04:00
}
pub fn eval_function(&mut self, function: Function, args: CallArgs) -> SassResult<Value> {
let Function {
mut scope,
body,
args: fn_args,
..
} = function;
self.eval_args(fn_args, args, &mut scope)?;
2020-06-18 17:18:31 -04:00
2020-06-16 22:34:01 -04:00
let mut return_value = Parser {
toks: &mut body.into_iter().peekmore(),
2020-06-16 22:34:01 -04:00
map: self.map,
path: self.path,
scopes: &mut NeverEmptyVec::new(scope),
2020-06-16 22:34:01 -04:00
global_scope: self.global_scope,
super_selectors: self.super_selectors,
span_before: self.span_before,
2020-06-26 01:02:06 -04:00
content: self.content,
2020-07-05 10:13:49 -04:00
flags: self.flags | ContextFlags::IN_FUNCTION,
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-06-18 17:18:31 -04:00
2020-06-16 22:34:01 -04:00
debug_assert!(return_value.len() <= 1);
match return_value
.pop()
.ok_or(("Function finished without @return.", self.span_before))?
{
2020-06-25 00:27:24 -04:00
Stmt::Return(v) => Ok(*v),
2020-06-16 22:34:01 -04:00
_ => todo!("should be unreachable"),
}
}
}