box function references
This commit is contained in:
parent
add1698180
commit
e25a9f7b12
@ -2,32 +2,13 @@ use crate::{builtin::builtin_imports::*, evaluate::div};
|
|||||||
|
|
||||||
pub(crate) fn percentage(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
pub(crate) fn percentage(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||||
args.max_args(1)?;
|
args.max_args(1)?;
|
||||||
let num = match args.get_err(0, "number")? {
|
let num = args
|
||||||
Value::Dimension(SassNumber {
|
.get_err(0, "number")?
|
||||||
num: n,
|
.assert_number_with_name("number", args.span)?;
|
||||||
unit: Unit::None,
|
num.assert_no_units("number", args.span)?;
|
||||||
as_slash: _,
|
|
||||||
}) => n * Number::from(100),
|
|
||||||
v @ Value::Dimension(SassNumber { .. }) => {
|
|
||||||
return Err((
|
|
||||||
format!(
|
|
||||||
"$number: Expected {} to have no units.",
|
|
||||||
v.inspect(args.span())?
|
|
||||||
),
|
|
||||||
args.span(),
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
v => {
|
|
||||||
return Err((
|
|
||||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
|
||||||
args.span(),
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(Value::Dimension(SassNumber {
|
Ok(Value::Dimension(SassNumber {
|
||||||
num,
|
num: Number(num.num.0 * 100.0),
|
||||||
unit: Unit::Percent,
|
unit: Unit::Percent,
|
||||||
as_slash: None,
|
as_slash: None,
|
||||||
}))
|
}))
|
||||||
@ -107,54 +88,26 @@ pub(crate) fn floor(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResu
|
|||||||
|
|
||||||
pub(crate) fn abs(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
pub(crate) fn abs(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||||
args.max_args(1)?;
|
args.max_args(1)?;
|
||||||
match args.get_err(0, "number")? {
|
let mut num = args
|
||||||
Value::Dimension(SassNumber {
|
.get_err(0, "number")?
|
||||||
num: n,
|
.assert_number_with_name("number", args.span())?;
|
||||||
unit: u,
|
|
||||||
as_slash: _,
|
num.num = num.num.abs();
|
||||||
}) => Ok(Value::Dimension(SassNumber {
|
|
||||||
num: (n.abs()),
|
Ok(Value::Dimension(num))
|
||||||
unit: u,
|
|
||||||
as_slash: None,
|
|
||||||
})),
|
|
||||||
v => Err((
|
|
||||||
format!("$number: {} is not a number.", v.inspect(args.span())?),
|
|
||||||
args.span(),
|
|
||||||
)
|
|
||||||
.into()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn comparable(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
pub(crate) fn comparable(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
|
||||||
args.max_args(2)?;
|
args.max_args(2)?;
|
||||||
let unit1 = match args.get_err(0, "number1")? {
|
let unit1 = args
|
||||||
Value::Dimension(SassNumber {
|
.get_err(0, "number1")?
|
||||||
num: _,
|
.assert_number_with_name("number1", args.span())?
|
||||||
unit: u,
|
.unit;
|
||||||
as_slash: _,
|
|
||||||
}) => u,
|
let unit2 = args
|
||||||
v => {
|
.get_err(1, "number2")?
|
||||||
return Err((
|
.assert_number_with_name("number2", args.span())?
|
||||||
format!("$number1: {} is not a number.", v.inspect(args.span())?),
|
.unit;
|
||||||
args.span(),
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let unit2 = match args.get_err(1, "number2")? {
|
|
||||||
Value::Dimension(SassNumber {
|
|
||||||
num: _,
|
|
||||||
unit: u,
|
|
||||||
as_slash: _,
|
|
||||||
}) => u,
|
|
||||||
v => {
|
|
||||||
return Err((
|
|
||||||
format!("$number2: {} is not a number.", v.inspect(args.span())?),
|
|
||||||
args.span(),
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Value::bool(unit1.comparable(&unit2)))
|
Ok(Value::bool(unit1.comparable(&unit2)))
|
||||||
}
|
}
|
||||||
|
@ -295,7 +295,7 @@ pub(crate) fn get_function(mut args: ArgumentResult, visitor: &mut Visitor) -> S
|
|||||||
};
|
};
|
||||||
|
|
||||||
match func {
|
match func {
|
||||||
Some(func) => Ok(Value::FunctionRef(func)),
|
Some(func) => Ok(Value::FunctionRef(Box::new(func))),
|
||||||
None => Err((format!("Function not found: {}", name), args.span()).into()),
|
None => Err((format!("Function not found: {}", name), args.span()).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,7 +318,7 @@ pub(crate) fn call(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResul
|
|||||||
|
|
||||||
args.remove_positional(0).unwrap();
|
args.remove_positional(0).unwrap();
|
||||||
|
|
||||||
visitor.run_function_callable_with_maybe_evaled(func, MaybeEvaledArguments::Evaled(args), span)
|
visitor.run_function_callable_with_maybe_evaled(*func, MaybeEvaledArguments::Evaled(args), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
|
@ -100,7 +100,7 @@ pub(crate) fn str_slice(mut args: ArgumentResult, visitor: &mut Visitor) -> Sass
|
|||||||
} else if start > 0 {
|
} else if start > 0 {
|
||||||
(start as usize).min(str_len + 1)
|
(start as usize).min(str_len + 1)
|
||||||
} else {
|
} else {
|
||||||
(start + str_len as i32 + 1).max(1) as usize
|
(start + str_len as i64 + 1).max(1) as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
let end = args
|
let end = args
|
||||||
@ -120,7 +120,7 @@ pub(crate) fn str_slice(mut args: ArgumentResult, visitor: &mut Visitor) -> Sass
|
|||||||
let mut end = end.num().assert_int(span)?;
|
let mut end = end.num().assert_int(span)?;
|
||||||
|
|
||||||
if end < 0 {
|
if end < 0 {
|
||||||
end += str_len as i32 + 1;
|
end += str_len as i64 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let end = (end.max(0) as usize).min(str_len + 1);
|
let end = (end.max(0) as usize).min(str_len + 1);
|
||||||
@ -232,7 +232,7 @@ pub(crate) fn str_insert(mut args: ArgumentResult, visitor: &mut Visitor) -> Sas
|
|||||||
} else if index_int == 0 {
|
} else if index_int == 0 {
|
||||||
insert(0, s1, &substr)
|
insert(0, s1, &substr)
|
||||||
} else {
|
} else {
|
||||||
let idx = (len as i32 + index_int + 1).max(0) as usize;
|
let idx = (len as i64 + index_int + 1).max(0) as usize;
|
||||||
insert(idx, s1, &substr)
|
insert(idx, s1, &substr)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -343,7 +343,7 @@ impl Module {
|
|||||||
.map(|(key, value)| {
|
.map(|(key, value)| {
|
||||||
(
|
(
|
||||||
Value::String(key.to_string(), QuoteKind::Quoted).span(span),
|
Value::String(key.to_string(), QuoteKind::Quoted).span(span),
|
||||||
Value::FunctionRef(value),
|
Value::FunctionRef(Box::new(value)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
|
@ -134,10 +134,10 @@ impl Environment {
|
|||||||
Ok(v) => Ok(v),
|
Ok(v) => Ok(v),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(v) = self.get_variable_from_global_modules(name.node) {
|
if let Some(v) = self.get_variable_from_global_modules(name.node) {
|
||||||
return Ok(v);
|
Ok(v)
|
||||||
|
} else {
|
||||||
|
Err(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(e)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,6 +64,16 @@ impl UserDefinedCallable for AstFunctionDecl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UserDefinedCallable for Arc<AstFunctionDecl> {
|
||||||
|
fn name(&self) -> Identifier {
|
||||||
|
self.name.node
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arguments(&self) -> &ArgumentDeclaration {
|
||||||
|
&self.arguments
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl UserDefinedCallable for AstMixin {
|
impl UserDefinedCallable for AstMixin {
|
||||||
fn name(&self) -> Identifier {
|
fn name(&self) -> Identifier {
|
||||||
self.name
|
self.name
|
||||||
@ -1112,7 +1122,7 @@ impl<'a> Visitor<'a> {
|
|||||||
// todo: independency
|
// todo: independency
|
||||||
|
|
||||||
let func = SassFunction::UserDefined(UserDefinedFunction {
|
let func = SassFunction::UserDefined(UserDefinedFunction {
|
||||||
function: Box::new(fn_decl),
|
function: Arc::new(fn_decl),
|
||||||
name,
|
name,
|
||||||
env: self.env.new_closure(),
|
env: self.env.new_closure(),
|
||||||
});
|
});
|
||||||
@ -1701,6 +1711,14 @@ impl<'a> Visitor<'a> {
|
|||||||
|
|
||||||
let direction = if from > to { -1 } else { 1 };
|
let direction = if from > to { -1 } else { 1 };
|
||||||
|
|
||||||
|
if to == i64::MAX || to == i64::MIN {
|
||||||
|
return Err((
|
||||||
|
"@for loop upper bound exceeds valid integer representation (i64::MAX)",
|
||||||
|
to_span,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
if !for_stmt.is_exclusive {
|
if !for_stmt.is_exclusive {
|
||||||
to += direction;
|
to += direction;
|
||||||
}
|
}
|
||||||
@ -2200,23 +2218,17 @@ impl<'a> Visitor<'a> {
|
|||||||
Ok(self.without_slash(val))
|
Ok(self.without_slash(val))
|
||||||
}
|
}
|
||||||
SassFunction::UserDefined(UserDefinedFunction { function, env, .. }) => self
|
SassFunction::UserDefined(UserDefinedFunction { function, env, .. }) => self
|
||||||
.run_user_defined_callable(
|
.run_user_defined_callable(arguments, function, &env, span, |function, visitor| {
|
||||||
arguments,
|
for stmt in function.children.clone() {
|
||||||
*function,
|
let result = visitor.visit_stmt(stmt)?;
|
||||||
&env,
|
|
||||||
span,
|
|
||||||
|function, visitor| {
|
|
||||||
for stmt in function.children {
|
|
||||||
let result = visitor.visit_stmt(stmt)?;
|
|
||||||
|
|
||||||
if let Some(val) = result {
|
if let Some(val) = result {
|
||||||
return Ok(val);
|
return Ok(val);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Err(("Function finished without @return.", span).into())
|
Err(("Function finished without @return.", span).into())
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
SassFunction::Plain { name } => {
|
SassFunction::Plain { name } => {
|
||||||
let arguments = match arguments {
|
let arguments = match arguments {
|
||||||
MaybeEvaledArguments::Invocation(args) => args,
|
MaybeEvaledArguments::Invocation(args) => args,
|
||||||
|
@ -40,8 +40,7 @@ pub(crate) enum Value {
|
|||||||
Map(SassMap),
|
Map(SassMap),
|
||||||
ArgList(ArgList),
|
ArgList(ArgList),
|
||||||
/// Returned by `get-function()`
|
/// Returned by `get-function()`
|
||||||
// todo: benchmark boxing this (function refs are infrequent)
|
FunctionRef(Box<SassFunction>),
|
||||||
FunctionRef(SassFunction),
|
|
||||||
Calculation(SassCalculation),
|
Calculation(SassCalculation),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ pub(crate) fn fuzzy_equals(a: f64, b: f64) -> bool {
|
|||||||
(a - b).abs() <= epsilon() && (a * inverse_epsilon()).round() == (b * inverse_epsilon()).round()
|
(a - b).abs() <= epsilon() && (a * inverse_epsilon()).round() == (b * inverse_epsilon()).round()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fuzzy_as_int(num: f64) -> Option<i32> {
|
pub(crate) fn fuzzy_as_int(num: f64) -> Option<i64> {
|
||||||
if !num.is_finite() {
|
if !num.is_finite() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ pub(crate) fn fuzzy_as_int(num: f64) -> Option<i32> {
|
|||||||
let rounded = num.round();
|
let rounded = num.round();
|
||||||
|
|
||||||
if fuzzy_equals(num, rounded) {
|
if fuzzy_equals(num, rounded) {
|
||||||
Some(rounded as i32)
|
Some(rounded as i64)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -110,14 +110,14 @@ impl Number {
|
|||||||
self.0.is_sign_negative() && !self.is_zero()
|
self.0.is_sign_negative() && !self.is_zero()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_int(self, span: Span) -> SassResult<i32> {
|
pub fn assert_int(self, span: Span) -> SassResult<i64> {
|
||||||
match fuzzy_as_int(self.0) {
|
match fuzzy_as_int(self.0) {
|
||||||
Some(i) => Ok(i),
|
Some(i) => Ok(i),
|
||||||
None => Err((format!("{} is not an int.", self.0), span).into()),
|
None => Err((format!("{} is not an int.", self.0), span).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assert_int_with_name(self, name: &'static str, span: Span) -> SassResult<i32> {
|
pub fn assert_int_with_name(self, name: &'static str, span: Span) -> SassResult<i64> {
|
||||||
match fuzzy_as_int(self.0) {
|
match fuzzy_as_int(self.0) {
|
||||||
Some(i) => Ok(i),
|
Some(i) => Ok(i),
|
||||||
None => Err((
|
None => Err((
|
||||||
|
@ -3,24 +3,15 @@
|
|||||||
//! Sass functions can be either user-defined or builtin.
|
//! Sass functions can be either user-defined or builtin.
|
||||||
//!
|
//!
|
||||||
//! User-defined functions are those that have been implemented in Sass
|
//! User-defined functions are those that have been implemented in Sass
|
||||||
//! using the @function rule. See the documentation of `crate::atrule::Function`
|
//! using the @function rule. See the documentation of [`crate::atrule::Function`]
|
||||||
//! for more information.
|
//! for more information.
|
||||||
//!
|
//!
|
||||||
//! Builtin functions are those that have been implemented in rust and are
|
//! Builtin functions are those that have been implemented in rust and are
|
||||||
//! in the global scope.
|
//! in the global scope.
|
||||||
|
|
||||||
use std::fmt;
|
use std::{fmt, sync::Arc};
|
||||||
|
|
||||||
// use codemap::Spanned;
|
use crate::{ast::AstFunctionDecl, builtin::Builtin, common::Identifier, evaluate::Environment};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
// error::SassResult,
|
|
||||||
ast::AstFunctionDecl,
|
|
||||||
// value::Value,
|
|
||||||
builtin::Builtin,
|
|
||||||
common::Identifier,
|
|
||||||
evaluate::Environment,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A Sass function
|
/// A Sass function
|
||||||
///
|
///
|
||||||
@ -39,7 +30,7 @@ pub(crate) enum SassFunction {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct UserDefinedFunction {
|
pub(crate) struct UserDefinedFunction {
|
||||||
pub function: Box<AstFunctionDecl>,
|
pub function: Arc<AstFunctionDecl>,
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
pub env: Environment,
|
pub env: Environment,
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user