From eac9d3267d72d95ffe3a1d082edcfcdc69b83426 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 15 May 2024 10:01:39 +0800 Subject: [PATCH 1/7] Remove benchmark --- benches/eval_array.rs | 2 +- benches/eval_expression.rs | 6 +++--- benches/eval_type.rs | 24 ++++++++++++------------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/benches/eval_array.rs b/benches/eval_array.rs index 397defc04..7287f2224 100644 --- a/benches/eval_array.rs +++ b/benches/eval_array.rs @@ -67,7 +67,7 @@ fn bench_eval_array_loop(bench: &mut Bencher) { let script = " let list = []; - for i in 0..1_888 { + for i in 0..10_000 { list.push(i); } diff --git a/benches/eval_expression.rs b/benches/eval_expression.rs index 35114effc..dfcdbb06e 100644 --- a/benches/eval_expression.rs +++ b/benches/eval_expression.rs @@ -125,7 +125,7 @@ fn bench_eval_deeply_nested(bench: &mut Bencher) { fn bench_eval_loop_number(bench: &mut Bencher) { let script = " let s = 0; - for x in 0..1_888 { + for x in 0..10000 { s += 1; } "; @@ -142,7 +142,7 @@ fn bench_eval_loop_number(bench: &mut Bencher) { fn bench_eval_loop_strings_build(bench: &mut Bencher) { let script = r#" let s; - for x in 0..1_888 { + for x in 0..10000 { s = "hello, world!" + "hello, world!"; } "#; @@ -159,7 +159,7 @@ fn bench_eval_loop_strings_build(bench: &mut Bencher) { fn bench_eval_loop_strings_no_build(bench: &mut Bencher) { let script = r#" let s; - for x in 0..1_888 { + for x in 0..10000 { s = "hello" + ""; } "#; diff --git a/benches/eval_type.rs b/benches/eval_type.rs index 7cee4394e..467afd553 100644 --- a/benches/eval_type.rs +++ b/benches/eval_type.rs @@ -62,23 +62,23 @@ fn bench_type_method(bench: &mut Bencher) { bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap()); } -#[bench] -fn bench_type_method_with_params(bench: &mut Bencher) { - let script = "foo.update(1)"; +// #[bench] +// fn bench_type_method_with_params(bench: &mut Bencher) { +// let script = "foo.update(1)"; - let mut engine = Engine::new(); - engine.set_optimization_level(OptimizationLevel::None); +// let mut engine = Engine::new(); +// engine.set_optimization_level(OptimizationLevel::None); - engine.register_type_with_name::("Test"); - engine.register_fn("update", Test::update); +// engine.register_type_with_name::("Test"); +// engine.register_fn("update", Test::update); - let ast = engine.compile_expression(script).unwrap(); +// let ast = engine.compile_expression(script).unwrap(); - let mut scope = Scope::new(); - scope.push("foo", Test { x: 42 }); +// let mut scope = Scope::new(); +// scope.push("foo", Test { x: 42 }); - bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap()); -} +// bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap()); +// } #[bench] fn bench_type_method_nested(bench: &mut Bencher) { From 598966a8ab8312e7b2dc26b15a3fd50badb16afc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 16 May 2024 10:38:48 +0800 Subject: [PATCH 2/7] Remove iteration benchmarks --- benches/eval_type.rs | 24 ++++----- benches/iterations.rs | 118 +++++++++++++++++++++--------------------- 2 files changed, 71 insertions(+), 71 deletions(-) diff --git a/benches/eval_type.rs b/benches/eval_type.rs index 467afd553..7cee4394e 100644 --- a/benches/eval_type.rs +++ b/benches/eval_type.rs @@ -62,23 +62,23 @@ fn bench_type_method(bench: &mut Bencher) { bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap()); } -// #[bench] -// fn bench_type_method_with_params(bench: &mut Bencher) { -// let script = "foo.update(1)"; +#[bench] +fn bench_type_method_with_params(bench: &mut Bencher) { + let script = "foo.update(1)"; -// let mut engine = Engine::new(); -// engine.set_optimization_level(OptimizationLevel::None); + let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::None); -// engine.register_type_with_name::("Test"); -// engine.register_fn("update", Test::update); + engine.register_type_with_name::("Test"); + engine.register_fn("update", Test::update); -// let ast = engine.compile_expression(script).unwrap(); + let ast = engine.compile_expression(script).unwrap(); -// let mut scope = Scope::new(); -// scope.push("foo", Test { x: 42 }); + let mut scope = Scope::new(); + scope.push("foo", Test { x: 42 }); -// bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap()); -// } + bench.iter(|| engine.run_ast_with_scope(&mut scope, &ast).unwrap()); +} #[bench] fn bench_type_method_nested(bench: &mut Bencher) { diff --git a/benches/iterations.rs b/benches/iterations.rs index 33ac414f4..2b6f9b5c6 100644 --- a/benches/iterations.rs +++ b/benches/iterations.rs @@ -1,78 +1,78 @@ -#![feature(test)] +// #![feature(test)] -///! Test 1,000 iterations -extern crate test; +// ///! Test 1,000 iterations +// extern crate test; -use rhai::{Engine, OptimizationLevel, INT}; -use test::Bencher; +// use rhai::{Engine, OptimizationLevel, INT}; +// use test::Bencher; -#[bench] -fn bench_iterations_1000(bench: &mut Bencher) { - let script = " - let x = 1_000; - - while x > 0 { - x -= 1; - } - "; +// #[bench] +// fn bench_iterations_1000(bench: &mut Bencher) { +// let script = " +// let x = 1_000; - let mut engine = Engine::new(); - engine.set_optimization_level(OptimizationLevel::None); +// while x > 0 { +// x -= 1; +// } +// "; - let ast = engine.compile(script).unwrap(); +// let mut engine = Engine::new(); +// engine.set_optimization_level(OptimizationLevel::None); - bench.iter(|| engine.run_ast(&ast).unwrap()); -} +// let ast = engine.compile(script).unwrap(); -#[bench] -fn bench_iterations_fibonacci(bench: &mut Bencher) { - let script = " - fn fibonacci(n) { - if n < 2 { - n - } else { - fibonacci(n-1) + fibonacci(n-2) - } - } +// bench.iter(|| engine.run_ast(&ast).unwrap()); +// } - fibonacci(20) - "; +// #[bench] +// fn bench_iterations_fibonacci(bench: &mut Bencher) { +// let script = " +// fn fibonacci(n) { +// if n < 2 { +// n +// } else { +// fibonacci(n-1) + fibonacci(n-2) +// } +// } - let mut engine = Engine::new(); - engine.set_optimization_level(OptimizationLevel::None); +// fibonacci(20) +// "; - let ast = engine.compile(script).unwrap(); +// let mut engine = Engine::new(); +// engine.set_optimization_level(OptimizationLevel::None); - bench.iter(|| engine.eval_ast::(&ast).unwrap()); -} +// let ast = engine.compile(script).unwrap(); -#[bench] -fn bench_iterations_array(bench: &mut Bencher) { - let script = " - let x = []; - x.pad(1000, 0); - for i in 0..1000 { x[i] = i % 256; } - "; +// bench.iter(|| engine.eval_ast::(&ast).unwrap()); +// } - let mut engine = Engine::new(); - engine.set_optimization_level(OptimizationLevel::None); +// #[bench] +// fn bench_iterations_array(bench: &mut Bencher) { +// let script = " +// let x = []; +// x.pad(1000, 0); +// for i in 0..1000 { x[i] = i % 256; } +// "; - let ast = engine.compile(script).unwrap(); +// let mut engine = Engine::new(); +// engine.set_optimization_level(OptimizationLevel::None); - bench.iter(|| engine.run_ast(&ast).unwrap()); -} +// let ast = engine.compile(script).unwrap(); -#[bench] -fn bench_iterations_blob(bench: &mut Bencher) { - let script = " - let x = blob(1000, 0); - for i in 0..1000 { x[i] = i % 256; } - "; +// bench.iter(|| engine.run_ast(&ast).unwrap()); +// } - let mut engine = Engine::new(); - engine.set_optimization_level(OptimizationLevel::None); +// #[bench] +// fn bench_iterations_blob(bench: &mut Bencher) { +// let script = " +// let x = blob(1000, 0); +// for i in 0..1000 { x[i] = i % 256; } +// "; - let ast = engine.compile(script).unwrap(); +// let mut engine = Engine::new(); +// engine.set_optimization_level(OptimizationLevel::None); - bench.iter(|| engine.run_ast(&ast).unwrap()); -} +// let ast = engine.compile(script).unwrap(); + +// bench.iter(|| engine.run_ast(&ast).unwrap()); +// } From 861d2dd60d3d0537184410b6d5c6dca45f226316 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 16 May 2024 10:46:20 +0800 Subject: [PATCH 3/7] Revert benchmark change. --- benches/iterations.rs | 116 +++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/benches/iterations.rs b/benches/iterations.rs index 2b6f9b5c6..197ca4767 100644 --- a/benches/iterations.rs +++ b/benches/iterations.rs @@ -1,78 +1,78 @@ -// #![feature(test)] +#![feature(test)] -// ///! Test 1,000 iterations -// extern crate test; +///! Test 1,000 iterations +extern crate test; -// use rhai::{Engine, OptimizationLevel, INT}; -// use test::Bencher; +use rhai::{Engine, OptimizationLevel, INT}; +use test::Bencher; -// #[bench] -// fn bench_iterations_1000(bench: &mut Bencher) { -// let script = " -// let x = 1_000; +#[bench] +fn bench_iterations_1000(bench: &mut Bencher) { + let script = " + let x = 1_000; -// while x > 0 { -// x -= 1; -// } -// "; + while x > 0 { + x -= 1; + } + "; -// let mut engine = Engine::new(); -// engine.set_optimization_level(OptimizationLevel::None); + let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::None); -// let ast = engine.compile(script).unwrap(); + let ast = engine.compile(script).unwrap(); -// bench.iter(|| engine.run_ast(&ast).unwrap()); -// } + bench.iter(|| engine.run_ast(&ast).unwrap()); +} -// #[bench] -// fn bench_iterations_fibonacci(bench: &mut Bencher) { -// let script = " -// fn fibonacci(n) { -// if n < 2 { -// n -// } else { -// fibonacci(n-1) + fibonacci(n-2) -// } -// } +#[bench] +fn bench_iterations_fibonacci(bench: &mut Bencher) { + let script = " + fn fibonacci(n) { + if n < 2 { + n + } else { + fibonacci(n-1) + fibonacci(n-2) + } + } -// fibonacci(20) -// "; + fibonacci(20) + "; -// let mut engine = Engine::new(); -// engine.set_optimization_level(OptimizationLevel::None); + let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::None); -// let ast = engine.compile(script).unwrap(); + let ast = engine.compile(script).unwrap(); -// bench.iter(|| engine.eval_ast::(&ast).unwrap()); -// } + bench.iter(|| engine.eval_ast::(&ast).unwrap()); +} -// #[bench] -// fn bench_iterations_array(bench: &mut Bencher) { -// let script = " -// let x = []; -// x.pad(1000, 0); -// for i in 0..1000 { x[i] = i % 256; } -// "; +#[bench] +fn bench_iterations_array(bench: &mut Bencher) { + let script = " + let x = []; + x.pad(1000, 0); + for i in 0..1000 { x[i] = i % 256; } + "; -// let mut engine = Engine::new(); -// engine.set_optimization_level(OptimizationLevel::None); + let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::None); -// let ast = engine.compile(script).unwrap(); + let ast = engine.compile(script).unwrap(); -// bench.iter(|| engine.run_ast(&ast).unwrap()); -// } + bench.iter(|| engine.run_ast(&ast).unwrap()); +} -// #[bench] -// fn bench_iterations_blob(bench: &mut Bencher) { -// let script = " -// let x = blob(1000, 0); -// for i in 0..1000 { x[i] = i % 256; } -// "; +#[bench] +fn bench_iterations_blob(bench: &mut Bencher) { + let script = " + let x = blob(1000, 0); + for i in 0..1000 { x[i] = i % 256; } + "; -// let mut engine = Engine::new(); -// engine.set_optimization_level(OptimizationLevel::None); + let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::None); -// let ast = engine.compile(script).unwrap(); + let ast = engine.compile(script).unwrap(); -// bench.iter(|| engine.run_ast(&ast).unwrap()); -// } + bench.iter(|| engine.run_ast(&ast).unwrap()); +} From 5b3445d2ff9ebf073232738ea7e07f1313cab656 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 17 May 2024 11:18:18 +0800 Subject: [PATCH 4/7] Catch data-race. --- CHANGELOG.md | 1 + src/eval/chaining.rs | 12 ++++++------ src/eval/expr.rs | 4 ++-- src/eval/stmt.rs | 7 +++++-- src/eval/target.rs | 23 +++++++++++++++-------- src/types/error.rs | 1 + tests/arrays.rs | 4 ++-- tests/maps.rs | 3 ++- 8 files changed, 34 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f328f411..59de695b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Bug fixes * `NativeCallContext<'_>` (with a lifetime parameter) now parses correctly in the `#[export_module]` macro. This is to allow for `rust_2018_idioms` lints (thanks [`@ltabis`](https://github.com/ltabis) [864](https://github.com/rhaiscript/rhai/issues/864)). * The `sync` feature now works properly in `no-std` builds (thanks [`@misssonder`](https://github.com/misssonder) [874](https://github.com/rhaiscript/rhai/pull/874)). +* More data-race conditions are caught and returned as errors instead of panicking. New features ------------ diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 0053d07fa..d58dded33 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -9,9 +9,9 @@ use crate::{ calc_fn_hash, Dynamic, Engine, ExclusiveRange, FnArgsVec, InclusiveRange, OnceCell, Position, RhaiResult, RhaiResultOf, Scope, ERR, }; -use std::hash::Hash; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{convert::TryInto, hash::Hash}; /// Function call hashes to index getters and setters. static INDEXER_HASHES: OnceCell<(u64, u64)> = OnceCell::new(); @@ -146,7 +146,7 @@ impl Engine { } }; - Ok(arr.get_mut(arr_idx).map(Target::from).unwrap()) + arr.get_mut(arr_idx).unwrap().try_into() } #[cfg(not(feature = "no_index"))] @@ -192,7 +192,7 @@ impl Engine { } if let Some(value) = map.get_mut(index.as_str()) { - Ok(Target::from(value)) + value.try_into() } else if self.fail_on_invalid_map_property() { Err(ERR::ErrorPropertyNotFound(index.to_string(), idx_pos).into()) } else { @@ -508,7 +508,7 @@ impl Engine { this_ptr.map_or_else( || Err(ERR::ErrorUnboundThis(*var_pos).into()), |this_ptr| { - let target = &mut this_ptr.into(); + let target = &mut this_ptr.try_into()?; let scope = Some(scope); self.eval_dot_index_chain_raw( global, caches, scope, None, lhs, expr, target, rhs, idx_values, @@ -969,7 +969,7 @@ impl Engine { })?; { - let orig_val = &mut (&mut orig_val).into(); + let orig_val = &mut (&mut orig_val).try_into()?; self.eval_op_assignment( global, caches, op_info, root, orig_val, new_val, @@ -1139,7 +1139,7 @@ impl Engine { _ => Err(err), })?; - let val = &mut (&mut val).into(); + let val = &mut (&mut val).try_into()?; let (result, may_be_changed) = self.eval_dot_index_chain_raw( global, caches, s, _this_ptr, root, rhs, val, &x.rhs, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index d6291b39f..bd904f823 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -7,7 +7,7 @@ use crate::types::dynamic::AccessMode; use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, SmartString, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{fmt::Write, num::NonZeroUsize}; +use std::{convert::TryInto, fmt::Write, num::NonZeroUsize}; impl Engine { /// Search for a module within an imports stack. @@ -142,7 +142,7 @@ impl Engine { let val = scope.get_mut_by_index(index); - Ok(val.into()) + val.try_into() } /// Search for a variable within the scope or within imports, /// depending on whether the variable name is namespace-qualified. diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index c7886cc00..247c66f75 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -8,9 +8,12 @@ use crate::func::{get_builtin_op_assignment_fn, get_hasher}; use crate::tokenizer::Token; use crate::types::dynamic::{AccessMode, Union}; use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, VarDefInfo, ERR, INT}; -use std::hash::{Hash, Hasher}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{ + convert::TryInto, + hash::{Hash, Hasher}, +}; impl Engine { /// If the value is a string, intern it. @@ -310,7 +313,7 @@ impl Engine { self.track_operation(global, lhs.position())?; - let target = &mut this_ptr.unwrap().into(); + let target = &mut this_ptr.unwrap().try_into()?; self.eval_op_assignment(global, caches, op_info, lhs, target, rhs_val)?; } diff --git a/src/eval/target.rs b/src/eval/target.rs index 298bc733e..ac3e49bf1 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -1,9 +1,12 @@ //! Type to hold a mutable reference to the target of an evaluation. -use crate::{Dynamic, Position, RhaiResultOf}; -use std::borrow::{Borrow, BorrowMut}; +use crate::{Dynamic, EvalAltResult, Position, RhaiError, RhaiResultOf}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{ + borrow::{Borrow, BorrowMut}, + convert::TryFrom, +}; /// Calculate an offset+len pair given an actual length of the underlying array. /// @@ -413,21 +416,25 @@ impl<'a> Target<'a> { } } -impl<'a> From<&'a mut Dynamic> for Target<'a> { +impl<'a> TryFrom<&'a mut Dynamic> for Target<'a> { + type Error = RhaiError; + #[inline] - fn from(value: &'a mut Dynamic) -> Self { + fn try_from(value: &'a mut Dynamic) -> Result { #[cfg(not(feature = "no_closure"))] if value.is_shared() { // Cloning is cheap for a shared value let shared_value = value.clone(); - let guard = value.write_lock::().unwrap(); - return Self::SharedValue { + let Some(guard) = value.write_lock::() else { + return Err(EvalAltResult::ErrorDataRace(String::new(), Position::NONE).into()); + }; + return Ok(Self::SharedValue { guard, shared_value, - }; + }); } - Self::RefMut(value) + Ok(Self::RefMut(value)) } } diff --git a/src/types/error.rs b/src/types/error.rs index 99ae18721..9ae277185 100644 --- a/src/types/error.rs +++ b/src/types/error.rs @@ -162,6 +162,7 @@ impl fmt::Display for EvalAltResult { Self::ErrorIndexNotFound(s, ..) => write!(f, "Invalid index: {s}")?, Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {s}")?, Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {s}")?, + Self::ErrorDataRace(s, ..) if s.is_empty() => write!(f, "Data race detected")?, Self::ErrorDataRace(s, ..) => write!(f, "Data race detected on variable '{s}'")?, Self::ErrorDotExpr(s, ..) if s.is_empty() => f.write_str("Malformed dot expression")?, diff --git a/tests/arrays.rs b/tests/arrays.rs index 7b58baab6..35e15d250 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -1,6 +1,6 @@ #![cfg(not(feature = "no_index"))] use rhai::{Array, Dynamic, Engine, EvalAltResult, ParseErrorType, Position, INT}; -use std::iter::FromIterator; +use std::{convert::TryInto, iter::FromIterator}; #[test] fn test_arrays() { @@ -509,7 +509,7 @@ fn test_array_invalid_index_callback() { engine.on_invalid_array_index(|arr, index, _| match index { -100 => { arr.push((42 as INT).into()); - Ok(arr.last_mut().unwrap().into()) + arr.last_mut().unwrap().try_into() } 100 => Ok(Dynamic::from(100 as INT).into()), _ => Err(EvalAltResult::ErrorArrayBounds(arr.len(), index, Position::NONE).into()), diff --git a/tests/maps.rs b/tests/maps.rs index e2391841b..fed8b844c 100644 --- a/tests/maps.rs +++ b/tests/maps.rs @@ -1,5 +1,6 @@ #![cfg(not(feature = "no_object"))] use rhai::{Dynamic, Engine, EvalAltResult, Map, ParseErrorType, Position, Scope, INT}; +use std::convert::TryInto; #[test] fn test_map_indexing() { @@ -264,7 +265,7 @@ fn test_map_missing_property_callback() { engine.on_map_missing_property(|map, prop, _| match prop { "x" => { map.insert("y".into(), (42 as INT).into()); - Ok(map.get_mut("y").unwrap().into()) + map.get_mut("y").unwrap().try_into() } "z" => Ok(Dynamic::from(100 as INT).into()), _ => Err(EvalAltResult::ErrorPropertyNotFound(prop.to_string(), Position::NONE).into()), From 06c2dfab2832ecf1eef2761b7cd91b46d6354db1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 17 May 2024 11:42:25 +0800 Subject: [PATCH 5/7] Fix doc-tests. --- src/api/events.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/api/events.rs b/src/api/events.rs index 3f426ffe0..b47eae074 100644 --- a/src/api/events.rs +++ b/src/api/events.rs @@ -356,6 +356,7 @@ impl Engine { /// ``` /// # fn main() -> Result<(), Box> { /// # use rhai::{Engine, Dynamic, EvalAltResult, Position}; + /// # use std::convert::TryInto; /// let mut engine = Engine::new(); /// /// engine.on_invalid_array_index(|arr, index, _| match index @@ -365,7 +366,7 @@ impl Engine { /// arr.push((42_i64).into()); /// // Return a mutable reference to an element /// let value_ref = arr.last_mut().unwrap(); - /// Ok(value_ref.into()) + /// value_ref.try_into() /// } /// 100 => { /// let value = Dynamic::from(100_i64); @@ -433,6 +434,7 @@ impl Engine { /// ``` /// # fn main() -> Result<(), Box> { /// # use rhai::{Engine, Dynamic, EvalAltResult, Position}; + /// # use std::convert::TryInto; /// let mut engine = Engine::new(); /// /// engine.on_map_missing_property(|map, prop, _| match prop @@ -442,7 +444,7 @@ impl Engine { /// map.insert("y".into(), (42_i64).into()); /// // Return a mutable reference to an element /// let value_ref = map.get_mut("y").unwrap(); - /// Ok(value_ref.into()) + /// value_ref.try_into() /// } /// "z" => { /// // Return a temporary value (not a reference) From c014343ff19ce4ba4422f9228dbd5d3ced4cbf9a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 21 May 2024 23:14:29 +0800 Subject: [PATCH 6/7] Add missing min/max functions. --- CHANGELOG.md | 1 + src/packages/logic.rs | 48 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59de695b8..374f4f5c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Bug fixes macro. This is to allow for `rust_2018_idioms` lints (thanks [`@ltabis`](https://github.com/ltabis) [864](https://github.com/rhaiscript/rhai/issues/864)). * The `sync` feature now works properly in `no-std` builds (thanks [`@misssonder`](https://github.com/misssonder) [874](https://github.com/rhaiscript/rhai/pull/874)). * More data-race conditions are caught and returned as errors instead of panicking. +* Missing `min` and `max` functions where both operands are floats or `Decimal` are added. New features ------------ diff --git a/src/packages/logic.rs b/src/packages/logic.rs index 946e4e1aa..1fa6a911e 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -115,6 +115,14 @@ mod min_max_functions { mod float_functions { use crate::INT; + #[rhai_fn(name = "max")] + pub fn max_ff_32(x: f32, y: f32) -> f32 { + if x >= y { + x + } else { + y + } + } #[rhai_fn(name = "max")] pub fn max_if_32(x: INT, y: f32) -> f32 { let (x, y) = (x as f32, y); @@ -134,6 +142,14 @@ mod float_functions { } } #[rhai_fn(name = "min")] + pub fn min_ff_32(x: f32, y: f32) -> 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); if x <= y { @@ -152,6 +168,14 @@ mod float_functions { } } #[rhai_fn(name = "max")] + pub fn max_ff_64(x: f64, y: f64) -> f64 { + 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); if x >= y { @@ -170,6 +194,14 @@ mod float_functions { } } #[rhai_fn(name = "min")] + pub fn min_ff_64(x: f64, y: f64) -> 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); if x <= y { @@ -419,6 +451,14 @@ mod decimal_functions { use crate::INT; use rust_decimal::Decimal; + #[rhai_fn(name = "max")] + pub fn max_dd(x: Decimal, y: Decimal) -> Decimal { + if x >= y { + x + } else { + y + } + } #[rhai_fn(name = "max")] pub fn max_id(x: INT, y: Decimal) -> Decimal { let x = x.into(); @@ -438,6 +478,14 @@ mod decimal_functions { } } #[rhai_fn(name = "min")] + pub fn min_dd(x: Decimal, y: Decimal) -> Decimal { + 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 { From a191b6f6391fa7762314f927963798dd18f68d03 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 22 May 2024 00:03:17 +0800 Subject: [PATCH 7/7] Add ?? break/continue/return/throw. --- CHANGELOG.md | 1 + src/parser.rs | 33 ++++++++++++++++++++++----------- tests/looping.rs | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 374f4f5c9..ccff89562 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ Bug fixes New features ------------ +* The `break`, `continue`, `return` and `throw` statements can now follow the `??` operator to short-circuit operations where the value is `()`. * A new symbol, `$func$`, is added to custom syntax to allow parsing of anonymous functions. diff --git a/src/parser.rs b/src/parser.rs index 048d44aa6..462b90057 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2265,20 +2265,31 @@ impl Engine { // Parse the RHS let rhs = match op_token { + Token::DoubleQuestion + if matches!( + state.input.peek().unwrap().0, + Token::Break | Token::Continue | Token::Return | Token::Throw + ) => + { + let stmt = self.parse_stmt(state, settings)?; + let block: StmtBlock = stmt.into(); + Expr::Stmt(block.into()) + } // [xxx..] | (xxx..) | {xxx..} | xxx.., | xxx..; | xxx.. => // [xxx..=] | (xxx..=) | {xxx..=} | xxx..=, | xxx..=; | xxx..= => - Token::ExclusiveRange | Token::InclusiveRange => { - let (next_op, next_pos) = state.input.peek().unwrap(); - - match next_op { + Token::ExclusiveRange | Token::InclusiveRange + if matches!( + state.input.peek().unwrap().0, Token::RightBracket - | Token::RightParen - | Token::RightBrace - | Token::Comma - | Token::SemiColon - | Token::DoubleArrow => Expr::Unit(*next_pos), - _ => self.parse_unary(state, settings)?, - } + | Token::RightParen + | Token::RightBrace + | Token::Comma + | Token::SemiColon + | Token::DoubleArrow + ) => + { + let (_, next_pos) = state.input.peek().unwrap(); + Expr::Unit(*next_pos) } _ => self.parse_unary(state, settings)?, }; diff --git a/tests/looping.rs b/tests/looping.rs index dc142ee8d..b891d67ba 100644 --- a/tests/looping.rs +++ b/tests/looping.rs @@ -28,6 +28,20 @@ fn test_loop() { 21 ); + assert_eq!( + engine + .eval::( + " + for n in 0..10 { + let x = if n <= 5 { n }; + x ?? break 42; + } + " + ) + .unwrap(), + 42 + ); + assert_eq!(*engine.compile("let x = 0; break;").unwrap_err().err_type(), ParseErrorType::LoopBreak); #[cfg(not(feature = "no_function"))]