From c7b556b7411b40de6448c3bcacee41758a8492fc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Feb 2021 16:53:35 +0800 Subject: [PATCH 1/8] Bump version. --- Cargo.toml | 2 +- RELEASES.md | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b4655469..0911adfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "rhai" -version = "0.19.12" +version = "0.19.13" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" diff --git a/RELEASES.md b/RELEASES.md index 9e80f364..3bfaee20 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,12 @@ Rhai Release Notes Version 0.19.12 =============== +This version is an incremental release with a number of enhancements and bug fixes. + +Notice that there are a number of breaking changes, especially with regards to replacing the `~` +exponential operator with `**`, and the addition of the `decimal` feature that turns on +[`Decimal`](https://crates.io/crates/rust_decimal) support. + Bug fixes --------- From cc90b5ffab8ec5b2aea35d06145e5899a89a37d0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Feb 2021 19:28:07 +0800 Subject: [PATCH 2/8] Remove unnecessary lifetimes. --- src/engine.rs | 6 +++--- src/fn_native.rs | 18 +++++++++--------- src/syntax.rs | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index b079ae9a..0b164d91 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -627,17 +627,17 @@ pub struct Limits { /// Context of a script evaluation process. #[derive(Debug)] -pub struct EvalContext<'e, 'x, 'px: 'x, 'a, 's, 'm, 'pm: 'm, 't, 'pt: 't> { +pub struct EvalContext<'e, 'x, 'px: 'x, 'a, 's, 'm, 't, 'pt: 't> { pub(crate) engine: &'e Engine, pub(crate) scope: &'x mut Scope<'px>, pub(crate) mods: &'a mut Imports, pub(crate) state: &'s mut State, - pub(crate) lib: &'m [&'pm Module], + pub(crate) lib: &'m [&'m Module], pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>, pub(crate) level: usize, } -impl<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 'pm, 't, 'pt> { +impl<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> { /// The current [`Engine`]. #[inline(always)] pub fn engine(&self) -> &Engine { diff --git a/src/fn_native.rs b/src/fn_native.rs index 7c69a768..59df6849 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -55,17 +55,17 @@ pub type Locked = crate::stdlib::sync::RwLock; /// Context of a native Rust function call. #[derive(Debug, Copy, Clone)] -pub struct NativeCallContext<'e, 'n, 's, 'a, 'm, 'pm: 'm> { +pub struct NativeCallContext<'e, 'n, 's, 'a, 'm> { engine: &'e Engine, fn_name: &'n str, source: Option<&'s str>, pub(crate) mods: Option<&'a Imports>, - pub(crate) lib: &'m [&'pm Module], + pub(crate) lib: &'m [&'m Module], } -impl<'e, 'n, 's, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> +impl<'e, 'n, 's, 'a, 'm, M: AsRef<[&'m Module]> + ?Sized> From<(&'e Engine, &'n str, Option<&'s str>, &'a Imports, &'m M)> - for NativeCallContext<'e, 'n, 's, 'a, 'm, 'pm> + for NativeCallContext<'e, 'n, 's, 'a, 'm> { #[inline(always)] fn from(value: (&'e Engine, &'n str, Option<&'s str>, &'a Imports, &'m M)) -> Self { @@ -79,8 +79,8 @@ impl<'e, 'n, 's, 'a, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> } } -impl<'e, 'n, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &'n str, &'m M)> - for NativeCallContext<'e, 'n, '_, '_, 'm, 'pm> +impl<'e, 'n, 'm, M: AsRef<[&'m Module]> + ?Sized> From<(&'e Engine, &'n str, &'m M)> + for NativeCallContext<'e, 'n, '_, '_, 'm> { #[inline(always)] fn from(value: (&'e Engine, &'n str, &'m M)) -> Self { @@ -94,10 +94,10 @@ impl<'e, 'n, 'm, 'pm: 'm, M: AsRef<[&'pm Module]> + ?Sized> From<(&'e Engine, &' } } -impl<'e, 'n, 's, 'a, 'm, 'pm> NativeCallContext<'e, 'n, 's, 'a, 'm, 'pm> { +impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> { /// Create a new [`NativeCallContext`]. #[inline(always)] - pub fn new(engine: &'e Engine, fn_name: &'n str, lib: &'m impl AsRef<[&'pm Module]>) -> Self { + pub fn new(engine: &'e Engine, fn_name: &'n str, lib: &'m impl AsRef<[&'m Module]>) -> Self { Self { engine, fn_name, @@ -116,7 +116,7 @@ impl<'e, 'n, 's, 'a, 'm, 'pm> NativeCallContext<'e, 'n, 's, 'a, 'm, 'pm> { fn_name: &'n str, source: &'s Option<&str>, imports: &'a mut Imports, - lib: &'m impl AsRef<[&'pm Module]>, + lib: &'m impl AsRef<[&'m Module]>, ) -> Self { Self { engine, diff --git a/src/syntax.rs b/src/syntax.rs index 1ec40876..96498a2c 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -61,7 +61,7 @@ impl Expression<'_> { } } -impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_> { +impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> { /// Evaluate an [expression tree][Expression]. /// /// # WARNING - Low Level API From f8a7ada2a24473579fce7846243ed7dd5c5a3aff Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 18 Feb 2021 10:47:57 +0800 Subject: [PATCH 3/8] Implement Add and AddAssign to Position, fix bug in Position::is_beginning_of_line --- src/token.rs | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/token.rs b/src/token.rs index 0717c632..fcbcadf6 100644 --- a/src/token.rs +++ b/src/token.rs @@ -9,6 +9,7 @@ use crate::stdlib::{ char, fmt, format, iter::Peekable, num::NonZeroUsize, + ops::{Add, AddAssign}, str::{Chars, FromStr}, string::{String, ToString}, }; @@ -120,7 +121,7 @@ impl Position { /// Is this [`Position`] at the beginning of a line? #[inline(always)] pub fn is_beginning_of_line(self) -> bool { - self.line == 0 && !self.is_none() + self.pos == 0 && !self.is_none() } /// Is there no [`Position`]? #[inline(always)] @@ -154,6 +155,31 @@ impl fmt::Debug for Position { } } +impl Add for Position { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + if rhs.is_none() { + self + } else { + Self { + line: self.line + rhs.line - 1, + pos: if rhs.is_beginning_of_line() { + self.pos + } else { + self.pos + rhs.pos - 1 + }, + } + } + } +} + +impl AddAssign for Position { + fn add_assign(&mut self, rhs: Self) { + *self = *self + rhs; + } +} + /// _(INTERNALS)_ A Rhai language token. /// Exported under the `internals` feature only. /// From 9a9adbefccc2c8c2631f57209aaab7d16e195b06 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 18 Feb 2021 14:33:12 +0800 Subject: [PATCH 4/8] Wrap eval errors in ErrorInFunctionCall. --- RELEASES.md | 15 +++++++++++++++ src/fn_call.rs | 34 ++++++++++++++++++++++++++++------ tests/operations.rs | 2 +- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 3bfaee20..a3ee4317 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,21 @@ Rhai Release Notes ================== +Version 0.19.13 +=============== + +Bug fixes +--------- + +* Bug in `Position::is_beginning_of_line` is fixed. + +Enhancements +------------ + +* Error position in `eval` statements is now wrapped in an `EvalAltResult::ErrorInFunctionCall`. +* `Position` now implements `Add` and `AddAssign`. + + Version 0.19.12 =============== diff --git a/src/fn_call.rs b/src/fn_call.rs index 31c726de..b0573dfe 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -798,6 +798,7 @@ impl Engine { self.eval_global_statements(scope, mods, &mut new_state, ast.statements(), lib, level); state.operations = new_state.operations; + result } @@ -1067,17 +1068,27 @@ impl Engine { if name == KEYWORD_EVAL && args_expr.len() == 1 { let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::())); + let script_expr = &args_expr[0]; + if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) { + let script_pos = script_expr.position(); + // eval - only in function call style let prev_len = scope.len(); let script = - self.eval_expr(scope, mods, state, lib, this_ptr, &args_expr[0], level)?; + self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)?; let script = script.as_str().map_err(|typ| { - self.make_type_mismatch_err::(typ, args_expr[0].position()) + self.make_type_mismatch_err::(typ, script_pos) })?; - let pos = args_expr[0].position(); - let result = - self.eval_script_expr_in_place(scope, mods, state, lib, script, pos, level + 1); + let result = self.eval_script_expr_in_place( + scope, + mods, + state, + lib, + script, + script_pos, + level + 1, + ); // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. @@ -1085,7 +1096,18 @@ impl Engine { state.always_search = true; } - return result; + return result.map_err(|err| { + Box::new(EvalAltResult::ErrorInFunctionCall( + KEYWORD_EVAL.to_string(), + state + .source + .as_ref() + .map_or_else(|| "", |s| s.as_str()) + .to_string(), + err, + pos, + )) + }); } } diff --git a/tests/operations.rs b/tests/operations.rs index fc8c25cb..6b812958 100644 --- a/tests/operations.rs +++ b/tests/operations.rs @@ -106,7 +106,7 @@ fn test_max_operations_eval() -> Result<(), Box> { "# ) .expect_err("should error"), - EvalAltResult::ErrorTooManyOperations(_) + EvalAltResult::ErrorInFunctionCall(_, _, err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(_)) )); Ok(()) From d2121e21830bcd0868f04c2a0877d7c0f2cc6698 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 18 Feb 2021 14:33:27 +0800 Subject: [PATCH 5/8] Add operators between FLOAT/Decimal and INT. --- src/packages/logic.rs | 173 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 1 deletion(-) diff --git a/src/packages/logic.rs b/src/packages/logic.rs index deb85c74..578234c8 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -70,13 +70,18 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, { { #[cfg(not(feature = "f32_float"))] reg_functions!(lib += float; f32); + combine_with_exported_module!(lib, "f32", f32_functions); #[cfg(feature = "f32_float")] reg_functions!(lib += float; f64); + combine_with_exported_module!(lib, "f64", f64_functions); } #[cfg(feature = "decimal")] - reg_functions!(lib += decimal; Decimal); + { + reg_functions!(lib += decimal; Decimal); + combine_with_exported_module!(lib, "decimal", decimal_functions); + } set_exported_fn!(lib, "!", not); }); @@ -106,3 +111,169 @@ gen_cmp_functions!(float => f64); #[cfg(feature = "decimal")] gen_cmp_functions!(decimal => Decimal); + +#[cfg(not(feature = "no_float"))] +#[export_module] +mod f32_functions { + use crate::INT; + + #[rhai_fn(name = "==")] + pub fn eq_if(x: INT, y: f32) -> bool { + (x as f32) == (y as f32) + } + #[rhai_fn(name = "==")] + pub fn eq_fi(x: f32, y: INT) -> bool { + (x as f32) == (y as f32) + } + #[rhai_fn(name = "!=")] + pub fn neq_if(x: INT, y: f32) -> bool { + (x as f32) != (y as f32) + } + #[rhai_fn(name = "!=")] + pub fn neq_fi(x: f32, y: INT) -> bool { + (x as f32) != (y as f32) + } + #[rhai_fn(name = ">")] + pub fn gt_if(x: INT, y: f32) -> bool { + (x as f32) > (y as f32) + } + #[rhai_fn(name = ">")] + pub fn gt_fi(x: f32, y: INT) -> bool { + (x as f32) > (y as f32) + } + #[rhai_fn(name = ">=")] + pub fn gte_if(x: INT, y: f32) -> bool { + (x as f32) >= (y as f32) + } + #[rhai_fn(name = ">=")] + pub fn gte_fi(x: f32, y: INT) -> bool { + (x as f32) >= (y as f32) + } + #[rhai_fn(name = "<")] + pub fn lt_if(x: INT, y: f32) -> bool { + (x as f32) < (y as f32) + } + #[rhai_fn(name = "<")] + pub fn lt_fi(x: f32, y: INT) -> bool { + (x as f32) < (y as f32) + } + #[rhai_fn(name = "<=")] + pub fn lte_if(x: INT, y: f32) -> bool { + (x as f32) <= (y as f32) + } + #[rhai_fn(name = "<=")] + pub fn lte_fi(x: f32, y: INT) -> bool { + (x as f32) <= (y as f32) + } +} + +#[cfg(not(feature = "no_float"))] +#[export_module] +mod f64_functions { + use crate::INT; + + #[rhai_fn(name = "==")] + pub fn eq_if(x: INT, y: f64) -> bool { + (x as f64) == (y as f64) + } + #[rhai_fn(name = "==")] + pub fn eq_fi(x: f64, y: INT) -> bool { + (x as f64) == (y as f64) + } + #[rhai_fn(name = "!=")] + pub fn neq_if(x: INT, y: f64) -> bool { + (x as f64) != (y as f64) + } + #[rhai_fn(name = "!=")] + pub fn neq_fi(x: f64, y: INT) -> bool { + (x as f64) != (y as f64) + } + #[rhai_fn(name = ">")] + pub fn gt_if(x: INT, y: f64) -> bool { + (x as f64) > (y as f64) + } + #[rhai_fn(name = ">")] + pub fn gt_fi(x: f64, y: INT) -> bool { + (x as f64) > (y as f64) + } + #[rhai_fn(name = ">=")] + pub fn gte_if(x: INT, y: f64) -> bool { + (x as f64) >= (y as f64) + } + #[rhai_fn(name = ">=")] + pub fn gte_fi(x: f64, y: INT) -> bool { + (x as f64) >= (y as f64) + } + #[rhai_fn(name = "<")] + pub fn lt_if(x: INT, y: f64) -> bool { + (x as f64) < (y as f64) + } + #[rhai_fn(name = "<")] + pub fn lt_fi(x: f64, y: INT) -> bool { + (x as f64) < (y as f64) + } + #[rhai_fn(name = "<=")] + pub fn lte_if(x: INT, y: f64) -> bool { + (x as f64) <= (y as f64) + } + #[rhai_fn(name = "<=")] + pub fn lte_fi(x: f64, y: INT) -> bool { + (x as f64) <= (y as f64) + } +} + +#[cfg(feature = "decimal")] +#[export_module] +mod decimal_functions { + use crate::INT; + use rust_decimal::Decimal; + + #[rhai_fn(name = "==")] + pub fn eq_if(x: INT, y: Decimal) -> bool { + Decimal::from(x) == y + } + #[rhai_fn(name = "==")] + pub fn eq_fi(x: Decimal, y: INT) -> bool { + x == Decimal::from(y) + } + #[rhai_fn(name = "!=")] + pub fn neq_if(x: INT, y: Decimal) -> bool { + Decimal::from(x) != y + } + #[rhai_fn(name = "!=")] + pub fn neq_fi(x: Decimal, y: INT) -> bool { + x != Decimal::from(y) + } + #[rhai_fn(name = ">")] + pub fn gt_if(x: INT, y: Decimal) -> bool { + Decimal::from(x) > y + } + #[rhai_fn(name = ">")] + pub fn gt_fi(x: Decimal, y: INT) -> bool { + x > Decimal::from(y) + } + #[rhai_fn(name = ">=")] + pub fn gte_if(x: INT, y: Decimal) -> bool { + Decimal::from(x) >= y + } + #[rhai_fn(name = ">=")] + pub fn gte_fi(x: Decimal, y: INT) -> bool { + x >= Decimal::from(y) + } + #[rhai_fn(name = "<")] + pub fn lt_if(x: INT, y: Decimal) -> bool { + Decimal::from(x) < y + } + #[rhai_fn(name = "<")] + pub fn lt_fi(x: Decimal, y: INT) -> bool { + x < Decimal::from(y) + } + #[rhai_fn(name = "<=")] + pub fn lte_if(x: INT, y: Decimal) -> bool { + Decimal::from(x) <= y + } + #[rhai_fn(name = "<=")] + pub fn lte_fi(x: Decimal, y: INT) -> bool { + x <= Decimal::from(y) + } +} From 61d7356e083e600345bb9cff1cc8adfd0988b5fb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 18 Feb 2021 17:42:49 +0800 Subject: [PATCH 6/8] Reduce spelling warnings in codegen. --- codegen/src/function.rs | 24 +++++++------- codegen/src/lib.rs | 36 ++++++++++---------- codegen/src/module.rs | 48 ++++++++++++++------------- codegen/src/register.rs | 8 ++--- codegen/src/rhai_module.rs | 64 ++++++++++++++++++------------------ codegen/src/test/module.rs | 22 ++++++------- codegen/tests/test_nested.rs | 6 ++-- 7 files changed, 105 insertions(+), 103 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 426f4edc..0054a7b7 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -378,8 +378,8 @@ impl Parse for ExportedFn { } // Check return type. - if let syn::ReturnType::Type(_, ref rtype) = fn_all.sig.output { - match flatten_type_groups(rtype.as_ref()) { + if let syn::ReturnType::Type(_, ref ret_type) = fn_all.sig.output { + match flatten_type_groups(ret_type.as_ref()) { syn::Type::Ptr(_) => { return Err(syn::Error::new( fn_all.sig.output.span(), @@ -495,8 +495,8 @@ impl ExportedFn { } pub(crate) fn return_type(&self) -> Option<&syn::Type> { - if let syn::ReturnType::Type(_, ref rtype) = self.signature.output { - Some(flatten_type_groups(rtype)) + if let syn::ReturnType::Type(_, ref ret_type) = self.signature.output { + Some(flatten_type_groups(ret_type)) } else { None } @@ -616,8 +616,8 @@ impl ExportedFn { let arguments: Vec = dynamic_signature .inputs .iter() - .filter_map(|fnarg| { - if let syn::FnArg::Typed(syn::PatType { ref pat, .. }) = fnarg { + .filter_map(|fn_arg| { + if let syn::FnArg::Typed(syn::PatType { ref pat, .. }) = fn_arg { if let syn::Pat::Ident(ref ident) = pat.as_ref() { Some(ident.ident.clone()) } else { @@ -718,7 +718,7 @@ impl ExportedFn { let arg_count = self.arg_count(); let is_method_call = self.mutable_receiver(); - let mut unpack_stmts: Vec = Vec::new(); + let mut unpack_statements: Vec = Vec::new(); let mut unpack_exprs: Vec = Vec::new(); let mut input_type_names: Vec = Vec::new(); let mut input_type_exprs: Vec = Vec::new(); @@ -748,7 +748,7 @@ impl ExportedFn { }; let downcast_span = quote_spanned!( arg_type.span()=> &mut args[0usize].write_lock::<#arg_type>().unwrap()); - unpack_stmts.push( + unpack_statements.push( syn::parse2::(quote! { let #var = #downcast_span; }) @@ -811,7 +811,7 @@ impl ExportedFn { } }; - unpack_stmts.push( + unpack_statements.push( syn::parse2::(quote! { let #var = #downcast_span; }) @@ -847,8 +847,8 @@ impl ExportedFn { // that as needing to borrow the entire array, all of the previous argument unpacking via // clone needs to happen first. if is_method_call { - let arg0 = unpack_stmts.remove(0); - unpack_stmts.push(arg0); + let arg0 = unpack_statements.remove(0); + unpack_statements.push(arg0); } // Handle "raw returns", aka cases where the result is a dynamic or an error. @@ -881,7 +881,7 @@ impl ExportedFn { debug_assert_eq!(args.len(), #arg_count, "wrong arg count: {} != {}", args.len(), #arg_count); - #(#unpack_stmts)* + #(#unpack_statements)* #return_expr } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index cdc54041..13396a8b 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -293,12 +293,12 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro /// ``` #[proc_macro] pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { - let (engine_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args) - { - Ok(triple) => triple, - Err(e) => return e.to_compile_error().into(), - }; - let gen_mod_path = crate::register::generated_module_path(&rust_modpath); + let (engine_expr, export_name, rust_mod_path) = + match crate::register::parse_register_macro(args) { + Ok(triple) => triple, + Err(e) => return e.to_compile_error().into(), + }; + let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); let tokens = quote! { #engine_expr.register_result_fn(&(#export_name), #gen_mod_path::dynamic_result_fn); }; @@ -332,12 +332,12 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS /// ``` #[proc_macro] pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { - let (module_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args) - { - Ok(triple) => triple, - Err(e) => return e.to_compile_error().into(), - }; - let gen_mod_path = crate::register::generated_module_path(&rust_modpath); + let (module_expr, export_name, rust_mod_path) = + match crate::register::parse_register_macro(args) { + Ok(triple) => triple, + Err(e) => return e.to_compile_error().into(), + }; + let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); let tokens = quote! { #module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public, Some(#gen_mod_path::token_input_names().as_ref()), @@ -374,12 +374,12 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream /// ``` #[proc_macro] pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { - let (module_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args) - { - Ok(triple) => triple, - Err(e) => return e.to_compile_error().into(), - }; - let gen_mod_path = crate::register::generated_module_path(&rust_modpath); + let (module_expr, export_name, rust_mod_path) = + match crate::register::parse_register_macro(args) { + Ok(triple) => triple, + Err(e) => return e.to_compile_error().into(), + }; + let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); let tokens = quote! { #module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public, Some(#gen_mod_path::token_input_names().as_ref()), diff --git a/codegen/src/module.rs b/codegen/src/module.rs index de6fbd28..fa9526ec 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -101,7 +101,7 @@ pub(crate) struct Module { mod_all: syn::ItemMod, fns: Vec, consts: Vec, - submodules: Vec, + sub_modules: Vec, params: ExportedModParams, } @@ -117,7 +117,7 @@ impl Parse for Module { let mut mod_all: syn::ItemMod = input.parse()?; let fns: Vec<_>; let mut consts: Vec<_> = new_vec![]; - let mut submodules: Vec<_> = Vec::new(); + let mut sub_modules: Vec<_> = Vec::new(); if let Some((_, ref mut content)) = mod_all.content { // Gather and parse functions. fns = content @@ -126,16 +126,16 @@ impl Parse for Module { syn::Item::Fn(f) => Some(f), _ => None, }) - .try_fold(Vec::new(), |mut vec, itemfn| { + .try_fold(Vec::new(), |mut vec, item_fn| { // #[cfg] attributes are not allowed on functions - crate::attrs::deny_cfg_attr(&itemfn.attrs)?; + crate::attrs::deny_cfg_attr(&item_fn.attrs)?; let params: ExportedFnParams = - match crate::attrs::inner_item_attributes(&mut itemfn.attrs, "rhai_fn") { + match crate::attrs::inner_item_attributes(&mut item_fn.attrs, "rhai_fn") { Ok(p) => p, Err(e) => return Err(e), }; - syn::parse2::(itemfn.to_token_stream()) + syn::parse2::(item_fn.to_token_stream()) .and_then(|mut f| { f.set_params(params)?; Ok(f) @@ -163,29 +163,31 @@ impl Parse for Module { _ => {} } } - // Gather and parse submodule definitions. + // Gather and parse sub-module definitions. // // They are actually removed from the module's body, because they will need // re-generating later when generated code is added. - submodules.reserve(content.len() - fns.len() - consts.len()); + sub_modules.reserve(content.len() - fns.len() - consts.len()); let mut i = 0; while i < content.len() { if let syn::Item::Mod(_) = &content[i] { - let mut itemmod = match content.remove(i) { + let mut item_mod = match content.remove(i) { syn::Item::Mod(m) => m, _ => unreachable!(), }; - let params: ExportedModParams = - match crate::attrs::inner_item_attributes(&mut itemmod.attrs, "rhai_mod") { - Ok(p) => p, - Err(e) => return Err(e), - }; + let params: ExportedModParams = match crate::attrs::inner_item_attributes( + &mut item_mod.attrs, + "rhai_mod", + ) { + Ok(p) => p, + Err(e) => return Err(e), + }; let module = - syn::parse2::(itemmod.to_token_stream()).and_then(|mut m| { + syn::parse2::(item_mod.to_token_stream()).and_then(|mut m| { m.set_params(params)?; Ok(m) })?; - submodules.push(module); + sub_modules.push(module); } else { i += 1; } @@ -197,7 +199,7 @@ impl Parse for Module { mod_all, fns, consts, - submodules, + sub_modules, params: ExportedModParams::default(), }) } @@ -251,7 +253,7 @@ impl Module { mut mod_all, mut fns, consts, - mut submodules, + mut sub_modules, params, .. } = self; @@ -266,13 +268,13 @@ impl Module { let mod_gen = crate::rhai_module::generate_body( &mut fns, &consts, - &mut submodules, + &mut sub_modules, ¶ms.scope, ); - // NB: submodules must have their new items for exporting generated in depth-first order + // NB: sub-modules must have their new items for exporting generated in depth-first order // to avoid issues caused by re-parsing them - let inner_modules: Vec = submodules.drain(..) + let inner_modules: Vec = sub_modules.drain(..) .try_fold::, _, Result, syn::Error>>( Vec::new(), |mut acc, m| { acc.push(m.generate_inner()?); Ok(acc) })?; @@ -309,8 +311,8 @@ impl Module { &self.fns } - pub fn submodules(&self) -> &[Module] { - &self.submodules + pub fn sub_modules(&self) -> &[Module] { + &self.sub_modules } pub fn content(&self) -> Option<&Vec> { diff --git a/codegen/src/register.rs b/codegen/src/register.rs index 515f13d1..748baa41 100644 --- a/codegen/src/register.rs +++ b/codegen/src/register.rs @@ -33,11 +33,11 @@ pub fn parse_register_macro( )); } let export_name = match &items[1] { - syn::Expr::Lit(litstr) => quote_spanned!(items[1].span()=> - #litstr.to_string()), + syn::Expr::Lit(lit_str) => quote_spanned!(items[1].span()=> + #lit_str.to_string()), expr => quote! { #expr }, }; - let rust_modpath = if let syn::Expr::Path(ref path) = &items[2] { + let rust_mod_path = if let syn::Expr::Path(ref path) = &items[2] { path.path.clone() } else { return Err(syn::Error::new( @@ -46,5 +46,5 @@ pub fn parse_register_macro( )); }; let module = items.remove(0); - Ok((module, export_name, rust_modpath)) + Ok((module, export_name, rust_mod_path)) } diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 5d9c148f..cd359d8b 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -14,11 +14,11 @@ pub(crate) type ExportedConst = (String, Box, syn::Expr); pub(crate) fn generate_body( fns: &mut [ExportedFn], consts: &[ExportedConst], - submodules: &mut [Module], + sub_modules: &mut [Module], parent_scope: &ExportScope, ) -> proc_macro2::TokenStream { - let mut set_fn_stmts: Vec = Vec::new(); - let mut set_const_stmts: Vec = Vec::new(); + let mut set_fn_statements: Vec = Vec::new(); + let mut set_const_statements: Vec = Vec::new(); let mut add_mod_blocks: Vec = Vec::new(); let mut set_flattened_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); @@ -27,7 +27,7 @@ pub(crate) fn generate_body( for (const_name, _, _) in consts { let const_literal = syn::LitStr::new(&const_name, proc_macro2::Span::call_site()); let const_ref = syn::Ident::new(&const_name, proc_macro2::Span::call_site()); - set_const_stmts.push( + set_const_statements.push( syn::parse2::(quote! { m.set_var(#const_literal, #const_ref); }) @@ -35,17 +35,17 @@ pub(crate) fn generate_body( ); } - for itemmod in submodules { - itemmod.update_scope(&parent_scope); - if itemmod.skipped() { + for item_mod in sub_modules { + item_mod.update_scope(&parent_scope); + if item_mod.skipped() { continue; } - let module_name = itemmod.module_name(); + let module_name = item_mod.module_name(); let exported_name: syn::LitStr = syn::LitStr::new( - itemmod.exported_name().as_ref(), + item_mod.exported_name().as_ref(), proc_macro2::Span::call_site(), ); - let cfg_attrs: Vec<&syn::Attribute> = itemmod + let cfg_attrs: Vec<&syn::Attribute> = item_mod .attrs() .iter() .filter(|&a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false)) @@ -83,7 +83,7 @@ pub(crate) fn generate_body( let fn_input_names: Vec = function .arg_list() - .map(|fnarg| match fnarg { + .map(|fn_arg| match fn_arg { syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"), syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => { format!("{}: {}", pat.to_token_stream(), print_type(ty)) @@ -93,7 +93,7 @@ pub(crate) fn generate_body( let fn_input_types: Vec = function .arg_list() - .map(|fnarg| match fnarg { + .map(|fn_arg| match fn_arg { syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"), syn::FnArg::Typed(syn::PatType { ref ty, .. }) => { let arg_type = match flatten_type_groups(ty.as_ref()) { @@ -169,7 +169,7 @@ pub(crate) fn generate_body( }, fn_literal.span(), ); - set_fn_stmts.push( + set_fn_statements.push( syn::parse2::(quote! { m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, Some(&[#(#fn_input_names,)* #return_type]), &[#(#fn_input_types),*], @@ -190,7 +190,7 @@ pub(crate) fn generate_body( gen_fn_tokens.push(function.generate_return_type(&fn_token_name.to_string())); } - let mut generate_fncall = syn::parse2::(quote! { + let mut generate_fn_call = syn::parse2::(quote! { pub mod generate_info { #[allow(unused_imports)] use super::*; @@ -203,8 +203,8 @@ pub(crate) fn generate_body( } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - #(#set_fn_stmts)* - #(#set_const_stmts)* + #(#set_fn_statements)* + #(#set_const_statements)* if flatten { #(#set_flattened_mod_blocks)* @@ -216,7 +216,7 @@ pub(crate) fn generate_body( }) .unwrap(); - let (_, generate_call_content) = generate_fncall.content.take().unwrap(); + let (_, generate_call_content) = generate_fn_call.content.take().unwrap(); quote! { #(#generate_call_content)* @@ -225,39 +225,39 @@ pub(crate) fn generate_body( } pub(crate) fn check_rename_collisions(fns: &Vec) -> Result<(), syn::Error> { - fn make_key(name: impl ToString, itemfn: &ExportedFn) -> String { - itemfn + fn make_key(name: impl ToString, item_fn: &ExportedFn) -> String { + item_fn .arg_list() - .fold(name.to_string(), |mut argstr, fnarg| { - let type_string: String = match fnarg { + .fold(name.to_string(), |mut arg_str, fn_arg| { + let type_string: String = match fn_arg { syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"), syn::FnArg::Typed(syn::PatType { ref ty, .. }) => print_type(ty), }; - argstr.push('.'); - argstr.push_str(&type_string); - argstr + arg_str.push('.'); + arg_str.push_str(&type_string); + arg_str }) } let mut renames = HashMap::::new(); let mut fn_defs = HashMap::::new(); - for itemfn in fns.iter() { - if !itemfn.params().name.is_empty() || itemfn.params().special != FnSpecialAccess::None { - let mut names: Vec<_> = itemfn + for item_fn in fns.iter() { + if !item_fn.params().name.is_empty() || item_fn.params().special != FnSpecialAccess::None { + let mut names: Vec<_> = item_fn .params() .name .iter() .map(|n| (n.clone(), n.clone())) .collect(); - if let Some((s, n, _)) = itemfn.params().special.get_fn_name() { + if let Some((s, n, _)) = item_fn.params().special.get_fn_name() { names.push((s, n)); } for (name, fn_name) in names { - let current_span = itemfn.params().span.unwrap(); - let key = make_key(&name, itemfn); + let current_span = item_fn.params().span.unwrap(); + let key = make_key(&name, item_fn); if let Some(other_span) = renames.insert(key, current_span) { let mut err = syn::Error::new( current_span, @@ -271,7 +271,7 @@ pub(crate) fn check_rename_collisions(fns: &Vec) -> Result<(), syn:: } } } else { - let ident = itemfn.name(); + let ident = item_fn.name(); if let Some(other_span) = fn_defs.insert(ident.to_string(), ident.span()) { let mut err = syn::Error::new( ident.span(), @@ -283,7 +283,7 @@ pub(crate) fn check_rename_collisions(fns: &Vec) -> Result<(), syn:: )); return Err(err); } - let key = make_key(ident, itemfn); + let key = make_key(ident, item_fn); if let Some(fn_span) = renames.get(&key) { let mut err = syn::Error::new( ident.span(), diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 3b3c0d2d..c83f45f6 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -106,10 +106,10 @@ mod module_tests { let item_mod = syn::parse2::(input_tokens).unwrap(); assert!(item_mod.fns().is_empty()); assert!(item_mod.consts().is_empty()); - assert_eq!(item_mod.submodules().len(), 1); - assert_eq!(&item_mod.submodules()[0].consts()[0].0, "MYSTIC_NUMBER"); + assert_eq!(item_mod.sub_modules().len(), 1); + assert_eq!(&item_mod.sub_modules()[0].consts()[0].0, "MYSTIC_NUMBER"); assert_eq!( - item_mod.submodules()[0].consts()[0].2, + item_mod.sub_modules()[0].consts()[0].2, syn::parse2::(quote! { 42 }).unwrap() ); } @@ -130,11 +130,11 @@ mod module_tests { let item_mod = syn::parse2::(input_tokens).unwrap(); assert!(item_mod.fns().is_empty()); assert!(item_mod.consts().is_empty()); - assert_eq!(item_mod.submodules().len(), 1); - assert_eq!(item_mod.submodules()[0].fns().len(), 1); - assert!(item_mod.submodules()[0].fns()[0].skipped()); - assert!(item_mod.submodules()[0].consts().is_empty()); - assert!(item_mod.submodules()[0].submodules().is_empty()); + assert_eq!(item_mod.sub_modules().len(), 1); + assert_eq!(item_mod.sub_modules()[0].fns().len(), 1); + assert!(item_mod.sub_modules()[0].fns()[0].skipped()); + assert!(item_mod.sub_modules()[0].consts().is_empty()); + assert!(item_mod.sub_modules()[0].sub_modules().is_empty()); } #[test] @@ -153,8 +153,8 @@ mod module_tests { let item_mod = syn::parse2::(input_tokens).unwrap(); assert!(item_mod.fns().is_empty()); assert!(item_mod.consts().is_empty()); - assert_eq!(item_mod.submodules().len(), 1); - assert!(item_mod.submodules()[0].skipped()); + assert_eq!(item_mod.sub_modules().len(), 1); + assert!(item_mod.sub_modules()[0].skipped()); } #[test] @@ -958,7 +958,7 @@ mod generate_tests { } #[test] - fn one_skipped_submodule() { + fn one_skipped_sub_module() { let input_tokens: TokenStream = quote! { pub mod one_fn { pub fn get_mystic_number() -> INT { diff --git a/codegen/tests/test_nested.rs b/codegen/tests/test_nested.rs index a7f898b5..8e3497de 100644 --- a/codegen/tests/test_nested.rs +++ b/codegen/tests/test_nested.rs @@ -29,7 +29,7 @@ fn one_fn_module_nested_attr_test() -> Result<(), Box> { Ok(()) } -pub mod one_fn_submodule_nested_attr { +pub mod one_fn_sub_module_nested_attr { use rhai::plugin::*; #[export_module] @@ -47,9 +47,9 @@ pub mod one_fn_submodule_nested_attr { } #[test] -fn one_fn_submodule_nested_attr_test() -> Result<(), Box> { +fn one_fn_sub_module_nested_attr_test() -> Result<(), Box> { let mut engine = Engine::new(); - let m = rhai::exported_module!(crate::one_fn_submodule_nested_attr::advanced_math); + let m = rhai::exported_module!(crate::one_fn_sub_module_nested_attr::advanced_math); engine.register_static_module("Math::Advanced", m.into()); assert_eq!( From 7b8322b6e131b7edaa1bade7b37cdd49017c1cb8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 18 Feb 2021 23:35:22 +0800 Subject: [PATCH 7/8] Scope implements IntoIterator. --- RELEASES.md | 4 +++- src/scope.rs | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/RELEASES.md b/RELEASES.md index a3ee4317..bd1b91fb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -12,8 +12,10 @@ Bug fixes Enhancements ------------ +* Comparisons between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT` are now built in. * Error position in `eval` statements is now wrapped in an `EvalAltResult::ErrorInFunctionCall`. * `Position` now implements `Add` and `AddAssign`. +* `Scope` now implements `IntoIterator`. Version 0.19.12 @@ -54,7 +56,7 @@ Enhancements * Functions resolution cache is used in more cases, making repeated function calls faster. * Added `atan(x, y)` and `hypot(x, y)` to `BasicMathPackage`. -* Added standard arithmetic operators between `FLOAT` and `INT`. +* Added standard arithmetic operators between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT`. Version 0.19.11 diff --git a/src/scope.rs b/src/scope.rs index 77c74e9e..4adda3e1 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -64,6 +64,20 @@ impl Default for Scope<'_> { } } +impl<'a> IntoIterator for Scope<'a> { + type Item = (Cow<'a, str>, Dynamic); + type IntoIter = Box + 'a>; + + fn into_iter(self) -> Self::IntoIter { + Box::new( + self.values + .into_iter() + .zip(self.names.into_iter()) + .map(|(value, (name, _))| (name, value)), + ) + } +} + impl<'a> Scope<'a> { /// Create a new [`Scope`]. /// From bd5ff457d26703dd08b27b98b6f2835ac571882e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 19 Feb 2021 11:47:26 +0800 Subject: [PATCH 8/8] Built-in interop between FLOAT/Decimal and INT. --- RELEASES.md | 7 +- src/fn_call.rs | 319 +++++++++++++++++++++---------------- src/packages/arithmetic.rs | 220 +++++++++++-------------- src/parser.rs | 40 +---- 4 files changed, 287 insertions(+), 299 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index bd1b91fb..34b1b597 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,10 +9,15 @@ Bug fixes * Bug in `Position::is_beginning_of_line` is fixed. -Enhancements +New features ------------ * Comparisons between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT` are now built in. + +Enhancements +------------ + +* Built-in operators between `FLOAT`/[`Decimal`](https://crates.io/crates/rust_decimal) and `INT` are now implemented for more speed under those cases. * Error position in `eval` statements is now wrapped in an `EvalAltResult::ErrorInFunctionCall`. * `Position` now implements `Add` and `AddAssign`. * `Scope` now implements `IntoIterator`. diff --git a/src/fn_call.rs b/src/fn_call.rs index b0573dfe..98251113 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -32,6 +32,9 @@ use crate::FLOAT; #[cfg(not(feature = "no_object"))] use crate::Map; +#[cfg(feature = "decimal")] +use rust_decimal::Decimal; + #[cfg(feature = "no_std")] #[cfg(not(feature = "no_float"))] use num_traits::float::Float; @@ -1347,33 +1350,115 @@ pub fn run_builtin_binary_op( x: &Dynamic, y: &Dynamic, ) -> Result, Box> { - let args_type = x.type_id(); + let first_type = x.type_id(); let second_type = y.type_id(); - if second_type != args_type { - if args_type == TypeId::of::() && second_type == TypeId::of::() { + let type_id = (first_type, second_type); + + #[cfg(not(feature = "no_float"))] + if let Some((x, y)) = if type_id == (TypeId::of::(), TypeId::of::()) { + Some((x.clone().cast::(), y.clone().cast::())) + } else if type_id == (TypeId::of::(), TypeId::of::()) { + Some((x.clone().cast::(), y.clone().cast::() as FLOAT)) + } else if type_id == (TypeId::of::(), TypeId::of::()) { + Some((x.clone().cast::() as FLOAT, y.clone().cast::())) + } else { + None + } { + match op { + "+" => return Ok(Some((x + y).into())), + "-" => return Ok(Some((x - y).into())), + "*" => return Ok(Some((x * y).into())), + "/" => return Ok(Some((x / y).into())), + "%" => return Ok(Some((x % y).into())), + "**" => return Ok(Some(x.powf(y).into())), + "==" => return Ok(Some((x == y).into())), + "!=" => return Ok(Some((x != y).into())), + ">" => return Ok(Some((x > y).into())), + ">=" => return Ok(Some((x >= y).into())), + "<" => return Ok(Some((x < y).into())), + "<=" => return Ok(Some((x <= y).into())), + _ => return Ok(None), + } + } + + #[cfg(feature = "decimal")] + if let Some((x, y)) = if type_id == (TypeId::of::(), TypeId::of::()) { + Some(( + *x.read_lock::().unwrap(), + *y.read_lock::().unwrap(), + )) + } else if type_id == (TypeId::of::(), TypeId::of::()) { + Some(( + *x.read_lock::().unwrap(), + y.clone().cast::().into(), + )) + } else if type_id == (TypeId::of::(), TypeId::of::()) { + Some(( + x.clone().cast::().into(), + *y.read_lock::().unwrap(), + )) + } else { + None + } { + if cfg!(not(feature = "unchecked")) { + use crate::packages::arithmetic::decimal_functions::*; + + match op { + "+" => return add(x, y).map(Some), + "-" => return subtract(x, y).map(Some), + "*" => return multiply(x, y).map(Some), + "/" => return divide(x, y).map(Some), + "%" => return modulo(x, y).map(Some), + _ => (), + } + } else { + match op { + "+" => return Ok(Some((x + y).into())), + "-" => return Ok(Some((x - y).into())), + "*" => return Ok(Some((x * y).into())), + "/" => return Ok(Some((x / y).into())), + "%" => return Ok(Some((x % y).into())), + _ => (), + } + } + + match op { + "==" => return Ok(Some((x == y).into())), + "!=" => return Ok(Some((x != y).into())), + ">" => return Ok(Some((x > y).into())), + ">=" => return Ok(Some((x >= y).into())), + "<" => return Ok(Some((x < y).into())), + "<=" => return Ok(Some((x <= y).into())), + _ => return Ok(None), + } + } + + if second_type != first_type { + if type_id == (TypeId::of::(), TypeId::of::()) { let x = x.clone().cast::(); let y = &*y.read_lock::().unwrap(); match op { "+" => return Ok(Some(format!("{}{}", x, y).into())), - _ => (), + _ => return Ok(None), } - } else if args_type == TypeId::of::() - && second_type == TypeId::of::() - { + } + + if type_id == (TypeId::of::(), TypeId::of::()) { let x = &*x.read_lock::().unwrap(); let y = y.clone().cast::(); match op { "+" => return Ok(Some((x + y).into())), - _ => (), + _ => return Ok(None), } } + return Ok(None); } - if args_type == TypeId::of::() { + if first_type == TypeId::of::() { let x = x.clone().cast::(); let y = y.clone().cast::(); @@ -1415,9 +1500,11 @@ pub fn run_builtin_binary_op( "&" => return Ok(Some((x & y).into())), "|" => return Ok(Some((x | y).into())), "^" => return Ok(Some((x ^ y).into())), - _ => (), + _ => return Ok(None), } - } else if args_type == TypeId::of::() { + } + + if first_type == TypeId::of::() { let x = x.clone().cast::(); let y = y.clone().cast::(); @@ -1427,9 +1514,11 @@ pub fn run_builtin_binary_op( "^" => return Ok(Some((x ^ y).into())), "==" => return Ok(Some((x == y).into())), "!=" => return Ok(Some((x != y).into())), - _ => (), + _ => return Ok(None), } - } else if args_type == TypeId::of::() { + } + + if first_type == TypeId::of::() { let x = &*x.read_lock::().unwrap(); let y = &*y.read_lock::().unwrap(); @@ -1441,9 +1530,11 @@ pub fn run_builtin_binary_op( ">=" => return Ok(Some((x >= y).into())), "<" => return Ok(Some((x < y).into())), "<=" => return Ok(Some((x <= y).into())), - _ => (), + _ => return Ok(None), } - } else if args_type == TypeId::of::() { + } + + if first_type == TypeId::of::() { let x = x.clone().cast::(); let y = y.clone().cast::(); @@ -1455,73 +1546,15 @@ pub fn run_builtin_binary_op( ">=" => return Ok(Some((x >= y).into())), "<" => return Ok(Some((x < y).into())), "<=" => return Ok(Some((x <= y).into())), - _ => (), + _ => return Ok(None), } - } else if args_type == TypeId::of::<()>() { + } + + if first_type == TypeId::of::<()>() { match op { "==" => return Ok(Some(true.into())), "!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())), - _ => (), - } - } - - #[cfg(not(feature = "no_float"))] - if args_type == TypeId::of::() { - let x = x.clone().cast::(); - let y = y.clone().cast::(); - - match op { - "+" => return Ok(Some((x + y).into())), - "-" => return Ok(Some((x - y).into())), - "*" => return Ok(Some((x * y).into())), - "/" => return Ok(Some((x / y).into())), - "%" => return Ok(Some((x % y).into())), - "**" => return Ok(Some(x.powf(y).into())), - "==" => return Ok(Some((x == y).into())), - "!=" => return Ok(Some((x != y).into())), - ">" => return Ok(Some((x > y).into())), - ">=" => return Ok(Some((x >= y).into())), - "<" => return Ok(Some((x < y).into())), - "<=" => return Ok(Some((x <= y).into())), - _ => (), - } - } - - #[cfg(feature = "decimal")] - if args_type == TypeId::of::() { - let x = x.clone().cast::(); - let y = y.clone().cast::(); - - if cfg!(not(feature = "unchecked")) { - use crate::packages::arithmetic::decimal_functions::*; - - match op { - "+" => return add(x, y).map(Some), - "-" => return subtract(x, y).map(Some), - "*" => return multiply(x, y).map(Some), - "/" => return divide(x, y).map(Some), - "%" => return modulo(x, y).map(Some), - _ => (), - } - } else { - match op { - "+" => return Ok(Some((x + y).into())), - "-" => return Ok(Some((x - y).into())), - "*" => return Ok(Some((x * y).into())), - "/" => return Ok(Some((x / y).into())), - "%" => return Ok(Some((x % y).into())), - _ => (), - } - } - - match op { - "==" => return Ok(Some((x == y).into())), - "!=" => return Ok(Some((x != y).into())), - ">" => return Ok(Some((x > y).into())), - ">=" => return Ok(Some((x >= y).into())), - "<" => return Ok(Some((x < y).into())), - "<=" => return Ok(Some((x <= y).into())), - _ => (), + _ => return Ok(None), } } @@ -1534,24 +1567,80 @@ pub fn run_builtin_op_assignment( x: &mut Dynamic, y: &Dynamic, ) -> Result, Box> { - let args_type = x.type_id(); + let first_type = x.type_id(); let second_type = y.type_id(); - if second_type != args_type { - if args_type == TypeId::of::() && second_type == TypeId::of::() { + let type_id = (first_type, second_type); + + #[cfg(not(feature = "no_float"))] + if let Some((mut x, y)) = if type_id == (TypeId::of::(), TypeId::of::()) { + let y = y.clone().cast::(); + Some((x.write_lock::().unwrap(), y)) + } else if type_id == (TypeId::of::(), TypeId::of::()) { + let y = y.clone().cast::() as FLOAT; + Some((x.write_lock::().unwrap(), y)) + } else { + None + } { + match op { + "+=" => return Ok(Some(*x += y)), + "-=" => return Ok(Some(*x -= y)), + "*=" => return Ok(Some(*x *= y)), + "/=" => return Ok(Some(*x /= y)), + "%=" => return Ok(Some(*x %= y)), + "**=" => return Ok(Some(*x = x.powf(y))), + _ => return Ok(None), + } + } + + #[cfg(feature = "decimal")] + if let Some((mut x, y)) = if type_id == (TypeId::of::(), TypeId::of::()) { + let y = *y.read_lock::().unwrap(); + Some((x.write_lock::().unwrap(), y)) + } else if type_id == (TypeId::of::(), TypeId::of::()) { + let y = y.clone().cast::().into(); + Some((x.write_lock::().unwrap(), y)) + } else { + None + } { + if cfg!(not(feature = "unchecked")) { + use crate::packages::arithmetic::decimal_functions::*; + + match op { + "+=" => return Ok(Some(*x = add(*x, y)?.as_decimal().unwrap())), + "-=" => return Ok(Some(*x = subtract(*x, y)?.as_decimal().unwrap())), + "*=" => return Ok(Some(*x = multiply(*x, y)?.as_decimal().unwrap())), + "/=" => return Ok(Some(*x = divide(*x, y)?.as_decimal().unwrap())), + "%=" => return Ok(Some(*x = modulo(*x, y)?.as_decimal().unwrap())), + _ => (), + } + } else { + match op { + "+=" => return Ok(Some(*x += y)), + "-=" => return Ok(Some(*x -= y)), + "*=" => return Ok(Some(*x *= y)), + "/=" => return Ok(Some(*x /= y)), + "%=" => return Ok(Some(*x %= y)), + _ => (), + } + } + } + + if second_type != first_type { + if type_id == (TypeId::of::(), TypeId::of::()) { let y = y.read_lock::().unwrap().deref().clone(); let mut x = x.write_lock::().unwrap(); match op { "+=" => return Ok(Some(*x += y)), - _ => (), + _ => return Ok(None), } } return Ok(None); } - if args_type == TypeId::of::() { + if first_type == TypeId::of::() { let y = y.clone().cast::(); let mut x = x.write_lock::().unwrap(); @@ -1587,76 +1676,38 @@ pub fn run_builtin_op_assignment( "&=" => return Ok(Some(*x &= y)), "|=" => return Ok(Some(*x |= y)), "^=" => return Ok(Some(*x ^= y)), - _ => (), + _ => return Ok(None), } - } else if args_type == TypeId::of::() { + } + + if first_type == TypeId::of::() { let y = y.clone().cast::(); let mut x = x.write_lock::().unwrap(); match op { "&=" => return Ok(Some(*x = *x && y)), "|=" => return Ok(Some(*x = *x || y)), - _ => (), + _ => return Ok(None), } - } else if args_type == TypeId::of::() { + } + + if first_type == TypeId::of::() { let y = y.read_lock::().unwrap().deref().clone(); let mut x = x.write_lock::().unwrap(); match op { "+=" => return Ok(Some(*x = format!("{}{}", *x, y).into())), - _ => (), + _ => return Ok(None), } - } else if args_type == TypeId::of::() { + } + + if first_type == TypeId::of::() { let y = y.read_lock::().unwrap().deref().clone(); let mut x = x.write_lock::().unwrap(); match op { "+=" => return Ok(Some(*x += y)), - _ => (), - } - } - - #[cfg(not(feature = "no_float"))] - if args_type == TypeId::of::() { - let y = y.clone().cast::(); - let mut x = x.write_lock::().unwrap(); - - match op { - "+=" => return Ok(Some(*x += y)), - "-=" => return Ok(Some(*x -= y)), - "*=" => return Ok(Some(*x *= y)), - "/=" => return Ok(Some(*x /= y)), - "%=" => return Ok(Some(*x %= y)), - "**=" => return Ok(Some(*x = x.powf(y))), - _ => (), - } - } - - #[cfg(feature = "decimal")] - if args_type == TypeId::of::() { - let y = y.clone().cast::(); - let mut x = x.write_lock::().unwrap(); - - if cfg!(not(feature = "unchecked")) { - use crate::packages::arithmetic::decimal_functions::*; - - match op { - "+=" => return Ok(Some(*x = add(*x, y)?.as_decimal().unwrap())), - "-=" => return Ok(Some(*x = subtract(*x, y)?.as_decimal().unwrap())), - "*=" => return Ok(Some(*x = multiply(*x, y)?.as_decimal().unwrap())), - "/=" => return Ok(Some(*x = divide(*x, y)?.as_decimal().unwrap())), - "%=" => return Ok(Some(*x = modulo(*x, y)?.as_decimal().unwrap())), - _ => (), - } - } else { - match op { - "+=" => return Ok(Some(*x += y)), - "-=" => return Ok(Some(*x -= y)), - "*=" => return Ok(Some(*x *= y)), - "/=" => return Ok(Some(*x /= y)), - "%=" => return Ok(Some(*x %= y)), - _ => (), - } + _ => return Ok(None), } } diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index c86c61cc..4d81eda6 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -254,47 +254,47 @@ mod f32_functions { pub fn pow_f_f(x: f32, y: f32) -> f32 { x.powf(y) } - } - #[rhai_fn(name = "+")] - pub fn add_if(x: INT, y: f32) -> f32 { - (x as f32) + (y as f32) - } - #[rhai_fn(name = "+")] - pub fn add_fi(x: f32, y: INT) -> f32 { - (x as f32) + (y as f32) - } - #[rhai_fn(name = "-")] - pub fn subtract_if(x: INT, y: f32) -> f32 { - (x as f32) - (y as f32) - } - #[rhai_fn(name = "-")] - pub fn subtract_fi(x: f32, y: INT) -> f32 { - (x as f32) - (y as f32) - } - #[rhai_fn(name = "*")] - pub fn multiply_if(x: INT, y: f32) -> f32 { - (x as f32) * (y as f32) - } - #[rhai_fn(name = "*")] - pub fn multiply_fi(x: f32, y: INT) -> f32 { - (x as f32) * (y as f32) - } - #[rhai_fn(name = "/")] - pub fn divide_if(x: INT, y: f32) -> f32 { - (x as f32) / (y as f32) - } - #[rhai_fn(name = "/")] - pub fn divide_fi(x: f32, y: INT) -> f32 { - (x as f32) / (y as f32) - } - #[rhai_fn(name = "%")] - pub fn modulo_if(x: INT, y: f32) -> f32 { - (x as f32) % (y as f32) - } - #[rhai_fn(name = "%")] - pub fn modulo_fi(x: f32, y: INT) -> f32 { - (x as f32) % (y as f32) + #[rhai_fn(name = "+")] + pub fn add_if(x: INT, y: f32) -> f32 { + (x as f32) + (y as f32) + } + #[rhai_fn(name = "+")] + pub fn add_fi(x: f32, y: INT) -> f32 { + (x as f32) + (y as f32) + } + #[rhai_fn(name = "-")] + pub fn subtract_if(x: INT, y: f32) -> f32 { + (x as f32) - (y as f32) + } + #[rhai_fn(name = "-")] + pub fn subtract_fi(x: f32, y: INT) -> f32 { + (x as f32) - (y as f32) + } + #[rhai_fn(name = "*")] + pub fn multiply_if(x: INT, y: f32) -> f32 { + (x as f32) * (y as f32) + } + #[rhai_fn(name = "*")] + pub fn multiply_fi(x: f32, y: INT) -> f32 { + (x as f32) * (y as f32) + } + #[rhai_fn(name = "/")] + pub fn divide_if(x: INT, y: f32) -> f32 { + (x as f32) / (y as f32) + } + #[rhai_fn(name = "/")] + pub fn divide_fi(x: f32, y: INT) -> f32 { + (x as f32) / (y as f32) + } + #[rhai_fn(name = "%")] + pub fn modulo_if(x: INT, y: f32) -> f32 { + (x as f32) % (y as f32) + } + #[rhai_fn(name = "%")] + pub fn modulo_fi(x: f32, y: INT) -> f32 { + (x as f32) % (y as f32) + } } #[rhai_fn(name = "-")] @@ -359,47 +359,47 @@ mod f64_functions { pub fn pow_f_f(x: f64, y: f64) -> f64 { x.powf(y) } - } - #[rhai_fn(name = "+")] - pub fn add_if(x: INT, y: f64) -> f64 { - (x as f64) + (y as f64) - } - #[rhai_fn(name = "+")] - pub fn add_fi(x: f64, y: INT) -> f64 { - (x as f64) + (y as f64) - } - #[rhai_fn(name = "-")] - pub fn subtract_if(x: INT, y: f64) -> f64 { - (x as f64) - (y as f64) - } - #[rhai_fn(name = "-")] - pub fn subtract_fi(x: f64, y: INT) -> f64 { - (x as f64) - (y as f64) - } - #[rhai_fn(name = "*")] - pub fn multiply_if(x: INT, y: f64) -> f64 { - (x as f64) * (y as f64) - } - #[rhai_fn(name = "*")] - pub fn multiply_fi(x: f64, y: INT) -> f64 { - (x as f64) * (y as f64) - } - #[rhai_fn(name = "/")] - pub fn divide_if(x: INT, y: f64) -> f64 { - (x as f64) / (y as f64) - } - #[rhai_fn(name = "/")] - pub fn divide_fi(x: f64, y: INT) -> f64 { - (x as f64) / (y as f64) - } - #[rhai_fn(name = "%")] - pub fn modulo_if(x: INT, y: f64) -> f64 { - (x as f64) % (y as f64) - } - #[rhai_fn(name = "%")] - pub fn modulo_fi(x: f64, y: INT) -> f64 { - (x as f64) % (y as f64) + #[rhai_fn(name = "+")] + pub fn add_if(x: INT, y: f64) -> f64 { + (x as f64) + (y as f64) + } + #[rhai_fn(name = "+")] + pub fn add_fi(x: f64, y: INT) -> f64 { + (x as f64) + (y as f64) + } + #[rhai_fn(name = "-")] + pub fn subtract_if(x: INT, y: f64) -> f64 { + (x as f64) - (y as f64) + } + #[rhai_fn(name = "-")] + pub fn subtract_fi(x: f64, y: INT) -> f64 { + (x as f64) - (y as f64) + } + #[rhai_fn(name = "*")] + pub fn multiply_if(x: INT, y: f64) -> f64 { + (x as f64) * (y as f64) + } + #[rhai_fn(name = "*")] + pub fn multiply_fi(x: f64, y: INT) -> f64 { + (x as f64) * (y as f64) + } + #[rhai_fn(name = "/")] + pub fn divide_if(x: INT, y: f64) -> f64 { + (x as f64) / (y as f64) + } + #[rhai_fn(name = "/")] + pub fn divide_fi(x: f64, y: INT) -> f64 { + (x as f64) / (y as f64) + } + #[rhai_fn(name = "%")] + pub fn modulo_if(x: INT, y: f64) -> f64 { + (x as f64) % (y as f64) + } + #[rhai_fn(name = "%")] + pub fn modulo_fi(x: f64, y: INT) -> f64 { + (x as f64) % (y as f64) + } } #[rhai_fn(name = "-")] @@ -440,61 +440,37 @@ mod f64_functions { mod decimal_functions { use rust_decimal::{prelude::Zero, Decimal}; - #[rhai_fn(name = "+", return_raw)] + #[rhai_fn(skip, return_raw)] pub fn add(x: Decimal, y: Decimal) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_add(y) .ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))) - .map(Dynamic::from) + .map(Into::::into) } else { Ok(Dynamic::from(x + y)) } } - #[rhai_fn(name = "+", return_raw)] - pub fn add_id(x: INT, y: Decimal) -> Result> { - add(x.into(), y) - } - #[rhai_fn(name = "+", return_raw)] - pub fn add_di(x: Decimal, y: INT) -> Result> { - add(x, y.into()) - } - #[rhai_fn(name = "-", return_raw)] + #[rhai_fn(skip, return_raw)] pub fn subtract(x: Decimal, y: Decimal) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_sub(y) .ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))) - .map(Dynamic::from) + .map(Into::::into) } else { Ok(Dynamic::from(x - y)) } } - #[rhai_fn(name = "-", return_raw)] - pub fn subtract_id(x: INT, y: Decimal) -> Result> { - subtract(x.into(), y) - } - #[rhai_fn(name = "-", return_raw)] - pub fn subtract_di(x: Decimal, y: INT) -> Result> { - subtract(x, y.into()) - } - #[rhai_fn(name = "*", return_raw)] + #[rhai_fn(skip, return_raw)] pub fn multiply(x: Decimal, y: Decimal) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_mul(y) .ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))) - .map(Dynamic::from) + .map(Into::::into) } else { Ok(Dynamic::from(x * y)) } } - #[rhai_fn(name = "*", return_raw)] - pub fn multiply_id(x: INT, y: Decimal) -> Result> { - multiply(x.into(), y) - } - #[rhai_fn(name = "*", return_raw)] - pub fn multiply_di(x: Decimal, y: INT) -> Result> { - multiply(x, y.into()) - } - #[rhai_fn(name = "/", return_raw)] + #[rhai_fn(skip, return_raw)] pub fn divide(x: Decimal, y: Decimal) -> Result> { if cfg!(not(feature = "unchecked")) { // Detect division by zero @@ -503,21 +479,13 @@ mod decimal_functions { } else { x.checked_div(y) .ok_or_else(|| make_err(format!("Division overflow: {} / {}", x, y))) - .map(Dynamic::from) + .map(Into::::into) } } else { Ok(Dynamic::from(x / y)) } } - #[rhai_fn(name = "/", return_raw)] - pub fn divide_id(x: INT, y: Decimal) -> Result> { - divide(x.into(), y) - } - #[rhai_fn(name = "/", return_raw)] - pub fn divide_di(x: Decimal, y: INT) -> Result> { - divide(x, y.into()) - } - #[rhai_fn(name = "%", return_raw)] + #[rhai_fn(skip, return_raw)] pub fn modulo(x: Decimal, y: Decimal) -> Result> { if cfg!(not(feature = "unchecked")) { x.checked_rem(y) @@ -527,19 +495,11 @@ mod decimal_functions { x, y )) }) - .map(Dynamic::from) + .map(Into::::into) } else { Ok(Dynamic::from(x % y)) } } - #[rhai_fn(name = "%", return_raw)] - pub fn modulo_id(x: INT, y: Decimal) -> Result> { - modulo(x.into(), y) - } - #[rhai_fn(name = "%", return_raw)] - pub fn modulo_di(x: Decimal, y: INT) -> Result> { - modulo(x, y.into()) - } #[rhai_fn(name = "-")] pub fn neg(x: Decimal) -> Decimal { -x diff --git a/src/parser.rs b/src/parser.rs index 4cec86d3..bd8b2556 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3167,48 +3167,20 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { #[cfg(not(feature = "no_float"))] Union::Float(value, _) => Some(Expr::FloatConstant(value, pos)), + #[cfg(feature = "decimal")] + Union::Decimal(value, _) => Some(Expr::DynamicConstant(Box::new((*value).into()), pos)), + Union::Unit(_, _) => Some(Expr::Unit(pos)), Union::Int(value, _) => Some(Expr::IntegerConstant(value, pos)), Union::Char(value, _) => Some(Expr::CharConstant(value, pos)), Union::Str(value, _) => Some(Expr::StringConstant(value, pos)), Union::Bool(value, _) => Some(Expr::BoolConstant(value, pos)), + #[cfg(not(feature = "no_index"))] - Union::Array(array, _) => { - let items: Vec<_> = array - .into_iter() - .map(|x| map_dynamic_to_expr(x, pos)) - .collect(); + Union::Array(array, _) => Some(Expr::DynamicConstant(Box::new((*array).into()), pos)), - if items.iter().all(Option::is_some) { - Some(Expr::Array( - Box::new(items.into_iter().map(Option::unwrap).collect()), - pos, - )) - } else { - None - } - } #[cfg(not(feature = "no_object"))] - Union::Map(map, _) => { - let items: Vec<_> = map - .into_iter() - .map(|(name, value)| (Ident { name, pos }, map_dynamic_to_expr(value, pos))) - .collect(); - - if items.iter().all(|(_, expr)| expr.is_some()) { - Some(Expr::Map( - Box::new( - items - .into_iter() - .map(|(k, expr)| (k, expr.unwrap())) - .collect(), - ), - pos, - )) - } else { - None - } - } + Union::Map(map, _) => Some(Expr::DynamicConstant(Box::new((*map).into()), pos)), _ => None, }