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]
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"

View File

@ -1,9 +1,37 @@
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
===============
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
---------
@ -33,7 +61,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

View File

@ -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<syn::Ident> = 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<syn::Stmt> = Vec::new();
let mut unpack_statements: Vec<syn::Stmt> = Vec::new();
let mut unpack_exprs: Vec<syn::Expr> = Vec::new();
let mut input_type_names: Vec<String> = Vec::new();
let mut input_type_exprs: Vec<syn::Expr> = 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::<syn::Stmt>(quote! {
let #var = #downcast_span;
})
@ -811,7 +811,7 @@ impl ExportedFn {
}
};
unpack_stmts.push(
unpack_statements.push(
syn::parse2::<syn::Stmt>(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
}

View File

@ -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)
{
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_modpath);
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)
{
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_modpath);
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)
{
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_modpath);
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()),

View File

@ -101,7 +101,7 @@ pub(crate) struct Module {
mod_all: syn::ItemMod,
fns: Vec<ExportedFn>,
consts: Vec<ExportedConst>,
submodules: Vec<Module>,
sub_modules: Vec<Module>,
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::<ExportedFn>(itemfn.to_token_stream())
syn::parse2::<ExportedFn>(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") {
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::<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)?;
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,
&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
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>, _,
Result<Vec<proc_macro2::TokenStream>, 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<syn::Item>> {

View File

@ -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))
}

View File

@ -14,11 +14,11 @@ pub(crate) type ExportedConst = (String, Box<syn::Type>, 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<syn::Stmt> = Vec::new();
let mut set_const_stmts: Vec<syn::Stmt> = Vec::new();
let mut set_fn_statements: 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 set_flattened_mod_blocks: Vec<syn::ExprBlock> = Vec::new();
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 {
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::<syn::Stmt>(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<String> = 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<syn::Expr> = 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::<syn::Stmt>(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::<syn::ItemMod>(quote! {
let mut generate_fn_call = syn::parse2::<syn::ItemMod>(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<ExportedFn>) -> 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::<String, proc_macro2::Span>::new();
let mut fn_defs = HashMap::<String, proc_macro2::Span>::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<ExportedFn>) -> 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<ExportedFn>) -> 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(),

View File

@ -106,10 +106,10 @@ mod module_tests {
let item_mod = syn::parse2::<Module>(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::<syn::Expr>(quote! { 42 }).unwrap()
);
}
@ -130,11 +130,11 @@ mod module_tests {
let item_mod = syn::parse2::<Module>(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::<Module>(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 {

View File

@ -29,7 +29,7 @@ fn one_fn_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
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<EvalAltResult>> {
fn one_fn_sub_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
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!(

View File

@ -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 {

View File

@ -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;
@ -798,6 +801,7 @@ impl Engine {
self.eval_global_statements(scope, mods, &mut new_state, ast.statements(), lib, level);
state.operations = new_state.operations;
result
}
@ -1067,17 +1071,27 @@ impl Engine {
if name == KEYWORD_EVAL && args_expr.len() == 1 {
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) {
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::<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(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 +1099,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,
))
});
}
}
@ -1325,33 +1350,115 @@ pub fn run_builtin_binary_op(
x: &Dynamic,
y: &Dynamic,
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
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::<char>() && second_type == TypeId::of::<ImmutableString>() {
let type_id = (first_type, second_type);
#[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 y = &*y.read_lock::<ImmutableString>().unwrap();
match op {
"+" => 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 y = y.clone().cast::<char>();
match op {
"+" => return Ok(Some((x + y).into())),
_ => (),
_ => 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 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(None),
}
} else if args_type == TypeId::of::<bool>() {
}
if first_type == TypeId::of::<bool>() {
let x = x.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(None),
}
} else if args_type == TypeId::of::<ImmutableString>() {
}
if first_type == TypeId::of::<ImmutableString>() {
let x = &*x.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(None),
}
} else if args_type == TypeId::of::<char>() {
}
if first_type == TypeId::of::<char>() {
let x = x.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(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::<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())),
_ => (),
_ => return Ok(None),
}
}
@ -1512,24 +1567,80 @@ pub fn run_builtin_op_assignment(
x: &mut Dynamic,
y: &Dynamic,
) -> Result<Option<()>, Box<EvalAltResult>> {
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::<ImmutableString>() && second_type == TypeId::of::<char>() {
let type_id = (first_type, second_type);
#[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 mut x = x.write_lock::<ImmutableString>().unwrap();
match op {
"+=" => return Ok(Some(*x += y)),
_ => (),
_ => 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 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(None),
}
} else if args_type == TypeId::of::<bool>() {
}
if first_type == TypeId::of::<bool>() {
let y = y.clone().cast::<bool>();
let mut x = x.write_lock::<bool>().unwrap();
match op {
"&=" => 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 mut x = x.write_lock::<Dynamic>().unwrap();
match op {
"+=" => 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 mut x = x.write_lock::<ImmutableString>().unwrap();
match op {
"+=" => return Ok(Some(*x += y)),
_ => (),
}
}
#[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)),
_ => (),
}
_ => return Ok(None),
}
}

View File

@ -55,17 +55,17 @@ pub type Locked<T> = crate::stdlib::sync::RwLock<T>;
/// 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,

View File

@ -254,7 +254,6 @@ 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 {
@ -296,6 +295,7 @@ mod f32_functions {
pub fn modulo_fi(x: f32, y: INT) -> f32 {
(x as f32) % (y as f32)
}
}
#[rhai_fn(name = "-")]
pub fn neg(x: f32) -> f32 {
@ -359,7 +359,6 @@ 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 {
@ -401,6 +400,7 @@ mod f64_functions {
pub fn modulo_fi(x: f64, y: INT) -> f64 {
(x as f64) % (y as f64)
}
}
#[rhai_fn(name = "-")]
pub fn neg(x: f64) -> f64 {
@ -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<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
x.checked_add(y)
.ok_or_else(|| make_err(format!("Addition overflow: {} + {}", x, y)))
.map(Dynamic::from)
.map(Into::<Dynamic>::into)
} else {
Ok(Dynamic::from(x + y))
}
}
#[rhai_fn(name = "+", 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)]
#[rhai_fn(skip, return_raw)]
pub fn subtract(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
x.checked_sub(y)
.ok_or_else(|| make_err(format!("Subtraction overflow: {} - {}", x, y)))
.map(Dynamic::from)
.map(Into::<Dynamic>::into)
} else {
Ok(Dynamic::from(x - y))
}
}
#[rhai_fn(name = "-", 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)]
#[rhai_fn(skip, return_raw)]
pub fn multiply(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
x.checked_mul(y)
.ok_or_else(|| make_err(format!("Multiplication overflow: {} * {}", x, y)))
.map(Dynamic::from)
.map(Into::<Dynamic>::into)
} else {
Ok(Dynamic::from(x * y))
}
}
#[rhai_fn(name = "*", 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)]
#[rhai_fn(skip, return_raw)]
pub fn divide(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
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::<Dynamic>::into)
}
} else {
Ok(Dynamic::from(x / y))
}
}
#[rhai_fn(name = "/", 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)]
#[rhai_fn(skip, return_raw)]
pub fn modulo(x: Decimal, y: Decimal) -> Result<Dynamic, Box<EvalAltResult>> {
if cfg!(not(feature = "unchecked")) {
x.checked_rem(y)
@ -527,19 +495,11 @@ mod decimal_functions {
x, y
))
})
.map(Dynamic::from)
.map(Into::<Dynamic>::into)
} else {
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 = "-")]
pub fn neg(x: Decimal) -> Decimal {
-x

View File

@ -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);
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)
}
}

View File

@ -3167,48 +3167,20 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
#[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,
}

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> {
/// Create a new [`Scope`].
///

View File

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

View File

@ -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.
///

View File

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