From 051185e2802bfc4450f8c06bbd89ca8ea62b4583 Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Sat, 4 Apr 2020 12:31:43 -0400 Subject: [PATCH] implement builtin function call() --- src/args.rs | 16 ++++++++++++++++ src/builtin/meta.rs | 29 ++++++++++++++--------------- src/value/function.rs | 13 +++++++++++++ tests/get-function.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 15 deletions(-) diff --git a/src/args.rs b/src/args.rs index b24449a..98715b4 100644 --- a/src/args.rs +++ b/src/args.rs @@ -44,6 +44,13 @@ impl CallArg { Self::Positional(p) => Ok(*p), } } + + pub fn decrement(self) -> CallArg { + match self { + Self::Named(..) => self, + Self::Positional(p) => Self::Positional(p - 1), + } + } } impl CallArgs { @@ -74,6 +81,15 @@ impl CallArgs { Ok(vals) } + pub fn decrement(self) -> Self { + CallArgs( + self.0 + .into_iter() + .map(|(k, v)| (k.decrement(), v)) + .collect(), + ) + } + pub fn len(&self) -> usize { self.0.len() } diff --git a/src/builtin/meta.rs b/src/builtin/meta.rs index 0158cee..672b936 100644 --- a/src/builtin/meta.rs +++ b/src/builtin/meta.rs @@ -142,29 +142,28 @@ pub(crate) fn register(f: &mut HashMap) { Value::Ident(s, _) => s, 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) { 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), - None => return Err(e), + None => return Err(format!("Function not found: {}", name).into()), }, }; Ok(Value::Function(func)) }), ); - f.insert("call".to_owned(), Builtin::new(|_args, _scope| { - todo!("builtin function `call()` is blocked on refactoring how call args are stored and parsed") - // let func = arg!(args, 0, "function").to_string(); - // let func = match scope.get_fn(&func) { - // Ok(f) => f, - // Err(_) => match GLOBAL_FUNCTIONS.get(&func) { - // Some(f) => return f(&args, scope), - // None => todo!("called undefined function"), - // }, - // }; - // Some(func.clone().args(&args).call()) - })); + f.insert( + "call".to_owned(), + Builtin::new(|mut args, scope| { + let func = match arg!(args, 0, "function") { + Value::Function(f) => f, + v => return Err(format!("$function: {} is not a function reference.", v).into()), + }; + func.call(args.decrement(), scope) + }), + ); } diff --git a/src/value/function.rs b/src/value/function.rs index 3c797d6..d091f70 100644 --- a/src/value/function.rs +++ b/src/value/function.rs @@ -1,7 +1,12 @@ use std::fmt; +use crate::args::CallArgs; use crate::atrule::Function; use crate::builtin::Builtin; +use crate::error::SassResult; +use crate::scope::Scope; +use crate::selector::Selector; +use crate::value::Value; #[derive(Clone)] pub(crate) enum SassFunction { @@ -16,6 +21,14 @@ impl SassFunction { Self::UserDefined(_, name) => name, } } + + pub fn call(self, args: CallArgs, scope: &Scope) -> SassResult { + 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 { diff --git a/tests/get-function.rs b/tests/get-function.rs index 006ca39..bff5933 100644 --- a/tests/get-function.rs +++ b/tests/get-function.rs @@ -52,3 +52,45 @@ test!( "a {b: inspect(get-function(lighten));}", "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." +);