Skip to content

Commit

Permalink
Merge pull request #878 from schungx/master
Browse files Browse the repository at this point in the history
Add ?? break/continue/return/throw.
  • Loading branch information
schungx authored May 22, 2024
2 parents e9cac35 + a191b6f commit 7c85d3c
Show file tree
Hide file tree
Showing 15 changed files with 129 additions and 39 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ 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.
* Missing `min` and `max` functions where both operands are floats or `Decimal` are added.

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.


Expand Down
2 changes: 1 addition & 1 deletion benches/eval_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions benches/eval_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
";
Expand All @@ -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!";
}
"#;
Expand All @@ -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" + "";
}
"#;
Expand Down
2 changes: 1 addition & 1 deletion benches/iterations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use test::Bencher;
fn bench_iterations_1000(bench: &mut Bencher) {
let script = "
let x = 1_000;
while x > 0 {
x -= 1;
}
Expand Down
6 changes: 4 additions & 2 deletions src/api/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ impl Engine {
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # use rhai::{Engine, Dynamic, EvalAltResult, Position};
/// # use std::convert::TryInto;
/// let mut engine = Engine::new();
///
/// engine.on_invalid_array_index(|arr, index, _| match index
Expand All @@ -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);
Expand Down Expand Up @@ -433,6 +434,7 @@ impl Engine {
/// ```
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// # use rhai::{Engine, Dynamic, EvalAltResult, Position};
/// # use std::convert::TryInto;
/// let mut engine = Engine::new();
///
/// engine.on_map_missing_property(|map, prop, _| match prop
Expand All @@ -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)
Expand Down
12 changes: 6 additions & 6 deletions src/eval/chaining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ use crate::{
calc_fn_hash, Dynamic, Engine, ExclusiveRange, FnArgsVec, InclusiveRange, OnceCell, Position,

Check warning on line 9 in src/eval/chaining.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_index,serde,metadata,internals,debugging, sta...

unused imports: `ExclusiveRange`, `InclusiveRange`
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();
Expand Down Expand Up @@ -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"))]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/eval/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 5 additions & 2 deletions src/eval/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Check warning on line 14 in src/eval/stmt.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_function,serde,metadata,internals,debugging, ...

unused import: `convert::TryInto`

Check warning on line 14 in src/eval/stmt.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_time,no_function,no_float,no_position,no_inde...

unused import: `convert::TryInto`

Check warning on line 14 in src/eval/stmt.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,sync,no_time,no_function,no_float,no_position,no...

unused import: `convert::TryInto`
hash::{Hash, Hasher},
};

impl Engine {
/// If the value is a string, intern it.
Expand Down Expand Up @@ -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)?;
}
Expand Down
23 changes: 15 additions & 8 deletions src/eval/target.rs
Original file line number Diff line number Diff line change
@@ -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};

Check warning on line 3 in src/eval/target.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_function,serde,metadata,internals,debugging, ...

unused import: `EvalAltResult`

Check warning on line 3 in src/eval/target.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_time,no_function,no_float,no_position,no_inde...

unused import: `EvalAltResult`

Check warning on line 3 in src/eval/target.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_closure,serde,metadata,internals,debugging, s...

unused import: `EvalAltResult`

Check warning on line 3 in src/eval/target.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,sync,no_time,no_function,no_float,no_position,no...

unused import: `EvalAltResult`
#[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.
///
Expand Down Expand Up @@ -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<Self, Self::Error> {
#[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::<Dynamic>().unwrap();
return Self::SharedValue {
let Some(guard) = value.write_lock::<Dynamic>() else {
return Err(EvalAltResult::ErrorDataRace(String::new(), Position::NONE).into());
};
return Ok(Self::SharedValue {
guard,
shared_value,
};
});
}

Self::RefMut(value)
Ok(Self::RefMut(value))
}
}

Expand Down
48 changes: 48 additions & 0 deletions src/packages/logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -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();
Expand All @@ -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 {
Expand Down
33 changes: 22 additions & 11 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?,
};
Expand Down
1 change: 1 addition & 0 deletions src/types/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")?,
Expand Down
4 changes: 2 additions & 2 deletions tests/arrays.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![cfg(not(feature = "no_index"))]
use rhai::{Array, Dynamic, Engine, EvalAltResult, ParseErrorType, Position, INT};

Check warning on line 2 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (nightly, ubuntu-latest, true, --features unstable)

unused imports: `EvalAltResult` and `Position`

Check warning on line 2 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,unicode-xid-ident, stable, false)

unused imports: `EvalAltResult`, `Position`

Check warning on line 2 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,metadata, stable, false)

unused imports: `EvalAltResult`, `Position`

Check warning on line 2 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,decimal, stable, false)

unused imports: `EvalAltResult`, `Position`
use std::iter::FromIterator;
use std::{convert::TryInto, iter::FromIterator};

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (stable, macos-latest, false)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (nightly, ubuntu-latest, true, --features unstable)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (beta, ubuntu-latest, false, --features unstable)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (stable, windows-latest, false)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_float,decimal, stable, false)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,serde, stable, false)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,unicode-xid-ident, stable, false)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,metadata, stable, false)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,decimal, stable, false)

unused import: `convert::TryInto`

Check warning on line 3 in tests/arrays.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, stable, false)

unused import: `convert::TryInto`

#[test]
fn test_arrays() {
Expand Down Expand Up @@ -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()),
Expand Down
Loading

0 comments on commit 7c85d3c

Please sign in to comment.