implement builtin function call()

This commit is contained in:
ConnorSkees 2020-04-04 12:31:43 -04:00
parent c0eaf4c5f6
commit 051185e280
4 changed files with 85 additions and 15 deletions

View File

@ -44,6 +44,13 @@ impl CallArg {
Self::Positional(p) => Ok(*p), Self::Positional(p) => Ok(*p),
} }
} }
pub fn decrement(self) -> CallArg {
match self {
Self::Named(..) => self,
Self::Positional(p) => Self::Positional(p - 1),
}
}
} }
impl CallArgs { impl CallArgs {
@ -74,6 +81,15 @@ impl CallArgs {
Ok(vals) Ok(vals)
} }
pub fn decrement(self) -> Self {
CallArgs(
self.0
.into_iter()
.map(|(k, v)| (k.decrement(), v))
.collect(),
)
}
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.0.len()
} }

View File

@ -142,29 +142,28 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
Value::Ident(s, _) => s, Value::Ident(s, _) => s,
v => return Err(format!("$name: {} is not a string.", v).into()), v => return Err(format!("$name: {} is not a string.", v).into()),
}; };
let css = arg!(args, 1, "css" = Value::False).is_true()?; // let css = arg!(args, 1, "css" = Value::False).is_true()?;
// let module = arg!(args, 2, "module" = Value::Null);
let func = match scope.get_fn(&name) { let func = match scope.get_fn(&name) {
Ok(f) => SassFunction::UserDefined(Box::new(f), name), Ok(f) => SassFunction::UserDefined(Box::new(f), name),
Err(e) => match GLOBAL_FUNCTIONS.get(&name) { Err(..) => match GLOBAL_FUNCTIONS.get(&name) {
Some(f) => SassFunction::Builtin(f.clone(), name), Some(f) => SassFunction::Builtin(f.clone(), name),
None => return Err(e), None => return Err(format!("Function not found: {}", name).into()),
}, },
}; };
Ok(Value::Function(func)) Ok(Value::Function(func))
}), }),
); );
f.insert("call".to_owned(), Builtin::new(|_args, _scope| { f.insert(
todo!("builtin function `call()` is blocked on refactoring how call args are stored and parsed") "call".to_owned(),
// let func = arg!(args, 0, "function").to_string(); Builtin::new(|mut args, scope| {
// let func = match scope.get_fn(&func) { let func = match arg!(args, 0, "function") {
// Ok(f) => f, Value::Function(f) => f,
// Err(_) => match GLOBAL_FUNCTIONS.get(&func) { v => return Err(format!("$function: {} is not a function reference.", v).into()),
// Some(f) => return f(&args, scope), };
// None => todo!("called undefined function"), func.call(args.decrement(), scope)
// }, }),
// }; );
// Some(func.clone().args(&args).call())
}));
} }

View File

@ -1,7 +1,12 @@
use std::fmt; use std::fmt;
use crate::args::CallArgs;
use crate::atrule::Function; use crate::atrule::Function;
use crate::builtin::Builtin; use crate::builtin::Builtin;
use crate::error::SassResult;
use crate::scope::Scope;
use crate::selector::Selector;
use crate::value::Value;
#[derive(Clone)] #[derive(Clone)]
pub(crate) enum SassFunction { pub(crate) enum SassFunction {
@ -16,6 +21,14 @@ impl SassFunction {
Self::UserDefined(_, name) => name, Self::UserDefined(_, name) => name,
} }
} }
pub fn call(self, args: CallArgs, scope: &Scope) -> SassResult<Value> {
match self {
Self::Builtin(f, ..) => f.0(args, scope),
// todo: superselector
Self::UserDefined(f, ..) => f.clone().args(args)?.call(&Selector::new(), f.body()),
}
}
} }
impl fmt::Debug for SassFunction { impl fmt::Debug for SassFunction {

View File

@ -52,3 +52,45 @@ test!(
"a {b: inspect(get-function(lighten));}", "a {b: inspect(get-function(lighten));}",
"a {\n b: get-function(\"lighten\");\n}\n" "a {\n b: get-function(\"lighten\");\n}\n"
); );
test!(
call_user_defined_no_args,
"@function user-defined() {\n @return foo;\n}\n
a {b: call(get-function(user-defined));}",
"a {\n b: foo;\n}\n"
);
test!(
call_user_defined_positional_args,
"@function user-defined($a, $b) {\n @return $a, $b;\n}\n
a {b: call(get-function(user-defined), a, b);}",
"a {\n b: a, b;\n}\n"
);
test!(
call_user_defined_keyword_args,
"@function user-defined($a, $b) {\n @return $a, $b;\n}\n
a {b: call(get-function(user-defined), $a: a, $b: b);}",
"a {\n b: a, b;\n}\n"
);
test!(
call_builtin_positional_args,
"a {b: call(get-function(lighten), red, 5);}",
"a {\n b: #ff1a1a;\n}\n"
);
test!(
call_builtin_keyword_args,
"a {b: call(get-function(lighten), $color: red, $amount: 5);}",
"a {\n b: #ff1a1a;\n}\n"
);
// test!(
// call_user_defined_super_selector,
// "@function user-defined() {\n @return &;\n}\n
// a {b: call(get-function(user-defined));}",
// "a {\n b: a;\n}\n"
// );
error!(
undefined_function,
"a {color: get-function(foo);}", "Error: Function not found: foo"
);
error!(
non_function_call,
"a {color: call(4);}", "Error: $function: 4 is not a function reference."
);