box function references

This commit is contained in:
Connor Skees 2022-12-27 15:16:07 -05:00
parent add1698180
commit e25a9f7b12
9 changed files with 67 additions and 112 deletions

View File

@ -2,32 +2,13 @@ use crate::{builtin::builtin_imports::*, evaluate::div};
pub(crate) fn percentage(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
args.max_args(1)?;
let num = match args.get_err(0, "number")? {
Value::Dimension(SassNumber {
num: n,
unit: Unit::None,
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())
}
};
let num = args
.get_err(0, "number")?
.assert_number_with_name("number", args.span)?;
num.assert_no_units("number", args.span)?;
Ok(Value::Dimension(SassNumber {
num,
num: Number(num.num.0 * 100.0),
unit: Unit::Percent,
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> {
args.max_args(1)?;
match args.get_err(0, "number")? {
Value::Dimension(SassNumber {
num: n,
unit: u,
as_slash: _,
}) => Ok(Value::Dimension(SassNumber {
num: (n.abs()),
unit: u,
as_slash: None,
})),
v => Err((
format!("$number: {} is not a number.", v.inspect(args.span())?),
args.span(),
)
.into()),
}
let mut num = args
.get_err(0, "number")?
.assert_number_with_name("number", args.span())?;
num.num = num.num.abs();
Ok(Value::Dimension(num))
}
pub(crate) fn comparable(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResult<Value> {
args.max_args(2)?;
let unit1 = match args.get_err(0, "number1")? {
Value::Dimension(SassNumber {
num: _,
unit: u,
as_slash: _,
}) => u,
v => {
return Err((
format!("$number1: {} is not a number.", v.inspect(args.span())?),
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())
}
};
let unit1 = args
.get_err(0, "number1")?
.assert_number_with_name("number1", args.span())?
.unit;
let unit2 = args
.get_err(1, "number2")?
.assert_number_with_name("number2", args.span())?
.unit;
Ok(Value::bool(unit1.comparable(&unit2)))
}

View File

@ -295,7 +295,7 @@ pub(crate) fn get_function(mut args: ArgumentResult, visitor: &mut Visitor) -> S
};
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()),
}
}
@ -318,7 +318,7 @@ pub(crate) fn call(mut args: ArgumentResult, visitor: &mut Visitor) -> SassResul
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)]

View File

@ -100,7 +100,7 @@ pub(crate) fn str_slice(mut args: ArgumentResult, visitor: &mut Visitor) -> Sass
} else if start > 0 {
(start as usize).min(str_len + 1)
} else {
(start + str_len as i32 + 1).max(1) as usize
(start + str_len as i64 + 1).max(1) as usize
};
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)?;
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);
@ -232,7 +232,7 @@ pub(crate) fn str_insert(mut args: ArgumentResult, visitor: &mut Visitor) -> Sas
} else if index_int == 0 {
insert(0, s1, &substr)
} 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)
};

View File

@ -343,7 +343,7 @@ impl Module {
.map(|(key, value)| {
(
Value::String(key.to_string(), QuoteKind::Quoted).span(span),
Value::FunctionRef(value),
Value::FunctionRef(Box::new(value)),
)
})
.collect::<Vec<_>>(),

View File

@ -134,13 +134,13 @@ impl Environment {
Ok(v) => Ok(v),
Err(e) => {
if let Some(v) = self.get_variable_from_global_modules(name.node) {
return Ok(v);
}
Ok(v)
} else {
Err(e)
}
}
}
}
pub fn insert_var(
&mut self,

View File

@ -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 {
fn name(&self) -> Identifier {
self.name
@ -1112,7 +1122,7 @@ impl<'a> Visitor<'a> {
// todo: independency
let func = SassFunction::UserDefined(UserDefinedFunction {
function: Box::new(fn_decl),
function: Arc::new(fn_decl),
name,
env: self.env.new_closure(),
});
@ -1701,6 +1711,14 @@ impl<'a> Visitor<'a> {
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 {
to += direction;
}
@ -2200,13 +2218,8 @@ impl<'a> Visitor<'a> {
Ok(self.without_slash(val))
}
SassFunction::UserDefined(UserDefinedFunction { function, env, .. }) => self
.run_user_defined_callable(
arguments,
*function,
&env,
span,
|function, visitor| {
for stmt in function.children {
.run_user_defined_callable(arguments, function, &env, span, |function, visitor| {
for stmt in function.children.clone() {
let result = visitor.visit_stmt(stmt)?;
if let Some(val) = result {
@ -2215,8 +2228,7 @@ impl<'a> Visitor<'a> {
}
Err(("Function finished without @return.", span).into())
},
),
}),
SassFunction::Plain { name } => {
let arguments = match arguments {
MaybeEvaledArguments::Invocation(args) => args,

View File

@ -40,8 +40,7 @@ pub(crate) enum Value {
Map(SassMap),
ArgList(ArgList),
/// Returned by `get-function()`
// todo: benchmark boxing this (function refs are infrequent)
FunctionRef(SassFunction),
FunctionRef(Box<SassFunction>),
Calculation(SassCalculation),
}

View File

@ -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()
}
pub(crate) fn fuzzy_as_int(num: f64) -> Option<i32> {
pub(crate) fn fuzzy_as_int(num: f64) -> Option<i64> {
if !num.is_finite() {
return None;
}
@ -55,7 +55,7 @@ pub(crate) fn fuzzy_as_int(num: f64) -> Option<i32> {
let rounded = num.round();
if fuzzy_equals(num, rounded) {
Some(rounded as i32)
Some(rounded as i64)
} else {
None
}
@ -110,14 +110,14 @@ impl Number {
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) {
Some(i) => Ok(i),
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) {
Some(i) => Ok(i),
None => Err((

View File

@ -3,24 +3,15 @@
//! Sass functions can be either user-defined or builtin.
//!
//! 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.
//!
//! Builtin functions are those that have been implemented in rust and are
//! in the global scope.
use std::fmt;
use std::{fmt, sync::Arc};
// use codemap::Spanned;
use crate::{
// error::SassResult,
ast::AstFunctionDecl,
// value::Value,
builtin::Builtin,
common::Identifier,
evaluate::Environment,
};
use crate::{ast::AstFunctionDecl, builtin::Builtin, common::Identifier, evaluate::Environment};
/// A Sass function
///
@ -39,7 +30,7 @@ pub(crate) enum SassFunction {
#[derive(Debug, Clone)]
pub(crate) struct UserDefinedFunction {
pub function: Box<AstFunctionDecl>,
pub function: Arc<AstFunctionDecl>,
pub name: Identifier,
pub env: Environment,
}