initial implementation of get-function()
This commit is contained in:
parent
d26a81253f
commit
3f98d1abca
@ -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<Stmt>,
|
||||
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<Stmt>) -> Self {
|
||||
Function { scope, args, body }
|
||||
pub fn new(scope: Scope, args: FuncArgs, body: Vec<Stmt>, pos: Pos) -> Self {
|
||||
Function {
|
||||
scope,
|
||||
args,
|
||||
body,
|
||||
pos,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decl_from_tokens<I: Iterator<Item = Token>>(
|
||||
@ -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<Function> {
|
||||
|
@ -133,6 +133,19 @@ pub(crate) fn register(f: &mut HashMap<String, Builtin>) {
|
||||
}
|
||||
}),
|
||||
);
|
||||
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();
|
||||
|
@ -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<Value>),
|
||||
// Returned by `get-function()`
|
||||
// Function(String)
|
||||
/// Returned by `get-function()`
|
||||
Function(Box<Function>, bool),
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
@ -53,6 +54,7 @@ impl Display for Value {
|
||||
.collect::<Vec<String>>()
|
||||
.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"),
|
||||
|
@ -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()),
|
||||
|
34
tests/get-function.rs
Normal file
34
tests/get-function.rs
Normal file
@ -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"
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user