2020-06-16 22:34:01 -04:00
|
|
|
use std::mem;
|
|
|
|
|
|
|
|
use codemap::Spanned;
|
|
|
|
use peekmore::PeekMore;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
args::CallArgs,
|
|
|
|
atrule::Function,
|
|
|
|
error::SassResult,
|
|
|
|
utils::{read_until_closing_curly_brace, read_until_semicolon_or_closing_curly_brace},
|
|
|
|
value::Value,
|
|
|
|
Token,
|
|
|
|
};
|
|
|
|
|
2020-06-18 18:14:35 -04:00
|
|
|
use super::{NeverEmptyVec, Parser, Stmt};
|
|
|
|
|
2020-06-22 11:35:15 -04:00
|
|
|
/// Names that functions are not allowed to have
|
|
|
|
const FORBIDDEN_IDENTIFIERS: [&str; 7] =
|
|
|
|
["calc", "element", "expression", "url", "and", "or", "not"];
|
|
|
|
|
2020-06-22 12:41:59 -04:00
|
|
|
fn unvendor(name: &str) -> &str {
|
2020-06-22 11:35:15 -04:00
|
|
|
let mut chars = name.chars();
|
|
|
|
if !matches!(chars.next(), Some('-')) {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
if matches!(chars.next(), Some('-')) {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
if name.chars().count() < 2 {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
let mut pos = 2;
|
|
|
|
for c in chars {
|
|
|
|
if c == '-' {
|
|
|
|
return &name[pos..];
|
|
|
|
}
|
|
|
|
pos += 1;
|
|
|
|
}
|
|
|
|
name
|
|
|
|
}
|
|
|
|
|
2020-06-16 22:34:01 -04:00
|
|
|
impl<'a> Parser<'a> {
|
|
|
|
pub(super) fn parse_function(&mut self) -> SassResult<()> {
|
2020-06-22 11:35:15 -04:00
|
|
|
self.whitespace_or_comment();
|
|
|
|
let Spanned { node: name, span } = self.parse_identifier()?;
|
|
|
|
|
2020-06-16 22:34:01 -04:00
|
|
|
if self.in_mixin {
|
2020-06-22 11:35:15 -04:00
|
|
|
return Err(("Mixins may not contain function declarations.", span).into());
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.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, mut function: Function, args: CallArgs) -> SassResult<Value> {
|
|
|
|
self.eval_fn_args(&mut function, args)?;
|
2020-06-18 17:18:31 -04:00
|
|
|
|
2020-06-16 22:34:01 -04:00
|
|
|
let mut return_value = Parser {
|
|
|
|
toks: &mut function.body.into_iter().peekmore(),
|
|
|
|
map: self.map,
|
|
|
|
path: self.path,
|
2020-06-18 18:14:35 -04:00
|
|
|
scopes: &mut NeverEmptyVec::new(function.scope),
|
2020-06-16 22:34:01 -04:00
|
|
|
global_scope: self.global_scope,
|
|
|
|
super_selectors: self.super_selectors,
|
|
|
|
span_before: self.span_before,
|
|
|
|
content: self.content.clone(),
|
|
|
|
in_mixin: self.in_mixin,
|
|
|
|
in_function: true,
|
|
|
|
in_control_flow: self.in_control_flow,
|
|
|
|
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"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn eval_fn_args(&mut self, function: &mut Function, mut args: CallArgs) -> SassResult<()> {
|
2020-06-18 17:18:31 -04:00
|
|
|
self.scopes.push(self.scopes.last().clone());
|
2020-06-16 22:34:01 -04:00
|
|
|
for (idx, arg) in function.args.0.iter_mut().enumerate() {
|
|
|
|
if arg.is_variadic {
|
|
|
|
let span = args.span();
|
|
|
|
let arg_list = Value::ArgList(self.variadic_args(args)?);
|
2020-06-18 17:18:31 -04:00
|
|
|
function.scope.insert_var(
|
2020-06-16 22:34:01 -04:00
|
|
|
arg.name.clone(),
|
|
|
|
Spanned {
|
|
|
|
node: arg_list,
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let val = match args.get(idx, arg.name.clone()) {
|
|
|
|
Some(v) => self.parse_value_from_vec(v)?,
|
|
|
|
None => match arg.default.as_mut() {
|
|
|
|
Some(v) => self.parse_value_from_vec(mem::take(v))?,
|
|
|
|
None => {
|
|
|
|
return Err(
|
|
|
|
(format!("Missing argument ${}.", &arg.name), args.span()).into()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
self.scopes
|
|
|
|
.last_mut()
|
2020-06-18 17:18:31 -04:00
|
|
|
.insert_var(arg.name.clone(), val.clone())?;
|
|
|
|
function.scope.insert_var(mem::take(&mut arg.name), val)?;
|
2020-06-16 22:34:01 -04:00
|
|
|
}
|
2020-06-18 17:18:31 -04:00
|
|
|
self.scopes.pop();
|
2020-06-16 22:34:01 -04:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|