Merge pull request #355 from schungx/master

Built-in interop between FLOAT/Decimal and INT.
This commit is contained in:
Stephen Chung 2021-02-19 12:02:14 +08:00 committed by GitHub
commit 1b33c60988
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 671 additions and 425 deletions

View File

@ -6,7 +6,7 @@ members = [
[package] [package]
name = "rhai" name = "rhai"
version = "0.19.12" version = "0.19.13"
edition = "2018" edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
description = "Embedded scripting for Rust" description = "Embedded scripting for Rust"

View File

@ -1,9 +1,37 @@
Rhai Release Notes Rhai Release Notes
================== ==================
Version 0.19.13
===============
Bug fixes
---------
* Bug in `Position::is_beginning_of_line` is fixed.
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`.
Version 0.19.12 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 Bug fixes
--------- ---------
@ -33,7 +61,7 @@ Enhancements
* Functions resolution cache is used in more cases, making repeated function calls faster. * Functions resolution cache is used in more cases, making repeated function calls faster.
* Added `atan(x, y)` and `hypot(x, y)` to `BasicMathPackage`. * 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 Version 0.19.11

View File

@ -378,8 +378,8 @@ impl Parse for ExportedFn {
} }
// Check return type. // Check return type.
if let syn::ReturnType::Type(_, ref rtype) = fn_all.sig.output { if let syn::ReturnType::Type(_, ref ret_type) = fn_all.sig.output {
match flatten_type_groups(rtype.as_ref()) { match flatten_type_groups(ret_type.as_ref()) {
syn::Type::Ptr(_) => { syn::Type::Ptr(_) => {
return Err(syn::Error::new( return Err(syn::Error::new(
fn_all.sig.output.span(), fn_all.sig.output.span(),
@ -495,8 +495,8 @@ impl ExportedFn {
} }
pub(crate) fn return_type(&self) -> Option<&syn::Type> { pub(crate) fn return_type(&self) -> Option<&syn::Type> {
if let syn::ReturnType::Type(_, ref rtype) = self.signature.output { if let syn::ReturnType::Type(_, ref ret_type) = self.signature.output {
Some(flatten_type_groups(rtype)) Some(flatten_type_groups(ret_type))
} else { } else {
None None
} }
@ -616,8 +616,8 @@ impl ExportedFn {
let arguments: Vec<syn::Ident> = dynamic_signature let arguments: Vec<syn::Ident> = dynamic_signature
.inputs .inputs
.iter() .iter()
.filter_map(|fnarg| { .filter_map(|fn_arg| {
if let syn::FnArg::Typed(syn::PatType { ref pat, .. }) = fnarg { if let syn::FnArg::Typed(syn::PatType { ref pat, .. }) = fn_arg {
if let syn::Pat::Ident(ref ident) = pat.as_ref() { if let syn::Pat::Ident(ref ident) = pat.as_ref() {
Some(ident.ident.clone()) Some(ident.ident.clone())
} else { } else {
@ -718,7 +718,7 @@ impl ExportedFn {
let arg_count = self.arg_count(); let arg_count = self.arg_count();
let is_method_call = self.mutable_receiver(); let is_method_call = self.mutable_receiver();
let mut unpack_stmts: Vec<syn::Stmt> = Vec::new(); let mut unpack_statements: Vec<syn::Stmt> = Vec::new();
let mut unpack_exprs: Vec<syn::Expr> = Vec::new(); let mut unpack_exprs: Vec<syn::Expr> = Vec::new();
let mut input_type_names: Vec<String> = Vec::new(); let mut input_type_names: Vec<String> = Vec::new();
let mut input_type_exprs: Vec<syn::Expr> = Vec::new(); let mut input_type_exprs: Vec<syn::Expr> = Vec::new();
@ -748,7 +748,7 @@ impl ExportedFn {
}; };
let downcast_span = quote_spanned!( let downcast_span = quote_spanned!(
arg_type.span()=> &mut args[0usize].write_lock::<#arg_type>().unwrap()); arg_type.span()=> &mut args[0usize].write_lock::<#arg_type>().unwrap());
unpack_stmts.push( unpack_statements.push(
syn::parse2::<syn::Stmt>(quote! { syn::parse2::<syn::Stmt>(quote! {
let #var = #downcast_span; let #var = #downcast_span;
}) })
@ -811,7 +811,7 @@ impl ExportedFn {
} }
}; };
unpack_stmts.push( unpack_statements.push(
syn::parse2::<syn::Stmt>(quote! { syn::parse2::<syn::Stmt>(quote! {
let #var = #downcast_span; 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 // that as needing to borrow the entire array, all of the previous argument unpacking via
// clone needs to happen first. // clone needs to happen first.
if is_method_call { if is_method_call {
let arg0 = unpack_stmts.remove(0); let arg0 = unpack_statements.remove(0);
unpack_stmts.push(arg0); unpack_statements.push(arg0);
} }
// Handle "raw returns", aka cases where the result is a dynamic or an error. // 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, debug_assert_eq!(args.len(), #arg_count,
"wrong arg count: {} != {}", "wrong arg count: {} != {}",
args.len(), #arg_count); args.len(), #arg_count);
#(#unpack_stmts)* #(#unpack_statements)*
#return_expr #return_expr
} }

View File

@ -293,12 +293,12 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro
/// ``` /// ```
#[proc_macro] #[proc_macro]
pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { 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) let (engine_expr, export_name, rust_mod_path) =
{ match crate::register::parse_register_macro(args) {
Ok(triple) => triple, Ok(triple) => triple,
Err(e) => return e.to_compile_error().into(), Err(e) => return e.to_compile_error().into(),
}; };
let gen_mod_path = crate::register::generated_module_path(&rust_modpath); let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
let tokens = quote! { let tokens = quote! {
#engine_expr.register_result_fn(&(#export_name), #gen_mod_path::dynamic_result_fn); #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] #[proc_macro]
pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { 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) let (module_expr, export_name, rust_mod_path) =
{ match crate::register::parse_register_macro(args) {
Ok(triple) => triple, Ok(triple) => triple,
Err(e) => return e.to_compile_error().into(), Err(e) => return e.to_compile_error().into(),
}; };
let gen_mod_path = crate::register::generated_module_path(&rust_modpath); let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
let tokens = quote! { let tokens = quote! {
#module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public, #module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public,
Some(#gen_mod_path::token_input_names().as_ref()), 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] #[proc_macro]
pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { 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) let (module_expr, export_name, rust_mod_path) =
{ match crate::register::parse_register_macro(args) {
Ok(triple) => triple, Ok(triple) => triple,
Err(e) => return e.to_compile_error().into(), Err(e) => return e.to_compile_error().into(),
}; };
let gen_mod_path = crate::register::generated_module_path(&rust_modpath); let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
let tokens = quote! { let tokens = quote! {
#module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public, #module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public,
Some(#gen_mod_path::token_input_names().as_ref()), Some(#gen_mod_path::token_input_names().as_ref()),

View File

@ -101,7 +101,7 @@ pub(crate) struct Module {
mod_all: syn::ItemMod, mod_all: syn::ItemMod,
fns: Vec<ExportedFn>, fns: Vec<ExportedFn>,
consts: Vec<ExportedConst>, consts: Vec<ExportedConst>,
submodules: Vec<Module>, sub_modules: Vec<Module>,
params: ExportedModParams, params: ExportedModParams,
} }
@ -117,7 +117,7 @@ impl Parse for Module {
let mut mod_all: syn::ItemMod = input.parse()?; let mut mod_all: syn::ItemMod = input.parse()?;
let fns: Vec<_>; let fns: Vec<_>;
let mut consts: Vec<_> = new_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 { if let Some((_, ref mut content)) = mod_all.content {
// Gather and parse functions. // Gather and parse functions.
fns = content fns = content
@ -126,16 +126,16 @@ impl Parse for Module {
syn::Item::Fn(f) => Some(f), syn::Item::Fn(f) => Some(f),
_ => None, _ => None,
}) })
.try_fold(Vec::new(), |mut vec, itemfn| { .try_fold(Vec::new(), |mut vec, item_fn| {
// #[cfg] attributes are not allowed on functions // #[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 = 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, Ok(p) => p,
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
syn::parse2::<ExportedFn>(itemfn.to_token_stream()) syn::parse2::<ExportedFn>(item_fn.to_token_stream())
.and_then(|mut f| { .and_then(|mut f| {
f.set_params(params)?; f.set_params(params)?;
Ok(f) 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 // They are actually removed from the module's body, because they will need
// re-generating later when generated code is added. // 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; let mut i = 0;
while i < content.len() { while i < content.len() {
if let syn::Item::Mod(_) = &content[i] { 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, syn::Item::Mod(m) => m,
_ => unreachable!(), _ => unreachable!(),
}; };
let params: ExportedModParams = let params: ExportedModParams = match crate::attrs::inner_item_attributes(
match crate::attrs::inner_item_attributes(&mut itemmod.attrs, "rhai_mod") { &mut item_mod.attrs,
Ok(p) => p, "rhai_mod",
Err(e) => return Err(e), ) {
}; Ok(p) => p,
Err(e) => return Err(e),
};
let module = let module =
syn::parse2::<Module>(itemmod.to_token_stream()).and_then(|mut m| { syn::parse2::<Module>(item_mod.to_token_stream()).and_then(|mut m| {
m.set_params(params)?; m.set_params(params)?;
Ok(m) Ok(m)
})?; })?;
submodules.push(module); sub_modules.push(module);
} else { } else {
i += 1; i += 1;
} }
@ -197,7 +199,7 @@ impl Parse for Module {
mod_all, mod_all,
fns, fns,
consts, consts,
submodules, sub_modules,
params: ExportedModParams::default(), params: ExportedModParams::default(),
}) })
} }
@ -251,7 +253,7 @@ impl Module {
mut mod_all, mut mod_all,
mut fns, mut fns,
consts, consts,
mut submodules, mut sub_modules,
params, params,
.. ..
} = self; } = self;
@ -266,13 +268,13 @@ impl Module {
let mod_gen = crate::rhai_module::generate_body( let mod_gen = crate::rhai_module::generate_body(
&mut fns, &mut fns,
&consts, &consts,
&mut submodules, &mut sub_modules,
&params.scope, &params.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 // to avoid issues caused by re-parsing them
let inner_modules: Vec<proc_macro2::TokenStream> = submodules.drain(..) let inner_modules: Vec<proc_macro2::TokenStream> = sub_modules.drain(..)
.try_fold::<Vec<proc_macro2::TokenStream>, _, .try_fold::<Vec<proc_macro2::TokenStream>, _,
Result<Vec<proc_macro2::TokenStream>, syn::Error>>( Result<Vec<proc_macro2::TokenStream>, syn::Error>>(
Vec::new(), |mut acc, m| { acc.push(m.generate_inner()?); Ok(acc) })?; Vec::new(), |mut acc, m| { acc.push(m.generate_inner()?); Ok(acc) })?;
@ -309,8 +311,8 @@ impl Module {
&self.fns &self.fns
} }
pub fn submodules(&self) -> &[Module] { pub fn sub_modules(&self) -> &[Module] {
&self.submodules &self.sub_modules
} }
pub fn content(&self) -> Option<&Vec<syn::Item>> { pub fn content(&self) -> Option<&Vec<syn::Item>> {

View File

@ -33,11 +33,11 @@ pub fn parse_register_macro(
)); ));
} }
let export_name = match &items[1] { let export_name = match &items[1] {
syn::Expr::Lit(litstr) => quote_spanned!(items[1].span()=> syn::Expr::Lit(lit_str) => quote_spanned!(items[1].span()=>
#litstr.to_string()), #lit_str.to_string()),
expr => quote! { #expr }, 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() path.path.clone()
} else { } else {
return Err(syn::Error::new( return Err(syn::Error::new(
@ -46,5 +46,5 @@ pub fn parse_register_macro(
)); ));
}; };
let module = items.remove(0); let module = items.remove(0);
Ok((module, export_name, rust_modpath)) Ok((module, export_name, rust_mod_path))
} }

View File

@ -14,11 +14,11 @@ pub(crate) type ExportedConst = (String, Box<syn::Type>, syn::Expr);
pub(crate) fn generate_body( pub(crate) fn generate_body(
fns: &mut [ExportedFn], fns: &mut [ExportedFn],
consts: &[ExportedConst], consts: &[ExportedConst],
submodules: &mut [Module], sub_modules: &mut [Module],
parent_scope: &ExportScope, parent_scope: &ExportScope,
) -> proc_macro2::TokenStream { ) -> proc_macro2::TokenStream {
let mut set_fn_stmts: Vec<syn::Stmt> = Vec::new(); let mut set_fn_statements: Vec<syn::Stmt> = Vec::new();
let mut set_const_stmts: Vec<syn::Stmt> = Vec::new(); let mut set_const_statements: Vec<syn::Stmt> = Vec::new();
let mut add_mod_blocks: Vec<syn::ExprBlock> = Vec::new(); let mut add_mod_blocks: Vec<syn::ExprBlock> = Vec::new();
let mut set_flattened_mod_blocks: Vec<syn::ExprBlock> = Vec::new(); let mut set_flattened_mod_blocks: Vec<syn::ExprBlock> = Vec::new();
let str_type_path = syn::parse2::<syn::Path>(quote! { str }).unwrap(); let str_type_path = syn::parse2::<syn::Path>(quote! { str }).unwrap();
@ -27,7 +27,7 @@ pub(crate) fn generate_body(
for (const_name, _, _) in consts { for (const_name, _, _) in consts {
let const_literal = syn::LitStr::new(&const_name, proc_macro2::Span::call_site()); 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()); let const_ref = syn::Ident::new(&const_name, proc_macro2::Span::call_site());
set_const_stmts.push( set_const_statements.push(
syn::parse2::<syn::Stmt>(quote! { syn::parse2::<syn::Stmt>(quote! {
m.set_var(#const_literal, #const_ref); m.set_var(#const_literal, #const_ref);
}) })
@ -35,17 +35,17 @@ pub(crate) fn generate_body(
); );
} }
for itemmod in submodules { for item_mod in sub_modules {
itemmod.update_scope(&parent_scope); item_mod.update_scope(&parent_scope);
if itemmod.skipped() { if item_mod.skipped() {
continue; continue;
} }
let module_name = itemmod.module_name(); let module_name = item_mod.module_name();
let exported_name: syn::LitStr = syn::LitStr::new( let exported_name: syn::LitStr = syn::LitStr::new(
itemmod.exported_name().as_ref(), item_mod.exported_name().as_ref(),
proc_macro2::Span::call_site(), proc_macro2::Span::call_site(),
); );
let cfg_attrs: Vec<&syn::Attribute> = itemmod let cfg_attrs: Vec<&syn::Attribute> = item_mod
.attrs() .attrs()
.iter() .iter()
.filter(|&a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false)) .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<String> = function let fn_input_names: Vec<String> = function
.arg_list() .arg_list()
.map(|fnarg| match fnarg { .map(|fn_arg| match fn_arg {
syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"), syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"),
syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => { syn::FnArg::Typed(syn::PatType { pat, ty, .. }) => {
format!("{}: {}", pat.to_token_stream(), print_type(ty)) format!("{}: {}", pat.to_token_stream(), print_type(ty))
@ -93,7 +93,7 @@ pub(crate) fn generate_body(
let fn_input_types: Vec<syn::Expr> = function let fn_input_types: Vec<syn::Expr> = function
.arg_list() .arg_list()
.map(|fnarg| match fnarg { .map(|fn_arg| match fn_arg {
syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"), syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"),
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => { syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
let arg_type = match flatten_type_groups(ty.as_ref()) { let arg_type = match flatten_type_groups(ty.as_ref()) {
@ -169,7 +169,7 @@ pub(crate) fn generate_body(
}, },
fn_literal.span(), fn_literal.span(),
); );
set_fn_stmts.push( set_fn_statements.push(
syn::parse2::<syn::Stmt>(quote! { syn::parse2::<syn::Stmt>(quote! {
m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public, m.set_fn(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
Some(&[#(#fn_input_names,)* #return_type]), &[#(#fn_input_types),*], 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())); gen_fn_tokens.push(function.generate_return_type(&fn_token_name.to_string()));
} }
let mut generate_fncall = syn::parse2::<syn::ItemMod>(quote! { let mut generate_fn_call = syn::parse2::<syn::ItemMod>(quote! {
pub mod generate_info { pub mod generate_info {
#[allow(unused_imports)] #[allow(unused_imports)]
use super::*; use super::*;
@ -203,8 +203,8 @@ pub(crate) fn generate_body(
} }
#[allow(unused_mut)] #[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
#(#set_fn_stmts)* #(#set_fn_statements)*
#(#set_const_stmts)* #(#set_const_statements)*
if flatten { if flatten {
#(#set_flattened_mod_blocks)* #(#set_flattened_mod_blocks)*
@ -216,7 +216,7 @@ pub(crate) fn generate_body(
}) })
.unwrap(); .unwrap();
let (_, generate_call_content) = generate_fncall.content.take().unwrap(); let (_, generate_call_content) = generate_fn_call.content.take().unwrap();
quote! { quote! {
#(#generate_call_content)* #(#generate_call_content)*
@ -225,39 +225,39 @@ pub(crate) fn generate_body(
} }
pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> { pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> {
fn make_key(name: impl ToString, itemfn: &ExportedFn) -> String { fn make_key(name: impl ToString, item_fn: &ExportedFn) -> String {
itemfn item_fn
.arg_list() .arg_list()
.fold(name.to_string(), |mut argstr, fnarg| { .fold(name.to_string(), |mut arg_str, fn_arg| {
let type_string: String = match fnarg { let type_string: String = match fn_arg {
syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"), syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"),
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => print_type(ty), syn::FnArg::Typed(syn::PatType { ref ty, .. }) => print_type(ty),
}; };
argstr.push('.'); arg_str.push('.');
argstr.push_str(&type_string); arg_str.push_str(&type_string);
argstr arg_str
}) })
} }
let mut renames = HashMap::<String, proc_macro2::Span>::new(); let mut renames = HashMap::<String, proc_macro2::Span>::new();
let mut fn_defs = HashMap::<String, proc_macro2::Span>::new(); let mut fn_defs = HashMap::<String, proc_macro2::Span>::new();
for itemfn in fns.iter() { for item_fn in fns.iter() {
if !itemfn.params().name.is_empty() || itemfn.params().special != FnSpecialAccess::None { if !item_fn.params().name.is_empty() || item_fn.params().special != FnSpecialAccess::None {
let mut names: Vec<_> = itemfn let mut names: Vec<_> = item_fn
.params() .params()
.name .name
.iter() .iter()
.map(|n| (n.clone(), n.clone())) .map(|n| (n.clone(), n.clone()))
.collect(); .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)); names.push((s, n));
} }
for (name, fn_name) in names { for (name, fn_name) in names {
let current_span = itemfn.params().span.unwrap(); let current_span = item_fn.params().span.unwrap();
let key = make_key(&name, itemfn); let key = make_key(&name, item_fn);
if let Some(other_span) = renames.insert(key, current_span) { if let Some(other_span) = renames.insert(key, current_span) {
let mut err = syn::Error::new( let mut err = syn::Error::new(
current_span, current_span,
@ -271,7 +271,7 @@ pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::
} }
} }
} else { } else {
let ident = itemfn.name(); let ident = item_fn.name();
if let Some(other_span) = fn_defs.insert(ident.to_string(), ident.span()) { if let Some(other_span) = fn_defs.insert(ident.to_string(), ident.span()) {
let mut err = syn::Error::new( let mut err = syn::Error::new(
ident.span(), ident.span(),
@ -283,7 +283,7 @@ pub(crate) fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::
)); ));
return Err(err); return Err(err);
} }
let key = make_key(ident, itemfn); let key = make_key(ident, item_fn);
if let Some(fn_span) = renames.get(&key) { if let Some(fn_span) = renames.get(&key) {
let mut err = syn::Error::new( let mut err = syn::Error::new(
ident.span(), ident.span(),

View File

@ -106,10 +106,10 @@ mod module_tests {
let item_mod = syn::parse2::<Module>(input_tokens).unwrap(); let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert!(item_mod.fns().is_empty()); assert!(item_mod.fns().is_empty());
assert!(item_mod.consts().is_empty()); assert!(item_mod.consts().is_empty());
assert_eq!(item_mod.submodules().len(), 1); assert_eq!(item_mod.sub_modules().len(), 1);
assert_eq!(&item_mod.submodules()[0].consts()[0].0, "MYSTIC_NUMBER"); assert_eq!(&item_mod.sub_modules()[0].consts()[0].0, "MYSTIC_NUMBER");
assert_eq!( assert_eq!(
item_mod.submodules()[0].consts()[0].2, item_mod.sub_modules()[0].consts()[0].2,
syn::parse2::<syn::Expr>(quote! { 42 }).unwrap() syn::parse2::<syn::Expr>(quote! { 42 }).unwrap()
); );
} }
@ -130,11 +130,11 @@ mod module_tests {
let item_mod = syn::parse2::<Module>(input_tokens).unwrap(); let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert!(item_mod.fns().is_empty()); assert!(item_mod.fns().is_empty());
assert!(item_mod.consts().is_empty()); assert!(item_mod.consts().is_empty());
assert_eq!(item_mod.submodules().len(), 1); assert_eq!(item_mod.sub_modules().len(), 1);
assert_eq!(item_mod.submodules()[0].fns().len(), 1); assert_eq!(item_mod.sub_modules()[0].fns().len(), 1);
assert!(item_mod.submodules()[0].fns()[0].skipped()); assert!(item_mod.sub_modules()[0].fns()[0].skipped());
assert!(item_mod.submodules()[0].consts().is_empty()); assert!(item_mod.sub_modules()[0].consts().is_empty());
assert!(item_mod.submodules()[0].submodules().is_empty()); assert!(item_mod.sub_modules()[0].sub_modules().is_empty());
} }
#[test] #[test]
@ -153,8 +153,8 @@ mod module_tests {
let item_mod = syn::parse2::<Module>(input_tokens).unwrap(); let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert!(item_mod.fns().is_empty()); assert!(item_mod.fns().is_empty());
assert!(item_mod.consts().is_empty()); assert!(item_mod.consts().is_empty());
assert_eq!(item_mod.submodules().len(), 1); assert_eq!(item_mod.sub_modules().len(), 1);
assert!(item_mod.submodules()[0].skipped()); assert!(item_mod.sub_modules()[0].skipped());
} }
#[test] #[test]
@ -958,7 +958,7 @@ mod generate_tests {
} }
#[test] #[test]
fn one_skipped_submodule() { fn one_skipped_sub_module() {
let input_tokens: TokenStream = quote! { let input_tokens: TokenStream = quote! {
pub mod one_fn { pub mod one_fn {
pub fn get_mystic_number() -> INT { pub fn get_mystic_number() -> INT {

View File

@ -29,7 +29,7 @@ fn one_fn_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
Ok(()) Ok(())
} }
pub mod one_fn_submodule_nested_attr { pub mod one_fn_sub_module_nested_attr {
use rhai::plugin::*; use rhai::plugin::*;
#[export_module] #[export_module]
@ -47,9 +47,9 @@ pub mod one_fn_submodule_nested_attr {
} }
#[test] #[test]
fn one_fn_submodule_nested_attr_test() -> Result<(), Box<EvalAltResult>> { fn one_fn_sub_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); 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()); engine.register_static_module("Math::Advanced", m.into());
assert_eq!( assert_eq!(

View File

@ -627,17 +627,17 @@ pub struct Limits {
/// Context of a script evaluation process. /// Context of a script evaluation process.
#[derive(Debug)] #[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) engine: &'e Engine,
pub(crate) scope: &'x mut Scope<'px>, pub(crate) scope: &'x mut Scope<'px>,
pub(crate) mods: &'a mut Imports, pub(crate) mods: &'a mut Imports,
pub(crate) state: &'s mut State, 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) this_ptr: &'t mut Option<&'pt mut Dynamic>,
pub(crate) level: usize, 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`]. /// The current [`Engine`].
#[inline(always)] #[inline(always)]
pub fn engine(&self) -> &Engine { pub fn engine(&self) -> &Engine {

View File

@ -32,6 +32,9 @@ use crate::FLOAT;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::Map; use crate::Map;
#[cfg(feature = "decimal")]
use rust_decimal::Decimal;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use num_traits::float::Float; use num_traits::float::Float;
@ -798,6 +801,7 @@ impl Engine {
self.eval_global_statements(scope, mods, &mut new_state, ast.statements(), lib, level); self.eval_global_statements(scope, mods, &mut new_state, ast.statements(), lib, level);
state.operations = new_state.operations; state.operations = new_state.operations;
result result
} }
@ -1067,17 +1071,27 @@ impl Engine {
if name == KEYWORD_EVAL && args_expr.len() == 1 { if name == KEYWORD_EVAL && args_expr.len() == 1 {
let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>())); let hash_fn = calc_native_fn_hash(empty(), name, once(TypeId::of::<ImmutableString>()));
let script_expr = &args_expr[0];
if !self.has_override(Some(mods), Some(state), lib, hash_fn, hash_script, pub_only) { 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 // eval - only in function call style
let prev_len = scope.len(); let prev_len = scope.len();
let script = 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| { let script = script.as_str().map_err(|typ| {
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position()) self.make_type_mismatch_err::<ImmutableString>(typ, script_pos)
})?; })?;
let pos = args_expr[0].position(); let result = self.eval_script_expr_in_place(
let result = scope,
self.eval_script_expr_in_place(scope, mods, state, lib, script, pos, level + 1); mods,
state,
lib,
script,
script_pos,
level + 1,
);
// IMPORTANT! If the eval defines new variables in the current scope, // IMPORTANT! If the eval defines new variables in the current scope,
// all variable offsets from this point on will be mis-aligned. // all variable offsets from this point on will be mis-aligned.
@ -1085,7 +1099,18 @@ impl Engine {
state.always_search = true; 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,
))
});
} }
} }
@ -1325,33 +1350,115 @@ pub fn run_builtin_binary_op(
x: &Dynamic, x: &Dynamic,
y: &Dynamic, y: &Dynamic,
) -> Result<Option<Dynamic>, Box<EvalAltResult>> { ) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
let args_type = x.type_id(); let first_type = x.type_id();
let second_type = y.type_id(); let second_type = y.type_id();
if second_type != args_type { let type_id = (first_type, second_type);
if args_type == TypeId::of::<char>() && second_type == TypeId::of::<ImmutableString>() {
#[cfg(not(feature = "no_float"))]
if let Some((x, y)) = if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<FLOAT>()))
} else if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
Some((x.clone().cast::<FLOAT>(), y.clone().cast::<INT>() as FLOAT))
} else if type_id == (TypeId::of::<INT>(), TypeId::of::<FLOAT>()) {
Some((x.clone().cast::<INT>() as FLOAT, y.clone().cast::<FLOAT>()))
} 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::<Decimal>(), TypeId::of::<Decimal>()) {
Some((
*x.read_lock::<Decimal>().unwrap(),
*y.read_lock::<Decimal>().unwrap(),
))
} else if type_id == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
Some((
*x.read_lock::<Decimal>().unwrap(),
y.clone().cast::<INT>().into(),
))
} else if type_id == (TypeId::of::<INT>(), TypeId::of::<Decimal>()) {
Some((
x.clone().cast::<INT>().into(),
*y.read_lock::<Decimal>().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::<char>(), TypeId::of::<ImmutableString>()) {
let x = x.clone().cast::<char>(); let x = x.clone().cast::<char>();
let y = &*y.read_lock::<ImmutableString>().unwrap(); let y = &*y.read_lock::<ImmutableString>().unwrap();
match op { match op {
"+" => return Ok(Some(format!("{}{}", x, y).into())), "+" => return Ok(Some(format!("{}{}", x, y).into())),
_ => (), _ => return Ok(None),
} }
} else if args_type == TypeId::of::<ImmutableString>() }
&& second_type == TypeId::of::<char>()
{ if type_id == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
let x = &*x.read_lock::<ImmutableString>().unwrap(); let x = &*x.read_lock::<ImmutableString>().unwrap();
let y = y.clone().cast::<char>(); let y = y.clone().cast::<char>();
match op { match op {
"+" => return Ok(Some((x + y).into())), "+" => return Ok(Some((x + y).into())),
_ => (), _ => return Ok(None),
} }
} }
return Ok(None); return Ok(None);
} }
if args_type == TypeId::of::<INT>() { if first_type == TypeId::of::<INT>() {
let x = x.clone().cast::<INT>(); let x = x.clone().cast::<INT>();
let y = y.clone().cast::<INT>(); let y = y.clone().cast::<INT>();
@ -1393,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(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::<bool>() { }
if first_type == TypeId::of::<bool>() {
let x = x.clone().cast::<bool>(); let x = x.clone().cast::<bool>();
let y = y.clone().cast::<bool>(); let y = y.clone().cast::<bool>();
@ -1405,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(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::<ImmutableString>() { }
if first_type == TypeId::of::<ImmutableString>() {
let x = &*x.read_lock::<ImmutableString>().unwrap(); let x = &*x.read_lock::<ImmutableString>().unwrap();
let y = &*y.read_lock::<ImmutableString>().unwrap(); let y = &*y.read_lock::<ImmutableString>().unwrap();
@ -1419,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(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::<char>() { }
if first_type == TypeId::of::<char>() {
let x = x.clone().cast::<char>(); let x = x.clone().cast::<char>();
let y = y.clone().cast::<char>(); let y = y.clone().cast::<char>();
@ -1433,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(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 { match op {
"==" => return Ok(Some(true.into())), "==" => return Ok(Some(true.into())),
"!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())), "!=" | ">" | ">=" | "<" | "<=" => return Ok(Some(false.into())),
_ => (), _ => return Ok(None),
}
}
#[cfg(not(feature = "no_float"))]
if args_type == TypeId::of::<FLOAT>() {
let x = x.clone().cast::<FLOAT>();
let y = y.clone().cast::<FLOAT>();
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::<rust_decimal::Decimal>() {
let x = x.clone().cast::<rust_decimal::Decimal>();
let y = y.clone().cast::<rust_decimal::Decimal>();
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())),
_ => (),
} }
} }
@ -1512,24 +1567,80 @@ pub fn run_builtin_op_assignment(
x: &mut Dynamic, x: &mut Dynamic,
y: &Dynamic, y: &Dynamic,
) -> Result<Option<()>, Box<EvalAltResult>> { ) -> Result<Option<()>, Box<EvalAltResult>> {
let args_type = x.type_id(); let first_type = x.type_id();
let second_type = y.type_id(); let second_type = y.type_id();
if second_type != args_type { let type_id = (first_type, second_type);
if args_type == TypeId::of::<ImmutableString>() && second_type == TypeId::of::<char>() {
#[cfg(not(feature = "no_float"))]
if let Some((mut x, y)) = if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<FLOAT>()) {
let y = y.clone().cast::<FLOAT>();
Some((x.write_lock::<FLOAT>().unwrap(), y))
} else if type_id == (TypeId::of::<FLOAT>(), TypeId::of::<INT>()) {
let y = y.clone().cast::<INT>() as FLOAT;
Some((x.write_lock::<FLOAT>().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::<Decimal>(), TypeId::of::<Decimal>()) {
let y = *y.read_lock::<Decimal>().unwrap();
Some((x.write_lock::<Decimal>().unwrap(), y))
} else if type_id == (TypeId::of::<Decimal>(), TypeId::of::<INT>()) {
let y = y.clone().cast::<INT>().into();
Some((x.write_lock::<Decimal>().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::<ImmutableString>(), TypeId::of::<char>()) {
let y = y.read_lock::<char>().unwrap().deref().clone(); let y = y.read_lock::<char>().unwrap().deref().clone();
let mut x = x.write_lock::<ImmutableString>().unwrap(); let mut x = x.write_lock::<ImmutableString>().unwrap();
match op { match op {
"+=" => return Ok(Some(*x += y)), "+=" => return Ok(Some(*x += y)),
_ => (), _ => return Ok(None),
} }
} }
return Ok(None); return Ok(None);
} }
if args_type == TypeId::of::<INT>() { if first_type == TypeId::of::<INT>() {
let y = y.clone().cast::<INT>(); let y = y.clone().cast::<INT>();
let mut x = x.write_lock::<INT>().unwrap(); let mut x = x.write_lock::<INT>().unwrap();
@ -1565,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(Some(*x |= y)),
"^=" => return Ok(Some(*x ^= y)), "^=" => return Ok(Some(*x ^= y)),
_ => (), _ => return Ok(None),
} }
} else if args_type == TypeId::of::<bool>() { }
if first_type == TypeId::of::<bool>() {
let y = y.clone().cast::<bool>(); let y = y.clone().cast::<bool>();
let mut x = x.write_lock::<bool>().unwrap(); let mut x = x.write_lock::<bool>().unwrap();
match op { match op {
"&=" => return Ok(Some(*x = *x && y)), "&=" => return Ok(Some(*x = *x && y)),
"|=" => return Ok(Some(*x = *x || y)), "|=" => return Ok(Some(*x = *x || y)),
_ => (), _ => return Ok(None),
} }
} else if args_type == TypeId::of::<char>() { }
if first_type == TypeId::of::<char>() {
let y = y.read_lock::<char>().unwrap().deref().clone(); let y = y.read_lock::<char>().unwrap().deref().clone();
let mut x = x.write_lock::<Dynamic>().unwrap(); let mut x = x.write_lock::<Dynamic>().unwrap();
match op { match op {
"+=" => return Ok(Some(*x = format!("{}{}", *x, y).into())), "+=" => return Ok(Some(*x = format!("{}{}", *x, y).into())),
_ => (), _ => return Ok(None),
} }
} else if args_type == TypeId::of::<ImmutableString>() { }
if first_type == TypeId::of::<ImmutableString>() {
let y = y.read_lock::<ImmutableString>().unwrap().deref().clone(); let y = y.read_lock::<ImmutableString>().unwrap().deref().clone();
let mut x = x.write_lock::<ImmutableString>().unwrap(); let mut x = x.write_lock::<ImmutableString>().unwrap();
match op { match op {
"+=" => return Ok(Some(*x += y)), "+=" => return Ok(Some(*x += y)),
_ => (), _ => return Ok(None),
}
}
#[cfg(not(feature = "no_float"))]
if args_type == TypeId::of::<FLOAT>() {
let y = y.clone().cast::<FLOAT>();
let mut x = x.write_lock::<FLOAT>().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::<rust_decimal::Decimal>() {
let y = y.clone().cast::<rust_decimal::Decimal>();
let mut x = x.write_lock::<rust_decimal::Decimal>().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)),
_ => (),
}
} }
} }

View File

@ -55,17 +55,17 @@ pub type Locked<T> = crate::stdlib::sync::RwLock<T>;
/// Context of a native Rust function call. /// Context of a native Rust function call.
#[derive(Debug, Copy, Clone)] #[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, engine: &'e Engine,
fn_name: &'n str, fn_name: &'n str,
source: Option<&'s str>, source: Option<&'s str>,
pub(crate) mods: Option<&'a Imports>, 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)> 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)] #[inline(always)]
fn from(value: (&'e Engine, &'n str, Option<&'s str>, &'a Imports, &'m M)) -> Self { 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)> impl<'e, 'n, 'm, M: AsRef<[&'m Module]> + ?Sized> From<(&'e Engine, &'n str, &'m M)>
for NativeCallContext<'e, 'n, '_, '_, 'm, 'pm> for NativeCallContext<'e, 'n, '_, '_, 'm>
{ {
#[inline(always)] #[inline(always)]
fn from(value: (&'e Engine, &'n str, &'m M)) -> Self { 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`]. /// Create a new [`NativeCallContext`].
#[inline(always)] #[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 { Self {
engine, engine,
fn_name, fn_name,
@ -116,7 +116,7 @@ impl<'e, 'n, 's, 'a, 'm, 'pm> NativeCallContext<'e, 'n, 's, 'a, 'm, 'pm> {
fn_name: &'n str, fn_name: &'n str,
source: &'s Option<&str>, source: &'s Option<&str>,
imports: &'a mut Imports, imports: &'a mut Imports,
lib: &'m impl AsRef<[&'pm Module]>, lib: &'m impl AsRef<[&'m Module]>,
) -> Self { ) -> Self {
Self { Self {
engine, engine,

View File

@ -254,47 +254,47 @@ mod f32_functions {
pub fn pow_f_f(x: f32, y: f32) -> f32 { pub fn pow_f_f(x: f32, y: f32) -> f32 {
x.powf(y) x.powf(y)
} }
}
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn add_if(x: INT, y: f32) -> f32 { pub fn add_if(x: INT, y: f32) -> f32 {
(x as f32) + (y as f32) (x as f32) + (y as f32)
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn add_fi(x: f32, y: INT) -> f32 { pub fn add_fi(x: f32, y: INT) -> f32 {
(x as f32) + (y as f32) (x as f32) + (y as f32)
} }
#[rhai_fn(name = "-")] #[rhai_fn(name = "-")]
pub fn subtract_if(x: INT, y: f32) -> f32 { pub fn subtract_if(x: INT, y: f32) -> f32 {
(x as f32) - (y as f32) (x as f32) - (y as f32)
} }
#[rhai_fn(name = "-")] #[rhai_fn(name = "-")]
pub fn subtract_fi(x: f32, y: INT) -> f32 { pub fn subtract_fi(x: f32, y: INT) -> f32 {
(x as f32) - (y as f32) (x as f32) - (y as f32)
} }
#[rhai_fn(name = "*")] #[rhai_fn(name = "*")]
pub fn multiply_if(x: INT, y: f32) -> f32 { pub fn multiply_if(x: INT, y: f32) -> f32 {
(x as f32) * (y as f32) (x as f32) * (y as f32)
} }
#[rhai_fn(name = "*")] #[rhai_fn(name = "*")]
pub fn multiply_fi(x: f32, y: INT) -> f32 { pub fn multiply_fi(x: f32, y: INT) -> f32 {
(x as f32) * (y as f32) (x as f32) * (y as f32)
} }
#[rhai_fn(name = "/")] #[rhai_fn(name = "/")]
pub fn divide_if(x: INT, y: f32) -> f32 { pub fn divide_if(x: INT, y: f32) -> f32 {
(x as f32) / (y as f32) (x as f32) / (y as f32)
} }
#[rhai_fn(name = "/")] #[rhai_fn(name = "/")]
pub fn divide_fi(x: f32, y: INT) -> f32 { pub fn divide_fi(x: f32, y: INT) -> f32 {
(x as f32) / (y as f32) (x as f32) / (y as f32)
} }
#[rhai_fn(name = "%")] #[rhai_fn(name = "%")]
pub fn modulo_if(x: INT, y: f32) -> f32 { pub fn modulo_if(x: INT, y: f32) -> f32 {
(x as f32) % (y as f32) (x as f32) % (y as f32)
} }
#[rhai_fn(name = "%")] #[rhai_fn(name = "%")]
pub fn modulo_fi(x: f32, y: INT) -> f32 { pub fn modulo_fi(x: f32, y: INT) -> f32 {
(x as f32) % (y as f32) (x as f32) % (y as f32)
}
} }
#[rhai_fn(name = "-")] #[rhai_fn(name = "-")]
@ -359,47 +359,47 @@ mod f64_functions {
pub fn pow_f_f(x: f64, y: f64) -> f64 { pub fn pow_f_f(x: f64, y: f64) -> f64 {
x.powf(y) x.powf(y)
} }
}
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn add_if(x: INT, y: f64) -> f64 { pub fn add_if(x: INT, y: f64) -> f64 {
(x as f64) + (y as f64) (x as f64) + (y as f64)
} }
#[rhai_fn(name = "+")] #[rhai_fn(name = "+")]
pub fn add_fi(x: f64, y: INT) -> f64 { pub fn add_fi(x: f64, y: INT) -> f64 {
(x as f64) + (y as f64) (x as f64) + (y as f64)
} }
#[rhai_fn(name = "-")] #[rhai_fn(name = "-")]
pub fn subtract_if(x: INT, y: f64) -> f64 { pub fn subtract_if(x: INT, y: f64) -> f64 {
(x as f64) - (y as f64) (x as f64) - (y as f64)
} }
#[rhai_fn(name = "-")] #[rhai_fn(name = "-")]
pub fn subtract_fi(x: f64, y: INT) -> f64 { pub fn subtract_fi(x: f64, y: INT) -> f64 {
(x as f64) - (y as f64) (x as f64) - (y as f64)
} }
#[rhai_fn(name = "*")] #[rhai_fn(name = "*")]
pub fn multiply_if(x: INT, y: f64) -> f64 { pub fn multiply_if(x: INT, y: f64) -> f64 {
(x as f64) * (y as f64) (x as f64) * (y as f64)
} }
#[rhai_fn(name = "*")] #[rhai_fn(name = "*")]
pub fn multiply_fi(x: f64, y: INT) -> f64 { pub fn multiply_fi(x: f64, y: INT) -> f64 {
(x as f64) * (y as f64) (x as f64) * (y as f64)
} }
#[rhai_fn(name = "/")] #[rhai_fn(name = "/")]
pub fn divide_if(x: INT, y: f64) -> f64 { pub fn divide_if(x: INT, y: f64) -> f64 {
(x as f64) / (y as f64) (x as f64) / (y as f64)
} }
#[rhai_fn(name = "/")] #[rhai_fn(name = "/")]
pub fn divide_fi(x: f64, y: INT) -> f64 { pub fn divide_fi(x: f64, y: INT) -> f64 {
(x as f64) / (y as f64) (x as f64) / (y as f64)
} }
#[rhai_fn(name = "%")] #[rhai_fn(name = "%")]
pub fn modulo_if(x: INT, y: f64) -> f64 { pub fn modulo_if(x: INT, y: f64) -> f64 {
(x as f64) % (y as f64) (x as f64) % (y as f64)
} }
#[rhai_fn(name = "%")] #[rhai_fn(name = "%")]
pub fn modulo_fi(x: f64, y: INT) -> f64 { pub fn modulo_fi(x: f64, y: INT) -> f64 {
(x as f64) % (y as f64) (x as f64) % (y as f64)
}
} }
#[rhai_fn(name = "-")] #[rhai_fn(name = "-")]
@ -440,61 +440,37 @@ mod f64_functions {
mod decimal_functions { mod decimal_functions {
use rust_decimal::{prelude::Zero, Decimal}; use rust_decimal::{prelude::Zero, Decimal};
#[rhai_fn(name = "+", return_raw)] #[rhai_fn(skip, return_raw)]
pub fn add(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> { pub fn add(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_add(y) x.checked_add(y)
.ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y))) .ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y)))
.map(Dynamic::from) .map(Into::<Dynamic>::into)
} else { } else {
Ok(Dynamic::from(x + y)) Ok(Dynamic::from(x + y))
} }
} }
#[rhai_fn(name = "+", return_raw)] #[rhai_fn(skip, return_raw)]
pub fn add_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
add(x.into(), y)
}
#[rhai_fn(name = "+", return_raw)]
pub fn add_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
add(x, y.into())
}
#[rhai_fn(name = "-", return_raw)]
pub fn subtract(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> { pub fn subtract(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_sub(y) x.checked_sub(y)
.ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y))) .ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y)))
.map(Dynamic::from) .map(Into::<Dynamic>::into)
} else { } else {
Ok(Dynamic::from(x - y)) Ok(Dynamic::from(x - y))
} }
} }
#[rhai_fn(name = "-", return_raw)] #[rhai_fn(skip, return_raw)]
pub fn subtract_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
subtract(x.into(), y)
}
#[rhai_fn(name = "-", return_raw)]
pub fn subtract_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
subtract(x, y.into())
}
#[rhai_fn(name = "*", return_raw)]
pub fn multiply(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> { pub fn multiply(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_mul(y) x.checked_mul(y)
.ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y))) .ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y)))
.map(Dynamic::from) .map(Into::<Dynamic>::into)
} else { } else {
Ok(Dynamic::from(x * y)) Ok(Dynamic::from(x * y))
} }
} }
#[rhai_fn(name = "*", return_raw)] #[rhai_fn(skip, return_raw)]
pub fn multiply_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
multiply(x.into(), y)
}
#[rhai_fn(name = "*", return_raw)]
pub fn multiply_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
multiply(x, y.into())
}
#[rhai_fn(name = "/", return_raw)]
pub fn divide(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> { pub fn divide(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
// Detect division by zero // Detect division by zero
@ -503,21 +479,13 @@ mod decimal_functions {
} else { } else {
x.checked_div(y) x.checked_div(y)
.ok_or_else(|| make_err(format!("Division overflow: {} / {}", x, y))) .ok_or_else(|| make_err(format!("Division overflow: {} / {}", x, y)))
.map(Dynamic::from) .map(Into::<Dynamic>::into)
} }
} else { } else {
Ok(Dynamic::from(x / y)) Ok(Dynamic::from(x / y))
} }
} }
#[rhai_fn(name = "/", return_raw)] #[rhai_fn(skip, return_raw)]
pub fn divide_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
divide(x.into(), y)
}
#[rhai_fn(name = "/", return_raw)]
pub fn divide_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
divide(x, y.into())
}
#[rhai_fn(name = "%", return_raw)]
pub fn modulo(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> { pub fn modulo(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) { if cfg!(not(feature = "unchecked")) {
x.checked_rem(y) x.checked_rem(y)
@ -527,19 +495,11 @@ mod decimal_functions {
x, y x, y
)) ))
}) })
.map(Dynamic::from) .map(Into::<Dynamic>::into)
} else { } else {
Ok(Dynamic::from(x % y)) Ok(Dynamic::from(x % y))
} }
} }
#[rhai_fn(name = "%", return_raw)]
pub fn modulo_id(x: INT, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
modulo(x.into(), y)
}
#[rhai_fn(name = "%", return_raw)]
pub fn modulo_di(x: Decimal, y: INT) -> Result<Dynamic, Box<EvalAltResult>> {
modulo(x, y.into())
}
#[rhai_fn(name = "-")] #[rhai_fn(name = "-")]
pub fn neg(x: Decimal) -> Decimal { pub fn neg(x: Decimal) -> Decimal {
-x -x

View File

@ -70,13 +70,18 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, {
{ {
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
reg_functions!(lib += float; f32); reg_functions!(lib += float; f32);
combine_with_exported_module!(lib, "f32", f32_functions);
#[cfg(feature = "f32_float")] #[cfg(feature = "f32_float")]
reg_functions!(lib += float; f64); reg_functions!(lib += float; f64);
combine_with_exported_module!(lib, "f64", f64_functions);
} }
#[cfg(feature = "decimal")] #[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); set_exported_fn!(lib, "!", not);
}); });
@ -106,3 +111,169 @@ gen_cmp_functions!(float => f64);
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
gen_cmp_functions!(decimal => 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)
}
}

View File

@ -3167,48 +3167,20 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(value, _) => Some(Expr::FloatConstant(value, pos)), 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::Unit(_, _) => Some(Expr::Unit(pos)),
Union::Int(value, _) => Some(Expr::IntegerConstant(value, pos)), Union::Int(value, _) => Some(Expr::IntegerConstant(value, pos)),
Union::Char(value, _) => Some(Expr::CharConstant(value, pos)), Union::Char(value, _) => Some(Expr::CharConstant(value, pos)),
Union::Str(value, _) => Some(Expr::StringConstant(value, pos)), Union::Str(value, _) => Some(Expr::StringConstant(value, pos)),
Union::Bool(value, _) => Some(Expr::BoolConstant(value, pos)), Union::Bool(value, _) => Some(Expr::BoolConstant(value, pos)),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(array, _) => { Union::Array(array, _) => Some(Expr::DynamicConstant(Box::new((*array).into()), pos)),
let items: Vec<_> = array
.into_iter()
.map(|x| map_dynamic_to_expr(x, pos))
.collect();
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"))] #[cfg(not(feature = "no_object"))]
Union::Map(map, _) => { Union::Map(map, _) => Some(Expr::DynamicConstant(Box::new((*map).into()), pos)),
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
}
}
_ => None, _ => None,
} }

View File

@ -64,6 +64,20 @@ impl Default for Scope<'_> {
} }
} }
impl<'a> IntoIterator for Scope<'a> {
type Item = (Cow<'a, str>, Dynamic);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + '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> { impl<'a> Scope<'a> {
/// Create a new [`Scope`]. /// Create a new [`Scope`].
/// ///

View File

@ -61,7 +61,7 @@ impl Expression<'_> {
} }
} }
impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_, '_> { impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
/// Evaluate an [expression tree][Expression]. /// Evaluate an [expression tree][Expression].
/// ///
/// # WARNING - Low Level API /// # WARNING - Low Level API

View File

@ -9,6 +9,7 @@ use crate::stdlib::{
char, fmt, format, char, fmt, format,
iter::Peekable, iter::Peekable,
num::NonZeroUsize, num::NonZeroUsize,
ops::{Add, AddAssign},
str::{Chars, FromStr}, str::{Chars, FromStr},
string::{String, ToString}, string::{String, ToString},
}; };
@ -120,7 +121,7 @@ impl Position {
/// Is this [`Position`] at the beginning of a line? /// Is this [`Position`] at the beginning of a line?
#[inline(always)] #[inline(always)]
pub fn is_beginning_of_line(self) -> bool { pub fn is_beginning_of_line(self) -> bool {
self.line == 0 && !self.is_none() self.pos == 0 && !self.is_none()
} }
/// Is there no [`Position`]? /// Is there no [`Position`]?
#[inline(always)] #[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. /// _(INTERNALS)_ A Rhai language token.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///

View File

@ -106,7 +106,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorTooManyOperations(_) EvalAltResult::ErrorInFunctionCall(_, _, err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(_))
)); ));
Ok(()) Ok(())