From 1dfcef9d2683d1f1eba5f03ee967cc9e29d2d0da Mon Sep 17 00:00:00 2001 From: Adam Esterline Date: Sat, 22 Jun 2024 08:10:52 -0600 Subject: [PATCH] refactor: use a slice to pass function arguments This should allow use a temporary vec/slice when calling fuctions, which will allow us to release memory for function calls that will not be needed in the result. We are hoping this will lower our overall memory footprint. --- src/evaluator.rs | 54 ++++---- src/evaluator/functions.rs | 269 ++++++++++++++++++++----------------- src/evaluator/value.rs | 10 +- src/lib.rs | 2 +- 4 files changed, 173 insertions(+), 162 deletions(-) diff --git a/src/evaluator.rs b/src/evaluator.rs index 8d4ac473..f7815357 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -310,11 +310,10 @@ impl<'a> Evaluator<'a> { if group.index != index { return Err(Error::D1009MultipleKeys(char_index, key.to_string())); } - let args = Value::array_with_capacity(self.arena, 2, ArrayFlags::empty()); - args.push(group.data); - args.push(item); - group.data = - fn_append(self.fn_context("append", char_index, input, frame), args)?; + group.data = fn_append( + self.fn_context("append", char_index, input, frame), + &[group.data, item], + )?; } hash_map::Entry::Vacant(entry) => { entry.insert(Group { data: item, index }); @@ -363,11 +362,10 @@ impl<'a> Evaluator<'a> { } for i in 1..tuple_stream.len() { for (key, value) in tuple_stream[i].entries() { - let args = Value::array_with_capacity(self.arena, 2, ArrayFlags::empty()); - args.push(result.get_entry(&key[..])); - args.push(value); - let new_value = - fn_append(self.fn_context("append", char_index, input, frame), args)?; + let new_value = fn_append( + self.fn_context("append", char_index, input, frame), + &[result.get_entry(&key[..]), value], + )?; result.insert(key, new_value); } } @@ -543,7 +541,7 @@ impl<'a> Evaluator<'a> { result.push_str( &fn_string( self.fn_context("string", node.char_index, input, frame), - Value::wrap_in_array(self.arena, lhs, ArrayFlags::empty()), + &[lhs], )? .as_str(), ); @@ -552,7 +550,7 @@ impl<'a> Evaluator<'a> { result.push_str( &fn_string( self.fn_context("string", node.char_index, input, frame), - Value::wrap_in_array(self.arena, rhs, ArrayFlags::empty()), + &[rhs], )? .as_str(), ); @@ -595,25 +593,19 @@ impl<'a> Evaluator<'a> { frame, )?; - let args = Value::array_with_capacity(self.arena, 2, ArrayFlags::empty()); - args.push(lhs); - args.push(rhs); - Ok(self.apply_function( lhs_ast.char_index, Value::undefined(), chain, - args, + &[lhs, rhs], frame, )?) } else { - let args = Value::array_with_capacity(self.arena, 1, ArrayFlags::empty()); - args.push(lhs); Ok(self.apply_function( rhs_ast.char_index, Value::undefined(), rhs, - args, + &[lhs], frame, )?) } @@ -730,7 +722,7 @@ impl<'a> Evaluator<'a> { result = Value::wrap_in_array( self.arena, result, - flags.clone() | ArrayFlags::SEQUENCE | ArrayFlags::SINGLETON, + flags | ArrayFlags::SEQUENCE | ArrayFlags::SINGLETON, ); } result = result.clone_array_with_flags(self.arena, flags | ArrayFlags::SINGLETON); @@ -1209,8 +1201,7 @@ impl<'a> Evaluator<'a> { } } - let evaluated_args = - Value::array_with_capacity(self.arena, args.len(), ArrayFlags::empty()); + let mut evaluated_args = Vec::with_capacity(args.len()); if let Some(context) = context { evaluated_args.push(context); @@ -1225,7 +1216,7 @@ impl<'a> Evaluator<'a> { proc.char_index, input, evaluated_proc, - evaluated_args, + &evaluated_args, frame, )?; @@ -1249,8 +1240,7 @@ impl<'a> Evaluator<'a> { } = body.kind { let next = self.evaluate(proc, lambda_input, lambda_frame)?; - let evaluated_args = - Value::array_with_capacity(self.arena, args.len(), ArrayFlags::empty()); + let mut evaluated_args = Vec::with_capacity(args.len()); for arg in args { let arg = self.evaluate(arg, lambda_input, lambda_frame)?; @@ -1258,7 +1248,7 @@ impl<'a> Evaluator<'a> { } result = - self.apply_function(proc.char_index, input, next, evaluated_args, frame)?; + self.apply_function(proc.char_index, input, next, &evaluated_args, frame)?; } else { unreachable!() } @@ -1275,7 +1265,7 @@ impl<'a> Evaluator<'a> { char_index: usize, input: &'a Value<'a>, evaluated_proc: &'a Value<'a>, - evaluated_args: &'a Value<'a>, + evaluated_args: &[&'a Value<'a>], frame: &Frame<'a>, ) -> Result<&'a Value<'a>> { match evaluated_proc { @@ -1295,7 +1285,13 @@ impl<'a> Evaluator<'a> { // Bind the arguments to their respective names for (index, arg) in args.iter().enumerate() { if let AstKind::Var(ref name) = arg.kind { - frame.bind(name, evaluated_args.get_member(index)); + frame.bind( + name, + evaluated_args + .get(index) + .copied() + .unwrap_or_else(Value::undefined), + ); } else { unreachable!() } diff --git a/src/evaluator/functions.rs b/src/evaluator/functions.rs index 66275755..c784952e 100644 --- a/src/evaluator/functions.rs +++ b/src/evaluator/functions.rs @@ -79,7 +79,7 @@ impl<'a, 'e> FunctionContext<'a, 'e> { pub fn evaluate_function( &self, proc: &'a Value<'a>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { self.evaluator .apply_function(self.char_index, self.input, proc, args, &self.frame) @@ -126,10 +126,10 @@ pub fn fn_append_internal<'a>( pub fn fn_append<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let arg1 = &args[0]; - let arg2 = &args[1]; + let arg1 = args.first().copied().unwrap_or_else(Value::undefined); + let arg2 = args.get(1).copied().unwrap_or_else(Value::undefined); if arg1.is_undefined() { return Ok(arg2); @@ -169,11 +169,11 @@ pub fn fn_append<'a>( pub fn fn_boolean<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); Ok(match arg { Value::Undefined => Value::undefined(), Value::Null => Value::bool(context.arena, false), @@ -186,18 +186,10 @@ pub fn fn_boolean<'a>( Value::Object(ref obj) => Value::bool(context.arena, !obj.is_empty()), Value::Array { .. } => match arg.len() { 0 => Value::bool(context.arena, false), - 1 => fn_boolean( - context.clone(), - Value::wrap_in_array(context.arena, arg.get_member(0), ArrayFlags::empty()), - )?, + 1 => fn_boolean(context.clone(), &[arg.get_member(0)])?, _ => { for item in arg.members() { - if fn_boolean( - context.clone(), - Value::wrap_in_array(context.arena, item, ArrayFlags::empty()), - )? - .as_bool() - { + if fn_boolean(context.clone(), &[item])?.as_bool() { return Ok(Value::bool(context.arena, true)); } } @@ -211,9 +203,12 @@ pub fn fn_boolean<'a>( }) } -pub fn fn_map<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { - let arr = &args[0]; - let func = &args[1]; +pub fn fn_map<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { + let arr = args.first().copied().unwrap_or_else(Value::undefined); + let func = args.get(1).copied().unwrap_or_else(Value::undefined); if arr.is_undefined() { return Ok(Value::undefined()); @@ -226,7 +221,7 @@ pub fn fn_map<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Resu let result = Value::array(context.arena, ArrayFlags::SEQUENCE); for (index, item) in arr.members().enumerate() { - let args = Value::array(context.arena, ArrayFlags::empty()); + let mut args = Vec::new(); let arity = func.arity(); args.push(item); @@ -237,7 +232,7 @@ pub fn fn_map<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Resu args.push(arr); } - let mapped = context.evaluate_function(func, args)?; + let mapped = context.evaluate_function(func, &args)?; if !mapped.is_undefined() { result.push(mapped); } @@ -248,10 +243,10 @@ pub fn fn_map<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Resu pub fn fn_filter<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let arr = &args[0]; - let func = &args[1]; + let arr = args.first().copied().unwrap_or_else(Value::undefined); + let func = args.get(1).copied().unwrap_or_else(Value::undefined); if arr.is_undefined() { return Ok(Value::undefined()); @@ -264,7 +259,7 @@ pub fn fn_filter<'a>( let result = Value::array(context.arena, ArrayFlags::SEQUENCE); for (index, item) in arr.members().enumerate() { - let args = Value::array(context.arena, ArrayFlags::empty()); + let mut args = Vec::new(); let arity = func.arity(); args.push(item); @@ -275,7 +270,7 @@ pub fn fn_filter<'a>( args.push(arr); } - let include = context.evaluate_function(func, args)?; + let include = context.evaluate_function(func, &args)?; if include.is_truthy() { result.push(item); @@ -285,7 +280,10 @@ pub fn fn_filter<'a>( Ok(result) } -pub fn fn_each<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { +pub fn fn_each<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { let (obj, func) = if args.len() == 1 { let obj_arg = if context.input.is_array() && context.input.has_flags(ArrayFlags::WRAPPED) { &context.input[0] @@ -293,9 +291,9 @@ pub fn fn_each<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res context.input }; - (obj_arg, &args[0]) + (obj_arg, args[0]) } else { - (&args[0], &args[1]) + (args[0], args[1]) }; if obj.is_undefined() { @@ -308,13 +306,9 @@ pub fn fn_each<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res let result = Value::array(context.arena, ArrayFlags::SEQUENCE); for (key, value) in obj.entries() { - let args = Value::array(context.arena, ArrayFlags::empty()); let key = Value::string(context.arena, key); - args.push(value); - args.push(key); - - let mapped = context.evaluate_function(func, args)?; + let mapped = context.evaluate_function(func, &[value, key])?; if !mapped.is_undefined() { result.push(mapped); } @@ -323,7 +317,10 @@ pub fn fn_each<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res Ok(result) } -pub fn fn_keys<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { +pub fn fn_keys<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { let obj = if args.is_empty() { if context.input.is_array() && context.input.has_flags(ArrayFlags::WRAPPED) { &context.input[0] @@ -331,7 +328,7 @@ pub fn fn_keys<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res context.input } } else { - &args[0] + args[0] }; if obj.is_undefined() { @@ -365,7 +362,7 @@ pub fn fn_keys<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res pub fn fn_merge<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { let mut array_of_objects = if args.is_empty() { if context.input.is_array() && context.input.has_flags(ArrayFlags::WRAPPED) { @@ -374,7 +371,7 @@ pub fn fn_merge<'a>( context.input } } else { - &args[0] + args[0] }; if array_of_objects.is_undefined() { @@ -405,7 +402,7 @@ pub fn fn_merge<'a>( pub fn fn_string<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 2); @@ -416,14 +413,14 @@ pub fn fn_string<'a>( context.input } } else { - &args[0] + args.first().copied().unwrap_or_else(Value::undefined) }; if input.is_undefined() { return Ok(Value::undefined()); } - let pretty = &args[1]; + let pretty = args.get(1).copied().unwrap_or_else(Value::undefined); assert_arg!(pretty.is_undefined() || pretty.is_bool(), context, 2); if input.is_string() { @@ -443,8 +440,11 @@ pub fn fn_string<'a>( } } -pub fn fn_not<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { - let arg = &args[0]; +pub fn fn_not<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { + let arg = args.first().copied().unwrap_or_else(Value::undefined); Ok(if arg.is_undefined() { Value::undefined() @@ -455,9 +455,9 @@ pub fn fn_not<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Resu pub fn fn_lowercase<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); Ok(if !arg.is_string() { Value::undefined() @@ -468,9 +468,9 @@ pub fn fn_lowercase<'a>( pub fn fn_uppercase<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); if !arg.is_string() { Ok(Value::undefined()) @@ -479,8 +479,11 @@ pub fn fn_uppercase<'a>( } } -pub fn fn_trim<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { - let arg = &args[0]; +pub fn fn_trim<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { + let arg = args.first().copied().unwrap_or_else(Value::undefined); if !arg.is_string() { Ok(Value::undefined()) @@ -507,11 +510,11 @@ pub fn fn_trim<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res pub fn fn_substring<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let string = &args[0]; - let start = &args[1]; - let length = &args[2]; + let string = args.first().copied().unwrap_or_else(Value::undefined); + let start = args.get(1).copied().unwrap_or_else(Value::undefined); + let length = args.get(2).copied().unwrap_or_else(Value::undefined); if string.is_undefined() { return Ok(Value::undefined()); @@ -565,10 +568,10 @@ pub fn fn_substring<'a>( pub fn fn_contains<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let str_value = &args[0]; - let token_value = &args[1]; + let str_value = args.first().copied().unwrap_or_else(Value::undefined); + let token_value = args.get(1).copied().unwrap_or_else(Value::undefined); if str_value.is_undefined() { return Ok(Value::undefined()); @@ -588,12 +591,12 @@ pub fn fn_contains<'a>( pub fn fn_replace<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let str_value = &args[0]; - let pattern_value = &args[1]; - let replacement_value = &args[2]; - let limit_value = &args[3]; + let str_value = args.first().copied().unwrap_or_else(Value::undefined); + let pattern_value = args.get(1).copied().unwrap_or_else(Value::undefined); + let replacement_value = args.get(2).copied().unwrap_or_else(Value::undefined); + let limit_value = args.get(3).copied().unwrap_or_else(Value::undefined); if str_value.is_undefined() { return Ok(Value::undefined()); @@ -635,11 +638,11 @@ pub fn fn_replace<'a>( pub fn fn_split<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let str_value = &args[0]; - let separator_value = &args[1]; - let limit_value = &args[2]; + let str_value = args.first().copied().unwrap_or_else(Value::undefined); + let separator_value = args.get(1).copied().unwrap_or_else(Value::undefined); + let limit_value = args.get(2).copied().unwrap_or_else(Value::undefined); if str_value.is_undefined() { return Ok(Value::undefined()); @@ -681,8 +684,11 @@ pub fn fn_split<'a>( Ok(result) } -pub fn fn_abs<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { - let arg = &args[0]; +pub fn fn_abs<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { + let arg = args.first().copied().unwrap_or_else(Value::undefined); if arg.is_undefined() { return Ok(Value::undefined()); @@ -695,9 +701,9 @@ pub fn fn_abs<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Resu pub fn fn_floor<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); if arg.is_undefined() { return Ok(Value::undefined()); @@ -708,8 +714,11 @@ pub fn fn_floor<'a>( Ok(Value::number(context.arena, arg.as_f64().floor())) } -pub fn fn_ceil<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { - let arg = &args[0]; +pub fn fn_ceil<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { + let arg = args.first().copied().unwrap_or_else(Value::undefined); if arg.is_undefined() { return Ok(Value::undefined()); @@ -749,38 +758,37 @@ pub fn fn_lookup_internal<'a>( pub fn fn_lookup<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let input = &args[0]; - let key = &args[1]; + let input = args.first().copied().unwrap_or_else(Value::undefined); + let key = args.get(1).copied().unwrap_or_else(Value::undefined); assert_arg!(key.is_string(), context, 2); Ok(fn_lookup_internal(context.clone(), input, &key.as_str())) } pub fn fn_count<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg = &args[0]; + let count = match args.first() { + Some(Value::Array(a, _)) => a.len() as f64, + Some(Value::Undefined) => 0.0, + Some(_) => 1.0, + None => 0.0, + }; - Ok(Value::number( - context.arena, - if arg.is_undefined() { - 0.0 - } else if arg.is_array() { - arg.len() as f64 - } else { - 1.0 - }, - )) + Ok(Value::number(context.arena, count)) } -pub fn fn_max<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { +pub fn fn_max<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); // $max(undefined) and $max([]) return undefined if arg.is_undefined() || (arg.is_array() && arg.is_empty()) { @@ -798,10 +806,13 @@ pub fn fn_max<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Resu Ok(Value::number(context.arena, max)) } -pub fn fn_min<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { +pub fn fn_min<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); // $min(undefined) and $min([]) return undefined if arg.is_undefined() || (arg.is_array() && arg.is_empty()) { @@ -819,10 +830,13 @@ pub fn fn_min<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Resu Ok(Value::number(context.arena, min)) } -pub fn fn_sum<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { +pub fn fn_sum<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); // $sum(undefined) returns undefined if arg.is_undefined() { @@ -842,11 +856,11 @@ pub fn fn_sum<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Resu pub fn fn_number<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); match arg { Value::Undefined => Ok(Value::undefined()), @@ -870,12 +884,12 @@ pub fn fn_number<'a>( pub fn fn_exists<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { min_args!(context, args, 1); max_args!(context, args, 1); - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); match arg { Value::Undefined => Ok(Value::bool(context.arena, false)), @@ -885,10 +899,10 @@ pub fn fn_exists<'a>( pub fn fn_assert<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let condition = &args[0]; - let message = &args[1]; + let condition = args.first().copied().unwrap_or_else(Value::undefined); + let message = args.get(1).copied().unwrap_or_else(Value::undefined); assert_arg!(condition.is_bool(), context, 1); @@ -905,9 +919,9 @@ pub fn fn_assert<'a>( pub fn fn_error<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { - let message = &args[0]; + let message = args.first().copied().unwrap_or_else(Value::undefined); assert_arg!(message.is_undefined() || message.is_string(), context, 1); @@ -920,11 +934,11 @@ pub fn fn_error<'a>( pub fn fn_length<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg1 = &args[0]; + let arg1 = args.first().copied().unwrap_or_else(Value::undefined); if arg1.is_undefined() { return Ok(Value::undefined()); @@ -938,10 +952,12 @@ pub fn fn_length<'a>( )) } -pub fn fn_sqrt<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { +pub fn fn_sqrt<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - - let arg1 = &args[0]; + let arg1 = args.first().copied().unwrap_or_else(Value::undefined); if arg1.is_undefined() { return Ok(Value::undefined()); @@ -959,12 +975,12 @@ pub fn fn_sqrt<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res pub fn fn_power<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 2); - let number = &args[0]; - let exp = &args[1]; + let number = args.first().copied().unwrap_or_else(Value::undefined); + let exp = args.get(1).copied().unwrap_or_else(Value::undefined); if number.is_undefined() { return Ok(Value::undefined()); @@ -988,11 +1004,11 @@ pub fn fn_power<'a>( pub fn fn_reverse<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arr = &args[0]; + let arr = args.first().copied().unwrap_or_else(Value::undefined); if arr.is_undefined() { return Ok(Value::undefined()); @@ -1005,10 +1021,12 @@ pub fn fn_reverse<'a>( Ok(result) } -pub fn fn_join<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Result<&'a Value<'a>> { +pub fn fn_join<'a>( + context: FunctionContext<'a, '_>, + args: &[&'a Value<'a>], +) -> Result<&'a Value<'a>> { max_args!(context, args, 2); - - let strings = &args[0]; + let strings = args.first().copied().unwrap_or_else(Value::undefined); if strings.is_undefined() { return Ok(Value::undefined()); @@ -1020,7 +1038,7 @@ pub fn fn_join<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res assert_array_of_type!(strings.is_array(), context, 1, "string"); - let separator = &args[1]; + let separator = args.get(1).copied().unwrap_or_else(Value::undefined); assert_arg!( separator.is_undefined() || separator.is_string(), context, @@ -1047,11 +1065,11 @@ pub fn fn_join<'a>(context: FunctionContext<'a, '_>, args: &'a Value<'a>) -> Res pub fn fn_sort<'a, 'e>( context: FunctionContext<'a, 'e>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 2); - let arr = &args[0]; + let arr = args.first().copied().unwrap_or_else(Value::undefined); if arr.is_undefined() { return Ok(Value::undefined()); @@ -1069,7 +1087,7 @@ pub fn fn_sort<'a, 'e>( // at least it's just references. let unsorted = arr.members().collect::>>(); - let sorted = if args[1].is_undefined() { + let sorted = if args.get(1).is_none() { merge_sort( unsorted, &|a: &'a Value<'a>, b: &'a Value<'a>| match (a, b) { @@ -1079,13 +1097,10 @@ pub fn fn_sort<'a, 'e>( }, )? } else { - let comparator = args.get_member(1); + let comparator = args.get(1).copied().unwrap_or_else(Value::undefined); assert_arg!(comparator.is_function(), context, 2); merge_sort(unsorted, &|a: &'a Value<'a>, b: &'a Value<'a>| { - let args = Value::array_with_capacity(context.arena, 2, ArrayFlags::empty()); - args.push(a); - args.push(b); - let result = context.evaluate_function(comparator, args)?; + let result = context.evaluate_function(comparator, &[a, b])?; Ok(result.is_truthy()) })? }; @@ -1149,10 +1164,10 @@ where pub fn fn_base64_encode<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); if arg.is_undefined() { return Ok(Value::undefined()); } @@ -1167,10 +1182,10 @@ pub fn fn_base64_encode<'a>( pub fn fn_base64_decode<'a>( context: FunctionContext<'a, '_>, - args: &'a Value<'a>, + args: &[&'a Value<'a>], ) -> Result<&'a Value<'a>> { max_args!(context, args, 1); - let arg = &args[0]; + let arg = args.first().copied().unwrap_or_else(Value::undefined); if arg.is_undefined() { return Ok(Value::undefined()); } diff --git a/src/evaluator/value.rs b/src/evaluator/value.rs index 4d29ed2e..bd3040a1 100644 --- a/src/evaluator/value.rs +++ b/src/evaluator/value.rs @@ -22,7 +22,7 @@ use self::serialize::{DumpFormatter, PrettyFormatter, Serializer}; pub use iterator::MemberIterator; bitflags! { - #[derive(Clone)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ArrayFlags: u8 { const SEQUENCE = 0b00000001; const SINGLETON = 0b00000010; @@ -57,7 +57,7 @@ pub enum Value<'a> { NativeFn { name: String, arity: usize, - func: fn(FunctionContext<'a, '_>, &'a Value<'a>) -> Result<&'a Value<'a>>, + func: fn(FunctionContext<'a, '_>, &[&'a Value<'a>]) -> Result<&'a Value<'a>>, }, Transformer { pattern: std::boxed::Box, @@ -152,7 +152,7 @@ impl<'a> Value<'a> { arena: &'a Bump, name: &str, arity: usize, - func: fn(FunctionContext<'a, '_>, &'a Value<'a>) -> Result<&'a Value<'a>>, + func: fn(FunctionContext<'a, '_>, &[&'a Value<'a>]) -> Result<&'a Value<'a>>, ) -> &'a mut Value<'a> { arena.alloc(Value::NativeFn { name: name.to_string(), @@ -486,7 +486,7 @@ impl<'a> Value<'a> { pub fn get_flags(&self) -> ArrayFlags { match self { - Value::Array(_, flags) => flags.clone(), + Value::Array(_, flags) => *flags, _ => panic!("Not an array"), } } @@ -508,7 +508,7 @@ impl<'a> Value<'a> { Self::String(s) => arena.alloc(Value::String( bumpalo::collections::String::from_str_in(s.as_str(), arena), )), - Self::Array(a, f) => Value::array_from(a, arena, f.clone()), + Self::Array(a, f) => Value::array_from(a, arena, *f), Self::Object(o) => Value::object_from(o, arena), Self::Lambda { ast, input, frame } => Value::lambda(arena, ast, input, frame.clone()), Self::NativeFn { name, arity, func } => Value::nativefn(arena, name, *arity, *func), diff --git a/src/lib.rs b/src/lib.rs index 253b9337..c275328b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ impl<'a> JsonAta<'a> { &self, name: &str, arity: usize, - implementation: fn(FunctionContext<'a, '_>, &'a Value<'a>) -> Result<&'a Value<'a>>, + implementation: fn(FunctionContext<'a, '_>, &[&'a Value<'a>]) -> Result<&'a Value<'a>>, ) { self.frame.bind( name,