diff --git a/README.md b/README.md index 02b3e27b..b453b04c 100644 --- a/README.md +++ b/README.md @@ -374,19 +374,19 @@ engine.load_package(package.get()); // load the package manually The follow packages are available: -| Package | Description | In `CorePackage` | In `StandardPackage` | -| ------------------------ | ----------------------------------------------- | :--------------: | :------------------: | -| `ArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) | Yes | Yes | -| `BasicIteratorPackage` | Numeric ranges (e.g. `range(1, 10)`) | Yes | Yes | -| `LogicPackage` | Logic and comparison operators (e.g. `==`, `>`) | Yes | Yes | -| `BasicStringPackage` | Basic string functions | Yes | Yes | -| `BasicTimePackage` | Basic time functions (e.g. [timestamps]) | Yes | Yes | -| `MoreStringPackage` | Additional string functions | No | Yes | -| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes | -| `BasicArrayPackage` | Basic [array] functions | No | Yes | -| `BasicMapPackage` | Basic [object map] functions | No | Yes | -| `CorePackage` | Basic essentials | | | -| `StandardPackage` | Standard library | | | +| Package | Description | In `CorePackage` | In `StandardPackage` | +| ---------------------- | ----------------------------------------------- | :--------------: | :------------------: | +| `ArithmeticPackage` | Arithmetic operators (e.g. `+`, `-`, `*`, `/`) | Yes | Yes | +| `BasicIteratorPackage` | Numeric ranges (e.g. `range(1, 10)`) | Yes | Yes | +| `LogicPackage` | Logic and comparison operators (e.g. `==`, `>`) | Yes | Yes | +| `BasicStringPackage` | Basic string functions | Yes | Yes | +| `BasicTimePackage` | Basic time functions (e.g. [timestamps]) | Yes | Yes | +| `MoreStringPackage` | Additional string functions | No | Yes | +| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes | +| `BasicArrayPackage` | Basic [array] functions | No | Yes | +| `BasicMapPackage` | Basic [object map] functions | No | Yes | +| `CorePackage` | Basic essentials | | | +| `StandardPackage` | Standard library | | | Evaluate expressions only ------------------------- @@ -427,7 +427,7 @@ The following primitive types are supported natively: | **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` | | **Unicode character** | `char` | `"char"` | `"A"`, `"x"` etc. | | **Unicode string** | `String` (_not_ `&str`) | `"string"` | `"hello"` etc. | -| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ? ? ? ]"` | +| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` | | **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` | | **Timestamp** (implemented in the [`BasicTimePackage`](#packages)) | `std::time::Instant` | `"timestamp"` | _not supported_ | | **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ | @@ -1372,7 +1372,8 @@ y[2] == 3; y[3] == 4; (1 in y) == true; // use 'in' to test if an item exists in the array -(42 in y) == false; +(42 in y) == false; // 'in' uses the '==' operator (which users can override) + // to check if the target item exists in the array y[1] = 42; // array elements can be reassigned @@ -1494,7 +1495,7 @@ y.a == 42; y["baz!$@"] == 123.456; // access via index notation -"baz!$@" in y == true; // use 'in' to test if a property exists in the object map, prints true +"baz!$@" in y == true; // use 'in' to test if a property exists in the object map ("z" in y) == false; ts.obj = y; // object maps can be assigned completely (by value copy) diff --git a/src/any.rs b/src/any.rs index d9aad2a6..a2c8b59f 100644 --- a/src/any.rs +++ b/src/any.rs @@ -396,7 +396,24 @@ impl Dynamic { /// assert_eq!(x.cast::(), 42); /// ``` pub fn cast(self) -> T { - self.try_cast::().unwrap() + //self.try_cast::().unwrap() + + if TypeId::of::() == TypeId::of::() { + return cast_box::<_, T>(Box::new(self)).unwrap(); + } + + match self.0 { + Union::Unit(ref value) => (value as &dyn Variant).downcast_ref::().unwrap().clone(), + Union::Bool(ref value) => (value as &dyn Variant).downcast_ref::().unwrap().clone(), + Union::Str(value) => cast_box::<_, T>(value).unwrap(), + Union::Char(ref value) => (value as &dyn Variant).downcast_ref::().unwrap().clone(), + Union::Int(ref value) => (value as &dyn Variant).downcast_ref::().unwrap().clone(), + #[cfg(not(feature = "no_float"))] + Union::Float(ref value) => (value as &dyn Variant).downcast_ref::().unwrap().clone(), + Union::Array(value) => cast_box::<_, T>(value).unwrap(), + Union::Map(value) => cast_box::<_, T>(value).unwrap(), + Union::Variant(value) => value.as_ref().as_ref().downcast_ref::().unwrap().clone(), + } } /// Get a reference of a specific type to the `Dynamic`. diff --git a/src/engine.rs b/src/engine.rs index c24b924c..e4cdfa7c 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -692,7 +692,7 @@ impl Engine { &self, fn_lib: &FunctionsLib, fn_name: &str, - args: &mut [&mut Dynamic], + args: &mut FnCallArgs, def_val: Option<&Dynamic>, pos: Position, level: usize, @@ -1227,7 +1227,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] Expr::Array(contents, _) => Ok(Dynamic(Union::Array(Box::new( contents - .into_iter() + .iter() .map(|item| self.eval_expr(scope, state, fn_lib, item, level)) .collect::, _>>()?, )))), @@ -1235,9 +1235,9 @@ impl Engine { #[cfg(not(feature = "no_object"))] Expr::Map(contents, _) => Ok(Dynamic(Union::Map(Box::new( contents - .into_iter() + .iter() .map(|(key, expr, _)| { - self.eval_expr(scope, state, fn_lib, &expr, level) + self.eval_expr(scope, state, fn_lib, expr, level) .map(|val| (key.clone(), val)) }) .collect::, _>>()?, diff --git a/src/fn_call.rs b/src/fn_call.rs index a28522de..c64ba035 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -22,7 +22,7 @@ macro_rules! impl_args { fn into_vec(self) -> Vec { let ($($p,)*) = self; - #[allow(unused_variables, unused_mut)] + #[allow(unused_mut)] let mut v = Vec::new(); $(v.push($p.into_dynamic());)* @@ -42,5 +42,4 @@ macro_rules! impl_args { }; } -#[rustfmt::skip] impl_args!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V); diff --git a/src/fn_func.rs b/src/fn_func.rs index b9add7e4..af538306 100644 --- a/src/fn_func.rs +++ b/src/fn_func.rs @@ -116,5 +116,4 @@ macro_rules! def_anonymous_fn { }; } -#[rustfmt::skip] def_anonymous_fn!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V); diff --git a/src/fn_register.rs b/src/fn_register.rs index e711ee54..9368d6a3 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -7,7 +7,7 @@ use crate::engine::{calc_fn_spec, Engine, FnCallArgs}; use crate::result::EvalAltResult; use crate::token::Position; -use crate::stdlib::{any::TypeId, boxed::Box, string::ToString}; +use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString}; /// A trait to register custom functions with the `Engine`. pub trait RegisterFn { @@ -115,16 +115,19 @@ pub trait RegisterResultFn { pub struct Mut(T); //pub struct Ref(T); -/// Identity dereferencing function. +/// Dereference into &mut. #[inline] -pub fn identity(data: &mut T) -> &mut T { - data +pub fn by_ref(data: &mut Dynamic) -> &mut T { + // Directly cast the &mut Dynamic into &mut T to access the underlying data. + data.downcast_mut::().unwrap() } -/// Clone dereferencing function. +/// Dereference into value. #[inline] -pub fn cloned(data: &mut T) -> T { - data.clone() +pub fn by_value(data: &mut Dynamic) -> T { + // We consume the argument and then replace it with () - the argument is not supposed to be used again. + // This way, we avoid having to clone the argument again, because it is already a clone when passed here. + mem::replace(data, Default::default()).cast::() } /// This macro counts the number of arguments via recursion. @@ -135,7 +138,7 @@ macro_rules! count_args { /// This macro creates a closure wrapping a registered function. macro_rules! make_func { - ($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $clone:expr),*) => { + ($fn_name:ident : $fn:ident : $map:expr ; $($par:ident => $convert:expr),*) => { // ^ function name // ^ function pointer // ^ result mapping function @@ -153,14 +156,16 @@ macro_rules! make_func { #[allow(unused_variables, unused_mut)] let mut drain = args.iter_mut(); $( - // Downcast every element, return in case of a type mismatch - let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap(); + // Downcast every element, panic in case of a type mismatch (which shouldn't happen). + // Call the user-supplied function using ($convert) to access it either by value or by reference. + let $par = ($convert)(drain.next().unwrap()); )* - // Call the user-supplied function using ($clone) to - // potentially clone the value, otherwise pass the reference. - let r = $fn($(($clone)($par)),*); - $map(r, pos) + // Call the function with each parameter value + let r = $fn($($par),*); + + // Map the result + $map(r, pos) }; }; } @@ -259,18 +264,17 @@ macro_rules! def_register { //def_register!(imp_pop $($par => $mark => $param),*); }; ($p0:ident $(, $p:ident)*) => { - def_register!(imp $p0 => $p0 => $p0 => cloned $(, $p => $p => $p => cloned)*); - def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => cloned)*); + def_register!(imp $p0 => $p0 => $p0 => by_value $(, $p => $p => $p => by_value)*); + def_register!(imp $p0 => Mut<$p0> => &mut $p0 => by_ref $(, $p => $p => $p => by_value)*); // handle the first parameter ^ first parameter passed through - // ^ others passed by value (cloned) + // ^ others passed by value (by_value) // Currently does not support first argument which is a reference, as there will be // conflicting implementations since &T: Any and T: Any cannot be distinguished - //def_register!(imp $p0 => Ref<$p0> => &$p0 => identity $(, $p => $p => $p => Clone::clone)*); + //def_register!(imp $p0 => Ref<$p0> => &$p0 => by_ref $(, $p => $p => $p => by_value)*); def_register!($($p),*); }; } -#[rustfmt::skip] def_register!(A, B, C, D, E, F, G, H, J, K, L, M, N, P, Q, R, S, T, U, V); diff --git a/src/optimize.rs b/src/optimize.rs index 268c504a..d018b4c5 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -371,12 +371,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { (Expr::Variable(var, sp, _), Expr::Variable(var2, sp2, _)) if var == var2 && sp == sp2 => { // Assignment to the same variable - fold state.set_dirty(); - - Expr::Assignment( - Box::new(Expr::Variable(var, sp, pos)), - Box::new(optimize_expr(*expr2, state)), - pos, - ) + Expr::Assignment(Box::new(Expr::Variable(var, sp, pos)), Box::new(optimize_expr(*expr2, state)), pos) } // id1 = id2 = expr2 (id1, id2) => Expr::Assignment( diff --git a/src/parser.rs b/src/parser.rs index e682ee12..df7889c9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,7 +15,7 @@ use crate::stdlib::{ format, iter::Peekable, num::NonZeroUsize, - ops::Add, + ops::{Add, Deref, DerefMut}, rc::Rc, string::{String, ToString}, sync::Arc, @@ -193,18 +193,6 @@ impl Stack { pub fn new() -> Self { Self(Vec::new()) } - /// Get the number of variables in the `Stack`. - pub fn len(&self) -> usize { - self.0.len() - } - /// Push (add) a new variable onto the `Stack`. - pub fn push(&mut self, name: String) { - self.0.push(name); - } - /// Rewind the stack to a previous size. - pub fn rewind(&mut self, len: usize) { - self.0.truncate(len); - } /// Find a variable by name in the `Stack`, searching in reverse. /// The return value is the offset to be deducted from `Stack::len`, /// i.e. the top element of the `Stack` is offset 1. @@ -219,6 +207,20 @@ impl Stack { } } +impl Deref for Stack { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Stack { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + /// A statement. #[derive(Debug, Clone)] pub enum Stmt { @@ -1639,7 +1641,7 @@ fn parse_for<'a>( let body = parse_block(input, stack, true, allow_stmt_expr)?; - stack.rewind(prev_len); + stack.truncate(prev_len); Ok(Stmt::For(Box::new(name), Box::new(expr), Box::new(body))) } @@ -1747,7 +1749,7 @@ fn parse_block<'a>( } } - stack.rewind(prev_len); + stack.truncate(prev_len); Ok(Stmt::Block(statements, pos)) }