From 3f98d1abcacd1b9958ad8b1b753aaa6e5068097e Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Fri, 3 Apr 2020 21:38:34 -0400 Subject: [PATCH] initial implementation of get-function() --- src/atrule/function.rs | 22 +++++++++++++++++++--- src/builtin/meta.rs | 13 +++++++++++++ src/value/mod.rs | 8 +++++--- src/value/ops.rs | 4 ++-- tests/get-function.rs | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 tests/get-function.rs diff --git a/src/atrule/function.rs b/src/atrule/function.rs index ba2f639..406b216 100644 --- a/src/atrule/function.rs +++ b/src/atrule/function.rs @@ -4,6 +4,7 @@ use super::eat_stmts; use crate::args::{eat_func_args, CallArgs, FuncArgs}; use crate::atrule::AtRule; +use crate::common::Pos; use crate::error::SassResult; use crate::scope::Scope; use crate::selector::Selector; @@ -16,11 +17,25 @@ pub(crate) struct Function { scope: Scope, args: FuncArgs, body: Vec, + pos: Pos, } +impl PartialEq for Function { + fn eq(&self, other: &Self) -> bool { + self.pos == other.pos + } +} + +impl Eq for Function {} + impl Function { - pub fn new(scope: Scope, args: FuncArgs, body: Vec) -> Self { - Function { scope, args, body } + pub fn new(scope: Scope, args: FuncArgs, body: Vec, pos: Pos) -> Self { + Function { + scope, + args, + body, + pos, + } } pub fn decl_from_tokens>( @@ -28,6 +43,7 @@ impl Function { scope: Scope, super_selector: &Selector, ) -> SassResult<(String, Function)> { + let pos = toks.peek().unwrap().pos; let name = eat_ident(toks, &scope, super_selector)?; devour_whitespace(toks); let args = match toks.next() { @@ -40,7 +56,7 @@ impl Function { let body = eat_stmts(toks, &mut scope.clone(), super_selector)?; devour_whitespace(toks); - Ok((name, Function::new(scope, args, body))) + Ok((name, Function::new(scope, args, body, pos))) } pub fn args(mut self, mut args: CallArgs) -> SassResult { diff --git a/src/builtin/meta.rs b/src/builtin/meta.rs index 52bfda6..dd9cbb9 100644 --- a/src/builtin/meta.rs +++ b/src/builtin/meta.rs @@ -133,6 +133,19 @@ pub(crate) fn register(f: &mut HashMap) { } }), ); + f.insert( + "get-function".to_owned(), + Box::new(|mut args, scope| { + max_args!(args, 2); + let name = match arg!(args, 0, "name") { + 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()?; + + Ok(Value::Function(Box::new(scope.get_fn(&name)?), css)) + }), + ); f.insert("call".to_owned(), Box::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(); diff --git a/src/value/mod.rs b/src/value/mod.rs index 7d4d46f..ff0609e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -2,6 +2,7 @@ use std::cmp::Ordering; use std::fmt::{self, Display, Write}; use std::iter::Iterator; +use crate::atrule::Function; use crate::color::Color; use crate::common::{Brackets, ListSeparator, Op, QuoteKind}; use crate::error::SassResult; @@ -30,8 +31,8 @@ pub(crate) enum Value { Ident(String, QuoteKind), Map(SassMap), ArgList(Vec), - // Returned by `get-function()` - // Function(String) + /// Returned by `get-function()` + Function(Box, bool), } impl Display for Value { @@ -53,6 +54,7 @@ impl Display for Value { .collect::>() .join(", ") ), + Self::Function(..) => todo!("invalid CSS"), Self::List(vals, sep, brackets) => match brackets { Brackets::None => write!( f, @@ -166,7 +168,7 @@ impl Value { Self::Ident(..) | Self::Important => Ok("string"), Self::Dimension(..) => Ok("number"), Self::List(..) => Ok("list"), - // Self::Function(..) => Ok("function"), + Self::Function(..) => Ok("function"), Self::ArgList(..) => Ok("arglist"), Self::True | Self::False => Ok("bool"), Self::Null => Ok("null"), diff --git a/src/value/ops.rs b/src/value/ops.rs index e4ed729..709ec54 100644 --- a/src/value/ops.rs +++ b/src/value/ops.rs @@ -14,7 +14,7 @@ impl Add for Value { } let precedence = Op::Plus.precedence(); Ok(match self { - Self::ArgList(..) | Self::Map(..) => todo!(), + Self::Function(..) | Self::ArgList(..) | Self::Map(..) => todo!(), Self::Important | Self::True | Self::False => match other { Self::Ident(s, QuoteKind::Double) | Self::Ident(s, QuoteKind::Single) => { Value::Ident(format!("{}{}", self, s), QuoteKind::Double) @@ -86,7 +86,7 @@ impl Add for Value { Self::List(..) => Value::Ident(format!("{}{}", s1, other), quotes1), Self::UnaryOp(..) | Self::BinaryOp(..) => todo!(), Self::Paren(..) => (Self::Ident(s1, quotes1) + other.eval()?)?, - Self::ArgList(..) | Self::Map(..) => todo!(), + Self::Function(..) | Self::ArgList(..) | Self::Map(..) => todo!(), }, Self::List(..) => match other { Self::Ident(s, q) => Value::Ident(format!("{}{}", self, s), q.normalize()), diff --git a/tests/get-function.rs b/tests/get-function.rs new file mode 100644 index 0000000..d76a9f9 --- /dev/null +++ b/tests/get-function.rs @@ -0,0 +1,34 @@ +#![cfg(test)] + +#[macro_use] +mod macros; + +test!( + different_function_same_body_not_equal, + "@function user-defined() {@return null} + $first-reference: get-function(user-defined); + + @function user-defined() {@return null} + $second-reference: get-function(user-defined); + a {b: $first-reference == $second-reference}", + "a {\n b: false;\n}\n" +); +test!( + same_function_equal, + "@function user-defined() {@return null} + a {b: get-function(user-defined) == get-function(user-defined)}s", + "a {\n b: true;\n}\n" +); +test!( + different_name_same_body_not_equal, + "@function user-defined-1() {@return null} + @function user-defined-2() {@return null} + a {b: get-function(user-defined-1) == get-function(user-defined-2)}", + "a {\n b: false;\n}\n" +); +test!( + type_of_user_defined_function, + "@function user-defined() {@return null} + a {b: type-of(get-function(user-defined));}", + "a {\n b: function;\n}\n" +);