From 08f428def210a1c836c4ac16f59d00969bd26b14 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 30 Jan 2023 13:03:47 +0800 Subject: [PATCH 1/6] Fix diagnostic messages. --- src/api/formatting.rs | 6 +++--- src/bin/rhai-run.rs | 1 + src/eval/chaining.rs | 1 + src/types/fn_ptr.rs | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/api/formatting.rs b/src/api/formatting.rs index 9a342d10..07441f49 100644 --- a/src/api/formatting.rs +++ b/src/api/formatting.rs @@ -3,8 +3,8 @@ use crate::packages::iter_basic::{BitRange, CharsStream, StepRange}; use crate::parser::{ParseResult, ParseState}; use crate::types::StringsInterner; use crate::{ - Engine, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, OptimizationLevel, Position, - RhaiError, SmartString, ERR, + Engine, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, Position, RhaiError, + SmartString, ERR, }; use std::any::type_name; #[cfg(feature = "no_std")] @@ -288,7 +288,7 @@ impl Engine { stream.peekable(), &mut state, #[cfg(not(feature = "no_optimize"))] - OptimizationLevel::None, + crate::OptimizationLevel::None, #[cfg(feature = "no_optimize")] (), )?; diff --git a/src/bin/rhai-run.rs b/src/bin/rhai-run.rs index 470bf42c..4d59fc65 100644 --- a/src/bin/rhai-run.rs +++ b/src/bin/rhai-run.rs @@ -51,6 +51,7 @@ fn main() { }; // Initialize scripting engine + #[allow(unused_mut)] let mut engine = Engine::new(); #[cfg(not(feature = "no_optimize"))] diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index b7f12554..6833e923 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -543,6 +543,7 @@ impl Engine { let op_pos = parent.position(); #[cfg(feature = "debugging")] + #[allow(unused_mut)] let mut this_ptr = this_ptr; match ChainType::from(parent) { diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 155eaa27..b73246a7 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -71,6 +71,7 @@ impl FnPtr { /// Create a new function pointer without checking its parameters. #[inline(always)] #[must_use] + #[allow(dead_code)] pub(crate) fn new_unchecked( name: impl Into, curry: StaticVec, From faa48f78c345f31e120bfe9423bb12267767373d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 31 Jan 2023 16:37:50 +0800 Subject: [PATCH 2/6] Refine hashing. --- src/engine.rs | 2 +- src/func/hashing.rs | 52 +++++++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 3caa8183..3e07bda9 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -98,7 +98,7 @@ pub struct Engine { #[cfg(not(feature = "no_module"))] pub(crate) module_resolver: Box, - /// An empty [`ImmutableString`] for cloning purposes. + /// Strings interner. pub(crate) interned_strings: Locked, /// A set of symbols to disable. diff --git a/src/func/hashing.rs b/src/func/hashing.rs index bb5a0c2a..cffe5d9b 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -51,11 +51,7 @@ impl Hasher for StraightHasher { } #[inline(always)] fn write_u64(&mut self, i: u64) { - if i == 0 { - self.0 = ALT_ZERO_HASH; - } else { - self.0 = i; - } + self.0 = i; } } @@ -69,7 +65,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] #[must_use] fn build_hasher(&self) -> Self::Hasher { - StraightHasher(ALT_ZERO_HASH) + StraightHasher(0) } } @@ -99,16 +95,19 @@ pub fn get_hasher() -> ahash::AHasher { /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline] #[must_use] -pub fn calc_var_hash<'a>( - modules: impl IntoIterator>, - var_name: &str, -) -> u64 { +pub fn calc_var_hash<'a>(namespace: impl IntoIterator, var_name: &str) -> u64 { let s = &mut get_hasher(); + let mut count = 0; // We always skip the first module - let iter = modules.into_iter(); - iter.len().hash(s); - iter.skip(1).for_each(|m| m.hash(s)); + namespace.into_iter().for_each(|m| { + // We always skip the first module + if count > 0 { + m.hash(s); + } + count += 1; + }); + count.hash(s); var_name.hash(s); match s.finish() { @@ -135,16 +134,21 @@ pub fn calc_var_hash<'a>( #[inline] #[must_use] pub fn calc_fn_hash<'a>( - namespace: impl IntoIterator>, + namespace: impl IntoIterator, fn_name: &str, num: usize, ) -> u64 { let s = &mut get_hasher(); + let mut count = 0; - // We always skip the first module - let iter = namespace.into_iter(); - iter.len().hash(s); - iter.skip(1).for_each(|m| m.hash(s)); + namespace.into_iter().for_each(|m| { + // We always skip the first module + if count > 0 { + m.hash(s); + } + count += 1; + }); + count.hash(s); fn_name.hash(s); num.hash(s); @@ -163,17 +167,15 @@ pub fn calc_fn_hash<'a>( /// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`. #[inline] #[must_use] -pub fn calc_fn_hash_full( - base: u64, - params: impl IntoIterator>, -) -> u64 { +pub fn calc_fn_hash_full(base: u64, params: impl IntoIterator) -> u64 { let s = &mut get_hasher(); base.hash(s); - let iter = params.into_iter(); - iter.len().hash(s); - iter.for_each(|t| { + let mut count = 0; + params.into_iter().for_each(|t| { t.hash(s); + count += 1; }); + count.hash(s); match s.finish() { 0 => ALT_ZERO_HASH, From ca02d46a497503a77be56690039d37f0d4bb843c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 31 Jan 2023 19:44:46 +0800 Subject: [PATCH 3/6] Use iterators. --- src/optimizer.rs | 64 ++++++++++++++++++++++++++---------------------- src/parser.rs | 8 +++--- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/optimizer.rs b/src/optimizer.rs index ea9c1fd7..67211427 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -229,7 +229,7 @@ fn optimize_stmt_block( }); // Optimize each statement in the block - for stmt in &mut statements { + statements.iter_mut().for_each(|stmt| { match stmt { Stmt::Var(x, options, ..) => { if options.contains(ASTFlags::CONSTANT) { @@ -252,7 +252,7 @@ fn optimize_stmt_block( // Optimize the statement _ => optimize_stmt(stmt, state, preserve_result), } - } + }); // Remove all pure statements except the last one let mut index = 0; @@ -626,11 +626,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b state.set_dirty(); } - for r in &*ranges { + ranges.iter().for_each(|r| { let b = &mut expressions[r.index()]; optimize_expr(&mut b.condition, state, false); optimize_expr(&mut b.expr, state, false); - } + }); return; } } @@ -663,7 +663,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b optimize_expr(match_expr, state, false); // Optimize blocks - for b in expressions.as_mut() { + expressions.iter_mut().for_each(|b| { optimize_expr(&mut b.condition, state, false); optimize_expr(&mut b.expr, state, false); @@ -671,7 +671,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b b.expr = Expr::Unit(b.expr.position()); state.set_dirty(); } - } + }); // Remove false cases cases.retain(|_, list| { @@ -718,12 +718,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b } // Remove unused block statements - for index in 0..expressions.len() { + (0..expressions.len()).into_iter().for_each(|index| { if *def_case == Some(index) || cases.values().flat_map(|c| c.iter()).any(|&n| n == index) || ranges.iter().any(|r| r.index() == index) { - continue; + return; } let b = &mut expressions[index]; @@ -732,7 +732,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b b.expr = Expr::Unit(b.expr.position()); state.set_dirty(); } - } + }); } // while false { block } -> Noop @@ -1159,8 +1159,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); // Move constant arguments - for arg in &mut x.args { - match arg { + x.args.iter_mut().for_each(|arg| match arg { Expr::DynamicConstant(..) | Expr::Unit(..) | Expr::StringConstant(..) | Expr::CharConstant(..) | Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (), @@ -1172,8 +1171,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { state.set_dirty(); *arg = Expr::DynamicConstant(value.into(), arg.start_position()); }, - } - } + }); } // Eagerly call functions @@ -1209,7 +1207,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { } // id(args ..) or xxx.id(args ..) -> optimize function call arguments - Expr::FnCall(x, ..) | Expr::MethodCall(x, ..) => for arg in &mut x.args { + Expr::FnCall(x, ..) | Expr::MethodCall(x, ..) => x.args.iter_mut().for_each(|arg| { optimize_expr(arg, state, false); // Move constant arguments @@ -1226,7 +1224,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { *arg = Expr::DynamicConstant(value.into(), arg.start_position()); }, } - }, + }), // constant-name #[cfg(not(feature = "no_module"))] @@ -1304,20 +1302,25 @@ impl Engine { let mut state = OptimizerState::new(self, lib, optimization_level); // Add constants from global modules - for (name, value) in self.global_modules.iter().rev().flat_map(|m| m.iter_var()) { - state.push_var(name, AccessMode::ReadOnly, Some(value.clone())); - } + self.global_modules + .iter() + .rev() + .flat_map(|m| m.iter_var()) + .for_each(|(name, value)| { + state.push_var(name, AccessMode::ReadOnly, Some(value.clone())) + }); // Add constants and variables from the scope - if let Some(scope) = scope { - for (name, constant, value) in scope.iter() { + scope + .into_iter() + .flat_map(Scope::iter) + .for_each(|(name, constant, value)| { if constant { state.push_var(name, AccessMode::ReadOnly, Some(value)); } else { state.push_var(name, AccessMode::ReadWrite, None); } - } - } + }); optimize_stmt_block(statements, &mut state, true, false, true) } @@ -1339,15 +1342,16 @@ impl Engine { let mut module = crate::Module::new(); if optimization_level == OptimizationLevel::None { - for fn_def in functions { + functions.into_iter().for_each(|fn_def| { module.set_script_fn(fn_def); - } + }); } else { // We only need the script library's signatures for optimization purposes let mut lib2 = crate::Module::new(); - for fn_def in &functions { - lib2.set_script_fn(crate::ast::ScriptFnDef { + functions + .iter() + .map(|fn_def| crate::ast::ScriptFnDef { name: fn_def.name.clone(), access: fn_def.access, body: crate::ast::StmtBlock::NONE, @@ -1355,12 +1359,14 @@ impl Engine { #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] comments: Box::default(), + }) + .for_each(|script_def| { + lib2.set_script_fn(script_def); }); - } let lib2 = &[lib2.into()]; - for fn_def in functions { + functions.into_iter().for_each(|fn_def| { let mut fn_def = crate::func::shared_take_or_clone(fn_def); // Optimize the function body @@ -1369,7 +1375,7 @@ impl Engine { *fn_def.body = self.optimize_top_level(body, scope, lib2, optimization_level); module.set_script_fn(fn_def); - } + }); } module.into() diff --git a/src/parser.rs b/src/parser.rs index 590d97f3..fc235dcc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1244,14 +1244,14 @@ impl Engine { if !has_condition && ranges.is_empty() && r.len() <= SMALL_SWITCH_RANGE { // Unroll small range - for n in r { + r.into_iter().for_each(|n| { let hasher = &mut get_hasher(); Dynamic::from_int(n).hash(hasher); cases .entry(hasher.finish()) .and_modify(|cases| cases.push(index)) .or_insert_with(|| [index].into()); - } + }); } else { // Other range r.set_index(index); @@ -3983,9 +3983,9 @@ impl Engine { { let mut m = crate::Module::new(); - for fn_def in _lib { + _lib.into_iter().for_each(|fn_def| { m.set_script_fn(fn_def); - } + }); return Ok(AST::new(statements, m)); } From 675a45fac7d31a8a788d9d68021cb49c05e1e160 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 3 Feb 2023 15:11:40 +0800 Subject: [PATCH 4/6] Fix bug. --- CHANGELOG.md | 1 + src/func/builtin.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ce0981c0..56dfedc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Bug fixes * Complex indexing/dotting chains now parse correctly, for example: `a[b][c[d]].e` * `map` and `filter` for arrays are marked `pure`. Warnings are added to the documentation of pure array methods that take `this` closures. * Syntax such as `foo.bar::baz` no longer panics, but returns a proper parse error. +* `x += y` where `x` and `y` are `char` now works correctly. Version 1.12.0 diff --git a/src/func/builtin.rs b/src/func/builtin.rs index e23f45ae..592297af 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -10,9 +10,9 @@ use crate::{ Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, NativeCallContext, RhaiResult, SmartString, INT, }; +use std::any::TypeId; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{any::TypeId, fmt::Write}; #[cfg(not(feature = "no_float"))] use crate::FLOAT; @@ -753,7 +753,7 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti let x = &mut *args[0].write_lock::().unwrap(); let mut buf = SmartString::new_const(); - write!(&mut buf, "{y}").unwrap(); + buf.push(x.as_char().unwrap()); buf.push(y); Ok((*x = buf.into()).into()) From 9608e58dede403fe9b31fb21172ad93233e576fa Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 3 Feb 2023 15:58:07 +0800 Subject: [PATCH 5/6] Use remove for flags. --- src/module/mod.rs | 40 +++++++++++++++++++++++++--------------- src/parser.rs | 4 ++-- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/module/mod.rs b/src/module/mod.rs index f0f3716a..e299b2a5 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -462,7 +462,8 @@ impl Module { self.dynamic_functions_filter = None; self.type_iterators = None; self.all_type_iterators = None; - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); } /// Map a custom type to a friendly display name. @@ -746,7 +747,8 @@ impl Module { func: fn_def.into(), }, ); - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); hash_script } @@ -785,7 +787,8 @@ impl Module { self.all_functions = None; self.all_variables = None; self.all_type_iterators = None; - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); self.modules.get_or_insert_with(Default::default) } @@ -851,7 +854,8 @@ impl Module { self.modules .get_or_insert_with(Default::default) .insert(name.into(), sub_module.into()); - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); self } @@ -962,7 +966,8 @@ impl Module { pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { if let Some(f) = self.functions.as_mut().and_then(|m| m.get_mut(&hash_fn)) { f.metadata.namespace = namespace; - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); } self } @@ -1078,7 +1083,8 @@ impl Module { }, ); - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); hash_fn } @@ -1676,7 +1682,8 @@ impl Module { self.all_functions = None; self.all_variables = None; self.all_type_iterators = None; - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); #[cfg(feature = "metadata")] if !other.doc.as_deref().map_or(true, SmartString::is_empty) { @@ -1732,7 +1739,8 @@ impl Module { self.all_functions = None; self.all_variables = None; self.all_type_iterators = None; - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); #[cfg(feature = "metadata")] if !other.doc.as_deref().map_or(true, SmartString::is_empty) { @@ -1797,7 +1805,8 @@ impl Module { self.all_functions = None; self.all_variables = None; self.all_type_iterators = None; - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); #[cfg(feature = "metadata")] if !other.doc.as_deref().map_or(true, SmartString::is_empty) { @@ -1880,7 +1889,8 @@ impl Module { self.all_functions = None; self.all_variables = None; self.all_type_iterators = None; - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); #[cfg(feature = "metadata")] if !other.doc.as_deref().map_or(true, SmartString::is_empty) { @@ -1923,7 +1933,8 @@ impl Module { self.all_functions = None; self.all_variables = None; self.all_type_iterators = None; - self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; + self.flags + .remove(ModuleFlags::INDEXED | ModuleFlags::INDEXED_GLOBAL_FUNCTIONS); self } @@ -2306,7 +2317,7 @@ impl Module { path.push(""); - let r = index_module( + let has_global_functions = index_module( self, &mut path, &mut variables, @@ -2314,9 +2325,8 @@ impl Module { &mut type_iterators, ); - if r { - self.flags |= ModuleFlags::INDEXED_GLOBAL_FUNCTIONS; - } + self.flags + .set(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS, has_global_functions); self.all_variables = if variables.is_empty() { None diff --git a/src/parser.rs b/src/parser.rs index fc235dcc..9051b65d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2769,7 +2769,7 @@ impl Engine { }; if !orig_breakable { - settings.flags &= !ParseSettingFlags::BREAKABLE; + settings.flags.remove(ParseSettingFlags::BREAKABLE); } ensure_not_statement_expr(input, "a boolean")?; @@ -3146,7 +3146,7 @@ impl Engine { } // Parse statements inside the block - settings.flags &= !ParseSettingFlags::GLOBAL_LEVEL; + settings.flags.remove(ParseSettingFlags::GLOBAL_LEVEL); let stmt = self.parse_stmt(input, state, lib, settings)?; From 777f3e219ac219795c38bca3c3144e40e611b269 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 9 Feb 2023 22:09:16 +0800 Subject: [PATCH 6/6] Add max/min. --- CHANGELOG.md | 5 + src/packages/logic.rs | 224 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 222 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56dfedc9..5d7c0965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,11 @@ Bug fixes * Syntax such as `foo.bar::baz` no longer panics, but returns a proper parse error. * `x += y` where `x` and `y` are `char` now works correctly. +Enhancements +------------ + +* The functions `min` and `max` are added for numbers. + Version 1.12.0 ============== diff --git a/src/packages/logic.rs b/src/packages/logic.rs index 19e2fdde..e163eff6 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -21,6 +21,8 @@ macro_rules! gen_cmp_functions { #[rhai_fn(name = ">=")] pub fn gte(x: $arg_type, y: $arg_type) -> bool { x >= y } #[rhai_fn(name = "==")] pub fn eq(x: $arg_type, y: $arg_type) -> bool { x == y } #[rhai_fn(name = "!=")] pub fn ne(x: $arg_type, y: $arg_type) -> bool { x != y } + pub fn max(x: $arg_type, y: $arg_type) -> $arg_type { if x >= y { x } else { y } } + pub fn min(x: $arg_type, y: $arg_type) -> $arg_type { if x <= y { x } else { y } } } })* } }; @@ -53,15 +55,23 @@ def_package! { #[cfg(not(feature = "no_float"))] { - #[cfg(not(feature = "f32_float"))] - reg_functions!(lib += float; f32); - combine_with_exported_module!(lib, "f32", f32_functions); + combine_with_exported_module!(lib, "float", float_functions); + #[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); + { + reg_functions!(lib += float; f64); + combine_with_exported_module!(lib, "f64", f64_functions); + } } + #[cfg(feature = "decimal")] + combine_with_exported_module!(lib, "decimal", decimal_functions); + combine_with_exported_module!(lib, "logic", logic_functions); } } @@ -95,9 +105,90 @@ mod logic_functions { #[cfg(not(feature = "no_float"))] #[allow(clippy::cast_precision_loss)] #[export_module] -mod f32_functions { +mod float_functions { use crate::INT; + #[rhai_fn(name = "max")] + pub fn max_if_32(x: INT, y: f32) -> f32 { + let (x, y) = (x as f32, y as f32); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "max")] + pub fn max_fi_32(x: f32, y: INT) -> f32 { + let (x, y) = (x as f32, y as f32); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_if_32(x: INT, y: f32) -> f32 { + let (x, y) = (x as f32, y as f32); + if x <= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_fi_32(x: f32, y: INT) -> f32 { + let (x, y) = (x as f32, y as f32); + if x <= y { + x + } else { + y + } + } + #[rhai_fn(name = "max")] + pub fn max_if_64(x: INT, y: f64) -> f64 { + let (x, y) = (x as f64, y as f64); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "max")] + pub fn max_fi_64(x: f64, y: INT) -> f64 { + let (x, y) = (x as f64, y as f64); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_if_64(x: INT, y: f64) -> f64 { + let (x, y) = (x as f64, y as f64); + if x <= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_fi_64(x: f64, y: INT) -> f64 { + let (x, y) = (x as f64, y as f64); + if x <= y { + x + } else { + y + } + } +} + +#[cfg(not(feature = "no_float"))] +#[cfg(not(feature = "f32_float"))] +#[allow(clippy::cast_precision_loss)] +#[export_module] +mod f32_functions { + use crate::{FLOAT, INT}; + #[rhai_fn(name = "==")] pub fn eq_if(x: INT, y: f32) -> bool { (x as f32) == (y as f32) @@ -146,13 +237,51 @@ mod f32_functions { pub fn lte_fi(x: f32, y: INT) -> bool { (x as f32) <= (y as f32) } + + #[rhai_fn(name = "max")] + pub fn max_64_32(x: FLOAT, y: f32) -> FLOAT { + let (x, y) = (x as FLOAT, y as FLOAT); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "max")] + pub fn max_32_64(x: f32, y: FLOAT) -> FLOAT { + let (x, y) = (x as FLOAT, y as FLOAT); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_64_32(x: FLOAT, y: f32) -> FLOAT { + let (x, y) = (x as FLOAT, y as FLOAT); + if x <= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_32_64(x: f32, y: FLOAT) -> FLOAT { + let (x, y) = (x as FLOAT, y as FLOAT); + if x <= y { + x + } else { + y + } + } } #[cfg(not(feature = "no_float"))] +#[cfg(feature = "f32_float")] #[allow(clippy::cast_precision_loss)] #[export_module] mod f64_functions { - use crate::INT; + use crate::{FLOAT, INT}; #[rhai_fn(name = "==")] pub fn eq_if(x: INT, y: f64) -> bool { @@ -202,4 +331,85 @@ mod f64_functions { pub fn lte_fi(x: f64, y: INT) -> bool { (x as f64) <= (y as f64) } + + #[rhai_fn(name = "max")] + pub fn max_32_64(x: FLOAT, y: f64) -> FLOAT { + let (x, y) = (x as FLOAT, y as FLOAT); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "max")] + pub fn max_64_32(x: f64, y: FLOAT) -> FLOAT { + let (x, y) = (x as FLOAT, y as FLOAT); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_32_64(x: FLOAT, y: f64) -> FLOAT { + let (x, y) = (x as FLOAT, y as FLOAT); + if x <= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_64_32(x: f64, y: FLOAT) -> FLOAT { + let (x, y) = (x as FLOAT, y as FLOAT); + if x <= y { + x + } else { + y + } + } +} + +#[cfg(feature = "decimal")] +#[export_module] +mod decimal_functions { + use crate::INT; + use rust_decimal::Decimal; + + #[rhai_fn(name = "max")] + pub fn max_id(x: INT, y: Decimal) -> Decimal { + let x = x.into(); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "max")] + pub fn max_di(x: Decimal, y: INT) -> Decimal { + let y = y.into(); + if x >= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_id(x: INT, y: Decimal) -> Decimal { + let x = x.into(); + if x <= y { + x + } else { + y + } + } + #[rhai_fn(name = "min")] + pub fn min_di(x: Decimal, y: INT) -> Decimal { + let y = y.into(); + if x <= y { + x + } else { + y + } + } }