diff --git a/README.md b/README.md index c073d244..7977dbc3 100644 --- a/README.md +++ b/README.md @@ -439,12 +439,10 @@ There is no easy way for Rust to decide, at run-time, what type the `Dynamic` va function and match against the name). A `Dynamic` value's actual type can be checked via the `is` method. -The `cast` method (from the `rhai::AnyExt` trait) then converts the value into a specific, known type. +The `cast` method then converts the value into a specific, known type. Alternatively, use the `try_cast` method which does not panic but returns an error when the cast fails. ```rust -use rhai::AnyExt; // pull in the trait. - let list: Array = engine.eval("...")?; // return type is 'Array' let item = list[0]; // an element in an 'Array' is 'Dynamic' @@ -459,8 +457,6 @@ let value = item.try_cast::()?; // 'try_cast' does not panic whe The `type_name` method gets the name of the actual type as a static string slice, which you may match against. ```rust -use rhai::Any; // pull in the trait. - let list: Array = engine.eval("...")?; // return type is 'Array' let item = list[0]; // an element in an 'Array' is 'Dynamic' @@ -499,8 +495,6 @@ A number of traits, under the `rhai::` module namespace, provide additional func | Trait | Description | Methods | | ------------------- | --------------------------------------------------------------------------------- | --------------------------------------- | -| `Any` | Generic trait that represents a [`Dynamic`] type | `type_id`, `type_name`, `into_dynamic` | -| `AnyExt` | Extension trait to allows casting of a [`Dynamic`] value to Rust types | `cast`, `try_cast` | | `RegisterFn` | Trait for registering functions | `register_fn` | | `RegisterDynamicFn` | Trait for registering functions returning [`Dynamic`] | `register_dynamic_fn` | | `RegisterResultFn` | Trait for registering fallible functions returning `Result<`_T_`, EvalAltResult>` | `register_result_fn` | @@ -513,9 +507,9 @@ Rhai's scripting engine is very lightweight. It gets most of its abilities from To call these functions, they need to be registered with the [`Engine`]. ```rust -use rhai::{Engine, EvalAltResult}; +use rhai::{Dynamic, Engine, EvalAltResult}; use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn' -use rhai::{Any, Dynamic, RegisterDynamicFn}; // use 'RegisterDynamicFn' trait for 'register_dynamic_fn' +use rhai::{Dynamic, RegisterDynamicFn}; // use 'RegisterDynamicFn' trait for 'register_dynamic_fn' // Normal function fn add(x: i64, y: i64) -> i64 { @@ -524,7 +518,7 @@ fn add(x: i64, y: i64) -> i64 { // Function that returns a Dynamic value fn get_an_any() -> Dynamic { - (42_i64).into_dynamic() // 'into_dynamic' is defined by the 'rhai::Any' trait + Dynamic::from(42_i64) } fn main() -> Result<(), EvalAltResult> @@ -548,17 +542,16 @@ fn main() -> Result<(), EvalAltResult> } ``` -To return a [`Dynamic`] value from a Rust function, use the `into_dynamic()` method -(under the `rhai::Any` trait) to convert it. +To return a [`Dynamic`] value from a Rust function, use the `Dynamic::from` method. ```rust -use rhai::Any; // pull in the trait +use rhai::Dynamic; fn decide(yes_no: bool) -> Dynamic { if yes_no { - (42_i64).into_dynamic() + Dynamic::from(42_i64) } else { - String::from("hello!").into_dynamic() // remember &str is not supported by Rhai + Dynamic::from(String::from("hello!")) // remember &str is not supported by Rhai } } ``` diff --git a/src/any.rs b/src/any.rs index 1a7bda22..803fe2d2 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1,70 +1,38 @@ //! Helper module which defines the `Any` trait to to allow dynamic value handling. +use crate::engine::{Array, Map}; +use crate::parser::INT; + +#[cfg(not(feature = "no_float"))] +use crate::parser::FLOAT; + use crate::stdlib::{ - any::{type_name, TypeId}, + any::{type_name, Any, TypeId}, boxed::Box, fmt, + time::Instant, }; -/// An raw value of any type. +/// A trait to represent any type. /// /// Currently, `Variant` is not `Send` nor `Sync`, so it can practically be any type. /// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`. -pub type Variant = dyn Any; - -/// A boxed dynamic type containing any value. -/// -/// Currently, `Dynamic` is not `Send` nor `Sync`, so it can practically be any type. -/// Turn on the `sync` feature to restrict it to only types that implement `Send + Sync`. -pub type Dynamic = Box; - -/// A trait covering any type. -#[cfg(feature = "sync")] -pub trait Any: crate::stdlib::any::Any + Send + Sync { - /// Get the `TypeId` of this type. - fn type_id(&self) -> TypeId; - - /// Get the name of this type. - fn type_name(&self) -> &'static str; - - /// Convert into `Dynamic`. - fn into_dynamic(&self) -> Dynamic; - - /// This trait may only be implemented by `rhai`. - #[doc(hidden)] - fn _closed(&self) -> _Private; -} - -#[cfg(feature = "sync")] -impl Any for T { - fn type_id(&self) -> TypeId { - TypeId::of::() - } - - fn type_name(&self) -> &'static str { - type_name::() - } - - fn into_dynamic(&self) -> Dynamic { - Box::new(self.clone()) - } - - fn _closed(&self) -> _Private { - _Private - } -} - -/// A trait covering any type. #[cfg(not(feature = "sync"))] -pub trait Any: crate::stdlib::any::Any { - /// Get the `TypeId` of this type. - fn type_id(&self) -> TypeId; +pub trait Variant: Any { + /// Convert this `Variant` trait object to `&dyn Any`. + fn as_any(&self) -> &dyn Any; + + /// Convert this `Variant` trait object to `&mut dyn Any`. + fn as_mut_any(&mut self) -> &mut dyn Any; /// Get the name of this type. fn type_name(&self) -> &'static str; /// Convert into `Dynamic`. - fn into_dynamic(&self) -> Dynamic; + fn into_dynamic(self) -> Dynamic; + + /// Clone into `Dynamic`. + fn clone_into_dynamic(&self) -> Dynamic; /// This trait may only be implemented by `rhai`. #[doc(hidden)] @@ -72,101 +40,312 @@ pub trait Any: crate::stdlib::any::Any { } #[cfg(not(feature = "sync"))] -impl Any for T { - fn type_id(&self) -> TypeId { - TypeId::of::() +impl Variant for T { + fn as_any(&self) -> &dyn Any { + self as &dyn Any + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self as &mut dyn Any } - fn type_name(&self) -> &'static str { type_name::() } - - fn into_dynamic(&self) -> Dynamic { - Box::new(self.clone()) + fn into_dynamic(self) -> Dynamic { + Dynamic::from(self) + } + fn clone_into_dynamic(&self) -> Dynamic { + Dynamic::from(self.clone()) } - fn _closed(&self) -> _Private { _Private } } -impl Variant { +/// A trait to represent any type. +#[cfg(feature = "sync")] +pub trait Variant: Any + Send + Sync { + /// Convert this `Variant` trait object to `&dyn Any`. + fn as_any(&self) -> &dyn Any; + + /// Convert this `Variant` trait object to `&mut dyn Any`. + fn as_mut_any(&mut self) -> &mut dyn Any; + + /// Get the name of this type. + fn type_name(&self) -> &'static str; + + /// Convert into `Dynamic`. + fn into_dynamic(self) -> Dynamic; + + /// Clone into `Dynamic`. + fn clone_into_dynamic(&self) -> Dynamic; + + /// This trait may only be implemented by `rhai`. + #[doc(hidden)] + fn _closed(&self) -> _Private; +} + +#[cfg(feature = "sync")] +impl Variant for T { + fn as_any(&self) -> &dyn Any { + self as &dyn Any + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self as &mut dyn Any + } + fn type_name(&self) -> &'static str { + type_name::() + } + fn into_dynamic(self) -> Dynamic { + Dynamic::from(self) + } + fn clone_into_dynamic(&self) -> Dynamic { + Dynamic::from(self.clone()) + } + fn _closed(&self) -> _Private { + _Private + } +} + +impl dyn Variant { /// Is this `Variant` a specific type? pub fn is(&self) -> bool { - TypeId::of::() == ::type_id(self) + TypeId::of::() == self.type_id() } /// Get a reference of a specific type to the `Variant`. /// Returns `None` if the cast fails. pub fn downcast_ref(&self) -> Option<&T> { - if self.is::() { - unsafe { Some(&*(self as *const Variant as *const T)) } - } else { - None - } + Any::downcast_ref::(self.as_any()) } /// Get a mutable reference of a specific type to the `Variant`. /// Returns `None` if the cast fails. pub fn downcast_mut(&mut self) -> Option<&mut T> { - if self.is::() { - unsafe { Some(&mut *(self as *mut Variant as *mut T)) } - } else { - None + Any::downcast_mut::(self.as_mut_any()) + } +} + +/// A dynamic type containing any value. +pub struct Dynamic(pub(crate) Union); + +/// Internal `Dynamic` representation. +pub enum Union { + Unit(()), + Bool(bool), + Str(String), + Char(char), + Int(INT), + #[cfg(not(feature = "no_float"))] + Float(FLOAT), + Array(Array), + Map(Box), // Box it to reduce size + Variant(Box), +} + +impl Dynamic { + /// Is the value held by this `Dynamic` a particular type? + pub fn is(&self) -> bool { + self.type_id() == TypeId::of::() + } + + /// Get the TypeId of the value held by this `Dynamic`. + pub fn type_id(&self) -> TypeId { + match &self.0 { + Union::Unit(_) => TypeId::of::<()>(), + Union::Bool(_) => TypeId::of::(), + Union::Str(_) => TypeId::of::(), + Union::Char(_) => TypeId::of::(), + Union::Int(_) => TypeId::of::(), + #[cfg(not(feature = "no_float"))] + Union::Float(_) => TypeId::of::(), + Union::Array(_) => TypeId::of::(), + Union::Map(_) => TypeId::of::(), + Union::Variant(value) => (**value).type_id(), + } + } + + /// Get the name of the type of the value held by this `Dynamic`. + pub fn type_name(&self) -> &'static str { + match &self.0 { + Union::Unit(_) => "()", + Union::Bool(_) => "bool", + Union::Str(_) => "string", + Union::Char(_) => "char", + Union::Int(_) => type_name::(), + #[cfg(not(feature = "no_float"))] + Union::Float(_) => type_name::(), + Union::Array(_) => "array", + Union::Map(_) => "map", + + Union::Variant(value) if value.is::() => "timestamp", + Union::Variant(value) => (**value).type_name(), } } } -impl fmt::Debug for Variant { +impl fmt::Display for Dynamic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("?") + match &self.0 { + Union::Unit(_) => write!(f, ""), + Union::Bool(value) => write!(f, "{}", value), + Union::Str(value) => write!(f, "{}", value), + Union::Char(value) => write!(f, "{}", value), + Union::Int(value) => write!(f, "{}", value), + #[cfg(not(feature = "no_float"))] + Union::Float(value) => write!(f, "{}", value), + Union::Array(value) => write!(f, "{:?}", value), + Union::Map(value) => write!(f, "{:?}", value), + Union::Variant(_) => write!(f, "?"), + } + } +} + +impl fmt::Debug for Dynamic { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.0 { + Union::Unit(value) => write!(f, "{:?}", value), + Union::Bool(value) => write!(f, "{:?}", value), + Union::Str(value) => write!(f, "{:?}", value), + Union::Char(value) => write!(f, "{:?}", value), + Union::Int(value) => write!(f, "{:?}", value), + #[cfg(not(feature = "no_float"))] + Union::Float(value) => write!(f, "{:?}", value), + Union::Array(value) => write!(f, "{:?}", value), + Union::Map(value) => write!(f, "{:?}", value), + Union::Variant(_) => write!(f, "?"), + } } } impl Clone for Dynamic { fn clone(&self) -> Self { - self.as_ref().into_dynamic() + match &self.0 { + Union::Unit(value) => Self(Union::Unit(value.clone())), + Union::Bool(value) => Self(Union::Bool(value.clone())), + Union::Str(value) => Self(Union::Str(value.clone())), + Union::Char(value) => Self(Union::Char(value.clone())), + Union::Int(value) => Self(Union::Int(value.clone())), + #[cfg(not(feature = "no_float"))] + Union::Float(value) => Self(Union::Float(value.clone())), + Union::Array(value) => Self(Union::Array(value.clone())), + Union::Map(value) => Self(Union::Map(value.clone())), + Union::Variant(value) => (**value).clone_into_dynamic(), + } } } -/// An extension trait that allows down-casting a `Dynamic` value to a specific type. -pub trait AnyExt: Sized { - /// Get a copy of a `Dynamic` value as a specific type. - fn try_cast(self) -> Result; - - /// Get a copy of a `Dynamic` value as a specific type. +impl Dynamic { + /// Create a `Dynamic` from any type. /// - /// # Panics + /// # Examples /// - /// Panics if the cast fails (e.g. the type of the actual value is not the same as the specified type). - fn cast(self) -> T; + /// ``` + /// use rhai::Dynamic; + /// + /// let result = Dynamic::from(42_i64); + /// assert_eq!(result.type_name(), "i64"); + /// assert_eq!(result.to_string(), "42"); + /// + /// let result = Dynamic::from("hello".to_string()); + /// assert_eq!(result.type_name(), "string"); + /// assert_eq!(result.to_string(), "hello"); + /// ``` + pub fn from(value: T) -> Self { + if let Some(result) = (&value as &dyn Variant) + .downcast_ref::<()>() + .cloned() + .map(Union::Unit) + .or_else(|| { + (&value as &dyn Variant) + .downcast_ref::() + .cloned() + .map(Union::Bool) + .or_else(|| { + (&value as &dyn Variant) + .downcast_ref::() + .cloned() + .map(Union::Int) + .or_else(|| { + (&value as &dyn Variant) + .downcast_ref::() + .cloned() + .map(Union::Char) + }) + }) + }) + { + return Self(result); + } - /// This trait may only be implemented by `rhai`. - #[doc(hidden)] - fn _closed(&self) -> _Private; -} + #[cfg(not(feature = "no_float"))] + { + if let Some(result) = (&value as &dyn Variant) + .downcast_ref::() + .cloned() + .map(Union::Char) + { + return Self(result); + } + } + + fn cast_box(item: Box) -> Result> { + if TypeId::of::() == TypeId::of::() { + unsafe { + let raw: *mut dyn Any = Box::into_raw(item as Box); + Ok(*Box::from_raw(raw as *mut T)) + } + } else { + Err(item) + } + } + + let var = Box::new(value); + + Self( + cast_box::<_, String>(var) + .map(Union::Str) + .or_else(|var| { + cast_box::<_, Array>(var).map(Union::Array).or_else(|var| { + cast_box::<_, Map>(var) + .map(|v| Union::Map(Box::new(v))) + .or_else(|var| -> Result { + Ok(Union::Variant(var as Box)) + }) + }) + }) + .unwrap(), + ) + } -impl AnyExt for Dynamic { /// Get a copy of the `Dynamic` value as a specific type. /// + /// Returns an error with the name of the value's actual type when the cast fails. + /// /// # Example /// /// ``` - /// use rhai::{Dynamic, Any, AnyExt}; + /// use rhai::Dynamic; /// - /// let x: Dynamic = 42_u32.into_dynamic(); + /// let x = Dynamic::from(42_u32); /// /// assert_eq!(x.try_cast::().unwrap(), 42); /// ``` - fn try_cast(self) -> Result { - if self.is::() { - unsafe { - let raw: *mut Variant = Box::into_raw(self); - Ok(*Box::from_raw(raw as *mut T)) - } - } else { - Err(self) + pub fn try_cast(self) -> Result { + match &self.0 { + Union::Unit(value) => (value as &dyn Variant).downcast_ref::().cloned(), + Union::Bool(value) => (value as &dyn Variant).downcast_ref::().cloned(), + Union::Str(value) => (value as &dyn Variant).downcast_ref::().cloned(), + Union::Char(value) => (value as &dyn Variant).downcast_ref::().cloned(), + Union::Int(value) => (value as &dyn Variant).downcast_ref::().cloned(), + #[cfg(not(feature = "no_float"))] + Union::Float(value) => (value as &dyn Variant).downcast_ref::().cloned(), + Union::Array(value) => (value as &dyn Variant).downcast_ref::().cloned(), + Union::Map(value) => (value.as_ref() as &dyn Variant) + .downcast_ref::() + .cloned(), + Union::Variant(value) => value.as_ref().downcast_ref::().cloned(), } + .ok_or(self) } /// Get a copy of the `Dynamic` value as a specific type. @@ -178,18 +357,141 @@ impl AnyExt for Dynamic { /// # Example /// /// ``` - /// use rhai::{Dynamic, Any, AnyExt}; + /// use rhai::Dynamic; /// - /// let x: Dynamic = 42_u32.into_dynamic(); + /// let x = Dynamic::from(42_u32); /// /// assert_eq!(x.cast::(), 42); /// ``` - fn cast(self) -> T { - self.try_cast::().expect("cast failed") + pub fn cast(self) -> T { + self.try_cast::().unwrap() } - fn _closed(&self) -> _Private { - _Private + /// Get a reference of a specific type to the `Dynamic`. + /// Returns `None` if the cast fails. + pub fn downcast_ref(&self) -> Option<&T> { + match &self.0 { + Union::Unit(value) => (value as &dyn Variant).downcast_ref::(), + Union::Bool(value) => (value as &dyn Variant).downcast_ref::(), + Union::Str(value) => (value as &dyn Variant).downcast_ref::(), + Union::Char(value) => (value as &dyn Variant).downcast_ref::(), + Union::Int(value) => (value as &dyn Variant).downcast_ref::(), + #[cfg(not(feature = "no_float"))] + Union::Float(value) => (value as &dyn Variant).downcast_ref::(), + Union::Array(value) => (value as &dyn Variant).downcast_ref::(), + Union::Map(value) => (value.as_ref() as &dyn Variant).downcast_ref::(), + Union::Variant(value) => value.as_ref().downcast_ref::(), + } + } + + /// Get a mutable reference of a specific type to the `Dynamic`. + /// Returns `None` if the cast fails. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + match &mut self.0 { + Union::Unit(value) => (value as &mut dyn Variant).downcast_mut::(), + Union::Bool(value) => (value as &mut dyn Variant).downcast_mut::(), + Union::Str(value) => (value as &mut dyn Variant).downcast_mut::(), + Union::Char(value) => (value as &mut dyn Variant).downcast_mut::(), + Union::Int(value) => (value as &mut dyn Variant).downcast_mut::(), + #[cfg(not(feature = "no_float"))] + Union::Float(value) => (value as &mut dyn Variant).downcast_mut::(), + Union::Array(value) => (value as &mut dyn Variant).downcast_mut::(), + Union::Map(value) => (value.as_mut() as &mut dyn Variant).downcast_mut::(), + Union::Variant(value) => value.as_mut().downcast_mut::(), + } + } + + /// Cast the `Dynamic` as the system integer type `INT` and return it. + /// Returns the name of the actual type if the cast fails. + pub(crate) fn as_int(&self) -> Result { + match self.0 { + Union::Int(n) => Ok(n), + _ => Err(self.type_name()), + } + } + + /// Cast the `Dynamic` as the system integer type `INT` and return it. + /// Returns the name of the actual type if the cast fails. + #[cfg(not(feature = "no_float"))] + pub(crate) fn as_float(&self) -> Result { + match self.0 { + Union::Float(n) => Ok(n), + _ => Err(self.type_name()), + } + } + + /// Cast the `Dynamic` as a `bool` and return it. + /// Returns the name of the actual type if the cast fails. + pub(crate) fn as_bool(&self) -> Result { + match self.0 { + Union::Bool(b) => Ok(b), + _ => Err(self.type_name()), + } + } + + /// Cast the `Dynamic` as a `char` and return it. + /// Returns the name of the actual type if the cast fails. + pub(crate) fn as_char(&self) -> Result { + match self.0 { + Union::Char(n) => Ok(n), + _ => Err(self.type_name()), + } + } + + /// Cast the `Dynamic` as a string and return the string slice. + /// Returns the name of the actual type if the cast fails. + pub(crate) fn as_str(&self) -> Result<&str, &'static str> { + match &self.0 { + Union::Str(s) => Ok(s), + _ => Err(self.type_name()), + } + } + + /// Convert the `Dynamic` into `String` and return it. + /// Returns the name of the actual type if the cast fails. + pub(crate) fn take_string(self) -> Result { + match self.0 { + Union::Str(s) => Ok(s), + _ => Err(self.type_name()), + } + } + + /// Cast the `Dynamic` as an `Array` and return a reference to it. + /// Returns the name of the actual type if the cast fails. + pub(crate) fn as_array(&self) -> Result<&Array, &'static str> { + match &self.0 { + Union::Array(array) => Ok(array), + _ => Err(self.type_name()), + } + } + + /// Cast the `Dynamic` as a `Map` and return a reference to it. + /// Returns the name of the actual type if the cast fails. + pub(crate) fn as_map(&self) -> Result<&Map, &'static str> { + match &self.0 { + Union::Map(map) => Ok(map), + _ => Err(self.type_name()), + } + } + + pub(crate) fn from_unit() -> Self { + Self(Union::Unit(())) + } + pub(crate) fn from_bool(value: bool) -> Self { + Self(Union::Bool(value)) + } + pub(crate) fn from_int(value: INT) -> Self { + Self(Union::Int(value)) + } + #[cfg(not(feature = "no_float"))] + pub(crate) fn from_float(value: FLOAT) -> Self { + Self(Union::Float(value)) + } + pub(crate) fn from_char(value: char) -> Self { + Self(Union::Char(value)) + } + pub(crate) fn from_string(value: String) -> Self { + Self(Union::Str(value)) } } diff --git a/src/api.rs b/src/api.rs index fde9b5d4..9dc84298 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,6 +1,6 @@ //! Module that defines the extern API of `Engine`. -use crate::any::{Any, AnyExt, Dynamic}; +use crate::any::{Dynamic, Variant}; use crate::engine::{make_getter, make_setter, Engine, FnAny, FnSpec, Map}; use crate::error::ParseError; use crate::fn_call::FuncArgs; @@ -109,7 +109,7 @@ impl<'e> Engine<'e> { /// # } /// ``` #[cfg(not(feature = "no_object"))] - pub fn register_type(&mut self) { + pub fn register_type(&mut self) { self.register_type_with_name::(type_name::()); } @@ -157,7 +157,7 @@ impl<'e> Engine<'e> { /// # } /// ``` #[cfg(not(feature = "no_object"))] - pub fn register_type_with_name(&mut self, name: &str) { + pub fn register_type_with_name(&mut self, name: &str) { if self.type_names.is_none() { self.type_names = Some(HashMap::new()); } @@ -171,7 +171,7 @@ impl<'e> Engine<'e> { /// Register an iterator adapter for a type with the `Engine`. /// This is an advanced feature. - pub fn register_iterator(&mut self, f: F) { + pub fn register_iterator(&mut self, f: F) { if self.type_iterators.is_none() { self.type_iterators = Some(HashMap::new()); } @@ -221,8 +221,8 @@ impl<'e> Engine<'e> { #[cfg(not(feature = "no_object"))] pub fn register_get(&mut self, name: &str, callback: F) where - T: Any + Clone, - U: Any + Clone, + T: Variant + Clone, + U: Variant + Clone, F: ObjectGetCallback, { self.register_fn(&make_getter(name), callback); @@ -267,8 +267,8 @@ impl<'e> Engine<'e> { #[cfg(not(feature = "no_object"))] pub fn register_set(&mut self, name: &str, callback: F) where - T: Any + Clone, - U: Any + Clone, + T: Variant + Clone, + U: Variant + Clone, F: ObjectSetCallback, { self.register_fn(&make_setter(name), callback); @@ -315,8 +315,8 @@ impl<'e> Engine<'e> { #[cfg(not(feature = "no_object"))] pub fn register_get_set(&mut self, name: &str, get_fn: G, set_fn: S) where - T: Any + Clone, - U: Any + Clone, + T: Variant + Clone, + U: Variant + Clone, G: ObjectGetCallback, S: ObjectSetCallback, { @@ -487,7 +487,7 @@ impl<'e> Engine<'e> { /// /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { - /// use rhai::{Engine, AnyExt}; + /// use rhai::Engine; /// /// let engine = Engine::new(); /// @@ -612,7 +612,7 @@ impl<'e> Engine<'e> { /// # } /// ``` #[cfg(not(feature = "no_std"))] - pub fn eval_file(&self, path: PathBuf) -> Result { + pub fn eval_file(&self, path: PathBuf) -> Result { Self::read_file(path).and_then(|contents| self.eval::(&contents)) } @@ -636,7 +636,7 @@ impl<'e> Engine<'e> { /// # } /// ``` #[cfg(not(feature = "no_std"))] - pub fn eval_file_with_scope( + pub fn eval_file_with_scope( &self, scope: &mut Scope, path: PathBuf, @@ -658,7 +658,7 @@ impl<'e> Engine<'e> { /// # Ok(()) /// # } /// ``` - pub fn eval(&self, script: &str) -> Result { + pub fn eval(&self, script: &str) -> Result { self.eval_with_scope(&mut Scope::new(), script) } @@ -684,7 +684,7 @@ impl<'e> Engine<'e> { /// # Ok(()) /// # } /// ``` - pub fn eval_with_scope( + pub fn eval_with_scope( &self, scope: &mut Scope, script: &str, @@ -709,7 +709,7 @@ impl<'e> Engine<'e> { /// # Ok(()) /// # } /// ``` - pub fn eval_expression(&self, script: &str) -> Result { + pub fn eval_expression(&self, script: &str) -> Result { self.eval_expression_with_scope(&mut Scope::new(), script) } @@ -731,7 +731,7 @@ impl<'e> Engine<'e> { /// # Ok(()) /// # } /// ``` - pub fn eval_expression_with_scope( + pub fn eval_expression_with_scope( &self, scope: &mut Scope, script: &str, @@ -761,7 +761,7 @@ impl<'e> Engine<'e> { /// # Ok(()) /// # } /// ``` - pub fn eval_ast(&self, ast: &AST) -> Result { + pub fn eval_ast(&self, ast: &AST) -> Result { self.eval_ast_with_scope(&mut Scope::new(), ast) } @@ -794,7 +794,7 @@ impl<'e> Engine<'e> { /// # Ok(()) /// # } /// ``` - pub fn eval_ast_with_scope( + pub fn eval_ast_with_scope( &self, scope: &mut Scope, ast: &AST, @@ -803,7 +803,7 @@ impl<'e> Engine<'e> { .try_cast::() .map_err(|a| { EvalAltResult::ErrorMismatchOutputType( - self.map_type_name((*a).type_name()).to_string(), + self.map_type_name(a.type_name()).to_string(), Position::none(), ) }) @@ -816,7 +816,7 @@ impl<'e> Engine<'e> { ) -> Result { ast.0 .iter() - .try_fold(().into_dynamic(), |_, stmt| { + .try_fold(Dynamic::from_unit(), |_, stmt| { self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0) }) .or_else(|err| match err { @@ -875,7 +875,7 @@ impl<'e> Engine<'e> { ) -> Result<(), EvalAltResult> { ast.0 .iter() - .try_fold(().into_dynamic(), |_, stmt| { + .try_fold(Dynamic::from_unit(), |_, stmt| { self.eval_stmt(scope, Some(ast.1.as_ref()), stmt, 0) }) .map(|_| ()) @@ -921,7 +921,7 @@ impl<'e> Engine<'e> { /// # } /// ``` #[cfg(not(feature = "no_function"))] - pub fn call_fn( + pub fn call_fn( &self, scope: &mut Scope, ast: &AST, @@ -929,7 +929,7 @@ impl<'e> Engine<'e> { args: A, ) -> Result { let mut arg_values = args.into_vec(); - let mut args: Vec<_> = arg_values.iter_mut().map(Dynamic::as_mut).collect(); + let mut args: Vec<_> = arg_values.iter_mut().collect(); let fn_lib = Some(ast.1.as_ref()); let pos = Position::none(); @@ -937,7 +937,7 @@ impl<'e> Engine<'e> { .try_cast() .map_err(|a| { EvalAltResult::ErrorMismatchOutputType( - self.map_type_name((*a).type_name()).into(), + self.map_type_name(a.type_name()).into(), pos, ) }) diff --git a/src/builtin.rs b/src/builtin.rs index f77b4f4a..dfdda748 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -1,7 +1,7 @@ //! Helper module that allows registration of the _core library_ and //! _standard library_ of utility functions. -use crate::any::{Any, Dynamic}; +use crate::any::{Dynamic, Variant}; use crate::engine::{Engine, FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT}; use crate::fn_register::{RegisterDynamicFn, RegisterFn, RegisterResultFn}; use crate::parser::{Position, INT}; @@ -628,8 +628,8 @@ impl Engine<'_> { // Register map access functions #[cfg(not(feature = "no_index"))] self.register_fn("keys", |map: Map| { - map.into_iter() - .map(|(k, _)| k.into_dynamic()) + map.iter() + .map(|(k, _)| Dynamic::from(k.clone())) .collect::>() }); @@ -641,15 +641,16 @@ impl Engine<'_> { } // Register range function - fn reg_range(engine: &mut Engine) + fn reg_range(engine: &mut Engine) where Range: Iterator, { - engine.register_iterator::, _>(|a: &Dynamic| { + engine.register_iterator::, _>(|source: &Dynamic| { Box::new( - a.downcast_ref::>() + source + .downcast_ref::>() + .cloned() .unwrap() - .clone() .map(|x| x.into_dynamic()), ) as Box> }); @@ -678,12 +679,12 @@ impl Engine<'_> { struct StepRange(T, T, T) where for<'a> &'a T: Add<&'a T, Output = T>, - T: Any + Clone + PartialOrd; + T: Variant + Clone + PartialOrd; impl Iterator for StepRange where for<'a> &'a T: Add<&'a T, Output = T>, - T: Any + Clone + PartialOrd, + T: Variant + Clone + PartialOrd, { type Item = T; @@ -701,14 +702,15 @@ impl Engine<'_> { fn reg_step(engine: &mut Engine) where for<'a> &'a T: Add<&'a T, Output = T>, - T: Any + Clone + PartialOrd, + T: Variant + Clone + PartialOrd, StepRange: Iterator, { - engine.register_iterator::, _>(|a: &Dynamic| { + engine.register_iterator::, _>(|source: &Dynamic| { Box::new( - a.downcast_ref::>() + source + .downcast_ref::>() + .cloned() .unwrap() - .clone() .map(|x| x.into_dynamic()), ) as Box> }); @@ -866,19 +868,19 @@ impl Engine<'_> { } // Register array utility functions - fn push(list: &mut Array, item: T) { - list.push(Box::new(item)); + fn push(list: &mut Array, item: T) { + list.push(Dynamic::from(item)); } - fn ins(list: &mut Array, position: INT, item: T) { + fn ins(list: &mut Array, position: INT, item: T) { if position <= 0 { - list.insert(0, Box::new(item)); + list.insert(0, Dynamic::from(item)); } else if (position as usize) >= list.len() - 1 { push(list, item); } else { - list.insert(position as usize, Box::new(item)); + list.insert(position as usize, Dynamic::from(item)); } } - fn pad(list: &mut Array, len: INT, item: T) { + fn pad(list: &mut Array, len: INT, item: T) { if len >= 0 { while list.len() < len as usize { push(list, item.clone()); @@ -919,18 +921,18 @@ impl Engine<'_> { } self.register_dynamic_fn("pop", |list: &mut Array| { - list.pop().unwrap_or_else(|| ().into_dynamic()) + list.pop().unwrap_or_else(|| Dynamic::from_unit()) }); self.register_dynamic_fn("shift", |list: &mut Array| { if !list.is_empty() { - ().into_dynamic() + Dynamic::from_unit() } else { list.remove(0) } }); self.register_dynamic_fn("remove", |list: &mut Array, len: INT| { if len < 0 || (len as usize) >= list.len() { - ().into_dynamic() + Dynamic::from_unit() } else { list.remove(len as usize) } @@ -951,7 +953,7 @@ impl Engine<'_> { self.register_fn("len", |map: &mut Map| map.len() as INT); self.register_fn("clear", |map: &mut Map| map.clear()); self.register_dynamic_fn("remove", |x: &mut Map, name: String| { - x.remove(&name).unwrap_or(().into_dynamic()) + x.remove(&name).unwrap_or_else(|| Dynamic::from_unit()) }); self.register_fn("mixin", |map1: &mut Map, map2: Map| { map2.into_iter().for_each(|(key, value)| { diff --git a/src/engine.rs b/src/engine.rs index 73b1e800..59906675 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation `Engine`. -use crate::any::{Any, AnyExt, Dynamic, Variant}; +use crate::any::{Dynamic, Union}; use crate::error::ParseErrorType; use crate::optimize::OptimizationLevel; use crate::parser::{Expr, FnDef, Position, ReturnType, Stmt, INT}; @@ -8,7 +8,7 @@ use crate::result::EvalAltResult; use crate::scope::{EntryRef as ScopeSource, EntryType as ScopeEntryType, Scope}; use crate::stdlib::{ - any::{type_name, TypeId}, + any::TypeId, borrow::Cow, boxed::Box, cmp::Ordering, @@ -19,7 +19,6 @@ use crate::stdlib::{ rc::Rc, string::{String, ToString}, sync::Arc, - time::Instant, vec, vec::Vec, }; @@ -34,7 +33,7 @@ pub type Array = Vec; /// Not available under the `no_object` feature. pub type Map = HashMap; -pub type FnCallArgs<'a> = [&'a mut Variant]; +pub type FnCallArgs<'a> = [&'a mut Dynamic]; #[cfg(feature = "sync")] pub type FnAny = dyn Fn(&mut FnCallArgs, Position) -> Result + Send + Sync; @@ -98,20 +97,20 @@ impl IndexValue { #[derive(Debug)] enum Target<'a> { Scope(ScopeSource<'a>), - Value(&'a mut Variant), + Value(&'a mut Dynamic), } impl<'a> Target<'a> { - fn from(value: &'a mut Variant) -> Self { + fn from(value: &'a mut Dynamic) -> Self { Self::Value(value) } fn from_src(src: ScopeSource<'a>) -> Self { Self::Scope(src) } - fn get_mut(self, scope: &'a mut Scope) -> &'a mut Variant { + fn get_mut(self, scope: &'a mut Scope) -> &'a mut Dynamic { match self { Self::Value(t) => t, - Self::Scope(src) => scope.get_mut(src).as_mut(), + Self::Scope(src) => scope.get_mut(src), } } } @@ -300,7 +299,6 @@ impl Default for Engine<'_> { max_call_stack_depth: MAX_CALL_STACK_DEPTH, }; - engine.fill_type_names(); engine.register_core_lib(); #[cfg(not(feature = "no_stdlib"))] @@ -353,25 +351,6 @@ fn extract_prop_from_setter(fn_name: &str) -> Option<&str> { } impl Engine<'_> { - fn fill_type_names(&mut self) { - // User-friendly names for built-in types - self.type_names = Some( - [ - #[cfg(not(feature = "no_index"))] - (type_name::(), "array"), - #[cfg(not(feature = "no_object"))] - (type_name::(), "map"), - (type_name::(), "string"), - (type_name::(), "timestamp"), - (type_name::(), "dynamic"), - (type_name::(), "variant"), - ] - .iter() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect(), - ); - } - /// Create a new `Engine` pub fn new() -> Self { Default::default() @@ -400,7 +379,6 @@ impl Engine<'_> { max_call_stack_depth: MAX_CALL_STACK_DEPTH, }; - engine.fill_type_names(); engine.register_core_lib(); engine @@ -452,7 +430,7 @@ impl Engine<'_> { fn_def .params .iter() - .zip(args.into_iter().map(|x| (*x).into_dynamic())) + .zip(args.into_iter().map(|v| v.clone())) .map(|(name, value)| (name.clone(), ScopeEntryType::Normal, value)), ); @@ -478,7 +456,7 @@ impl Engine<'_> { fn_def .params .iter() - .zip(args.into_iter().map(|x| (*x).into_dynamic())) + .zip(args.into_iter().map(|v| v.clone())) .map(|(name, value)| (name, ScopeEntryType::Normal, value)), ); @@ -496,14 +474,13 @@ impl Engine<'_> { let spec = FnSpec { name: fn_name.into(), - args: args.iter().map(|a| Any::type_id(*a)).collect(), + args: args.iter().map(|a| a.type_id()).collect(), }; // Argument must be a string - fn cast_to_string(r: &Variant, pos: Position) -> Result<&str, EvalAltResult> { - r.downcast_ref::() - .map(String::as_str) - .ok_or_else(|| EvalAltResult::ErrorMismatchOutputType(r.type_name().into(), pos)) + fn cast_to_string(r: &Dynamic, pos: Position) -> Result<&str, EvalAltResult> { + r.as_str() + .map_err(|type_name| EvalAltResult::ErrorMismatchOutputType(type_name.into(), pos)) } // Search built-in's and external functions @@ -513,23 +490,26 @@ impl Engine<'_> { // See if the function match print/debug (which requires special processing) return match fn_name { - KEYWORD_PRINT if self.on_print.is_some() => Ok(self.on_print.as_ref().unwrap()( - cast_to_string(result.as_ref(), pos)?, - ) - .into_dynamic()), - KEYWORD_DEBUG if self.on_debug.is_some() => Ok(self.on_debug.as_ref().unwrap()( - cast_to_string(result.as_ref(), pos)?, - ) - .into_dynamic()), - KEYWORD_PRINT | KEYWORD_DEBUG => Ok(().into_dynamic()), + KEYWORD_PRINT if self.on_print.is_some() => { + self.on_print.as_ref().unwrap()(cast_to_string(&result, pos)?); + Ok(Dynamic::from_unit()) + } + KEYWORD_DEBUG if self.on_debug.is_some() => { + self.on_debug.as_ref().unwrap()(cast_to_string(&result, pos)?); + Ok(Dynamic::from_unit()) + } + KEYWORD_PRINT | KEYWORD_DEBUG => Ok(Dynamic::from_unit()), _ => Ok(result), }; } if let Some(prop) = extract_prop_from_getter(fn_name) { // Map property access - if let Some(map) = args[0].downcast_ref::() { - return Ok(map.get(prop).cloned().unwrap_or_else(|| ().into_dynamic())); + if let Ok(map) = args[0].as_map() { + return Ok(map + .get(prop) + .cloned() + .unwrap_or_else(|| Dynamic::from_unit())); } // Getter function not found @@ -540,12 +520,12 @@ impl Engine<'_> { } if let Some(prop) = extract_prop_from_setter(fn_name) { - let value = args[1].into_dynamic(); + let value = args[1].clone(); // Map property update - if let Some(map) = args[0].downcast_mut::() { + if let Dynamic(Union::Map(map)) = args[0] { map.insert(prop.to_string(), value); - return Ok(().into_dynamic()); + return Ok(Dynamic::from_unit()); } // Setter function not found @@ -592,9 +572,7 @@ impl Engine<'_> { let this_ptr = target.get_mut(scope); - let mut args: Vec<_> = once(this_ptr) - .chain(values.iter_mut().map(Dynamic::as_mut)) - .collect(); + let mut args: Vec<_> = once(this_ptr).chain(values.iter_mut()).collect(); let def_val = def_val.as_ref(); @@ -639,7 +617,7 @@ impl Engine<'_> { let mut args = [target.get_mut(scope)]; self.call_fn_raw(None, fn_lib, &make_getter(id), &mut args, None, *pos, 0) .and_then(|mut val| { - let target = Target::from(val.as_mut()); + let target = Target::from(&mut val); self.get_dot_val_helper(scope, fn_lib, target, rhs, level) }) } @@ -667,7 +645,7 @@ impl Engine<'_> { self.get_indexed_value(scope, fn_lib, &val, idx_expr, *op_pos, level) .and_then(|(mut val, _, _)| { - let target = Target::from(val.as_mut()); + let target = Target::from(&mut val); self.get_dot_val_helper(scope, fn_lib, target, rhs, level) }) } @@ -712,7 +690,7 @@ impl Engine<'_> { Expr::Index(idx_lhs, idx_expr, op_pos) => { let (idx_src_type, src, index, mut val) = self.eval_index_expr(scope, fn_lib, idx_lhs, idx_expr, *op_pos, level)?; - let target = Target::from(val.as_mut()); + let target = Target::from(&mut val); let value = self.get_dot_val_helper(scope, fn_lib, target, dot_rhs, level); // In case the expression mutated `target`, we need to update it back into the scope because it is cloned. @@ -743,7 +721,7 @@ impl Engine<'_> { // {expr}.??? expr => { let mut val = self.eval_expr(scope, fn_lib, expr, level)?; - self.get_dot_val_helper(scope, fn_lib, Target::from(val.as_mut()), dot_rhs, level) + self.get_dot_val_helper(scope, fn_lib, Target::from(&mut val), dot_rhs, level) } } } @@ -772,16 +750,21 @@ impl Engine<'_> { let idx_pos = idx_expr.position(); // val_array[idx] - if let Some(arr) = val.downcast_ref::() { + if let Ok(arr) = val.as_array() { let index = self .eval_expr(scope, fn_lib, idx_expr, level)? - .try_cast::() + .as_int() .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?; return if index >= 0 { arr.get(index as usize) - .cloned() - .map(|v| (v, IndexSourceType::Array, IndexValue::from_num(index))) + .map(|v| { + ( + v.clone(), + IndexSourceType::Array, + IndexValue::from_num(index), + ) + }) .ok_or_else(|| EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos)) } else { Err(EvalAltResult::ErrorArrayBounds(arr.len(), index, idx_pos)) @@ -789,26 +772,26 @@ impl Engine<'_> { } // val_map[idx] - if let Some(map) = val.downcast_ref::() { + if let Ok(map) = val.as_map() { let index = self .eval_expr(scope, fn_lib, idx_expr, level)? - .try_cast::() + .take_string() .map_err(|_| EvalAltResult::ErrorStringIndexExpr(idx_expr.position()))?; return Ok(( map.get(&index) .cloned() - .unwrap_or_else(|| ().into_dynamic()), + .unwrap_or_else(|| Dynamic::from_unit()), IndexSourceType::Map, IndexValue::from_str(index), )); } // val_string[idx] - if let Some(s) = val.downcast_ref::() { + if let Ok(s) = val.as_str() { let index = self .eval_expr(scope, fn_lib, idx_expr, level)? - .try_cast::() + .as_int() .map_err(|_| EvalAltResult::ErrorNumericIndexExpr(idx_expr.position()))?; return if index >= 0 { @@ -816,7 +799,7 @@ impl Engine<'_> { .nth(index as usize) .map(|ch| { ( - ch.into_dynamic(), + Dynamic::from_char(ch), IndexSourceType::String, IndexValue::from_num(index), ) @@ -921,14 +904,14 @@ impl Engine<'_> { IndexSourceType::Array => { let arr = scope.get_mut_by_type::(src); arr[idx.as_num()] = new_val.0; - Ok(().into_dynamic()) + Ok(Dynamic::from_unit()) } // map_id[idx] = val IndexSourceType::Map => { let arr = scope.get_mut_by_type::(src); arr.insert(idx.as_str(), new_val.0); - Ok(().into_dynamic()) + Ok(Dynamic::from_unit()) } // string_id[idx] = val @@ -938,10 +921,10 @@ impl Engine<'_> { // Value must be a character let ch = new_val .0 - .try_cast::() + .as_char() .map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?; Self::str_replace_char(s, idx.as_num(), ch); - Ok(().into_dynamic()) + Ok(Dynamic::from_unit()) } IndexSourceType::Expression => panic!("expression cannot be indexed for update"), @@ -955,27 +938,26 @@ impl Engine<'_> { new_val: Dynamic, pos: Position, ) -> Result { - if let Some(arr) = target.downcast_mut::() { - arr[idx.as_num()] = new_val; - return Ok(target); + match target { + Dynamic(Union::Array(ref mut arr)) => { + arr[idx.as_num()] = new_val; + } + Dynamic(Union::Map(ref mut map)) => { + map.insert(idx.as_str(), new_val); + } + Dynamic(Union::Str(ref mut s)) => { + // Value must be a character + let ch = new_val + .as_char() + .map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?; + + Self::str_replace_char(s, idx.as_num(), ch); + } + // All other variable types should be an error + _ => panic!("array, map or string source type expected for indexing"), } - if let Some(map) = target.downcast_mut::() { - map.insert(idx.as_str(), new_val); - return Ok(target); - } - - if let Some(s) = target.downcast_mut::() { - // Value must be a character - let ch = new_val - .try_cast::() - .map_err(|_| EvalAltResult::ErrorCharMismatch(pos))?; - Self::str_replace_char(s, idx.as_num(), ch); - return Ok(target); - } - - // All other variable types should be an error - panic!("array, map or string source type expected for indexing") + Ok(target) } /// Chain-evaluate a dot setter @@ -983,7 +965,7 @@ impl Engine<'_> { &self, scope: &mut Scope, fn_lib: Option<&FunctionsLib>, - this_ptr: &mut Variant, + this_ptr: &mut Dynamic, dot_rhs: &Expr, new_val: (&mut Dynamic, Position), level: usize, @@ -991,7 +973,7 @@ impl Engine<'_> { match dot_rhs { // xxx.id Expr::Property(id, pos) => { - let mut args = [this_ptr, new_val.0.as_mut()]; + let mut args = [this_ptr, new_val.0]; self.call_fn_raw(None, fn_lib, &make_setter(id), &mut args, None, *pos, 0) } @@ -1010,7 +992,7 @@ impl Engine<'_> { }) .and_then(|mut val| { let fn_name = make_setter(id); - let mut args = [this_ptr, val.as_mut()]; + let mut args = [this_ptr, &mut val]; self.call_fn_raw(None, fn_lib, &fn_name, &mut args, None, *pos, 0) }) } @@ -1029,13 +1011,12 @@ impl Engine<'_> { let fn_name = make_getter(id); self.call_fn_raw(None, fn_lib, &fn_name, &mut [this_ptr], None, *pos, 0) .and_then(|mut val| { - let value = val.as_mut(); - self.set_dot_val_helper(scope, fn_lib, value, rhs, new_val, level) + self.set_dot_val_helper(scope, fn_lib, &mut val, rhs, new_val, level) .map(|_| val) // Discard Ok return value }) .and_then(|mut val| { let fn_name = make_setter(id); - let mut args = [this_ptr, val.as_mut()]; + let mut args = [this_ptr, &mut val]; self.call_fn_raw(None, fn_lib, &fn_name, &mut args, None, *pos, 0) }) } @@ -1053,7 +1034,7 @@ impl Engine<'_> { )?; let val_pos = new_val.1; - let this_ptr = value.as_mut(); + let this_ptr = &mut value; self.set_dot_val_helper( scope, fn_lib, this_ptr, rhs, new_val, level, )?; @@ -1063,7 +1044,7 @@ impl Engine<'_> { }) .and_then(|mut v| { let fn_name = make_setter(id); - let mut args = [this_ptr, v.as_mut()]; + let mut args = [this_ptr, &mut v]; self.call_fn_raw(None, fn_lib, &fn_name, &mut args, None, *pos, 0) }) } @@ -1114,7 +1095,7 @@ impl Engine<'_> { _ => { // Avoid referencing scope which is used below as mut let entry = ScopeSource { name: id, ..src }; - let this_ptr = target.as_mut(); + let this_ptr = &mut target; let value = self .set_dot_val_helper(scope, fn_lib, this_ptr, dot_rhs, new_val, level); @@ -1132,7 +1113,7 @@ impl Engine<'_> { let (idx_src_type, src, index, mut target) = self.eval_index_expr(scope, fn_lib, lhs, idx_expr, *op_pos, level)?; let val_pos = new_val.1; - let this_ptr = target.as_mut(); + let this_ptr = &mut target; let value = self.set_dot_val_helper(scope, fn_lib, this_ptr, dot_rhs, new_val, level); @@ -1181,56 +1162,46 @@ impl Engine<'_> { let mut lhs_value = self.eval_expr(scope, fn_lib, lhs, level)?; let rhs_value = self.eval_expr(scope, fn_lib, rhs, level)?; - if rhs_value.is::() { - let mut rhs_value = rhs_value.cast::(); - let def_value = false.into_dynamic(); - let mut result = false; + match rhs_value { + Dynamic(Union::Array(mut rhs_value)) => { + let def_value = Dynamic::from_bool(false); + let mut result = false; - // Call the '==' operator to compare each value - for value in rhs_value.iter_mut() { - let args = &mut [lhs_value.as_mut(), value.as_mut()]; - let def_value = Some(&def_value); - if self - .call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)? - .try_cast::() - .unwrap_or(false) - { - result = true; - break; + // Call the '==' operator to compare each value + for value in rhs_value.iter_mut() { + let args = &mut [&mut lhs_value, value]; + let def_value = Some(&def_value); + if self + .call_fn_raw(None, fn_lib, "==", args, def_value, rhs.position(), level)? + .as_bool() + .unwrap_or(false) + { + result = true; + break; + } + } + + Ok(Dynamic::from_bool(result)) + } + Dynamic(Union::Map(rhs_value)) => { + // Only allows String or char + match lhs_value { + Dynamic(Union::Str(s)) => Ok(Dynamic::from_bool(rhs_value.contains_key(&s))), + Dynamic(Union::Char(c)) => { + Ok(Dynamic::from_bool(rhs_value.contains_key(&c.to_string()))) + } + _ => Err(EvalAltResult::ErrorInExpr(lhs.position())), } } - - Ok(result.into_dynamic()) - } else if rhs_value.is::() { - let rhs_value = rhs_value.cast::(); - - // Only allows String or char - if lhs_value.is::() { - Ok(rhs_value - .contains_key(&lhs_value.cast::()) - .into_dynamic()) - } else if lhs_value.is::() { - Ok(rhs_value - .contains_key(&lhs_value.cast::().to_string()) - .into_dynamic()) - } else { - Err(EvalAltResult::ErrorInExpr(lhs.position())) + Dynamic(Union::Str(rhs_value)) => { + // Only allows String or char + match lhs_value { + Dynamic(Union::Str(s)) => Ok(Dynamic::from_bool(rhs_value.contains(&s))), + Dynamic(Union::Char(c)) => Ok(Dynamic::from_bool(rhs_value.contains(c))), + _ => Err(EvalAltResult::ErrorInExpr(lhs.position())), + } } - } else if rhs_value.is::() { - let rhs_value = rhs_value.cast::(); - - // Only allows String or char - if lhs_value.is::() { - Ok(rhs_value - .contains(&lhs_value.cast::()) - .into_dynamic()) - } else if lhs_value.is::() { - Ok(rhs_value.contains(lhs_value.cast::()).into_dynamic()) - } else { - Err(EvalAltResult::ErrorInExpr(lhs.position())) - } - } else { - Err(EvalAltResult::ErrorInExpr(rhs.position())) + _ => Err(EvalAltResult::ErrorInExpr(rhs.position())), } } @@ -1243,10 +1214,11 @@ impl Engine<'_> { level: usize, ) -> Result { match expr { - Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()), - Expr::FloatConstant(f, _) => Ok(f.into_dynamic()), - Expr::StringConstant(s, _) => Ok(s.clone().into_owned().into_dynamic()), - Expr::CharConstant(c, _) => Ok(c.into_dynamic()), + Expr::IntegerConstant(i, _) => Ok(Dynamic::from_int(*i)), + #[cfg(not(feature = "no_float"))] + Expr::FloatConstant(f, _) => Ok(Dynamic::from_float(*f)), + Expr::StringConstant(s, _) => Ok(Dynamic::from_string(s.to_string())), + Expr::CharConstant(c, _) => Ok(Dynamic::from_char(*c)), Expr::Variable(id, pos) => Self::search_scope(scope, id, *pos).map(|(_, val)| val), Expr::Property(_, _) => panic!("unexpected property."), @@ -1262,7 +1234,7 @@ impl Engine<'_> { Expr::Variable(name, pos) => match scope.get(name) { None => { return Err(EvalAltResult::ErrorVariableNotFound( - name.clone().into_owned(), + name.to_string(), *pos, )) } @@ -1358,7 +1330,7 @@ impl Engine<'_> { .map(|val| arr.push(val)) })?; - Ok((arr).into_dynamic()) + Ok(Dynamic(Union::Array(arr))) } #[cfg(not(feature = "no_object"))] @@ -1371,7 +1343,7 @@ impl Engine<'_> { }) })?; - Ok((map).into_dynamic()) + Ok(Dynamic(Union::Map(Box::new(map)))) } Expr::FunctionCall(fn_name, args_expr_list, def_val, pos) => { @@ -1396,10 +1368,9 @@ impl Engine<'_> { && !has_override(self, fn_lib, KEYWORD_TYPE_OF) => { let result = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?; - Ok(self - .map_type_name((*result).type_name()) - .to_string() - .into_dynamic()) + Ok(Dynamic::from_string( + self.map_type_name(result.type_name()).to_string(), + )) } // eval @@ -1411,15 +1382,9 @@ impl Engine<'_> { let result = self.eval_expr(scope, fn_lib, &args_expr_list[0], level)?; // Get the script text by evaluating the expression - let script = result - .downcast_ref::() - .map(String::as_str) - .ok_or_else(|| { - EvalAltResult::ErrorMismatchOutputType( - result.type_name().into(), - pos, - ) - })?; + let script = result.as_str().map_err(|type_name| { + EvalAltResult::ErrorMismatchOutputType(type_name.into(), pos) + })?; // Compile the script text // No optimizations because we only run it once @@ -1461,7 +1426,7 @@ impl Engine<'_> { .map(|expr| self.eval_expr(scope, fn_lib, expr, level)) .collect::, _>>()?; - let mut args: Vec<_> = arg_values.iter_mut().map(Dynamic::as_mut).collect(); + let mut args: Vec<_> = arg_values.iter_mut().collect(); let def_val = def_val.as_ref(); self.call_fn_raw(None, fn_lib, fn_name, &mut args, def_val, *pos, level) } @@ -1472,41 +1437,41 @@ impl Engine<'_> { self.eval_in_expr(scope, fn_lib, lhs.as_ref(), rhs.as_ref(), level) } - Expr::And(lhs, rhs, _) => Ok(Box::new( + Expr::And(lhs, rhs, _) => Ok(Dynamic::from_bool( self .eval_expr(scope, fn_lib,lhs.as_ref(), level)? - .try_cast::() + .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("AND".into(), lhs.position()) })? && // Short-circuit using && self .eval_expr(scope, fn_lib,rhs.as_ref(), level)? - .try_cast::() + .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("AND".into(), rhs.position()) })?, )), - Expr::Or(lhs, rhs, _) => Ok(Box::new( + Expr::Or(lhs, rhs, _) => Ok(Dynamic::from_bool( self .eval_expr(scope,fn_lib, lhs.as_ref(), level)? - .try_cast::() + .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("OR".into(), lhs.position()) })? || // Short-circuit using || self .eval_expr(scope,fn_lib, rhs.as_ref(), level)? - .try_cast::() + .as_bool() .map_err(|_| { EvalAltResult::ErrorBooleanArgMismatch("OR".into(), rhs.position()) })?, )), - Expr::True(_) => Ok(true.into_dynamic()), - Expr::False(_) => Ok(false.into_dynamic()), - Expr::Unit(_) => Ok(().into_dynamic()), + Expr::True(_) => Ok(Dynamic::from_bool(true)), + Expr::False(_) => Ok(Dynamic::from_bool(false)), + Expr::Unit(_) => Ok(Dynamic::from_unit()), _ => panic!("should not appear: {:?}", expr), } @@ -1522,7 +1487,7 @@ impl Engine<'_> { ) -> Result { match stmt { // No-op - Stmt::Noop(_) => Ok(().into_dynamic()), + Stmt::Noop(_) => Ok(Dynamic::from_unit()), // Expression as statement Stmt::Expr(expr) => { @@ -1532,7 +1497,7 @@ impl Engine<'_> { result } else { // If it is an assignment, erase the result at the root - ().into_dynamic() + Dynamic::from_unit() }) } @@ -1540,7 +1505,7 @@ impl Engine<'_> { Stmt::Block(block, _) => { let prev_len = scope.len(); - let result = block.iter().try_fold(().into_dynamic(), |_, stmt| { + let result = block.iter().try_fold(Dynamic::from_unit(), |_, stmt| { self.eval_stmt(scope, fn_lib, stmt, level) }); @@ -1552,7 +1517,7 @@ impl Engine<'_> { // If-else statement Stmt::IfThenElse(guard, if_body, else_body) => self .eval_expr(scope, fn_lib, guard, level)? - .try_cast::() + .as_bool() .map_err(|_| EvalAltResult::ErrorLogicGuard(guard.position())) .and_then(|guard_val| { if guard_val { @@ -1560,26 +1525,23 @@ impl Engine<'_> { } else if let Some(stmt) = else_body { self.eval_stmt(scope, fn_lib, stmt.as_ref(), level) } else { - Ok(().into_dynamic()) + Ok(Dynamic::from_unit()) } }), // While loop Stmt::While(guard, body) => loop { - match self - .eval_expr(scope, fn_lib, guard, level)? - .try_cast::() - { + match self.eval_expr(scope, fn_lib, guard, level)?.as_bool() { Ok(guard_val) if guard_val => { match self.eval_stmt(scope, fn_lib, body, level) { Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (), Err(EvalAltResult::ErrorLoopBreak(true, _)) => { - return Ok(().into_dynamic()) + return Ok(Dynamic::from_unit()) } Err(x) => return Err(x), } } - Ok(_) => return Ok(().into_dynamic()), + Ok(_) => return Ok(Dynamic::from_unit()), Err(_) => return Err(EvalAltResult::ErrorLogicGuard(guard.position())), } }, @@ -1588,7 +1550,7 @@ impl Engine<'_> { Stmt::Loop(body) => loop { match self.eval_stmt(scope, fn_lib, body, level) { Ok(_) | Err(EvalAltResult::ErrorLoopBreak(false, _)) => (), - Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(().into_dynamic()), + Err(EvalAltResult::ErrorLoopBreak(true, _)) => return Ok(Dynamic::from_unit()), Err(x) => return Err(x), } }, @@ -1596,7 +1558,7 @@ impl Engine<'_> { // For loop Stmt::For(name, expr, body) => { let arr = self.eval_expr(scope, fn_lib, expr, level)?; - let tid = Any::type_id(arr.as_ref()); + let tid = arr.type_id(); if let Some(iter_fn) = self.type_iterators.as_ref().and_then(|t| t.get(&tid)) { // Add the loop variable - variable name is copied @@ -1620,7 +1582,7 @@ impl Engine<'_> { } scope.rewind(scope.len() - 1); - Ok(().into_dynamic()) + Ok(Dynamic::from_unit()) } else { Err(EvalAltResult::ErrorFor(expr.position())) } @@ -1634,7 +1596,7 @@ impl Engine<'_> { // Empty return Stmt::ReturnWithVal(None, ReturnType::Return, pos) => { - Err(EvalAltResult::Return(().into_dynamic(), *pos)) + Err(EvalAltResult::Return(Dynamic::from_unit(), *pos)) } // Return value @@ -1652,7 +1614,7 @@ impl Engine<'_> { Stmt::ReturnWithVal(Some(a), ReturnType::Exception, pos) => { let val = self.eval_expr(scope, fn_lib, a, level)?; Err(EvalAltResult::ErrorRuntime( - val.try_cast::().unwrap_or_else(|_| "".to_string()), + val.take_string().unwrap_or_else(|_| "".to_string()), *pos, )) } @@ -1662,13 +1624,13 @@ impl Engine<'_> { let val = self.eval_expr(scope, fn_lib, expr, level)?; // TODO - avoid copying variable name in inner block? scope.push_dynamic_value(name.clone(), ScopeEntryType::Normal, val, false); - Ok(().into_dynamic()) + Ok(Dynamic::from_unit()) } Stmt::Let(name, None, _) => { // TODO - avoid copying variable name in inner block? scope.push(name.clone(), ()); - Ok(().into_dynamic()) + Ok(Dynamic::from_unit()) } // Const statement @@ -1676,7 +1638,7 @@ impl Engine<'_> { let val = self.eval_expr(scope, fn_lib, expr, level)?; // TODO - avoid copying variable name in inner block? scope.push_dynamic_value(name.clone(), ScopeEntryType::Constant, val, true); - Ok(().into_dynamic()) + Ok(Dynamic::from_unit()) } Stmt::Const(_, _, _) => panic!("constant expression not constant!"), diff --git a/src/fn_call.rs b/src/fn_call.rs index c81b25bc..bdb2ec4b 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -2,8 +2,7 @@ #![allow(non_snake_case)] -use crate::any::{Any, Dynamic}; - +use crate::any::{Dynamic, Variant}; use crate::stdlib::{string::String, vec, vec::Vec}; /// Trait that represent arguments to a function call. @@ -18,7 +17,7 @@ pub trait FuncArgs { /// converted into `Dynamic`). macro_rules! impl_args { ($($p:ident),*) => { - impl<$($p: Any + Clone),*> FuncArgs for ($($p,)*) + impl<$($p: Variant + Clone),*> FuncArgs for ($($p,)*) { fn into_vec(self) -> Vec { let ($($p,)*) = self; diff --git a/src/fn_func.rs b/src/fn_func.rs index 7350d6d5..af45dae8 100644 --- a/src/fn_func.rs +++ b/src/fn_func.rs @@ -2,7 +2,7 @@ #![cfg(not(feature = "no_function"))] #![allow(non_snake_case)] -use crate::any::Any; +use crate::any::Variant; use crate::engine::Engine; use crate::error::ParseError; use crate::parser::AST; @@ -88,7 +88,7 @@ macro_rules! def_anonymous_fn { def_anonymous_fn!(imp); }; (imp $($par:ident),*) => { - impl<'e, $($par: Any + Clone,)* RET: Any + Clone> Func<($($par,)*), RET> for Engine<'e> + impl<'e, $($par: Variant + Clone,)* RET: Variant + Clone> Func<($($par,)*), RET> for Engine<'e> { #[cfg(feature = "sync")] type Output = Box Result + Send + Sync + 'e>; diff --git a/src/fn_register.rs b/src/fn_register.rs index 9030066a..a85dc094 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -2,7 +2,7 @@ #![allow(non_snake_case)] -use crate::any::{Any, Dynamic}; +use crate::any::{Dynamic, Variant}; use crate::engine::{Engine, FnCallArgs}; use crate::parser::Position; use crate::result::EvalAltResult; @@ -53,7 +53,7 @@ pub trait RegisterDynamicFn { /// /// // Function that returns a Dynamic value /// fn return_the_same_as_dynamic(x: i64) -> Dynamic { - /// Box::new(x) + /// Dynamic::from(x) /// } /// /// let mut engine = Engine::new(); @@ -137,7 +137,7 @@ macro_rules! def_register { // ^ function parameter actual type (T, &T or &mut T) // ^ dereferencing function impl< - $($par: Any + Clone,)* + $($par: Variant + Clone,)* #[cfg(feature = "sync")] FN: Fn($($param),*) -> RET + Send + Sync + 'static, @@ -145,7 +145,7 @@ macro_rules! def_register { #[cfg(not(feature = "sync"))] FN: Fn($($param),*) -> RET + 'static, - RET: Any + RET: Variant + Clone > RegisterFn for Engine<'_> { fn register_fn(&mut self, name: &str, f: FN) { @@ -163,13 +163,13 @@ macro_rules! def_register { let mut drain = args.iter_mut(); $( // Downcast every element, return in case of a type mismatch - let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap(); + let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap(); )* // Call the user-supplied function using ($clone) to // potentially clone the value, otherwise pass the reference. let r = f($(($clone)($par)),*); - Ok(Box::new(r) as Dynamic) + Ok(r.into_dynamic()) }; self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func)); @@ -177,7 +177,7 @@ macro_rules! def_register { } impl< - $($par: Any + Clone,)* + $($par: Variant + Clone,)* #[cfg(feature = "sync")] FN: Fn($($param),*) -> Dynamic + Send + Sync + 'static, @@ -201,7 +201,7 @@ macro_rules! def_register { let mut drain = args.iter_mut(); $( // Downcast every element, return in case of a type mismatch - let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap(); + let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap(); )* // Call the user-supplied function using ($clone) to @@ -213,14 +213,14 @@ macro_rules! def_register { } impl< - $($par: Any + Clone,)* + $($par: Variant + Clone,)* #[cfg(feature = "sync")] FN: Fn($($param),*) -> Result + Send + Sync + 'static, #[cfg(not(feature = "sync"))] FN: Fn($($param),*) -> Result + 'static, - RET: Any + RET: Variant + Clone > RegisterResultFn for Engine<'_> { fn register_result_fn(&mut self, name: &str, f: FN) { @@ -238,12 +238,12 @@ macro_rules! def_register { let mut drain = args.iter_mut(); $( // Downcast every element, return in case of a type mismatch - let $par = drain.next().unwrap().downcast_mut::<$par>().unwrap(); + let $par: &mut $par = drain.next().unwrap().downcast_mut().unwrap(); )* // Call the user-supplied function using ($clone) to // potentially clone the value, otherwise pass the reference. - f($(($clone)($par)),*).map(|r| Box::new(r) as Dynamic) + f($(($clone)($par)),*).map(|r| r.into_dynamic()) .map_err(|err| err.set_position(pos)) }; self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func)); diff --git a/src/lib.rs b/src/lib.rs index dd4155ec..66dc90af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ mod result; mod scope; mod stdlib; -pub use any::{Any, AnyExt, Dynamic, Variant}; +pub use any::Dynamic; pub use engine::Engine; pub use error::{ParseError, ParseErrorType}; pub use fn_call::FuncArgs; diff --git a/src/optimize.rs b/src/optimize.rs index fc5a4577..9852b649 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -1,4 +1,4 @@ -use crate::any::{Any, Dynamic}; +use crate::any::Dynamic; use crate::engine::{ Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF, @@ -116,7 +116,7 @@ fn call_fn( ) -> Result, EvalAltResult> { let spec = FnSpec { name: fn_name.into(), - args: args.iter().map(|a| Any::type_id(*a)).collect(), + args: args.iter().map(|a| a.type_id()).collect(), }; // Search built-in's and external functions @@ -570,7 +570,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { } let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect(); - let mut call_args: Vec<_> = arg_values.iter_mut().map(Dynamic::as_mut).collect(); + let mut call_args: Vec<_> = arg_values.iter_mut().collect(); // Save the typename of the first argument if it is `type_of()` // This is to avoid `call_args` being passed into the closure @@ -585,7 +585,7 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr { result.or_else(|| { if !arg_for_type_of.is_empty() { // Handle `type_of()` - Some(arg_for_type_of.to_string().into_dynamic()) + Some(Dynamic::from_string(arg_for_type_of.to_string())) } else { // Otherwise use the default value, if any def_value.clone() diff --git a/src/parser.rs b/src/parser.rs index 9874edcf..98cc9b55 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,7 @@ //! Main module defining the lexer and parser. -use crate::any::{Any, AnyExt, Dynamic}; -use crate::engine::{Array, Engine, FunctionsLib, Map}; +use crate::any::{Dynamic, Union}; +use crate::engine::{Engine, FunctionsLib}; use crate::error::{LexError, ParseError, ParseErrorType}; use crate::optimize::{optimize_into_ast, OptimizationLevel}; use crate::scope::{EntryType as ScopeEntryType, Scope}; @@ -440,25 +440,30 @@ impl Expr { /// Panics when the expression is not constant. pub fn get_constant_value(&self) -> Dynamic { match self { - Self::IntegerConstant(i, _) => i.into_dynamic(), - Self::FloatConstant(f, _) => f.into_dynamic(), - Self::CharConstant(c, _) => c.into_dynamic(), - Self::StringConstant(s, _) => s.clone().into_owned().into_dynamic(), - Self::True(_) => true.into_dynamic(), - Self::False(_) => false.into_dynamic(), - Self::Unit(_) => ().into_dynamic(), + Self::IntegerConstant(i, _) => Dynamic::from_int(*i), + #[cfg(not(feature = "no_float"))] + Self::FloatConstant(f, _) => Dynamic::from_float(*f), + Self::CharConstant(c, _) => Dynamic::from_char(*c), + Self::StringConstant(s, _) => Dynamic::from_string(s.to_string()), + Self::True(_) => Dynamic::from_bool(true), + Self::False(_) => Dynamic::from_bool(false), + Self::Unit(_) => Dynamic::from_unit(), - Self::Array(items, _) if items.iter().all(Self::is_constant) => items - .iter() - .map(Self::get_constant_value) - .collect::>() - .into_dynamic(), + Self::Array(items, _) if items.iter().all(Self::is_constant) => Dynamic(Union::Array( + items + .iter() + .map(Self::get_constant_value) + .collect::>(), + )), - Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => items - .iter() - .map(|(k, v, _)| (k.clone(), v.get_constant_value())) - .collect::>() - .into_dynamic(), + Self::Map(items, _) if items.iter().all(|(_, v, _)| v.is_constant()) => { + Dynamic(Union::Map(Box::new( + items + .iter() + .map(|(k, v, _)| (k.clone(), v.get_constant_value())) + .collect::>(), + ))) + } _ => panic!("cannot get value of non-constant expression"), } @@ -1958,7 +1963,7 @@ fn parse_unary<'a>( Ok(Expr::FunctionCall( "!".into(), vec![parse_primary(input, allow_stmt_expr)?], - Some(Box::new(false)), // NOT operator, when operating on invalid operand, defaults to false + Some(Dynamic::from_bool(false)), // NOT operator, when operating on invalid operand, defaults to false pos, )) } @@ -2270,37 +2275,37 @@ fn parse_binary_op<'a>( Token::EqualsTo => Expr::FunctionCall( "==".into(), vec![current_lhs, rhs], - Some((false).into_dynamic()), + Some(Dynamic::from_bool(false)), pos, ), Token::NotEqualsTo => Expr::FunctionCall( "!=".into(), vec![current_lhs, rhs], - Some((false).into_dynamic()), + Some(Dynamic::from_bool(false)), pos, ), Token::LessThan => Expr::FunctionCall( "<".into(), vec![current_lhs, rhs], - Some((false).into_dynamic()), + Some(Dynamic::from_bool(false)), pos, ), Token::LessThanEqualsTo => Expr::FunctionCall( "<=".into(), vec![current_lhs, rhs], - Some((false).into_dynamic()), + Some(Dynamic::from_bool(false)), pos, ), Token::GreaterThan => Expr::FunctionCall( ">".into(), vec![current_lhs, rhs], - Some((false).into_dynamic()), + Some(Dynamic::from_bool(false)), pos, ), Token::GreaterThanEqualsTo => Expr::FunctionCall( ">=".into(), vec![current_lhs, rhs], - Some((false).into_dynamic()), + Some(Dynamic::from_bool(false)), pos, ), @@ -2845,67 +2850,50 @@ pub fn parse<'a, 'e>( /// /// Returns Some(expression) if conversion is successful. Otherwise None. pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { - if value.is::() { - Some(Expr::IntegerConstant(value.cast(), pos)) - } else if value.is::() { - Some(Expr::CharConstant(value.cast(), pos)) - } else if value.is::() { - Some(Expr::StringConstant(value.cast::().into(), pos)) - } else if value.is::() { - Some(if value.cast::() { - Expr::True(pos) - } else { - Expr::False(pos) - }) - } else { + match value.0 { + 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.into(), pos)), + Union::Bool(true) => Some(Expr::True(pos)), + Union::Bool(false) => Some(Expr::False(pos)), #[cfg(not(feature = "no_index"))] - { - if value.is::() { - let array = value.cast::(); - let items: Vec<_> = array - .into_iter() - .map(|x| map_dynamic_to_expr(x, pos)) - .collect(); - if items.iter().all(Option::is_some) { - return Some(Expr::Array( - items.into_iter().map(Option::unwrap).collect(), - pos, - )); - } else { - return None; - } + Union::Array(array) => { + let items: Vec<_> = array + .into_iter() + .map(|x| map_dynamic_to_expr(x, pos)) + .collect(); + + if items.iter().all(Option::is_some) { + Some(Expr::Array( + items.into_iter().map(Option::unwrap).collect(), + pos, + )) + } else { + None } } - #[cfg(not(feature = "no_object"))] - { - if value.is::() { - let map = value.cast::(); - let items: Vec<_> = map - .into_iter() - .map(|(k, v)| (k, map_dynamic_to_expr(v, pos), pos)) - .collect(); - if items.iter().all(|(_, expr, _)| expr.is_some()) { - return Some(Expr::Map( - items - .into_iter() - .map(|(k, expr, pos)| (k, expr.unwrap(), pos)) - .collect(), - pos, - )); - } else { - return None; - } + Union::Map(map) => { + let items: Vec<_> = map + .into_iter() + .map(|(k, v)| (k, map_dynamic_to_expr(v, pos), pos)) + .collect(); + if items.iter().all(|(_, expr, _)| expr.is_some()) { + Some(Expr::Map( + items + .into_iter() + .map(|(k, expr, pos)| (k, expr.unwrap(), pos)) + .collect(), + pos, + )) + } else { + None } } - #[cfg(not(feature = "no_float"))] - { - if value.is::() { - return Some(Expr::FloatConstant(value.cast(), pos)); - } - } + Union::Float(value) => Some(Expr::FloatConstant(value, pos)), - None + _ => None, } } diff --git a/src/scope.rs b/src/scope.rs index b5e22170..203d2402 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,6 +1,6 @@ //! Module that defines the `Scope` type representing a function call-stack scope. -use crate::any::{Any, Dynamic}; +use crate::any::{Dynamic, Variant}; use crate::parser::{map_dynamic_to_expr, Expr, Position}; use crate::stdlib::{ @@ -20,7 +20,7 @@ pub enum EntryType { } /// An entry in the Scope. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Entry<'a> { /// Name of the entry. pub name: Cow<'a, str>, @@ -68,7 +68,7 @@ pub(crate) struct EntryRef<'a> { /// allowing for automatic _shadowing_. /// /// Currently, `Scope` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Scope<'a>(Vec>); impl<'a> Scope<'a> { @@ -157,8 +157,8 @@ impl<'a> Scope<'a> { /// my_scope.push("x", 42_i64); /// assert_eq!(my_scope.get_value::("x").unwrap(), 42); /// ``` - pub fn push>, T: Any + Clone>(&mut self, name: K, value: T) { - self.push_dynamic_value(name, EntryType::Normal, value.into_dynamic(), false); + pub fn push>, T: Variant + Clone>(&mut self, name: K, value: T) { + self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value), false); } /// Add (push) a new `Dynamic` entry to the Scope. @@ -166,11 +166,11 @@ impl<'a> Scope<'a> { /// # Examples /// /// ``` - /// use rhai::{Any, Scope}; + /// use rhai::{Dynamic, Scope}; /// /// let mut my_scope = Scope::new(); /// - /// my_scope.push_dynamic("x", (42_i64).into_dynamic()); + /// my_scope.push_dynamic("x", Dynamic::from(42_i64)); /// assert_eq!(my_scope.get_value::("x").unwrap(), 42); /// ``` pub fn push_dynamic>>(&mut self, name: K, value: Dynamic) { @@ -195,8 +195,8 @@ impl<'a> Scope<'a> { /// my_scope.push_constant("x", 42_i64); /// assert_eq!(my_scope.get_value::("x").unwrap(), 42); /// ``` - pub fn push_constant>, T: Any + Clone>(&mut self, name: K, value: T) { - self.push_dynamic_value(name, EntryType::Constant, value.into_dynamic(), true); + pub fn push_constant>, T: Variant + Clone>(&mut self, name: K, value: T) { + self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value), true); } /// Add (push) a new constant with a `Dynamic` value to the Scope. @@ -211,11 +211,11 @@ impl<'a> Scope<'a> { /// # Examples /// /// ``` - /// use rhai::{Any, Scope}; + /// use rhai::{Dynamic, Scope}; /// /// let mut my_scope = Scope::new(); /// - /// my_scope.push_constant_dynamic("x", (42_i64).into_dynamic()); + /// my_scope.push_constant_dynamic("x", Dynamic::from(42_i64)); /// assert_eq!(my_scope.get_value::("x").unwrap(), 42); /// ``` pub fn push_constant_dynamic>>(&mut self, name: K, value: Dynamic) { @@ -336,13 +336,12 @@ impl<'a> Scope<'a> { /// my_scope.push("x", 42_i64); /// assert_eq!(my_scope.get_value::("x").unwrap(), 42); /// ``` - pub fn get_value(&self, name: &str) -> Option { + pub fn get_value(&self, name: &str) -> Option { self.0 .iter() .rev() .find(|Entry { name: key, .. }| name == key) - .and_then(|Entry { value, .. }| value.downcast_ref::()) - .map(T::clone) + .and_then(|Entry { value, .. }| value.downcast_ref::().cloned()) } /// Update the value of the named entry. @@ -366,7 +365,7 @@ impl<'a> Scope<'a> { /// my_scope.set_value("x", 0_i64); /// assert_eq!(my_scope.get_value::("x").unwrap(), 0); /// ``` - pub fn set_value(&mut self, name: &'a str, value: T) { + pub fn set_value(&mut self, name: &'a str, value: T) { match self.get(name) { Some(( EntryRef { @@ -382,8 +381,8 @@ impl<'a> Scope<'a> { .. }, _, - )) => self.0.get_mut(index).unwrap().value = value.into_dynamic(), - None => self.push(name, value.into_dynamic()), + )) => self.0.get_mut(index).unwrap().value = Dynamic::from(value), + None => self.push(name, value), } } @@ -403,7 +402,7 @@ impl<'a> Scope<'a> { } /// Get a mutable reference to an entry in the Scope and downcast it to a specific type - pub(crate) fn get_mut_by_type(&mut self, key: EntryRef) -> &mut T { + pub(crate) fn get_mut_by_type(&mut self, key: EntryRef) -> &mut T { self.get_mut(key) .downcast_mut::() .expect("wrong type cast") diff --git a/tests/maps.rs b/tests/maps.rs index ac591966..c1b91187 100644 --- a/tests/maps.rs +++ b/tests/maps.rs @@ -1,6 +1,6 @@ #![cfg(not(feature = "no_object"))] -use rhai::{AnyExt, Engine, EvalAltResult, Map, Scope, INT}; +use rhai::{Engine, EvalAltResult, Map, Scope, INT}; #[test] fn test_map_indexing() -> Result<(), EvalAltResult> {