From 54f78c5cd98bd74fb06345eef5caae6c61d1fe7f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 5 Dec 2021 17:05:19 +0800 Subject: [PATCH 01/13] Use zero for no hash. --- src/ast.rs | 23 +++++++++------- src/func/call.rs | 15 ++++++----- src/func/hashing.rs | 65 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 688bb3b2..33b784d0 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,6 +1,7 @@ //! Module defining the AST (abstract syntax tree). use crate::calc_fn_hash; +use crate::func::hashing::DEFAULT_HASH; use crate::module::NamespaceRef; use crate::tokenizer::Token; use crate::types::dynamic::Union; @@ -1801,7 +1802,7 @@ impl OpAssignment<'_> { pub struct FnCallHashes { /// Pre-calculated hash for a script-defined function ([`None`] if native functions only). #[cfg(not(feature = "no_function"))] - pub script: Option, + pub script: u64, /// Pre-calculated hash for a native Rust function with no parameter types. pub native: u64, } @@ -1809,11 +1810,11 @@ pub struct FnCallHashes { impl fmt::Debug for FnCallHashes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(not(feature = "no_function"))] - if let Some(script) = self.script { - return if script == self.native { + if self.script != 0 { + return if self.script == self.native { fmt::Debug::fmt(&self.native, f) } else { - write!(f, "({}, {})", script, self.native) + write!(f, "({}, {})", self.script, self.native) }; } @@ -1824,9 +1825,11 @@ impl fmt::Debug for FnCallHashes { impl From for FnCallHashes { #[inline(always)] fn from(hash: u64) -> Self { + let hash = if hash == 0 { DEFAULT_HASH } else { hash }; + Self { #[cfg(not(feature = "no_function"))] - script: Some(hash), + script: hash, native: hash, } } @@ -1839,8 +1842,8 @@ impl FnCallHashes { pub const fn from_native(hash: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: None, - native: hash, + script: 0, + native: if hash == 0 { DEFAULT_HASH } else { hash }, } } /// Create a [`FnCallHashes`] with both native Rust and script function hashes. @@ -1849,8 +1852,8 @@ impl FnCallHashes { pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: Some(script), - native, + script: if script == 0 { DEFAULT_HASH } else { script }, + native: if native == 0 { DEFAULT_HASH } else { native }, } } /// Is this [`FnCallHashes`] native Rust only? @@ -1858,7 +1861,7 @@ impl FnCallHashes { #[must_use] pub const fn is_native_only(&self) -> bool { #[cfg(not(feature = "no_function"))] - return self.script.is_none(); + return self.script == 0; #[cfg(feature = "no_function")] return true; diff --git a/src/func/call.rs b/src/func/call.rs index aab599c2..4f0e1018 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -177,6 +177,10 @@ impl Engine { allow_dynamic: bool, is_op_assignment: bool, ) -> Option<&'s FnResolutionCacheEntry> { + if hash_script == 0 { + return None; + } + let fn_name = fn_name.as_ref(); let mut hash = args.as_ref().map_or(hash_script, |args| { @@ -562,13 +566,10 @@ impl Engine { // Scripted function call? #[cfg(not(feature = "no_function"))] - let hash_script = hashes.script; - - #[cfg(not(feature = "no_function"))] - if let Some(FnResolutionCacheEntry { func, source }) = hash_script.and_then(|hash| { - self.resolve_fn(mods, state, lib, fn_name, hash, None, false, false) - .cloned() - }) { + if let Some(FnResolutionCacheEntry { func, source }) = self + .resolve_fn(mods, state, lib, fn_name, hashes.script, None, false, false) + .cloned() + { // Script function call assert!(func.is_script()); diff --git a/src/func/hashing.rs b/src/func/hashing.rs index d0ede330..0159e84e 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -8,7 +8,13 @@ use std::{ iter::empty, }; -/// A hasher that only takes one single [`u64`] and returns it as a hash key. +pub const DEFAULT_HASH: u64 = 42; + +/// A hasher that only takes one single [`u64`] and returns it as a non-zero hash key. +/// +/// # Zeros +/// +/// If the value is zero, then it is mapped to `DEFAULT_HASH`. /// /// # Panics /// @@ -29,6 +35,10 @@ impl Hasher for StraightHasher { key.copy_from_slice(bytes); self.0 = u64::from_ne_bytes(key); + + if self.0 == 0 { + self.0 = DEFAULT_HASH + } } } @@ -41,7 +51,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] fn build_hasher(&self) -> Self::Hasher { - StraightHasher(42) + StraightHasher(DEFAULT_HASH) } } @@ -52,11 +62,15 @@ pub fn get_hasher() -> ahash::AHasher { ahash::AHasher::default() } -/// Calculate a [`u64`] hash key from a namespace-qualified variable name. +/// Calculate a non-zero [`u64`] hash key from a namespace-qualified variable name. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator. /// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. +/// /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. @@ -76,15 +90,23 @@ pub fn calc_qualified_var_hash<'a>( .for_each(|m| m.as_ref().hash(s)); len.hash(s); var_name.as_ref().hash(s); - s.finish() + + match s.finish() { + 0 => DEFAULT_HASH, + r => r, + } } -/// Calculate a [`u64`] hash key from a namespace-qualified function name +/// Calculate a non-zero [`u64`] hash key from a namespace-qualified function name /// and the number of parameters, but no parameter types. /// /// Module names are passed in via `&str` references from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator. /// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. +/// /// # Note /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. @@ -106,22 +128,34 @@ pub fn calc_qualified_fn_hash( len.hash(s); fn_name.as_ref().hash(s); num.hash(s); - s.finish() + + match s.finish() { + 0 => DEFAULT_HASH, + r => r, + } } -/// Calculate a [`u64`] hash key from a non-namespace-qualified function name +/// Calculate a non-zero [`u64`] hash key from a non-namespace-qualified function name /// and the number of parameters, but no parameter types. /// /// Parameter types are passed in via [`TypeId`] values from an iterator. +/// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. #[inline(always)] #[must_use] pub fn calc_fn_hash(fn_name: impl AsRef, num: usize) -> u64 { calc_qualified_fn_hash(empty::<&str>(), fn_name, num) } -/// Calculate a [`u64`] hash key from a list of parameter types. +/// Calculate a non-zero [`u64`] hash key from a list of parameter types. /// /// Parameter types are passed in via [`TypeId`] values from an iterator. +/// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. #[inline] #[must_use] pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { @@ -129,12 +163,23 @@ pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { let mut len = 0; params.inspect(|_| len += 1).for_each(|t| t.hash(s)); len.hash(s); - s.finish() + + match s.finish() { + 0 => DEFAULT_HASH, + r => r, + } } /// Combine two [`u64`] hashes by taking the XOR of them. +/// +/// # Zeros +/// +/// If the hash happens to be zero, it is mapped to `DEFAULT_HASH`. #[inline(always)] #[must_use] pub const fn combine_hashes(a: u64, b: u64) -> u64 { - a ^ b + match a ^ b { + 0 => DEFAULT_HASH, + r => r, + } } From 206b5051eb411f11ff061c4facf266bc9456a043 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 6 Dec 2021 10:34:13 +0800 Subject: [PATCH 02/13] Clarify non-zero hashes. --- src/ast.rs | 12 ++++++------ src/func/hashing.rs | 27 +++++++++++++++++++-------- src/module/mod.rs | 19 +++++++++---------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 33b784d0..c4778bef 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,7 +1,7 @@ //! Module defining the AST (abstract syntax tree). use crate::calc_fn_hash; -use crate::func::hashing::DEFAULT_HASH; +use crate::func::hashing::ALT_ZERO_HASH; use crate::module::NamespaceRef; use crate::tokenizer::Token; use crate::types::dynamic::Union; @@ -1800,7 +1800,7 @@ impl OpAssignment<'_> { /// to possible function overloading for different parameter types. #[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] pub struct FnCallHashes { - /// Pre-calculated hash for a script-defined function ([`None`] if native functions only). + /// Pre-calculated hash for a script-defined function (zero if native functions only). #[cfg(not(feature = "no_function"))] pub script: u64, /// Pre-calculated hash for a native Rust function with no parameter types. @@ -1825,7 +1825,7 @@ impl fmt::Debug for FnCallHashes { impl From for FnCallHashes { #[inline(always)] fn from(hash: u64) -> Self { - let hash = if hash == 0 { DEFAULT_HASH } else { hash }; + let hash = if hash == 0 { ALT_ZERO_HASH } else { hash }; Self { #[cfg(not(feature = "no_function"))] @@ -1843,7 +1843,7 @@ impl FnCallHashes { Self { #[cfg(not(feature = "no_function"))] script: 0, - native: if hash == 0 { DEFAULT_HASH } else { hash }, + native: if hash == 0 { ALT_ZERO_HASH } else { hash }, } } /// Create a [`FnCallHashes`] with both native Rust and script function hashes. @@ -1852,8 +1852,8 @@ impl FnCallHashes { pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { Self { #[cfg(not(feature = "no_function"))] - script: if script == 0 { DEFAULT_HASH } else { script }, - native: if native == 0 { DEFAULT_HASH } else { native }, + script: if script == 0 { ALT_ZERO_HASH } else { script }, + native: if native == 0 { ALT_ZERO_HASH } else { native }, } } /// Is this [`FnCallHashes`] native Rust only? diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 0159e84e..38a8b636 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -8,13 +8,24 @@ use std::{ iter::empty, }; -pub const DEFAULT_HASH: u64 = 42; +/// Dummy hash value to map zeros to. This value can be anything. +/// +/// # Notes +/// +/// Hashes are `u64`, and they can be zero (although extremely unlikely). +/// It is possible to hijack the zero value to indicate non-existence, +/// like [`None`] in [`Option`]. +/// +/// When a hash is calculated to be zero, it gets mapped to this alternate hash value. +/// This has the effect of releasing the zero value at the expense of causing the probability of +/// this value to double, which has minor impacts. +pub const ALT_ZERO_HASH: u64 = 42; /// A hasher that only takes one single [`u64`] and returns it as a non-zero hash key. /// /// # Zeros /// -/// If the value is zero, then it is mapped to `DEFAULT_HASH`. +/// If the value is zero, it is mapped to `ALT_ZERO_HASH`. /// /// # Panics /// @@ -37,7 +48,7 @@ impl Hasher for StraightHasher { self.0 = u64::from_ne_bytes(key); if self.0 == 0 { - self.0 = DEFAULT_HASH + self.0 = ALT_ZERO_HASH } } } @@ -51,7 +62,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] fn build_hasher(&self) -> Self::Hasher { - StraightHasher(DEFAULT_HASH) + StraightHasher(ALT_ZERO_HASH) } } @@ -92,7 +103,7 @@ pub fn calc_qualified_var_hash<'a>( var_name.as_ref().hash(s); match s.finish() { - 0 => DEFAULT_HASH, + 0 => ALT_ZERO_HASH, r => r, } } @@ -130,7 +141,7 @@ pub fn calc_qualified_fn_hash( num.hash(s); match s.finish() { - 0 => DEFAULT_HASH, + 0 => ALT_ZERO_HASH, r => r, } } @@ -165,7 +176,7 @@ pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { len.hash(s); match s.finish() { - 0 => DEFAULT_HASH, + 0 => ALT_ZERO_HASH, r => r, } } @@ -179,7 +190,7 @@ pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { #[must_use] pub const fn combine_hashes(a: u64, b: u64) -> u64 { match a ^ b { - 0 => DEFAULT_HASH, + 0 => ALT_ZERO_HASH, r => r, } } diff --git a/src/module/mod.rs b/src/module/mod.rs index 841c5d2b..7cc982d6 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -96,8 +96,7 @@ impl FuncInfo { } } -/// _(internals)_ Calculate a [`u64`] hash key from a namespace-qualified function name and -/// parameter types. +/// _(internals)_ Calculate a non-zero [`u64`] hash key from a namespace-qualified function name and parameter types. /// Exported under the `internals` feature only. /// /// Module names are passed in via `&str` references from an iterator. @@ -667,7 +666,7 @@ impl Module { type_id } - /// Set a Rust function into the [`Module`], returning a hash key. + /// Set a Rust function into the [`Module`], returning a non-zero hash key. /// /// If there is an existing Rust function of the same hash, it is replaced. /// @@ -729,7 +728,7 @@ impl Module { /// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine], /// the current set of functions, plus a list of mutable [`Dynamic`] references - /// into the [`Module`], returning a hash key. + /// into the [`Module`], returning a non-zero hash key. /// /// Use this to register a built-in function which must reference settings on the scripting /// [`Engine`][crate::Engine] (e.g. to prevent growing an array beyond the allowed maximum size), @@ -822,7 +821,7 @@ impl Module { ) } - /// Set a Rust function into the [`Module`], returning a hash key. + /// Set a Rust function into the [`Module`], returning a non-zero hash key. /// /// If there is a similar existing Rust function, it is replaced. /// @@ -861,7 +860,7 @@ impl Module { ) } - /// Set a Rust getter function taking one mutable parameter, returning a hash key. + /// Set a Rust getter function taking one mutable parameter, returning a non-zero hash key. /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing Rust getter function, it is replaced. @@ -898,7 +897,7 @@ impl Module { } /// Set a Rust setter function taking two parameters (the first one mutable) into the [`Module`], - /// returning a hash key. + /// returning a non-zero hash key. /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing setter Rust function, it is replaced. @@ -939,7 +938,7 @@ impl Module { } /// Set a Rust index getter taking two parameters (the first one mutable) into the [`Module`], - /// returning a hash key. + /// returning a non-zero hash key. /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing setter Rust function, it is replaced. @@ -1000,7 +999,7 @@ impl Module { } /// Set a Rust index setter taking three parameters (the first one mutable) into the [`Module`], - /// returning a hash key. + /// returning a non-zero hash key. /// This function is automatically exposed to the global namespace. /// /// If there is a similar existing Rust function, it is replaced. @@ -1060,7 +1059,7 @@ impl Module { ) } - /// Set a pair of Rust index getter and setter functions, returning both hash keys. + /// Set a pair of Rust index getter and setter functions, returning both non-zero hash keys. /// This is a short-hand for [`set_indexer_get_fn`][Module::set_indexer_get_fn] and /// [`set_indexer_set_fn`][Module::set_indexer_set_fn]. /// From cc98e82ea17b9df5aefa3fe22a14414a9a1cd9cd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 6 Dec 2021 11:12:54 +0800 Subject: [PATCH 03/13] Include hashes in JSON output. --- CHANGELOG.md | 1 + src/module/mod.rs | 48 +++++++++++++++++---------------------- src/serde/metadata.rs | 52 +++++++++++++++++-------------------------- 3 files changed, 41 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e8c0fb7..4bcba46a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Enhancements * Added `into_array` and `into_typed_array` for `Dynamic`. * Added `FnPtr::call` and `FnPtr::call_within_context` to simplify calling a function pointer. * BLob's can now be deserialized (using `from_dynamic`) into `Vec` via [`serde_bytes`](https://crates.io/crates/serde_bytes). +* A function's hashes are included in its JSON metadata to assist in debugging. Each function's `hashBase` field in the JSON object should map directly to the pre-calculated hash in the function call. Deprecated and Gated API's -------------------------- diff --git a/src/module/mod.rs b/src/module/mod.rs index 7cc982d6..144beb3d 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -106,7 +106,7 @@ impl FuncInfo { /// /// The first module name is skipped. Hashing starts from the _second_ module in the chain. #[inline] -fn calc_native_fn_hash( +pub fn calc_native_fn_hash( modules: impl Iterator>, fn_name: impl AsRef, params: &[TypeId], @@ -179,8 +179,7 @@ impl fmt::Debug for Module { d.field( "functions", &self - .functions - .values() + .iter_fn() .map(|f| f.func.to_string()) .collect::>(), ); @@ -358,9 +357,8 @@ impl Module { #[cfg(feature = "metadata")] #[inline] pub fn gen_fn_signatures(&self) -> impl Iterator + '_ { - self.functions - .values() - .filter(|f| match f.access { + self.iter_fn() + .filter(|&f| match f.access { FnAccess::Public => true, FnAccess::Private => false, }) @@ -501,8 +499,7 @@ impl Module { } else { let name = name.as_ref(); - self.functions - .values() + self.iter_fn() .find(|f| f.params == num_params && f.name == name) .and_then(|f| f.func.get_script_fn_def()) } @@ -1232,7 +1229,7 @@ impl Module { other .functions .iter() - .filter(|(_, f)| { + .filter(|&(_, f)| { _filter( f.namespace, f.access, @@ -1331,18 +1328,15 @@ impl Module { &Shared, ), > + '_ { - self.functions - .values() - .filter(|f| f.func.is_script()) - .map(|f| { - ( - f.namespace, - f.access, - f.name.as_str(), - f.params, - f.func.get_script_fn_def().expect("scripted function"), - ) - }) + self.iter_fn().filter(|&f| f.func.is_script()).map(|f| { + ( + f.namespace, + f.access, + f.name.as_str(), + f.params, + f.func.get_script_fn_def().expect("scripted function"), + ) + }) } /// Get an iterator over all script-defined functions in the [`Module`]. @@ -1358,9 +1352,8 @@ impl Module { pub fn iter_script_fn_info( &self, ) -> impl Iterator { - self.functions - .values() - .filter(|f| f.func.is_script()) + self.iter_fn() + .filter(|&f| f.func.is_script()) .map(|f| (f.namespace, f.access, f.name.as_str(), f.params)) } @@ -1461,13 +1454,12 @@ impl Module { #[cfg(not(feature = "no_function"))] if ast.has_functions() { ast.shared_lib() - .functions - .values() - .filter(|f| match f.access { + .iter_fn() + .filter(|&f| match f.access { FnAccess::Public => true, FnAccess::Private => false, }) - .filter(|f| f.func.is_script()) + .filter(|&f| f.func.is_script()) .for_each(|f| { // Encapsulate AST environment let mut func = f diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index ef67b4e2..0f79d941 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -1,8 +1,9 @@ -use crate::{Engine, AST}; +use crate::module::calc_native_fn_hash; +use crate::{calc_fn_hash, Engine, AST}; use serde::{Deserialize, Serialize}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{cmp::Ordering, collections::BTreeMap}; +use std::{cmp::Ordering, collections::BTreeMap, iter::empty}; #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -83,6 +84,8 @@ impl Ord for FnParam { #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct FnMetadata { + pub base_hash: u64, + pub full_hash: u64, pub namespace: FnNamespace, pub access: FnAccess, pub name: String, @@ -118,15 +121,23 @@ impl Ord for FnMetadata { impl From<&crate::module::FuncInfo> for FnMetadata { fn from(info: &crate::module::FuncInfo) -> Self { + let base_hash = calc_fn_hash(&info.name, info.params); + let (typ, full_hash) = if info.func.is_script() { + (FnType::Script, base_hash) + } else { + ( + FnType::Native, + calc_native_fn_hash(empty::<&str>(), &info.name, &info.param_types), + ) + }; + Self { + base_hash, + full_hash, namespace: info.namespace.into(), access: info.access.into(), name: info.name.to_string(), - typ: if info.func.is_script() { - FnType::Script - } else { - FnType::Native - }, + typ, num_params: info.params, params: info .param_names @@ -169,30 +180,6 @@ impl From<&crate::module::FuncInfo> for FnMetadata { } } -#[cfg(not(feature = "no_function"))] -impl From> for FnMetadata { - fn from(info: crate::ast::ScriptFnMetadata) -> Self { - Self { - namespace: FnNamespace::Global, - access: info.access.into(), - name: info.name.to_string(), - typ: FnType::Script, - num_params: info.params.len(), - params: info - .params - .iter() - .map(|&s| FnParam { - name: s.into(), - typ: Some("Dynamic".into()), - }) - .collect(), - return_type: Some("Dynamic".into()), - signature: info.to_string(), - doc_comments: info.comments.iter().map(|&s| s.into()).collect(), - } - } -} - #[derive(Debug, Clone, Serialize)] #[serde(rename_all = "camelCase")] struct ModuleMetadata { @@ -263,7 +250,8 @@ impl Engine { .for_each(|f| global.functions.push(f.into())); #[cfg(not(feature = "no_function"))] - _ast.iter_functions() + _ast.shared_lib() + .iter_fn() .for_each(|f| global.functions.push(f.into())); global.functions.sort(); From 5b64e0b383c8ec4528f97d710146ddb7f3ffe6cc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 6 Dec 2021 18:50:37 +0800 Subject: [PATCH 04/13] Use deref for Expression. --- CHANGELOG.md | 1 + src/custom_syntax.rs | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bcba46a..cd6a6e1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Enhancements * Added `FnPtr::call` and `FnPtr::call_within_context` to simplify calling a function pointer. * BLob's can now be deserialized (using `from_dynamic`) into `Vec` via [`serde_bytes`](https://crates.io/crates/serde_bytes). * A function's hashes are included in its JSON metadata to assist in debugging. Each function's `hashBase` field in the JSON object should map directly to the pre-calculated hash in the function call. +* `Expression` now derefs to `Expr`. Deprecated and Gated API's -------------------------- diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 03802854..56e96c5f 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -10,9 +10,9 @@ use crate::{ Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared, StaticVec, INT, }; -use std::any::TypeId; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{any::TypeId, ops::Deref}; /// Collection of special markers for custom syntax definition. pub mod markers { @@ -71,12 +71,6 @@ impl Expression<'_> { pub fn get_variable_name(&self) -> Option<&str> { self.0.get_variable_name(true) } - /// Get the expression. - #[inline(always)] - #[must_use] - pub(crate) const fn expr(&self) -> &Expr { - &self.0 - } /// Get the position of this expression. #[inline(always)] #[must_use] @@ -134,6 +128,22 @@ impl Expression<'_> { } } +impl AsRef for Expression<'_> { + #[inline(always)] + fn as_ref(&self) -> &Expr { + &self.0 + } +} + +impl Deref for Expression<'_> { + type Target = Expr; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> { /// Evaluate an [expression tree][Expression]. /// @@ -148,7 +158,7 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> { self.state, self.lib, self.this_ptr, - expr.expr(), + expr, self.level, ) } From 2a7a6484295ca14ae7484bbe2cdef6b2acc50632 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 6 Dec 2021 20:52:47 +0800 Subject: [PATCH 05/13] Reduce feature gates. --- src/api/compile.rs | 9 +-- src/api/deprecated.rs | 1 - src/api/limits.rs | 3 +- src/api/register.rs | 32 ++++----- src/ast.rs | 18 ++--- src/engine.rs | 55 +++++++-------- src/func/call.rs | 5 +- src/module/mod.rs | 20 ++---- src/packages/math_basic.rs | 2 +- src/parser.rs | 44 ++++++------ src/serde/de.rs | 14 ++-- src/serde/deserialize.rs | 20 ++---- src/serde/metadata.rs | 2 + src/serde/mod.rs | 4 +- src/serde/ser.rs | 32 ++++----- src/tokenizer.rs | 31 ++++----- src/types/dynamic.rs | 137 +++++++++++++++++-------------------- 17 files changed, 176 insertions(+), 253 deletions(-) diff --git a/src/api/compile.rs b/src/api/compile.rs index f089e7ed..9ca748cd 100644 --- a/src/api/compile.rs +++ b/src/api/compile.rs @@ -5,9 +5,6 @@ use crate::{Engine, ParseError, Scope, AST}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -#[cfg(not(feature = "no_object"))] -use crate::Map; - impl Engine { /// Compile a string into an [`AST`], which can be used later for evaluation. /// @@ -313,7 +310,7 @@ impl Engine { self.optimization_level, ) } - /// Parse a JSON string into an [object map][`Map`]. + /// Parse a JSON string into an [object map][crate::Map]. /// This is a light-weight alternative to using, say, /// [`serde_json`](https://crates.io/crates/serde_json) to deserialize the JSON. /// @@ -362,14 +359,14 @@ impl Engine { &self, json: impl AsRef, has_null: bool, - ) -> Result> { + ) -> Result> { use crate::tokenizer::Token; fn parse_json_inner( engine: &Engine, json: &str, has_null: bool, - ) -> Result> { + ) -> Result> { let mut scope = Scope::new(); let json_text = json.trim_start(); let scripts = if json_text.starts_with(Token::MapStart.literal_syntax()) { diff --git a/src/api/deprecated.rs b/src/api/deprecated.rs index 1bcd23b3..b315e636 100644 --- a/src/api/deprecated.rs +++ b/src/api/deprecated.rs @@ -4,7 +4,6 @@ use crate::{ Dynamic, Engine, EvalAltResult, FnPtr, ImmutableString, NativeCallContext, RhaiResult, Scope, AST, }; - #[cfg(feature = "no_std")] use std::prelude::v1::*; diff --git a/src/api/limits.rs b/src/api/limits.rs index f95ddc8f..7d9fd36c 100644 --- a/src/api/limits.rs +++ b/src/api/limits.rs @@ -2,11 +2,10 @@ #![cfg(not(feature = "unchecked"))] use crate::Engine; +use std::num::{NonZeroU64, NonZeroUsize}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::num::{NonZeroU64, NonZeroUsize}; - /// A type containing all the limits imposed by the [`Engine`]. /// /// Not available under `unchecked`. diff --git a/src/api/register.rs b/src/api/register.rs index a7f19301..304731b9 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -9,12 +9,6 @@ use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - impl Engine { /// Get the global namespace module (which is the last module in `global_modules`). #[inline(always)] @@ -529,7 +523,7 @@ impl Engine { /// /// # Panics /// - /// Panics if the type is [`Array`], [`Map`], [`String`], + /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. /// Indexers for arrays, object maps, strings and integers cannot be registered. /// @@ -573,11 +567,11 @@ impl Engine { get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static, ) -> &mut Self { #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for object maps."); } if TypeId::of::() == TypeId::of::() @@ -600,7 +594,7 @@ impl Engine { /// /// # Panics /// - /// Panics if the type is [`Array`], [`Map`], [`String`], + /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. /// Indexers for arrays, object maps, strings and integers cannot be registered. /// @@ -650,11 +644,11 @@ impl Engine { get_fn: impl Fn(&mut T, X) -> Result> + SendSync + 'static, ) -> &mut Self { #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for object maps."); } if TypeId::of::() == TypeId::of::() @@ -675,7 +669,7 @@ impl Engine { /// /// # Panics /// - /// Panics if the type is [`Array`], [`Map`], [`String`], + /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. /// Indexers for arrays, object maps, strings and integers cannot be registered. /// @@ -721,11 +715,11 @@ impl Engine { set_fn: impl Fn(&mut T, X, V) + SendSync + 'static, ) -> &mut Self { #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for object maps."); } if TypeId::of::() == TypeId::of::() @@ -746,7 +740,7 @@ impl Engine { /// /// # Panics /// - /// Panics if the type is [`Array`], [`Map`], [`String`], + /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. /// Indexers for arrays, object maps, strings and integers cannot be registered. /// @@ -799,11 +793,11 @@ impl Engine { set_fn: impl Fn(&mut T, X, V) -> Result<(), Box> + SendSync + 'static, ) -> &mut Self { #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for object maps."); } if TypeId::of::() == TypeId::of::() @@ -824,7 +818,7 @@ impl Engine { /// /// # Panics /// - /// Panics if the type is [`Array`], [`Map`], [`String`], + /// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`], /// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT]. /// Indexers for arrays, object maps, strings and integers cannot be registered. /// diff --git a/src/ast.rs b/src/ast.rs index c4778bef..15b9ba10 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -25,15 +25,9 @@ use std::{ #[cfg(not(feature = "no_float"))] use std::str::FromStr; -#[cfg(not(feature = "no_float"))] -use crate::FLOAT; - #[cfg(not(feature = "no_float"))] use num_traits::Float; -#[cfg(not(feature = "no_index"))] -use crate::Array; - /// A type representing the access mode of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnAccess { @@ -1918,7 +1912,7 @@ impl FnCallExpr { pub struct FloatWrapper(F); #[cfg(not(feature = "no_float"))] -impl Hash for FloatWrapper { +impl Hash for FloatWrapper { #[inline(always)] fn hash(&self, state: &mut H) { self.0.to_ne_bytes().hash(state); @@ -2022,11 +2016,11 @@ impl FloatWrapper { } #[cfg(not(feature = "no_float"))] -impl FloatWrapper { +impl FloatWrapper { /// Create a new [`FloatWrapper`]. #[inline(always)] #[must_use] - pub const fn new_const(value: FLOAT) -> Self { + pub const fn new_const(value: crate::FLOAT) -> Self { Self(value) } } @@ -2037,7 +2031,7 @@ impl FloatWrapper { pub enum Expr { /// Dynamic constant. /// - /// Used to hold complex constants such as [`Array`] or [`Map`][crate::Map] for quick cloning. + /// Used to hold complex constants such as [`Array`][crate::Array] or [`Map`][crate::Map] for quick cloning. /// Primitive data types should use the appropriate variants to avoid an allocation. DynamicConstant(Box, Position), /// Boolean constant. @@ -2048,7 +2042,7 @@ pub enum Expr { /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] - FloatConstant(FloatWrapper, Position), + FloatConstant(FloatWrapper, Position), /// Character constant. CharConstant(char, Position), /// [String][ImmutableString] constant. @@ -2224,7 +2218,7 @@ impl Expr { #[cfg(not(feature = "no_index"))] Self::Array(x, _) if self.is_constant() => { - let mut arr = Array::with_capacity(x.len()); + let mut arr = crate::Array::with_capacity(x.len()); arr.extend( x.iter() .map(|v| v.get_literal_value().expect("constant value")), diff --git a/src/engine.rs b/src/engine.rs index a6f9469b..512aa7b0 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -29,15 +29,6 @@ use std::{ ops::{Deref, DerefMut}, }; -#[cfg(not(feature = "no_index"))] -use crate::{Array, Blob}; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - -#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::ast::FnCallHashes; - pub type Precedence = NonZeroU8; /// _(internals)_ A stack of imported [modules][Module] plus mutable runtime global states. @@ -246,7 +237,7 @@ impl Imports { /// Get the pre-calculated index getter hash. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[must_use] - pub(crate) fn fn_hash_idx_get(&mut self) -> u64 { + pub(crate) fn hash_idx_get(&mut self) -> u64 { if self.fn_hash_indexing != (0, 0) { self.fn_hash_indexing.0 } else { @@ -259,7 +250,7 @@ impl Imports { /// Get the pre-calculated index setter hash. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[must_use] - pub(crate) fn fn_hash_idx_set(&mut self) -> u64 { + pub(crate) fn hash_idx_set(&mut self) -> u64 { if self.fn_hash_indexing != (0, 0) { self.fn_hash_indexing.1 } else { @@ -559,7 +550,7 @@ impl<'a> Target<'a> { #[cfg(not(feature = "no_index"))] Self::BitField(_, _, _) => TypeId::of::() == TypeId::of::(), #[cfg(not(feature = "no_index"))] - Self::BlobByte(_, _, _) => TypeId::of::() == TypeId::of::(), + Self::BlobByte(_, _, _) => TypeId::of::() == TypeId::of::(), #[cfg(not(feature = "no_index"))] Self::StringChar(_, _, _) => TypeId::of::() == TypeId::of::(), } @@ -641,7 +632,7 @@ impl<'a> Target<'a> { )) })?; - let value = &mut *value.write_lock::().expect("`Blob`"); + let value = &mut *value.write_lock::().expect("`Blob`"); let index = *index; @@ -1372,7 +1363,8 @@ impl Engine { if let Some(mut new_val) = try_setter { // Try to call index setter if value is changed - let hash_set = FnCallHashes::from_native(mods.fn_hash_idx_set()); + let hash_set = + crate::ast::FnCallHashes::from_native(mods.hash_idx_set()); let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; if let Err(err) = self.exec_fn_call( @@ -1418,7 +1410,8 @@ impl Engine { if let Some(mut new_val) = try_setter { // Try to call index setter - let hash_set = FnCallHashes::from_native(mods.fn_hash_idx_set()); + let hash_set = + crate::ast::FnCallHashes::from_native(mods.hash_idx_set()); let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; self.exec_fn_call( @@ -1461,7 +1454,7 @@ impl Engine { unreachable!("function call in dot chain should not be namespace-qualified") } // {xxx:map}.id op= ??? - Expr::Property(x) if target.is::() && new_val.is_some() => { + Expr::Property(x) if target.is::() && new_val.is_some() => { let (name, pos) = &x.2; let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); let index = name.into(); @@ -1479,7 +1472,7 @@ impl Engine { Ok((Dynamic::UNIT, true)) } // {xxx:map}.id - Expr::Property(x) if target.is::() => { + Expr::Property(x) if target.is::() => { let (name, pos) = &x.2; let index = name.into(); let val = self.get_indexed_mut( @@ -1493,7 +1486,7 @@ impl Engine { let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); if op_info.is_some() { - let hash = FnCallHashes::from_native(*hash_get); + let hash = crate::ast::FnCallHashes::from_native(*hash_get); let args = &mut [target.as_mut()]; let (mut orig_val, _) = self .exec_fn_call( @@ -1537,7 +1530,7 @@ impl Engine { new_val = orig_val; } - let hash = FnCallHashes::from_native(*hash_set); + let hash = crate::ast::FnCallHashes::from_native(*hash_set); let args = &mut [target.as_mut(), &mut new_val]; self.exec_fn_call( mods, state, lib, setter, hash, args, is_ref_mut, true, *pos, None, @@ -1547,7 +1540,8 @@ impl Engine { // Try an indexer if property does not exist EvalAltResult::ErrorDotExpr(_, _) => { let args = &mut [target, &mut name.into(), &mut new_val]; - let hash_set = FnCallHashes::from_native(mods.fn_hash_idx_set()); + let hash_set = + crate::ast::FnCallHashes::from_native(mods.hash_idx_set()); let pos = Position::NONE; self.exec_fn_call( @@ -1567,7 +1561,7 @@ impl Engine { // xxx.id Expr::Property(x) => { let ((getter, hash_get), _, (name, pos)) = x.as_ref(); - let hash = FnCallHashes::from_native(*hash_get); + let hash = crate::ast::FnCallHashes::from_native(*hash_get); let args = &mut [target.as_mut()]; self.exec_fn_call( mods, state, lib, getter, hash, args, is_ref_mut, true, *pos, None, @@ -1597,7 +1591,7 @@ impl Engine { } // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) - if target.is::() => + if target.is::() => { let val_target = &mut match x.lhs { Expr::Property(ref p) => { @@ -1641,8 +1635,8 @@ impl Engine { let ((getter, hash_get), (setter, hash_set), (name, pos)) = p.as_ref(); let rhs_chain = match_chaining_type(rhs); - let hash_get = FnCallHashes::from_native(*hash_get); - let hash_set = FnCallHashes::from_native(*hash_set); + let hash_get = crate::ast::FnCallHashes::from_native(*hash_get); + let hash_set = crate::ast::FnCallHashes::from_native(*hash_set); let mut arg_values = [target.as_mut(), &mut Dynamic::UNIT.clone()]; let args = &mut arg_values[..1]; @@ -1705,9 +1699,10 @@ impl Engine { EvalAltResult::ErrorDotExpr(_, _) => { let args = &mut [target.as_mut(), &mut name.into(), val]; - let hash_set = FnCallHashes::from_native( - mods.fn_hash_idx_set(), - ); + let hash_set = + crate::ast::FnCallHashes::from_native( + mods.hash_idx_set(), + ); self.exec_fn_call( mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, *pos, None, level, @@ -2160,7 +2155,7 @@ impl Engine { _ if use_indexers => { let args = &mut [target, &mut idx]; - let hash_get = FnCallHashes::from_native(mods.fn_hash_idx_get()); + let hash_get = crate::ast::FnCallHashes::from_native(mods.hash_idx_get()); let idx_pos = Position::NONE; self.exec_fn_call( @@ -2267,7 +2262,7 @@ impl Engine { Expr::Array(x, _) => Ok(x .iter() .try_fold( - Array::with_capacity(x.len()), + crate::Array::with_capacity(x.len()), |mut arr, item| -> Result<_, Box> { arr.push( self.eval_expr(scope, mods, state, lib, this_ptr, item, level)? @@ -2950,7 +2945,7 @@ impl Engine { } #[cfg(not(feature = "no_object"))] _ => { - let mut err_map = Map::new(); + let mut err_map = crate::Map::new(); let err_pos = err.take_position(); err_map.insert("message".into(), err.to_string().into()); diff --git a/src/func/call.rs b/src/func/call.rs index 4f0e1018..a4f679e9 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -23,9 +23,6 @@ use std::{ mem, }; -#[cfg(not(feature = "no_object"))] -use crate::Map; - /// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic]. pub type FnCallArgs<'a> = [&'a mut Dynamic]; @@ -841,7 +838,7 @@ impl Engine { // Check if it is a map method call in OOP style #[cfg(not(feature = "no_object"))] - if let Some(map) = target.read_lock::() { + if let Some(map) = target.read_lock::() { if let Some(val) = map.get(fn_name) { if let Some(fn_ptr) = val.read_lock::() { // Remap the function name diff --git a/src/module/mod.rs b/src/module/mod.rs index 144beb3d..8152f0a9 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -23,12 +23,6 @@ use std::{ ops::{Add, AddAssign, Deref, DerefMut}, }; -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - /// A type representing the namespace of a function. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum FnNamespace { @@ -942,7 +936,7 @@ impl Module { /// /// # Panics /// - /// Panics if the type is [`Array`] or [`Map`]. + /// Panics if the type is [`Array`][crate::Array] or [`Map`][crate::Map]. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Function Metadata @@ -971,11 +965,11 @@ impl Module { F: Fn(&mut A, B) -> Result> + SendSync + 'static, { #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for object maps."); } if TypeId::of::() == TypeId::of::() @@ -1003,7 +997,7 @@ impl Module { /// /// # Panics /// - /// Panics if the type is [`Array`] or [`Map`]. + /// Panics if the type is [`Array`][crate::Array] or [`Map`][crate::Map]. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Function Metadata @@ -1032,11 +1026,11 @@ impl Module { F: Fn(&mut A, B, C) -> Result<(), Box> + SendSync + 'static, { #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for arrays."); } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { panic!("Cannot register indexer for object maps."); } if TypeId::of::() == TypeId::of::() @@ -1064,7 +1058,7 @@ impl Module { /// /// # Panics /// - /// Panics if the type is [`Array`] or [`Map`]. + /// Panics if the type is [`Array`][crate::Array] or [`Map`][crate::Map]. /// Indexers for arrays, object maps and strings cannot be registered. /// /// # Function Metadata diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 1e65f66f..0479b937 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -6,7 +6,7 @@ use crate::{def_package, Position, INT}; use std::prelude::v1::*; #[cfg(not(feature = "no_float"))] -use crate::{EvalAltResult, FLOAT}; +use crate::FLOAT; #[cfg(feature = "no_std")] #[cfg(not(feature = "no_float"))] diff --git a/src/parser.rs b/src/parser.rs index 4ed28b47..92e36a2b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -27,12 +27,6 @@ use std::{ ops::AddAssign, }; -#[cfg(not(feature = "no_float"))] -use crate::{custom_syntax::markers::CUSTOM_SYNTAX_MARKER_FLOAT, FLOAT}; - -#[cfg(not(feature = "no_function"))] -use crate::FnAccess; - type PERR = ParseErrorType; type FunctionsLib = BTreeMap>; @@ -1573,7 +1567,7 @@ fn parse_unary( .map(|i| Expr::IntegerConstant(i, pos)) .or_else(|| { #[cfg(not(feature = "no_float"))] - return Some(Expr::FloatConstant((-(num as FLOAT)).into(), pos)); + return Some(Expr::FloatConstant((-(num as crate::FLOAT)).into(), pos)); #[cfg(feature = "no_float")] return None; }) @@ -2173,19 +2167,23 @@ fn parse_custom_syntax( } }, #[cfg(not(feature = "no_float"))] - CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) { - (Token::FloatConstant(f), pos) => { - inputs.push(Expr::FloatConstant(f, pos)); - segments.push(f.to_string().into()); - tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_FLOAT)); + crate::custom_syntax::markers::CUSTOM_SYNTAX_MARKER_FLOAT => { + match input.next().expect(NEVER_ENDS) { + (Token::FloatConstant(f), pos) => { + inputs.push(Expr::FloatConstant(f, pos)); + segments.push(f.to_string().into()); + tokens.push(state.get_identifier( + crate::custom_syntax::markers::CUSTOM_SYNTAX_MARKER_FLOAT, + )); + } + (_, pos) => { + return Err(PERR::MissingSymbol( + "Expecting a floating-point number".to_string(), + ) + .into_err(pos)) + } } - (_, pos) => { - return Err(PERR::MissingSymbol( - "Expecting a floating-point number".to_string(), - ) - .into_err(pos)) - } - }, + } CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { let s: ImmutableString = state.get_identifier(s).into(); @@ -2849,9 +2847,9 @@ fn parse_stmt( Token::Fn | Token::Private => { let access = if matches!(token, Token::Private) { eat_token(input, Token::Private); - FnAccess::Private + crate::FnAccess::Private } else { - FnAccess::Public + crate::FnAccess::Public }; match input.next().expect(NEVER_ENDS) { @@ -3055,7 +3053,7 @@ fn parse_fn( input: &mut TokenStream, state: &mut ParseState, lib: &mut FunctionsLib, - access: FnAccess, + access: crate::FnAccess, settings: ParseSettings, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] @@ -3281,7 +3279,7 @@ fn parse_anon_fn( // Define the function let script = ScriptFnDef { name: fn_name.clone(), - access: FnAccess::Public, + access: crate::FnAccess::Public, params, body: body.into(), lib: None, diff --git a/src/serde/de.rs b/src/serde/de.rs index 6d299ec2..bd055ce0 100644 --- a/src/serde/de.rs +++ b/src/serde/de.rs @@ -8,12 +8,6 @@ use serde::{Deserialize, Deserializer}; use std::prelude::v1::*; use std::{any::type_name, fmt}; -#[cfg(not(feature = "no_index"))] -use crate::{Array, Blob}; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - /// Deserializer for [`Dynamic`][crate::Dynamic] which is kept as a reference. /// /// The reference is necessary because the deserialized type may hold references @@ -363,7 +357,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { #[cfg(not(feature = "no_index"))] return self .value - .downcast_ref::() + .downcast_ref::() .map_or_else(|| self.type_error(), |x| _visitor.visit_bytes(x)); #[cfg(feature = "no_index")] @@ -412,7 +406,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { fn deserialize_seq>(self, _visitor: V) -> Result> { #[cfg(not(feature = "no_index"))] - return self.value.downcast_ref::().map_or_else( + return self.value.downcast_ref::().map_or_else( || self.type_error(), |arr| _visitor.visit_seq(IterateDynamicArray::new(arr.iter())), ); @@ -440,7 +434,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { fn deserialize_map>(self, _visitor: V) -> Result> { #[cfg(not(feature = "no_object"))] - return self.value.downcast_ref::().map_or_else( + return self.value.downcast_ref::().map_or_else( || self.type_error(), |map| { _visitor.visit_map(IterateMap::new( @@ -473,7 +467,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { visitor.visit_enum(s.as_str().into_deserializer()) } else { #[cfg(not(feature = "no_object"))] - if let Some(map) = self.value.downcast_ref::() { + if let Some(map) = self.value.downcast_ref::() { let mut iter = map.iter(); let first = iter.next(); let second = iter.next(); diff --git a/src/serde/deserialize.rs b/src/serde/deserialize.rs index de970d54..320a01f4 100644 --- a/src/serde/deserialize.rs +++ b/src/serde/deserialize.rs @@ -6,18 +6,6 @@ use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_index"))] -use serde::de::SeqAccess; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - -#[cfg(not(feature = "no_object"))] -use serde::de::MapAccess; - struct DynamicVisitor; impl<'d> Visitor<'d> for DynamicVisitor { @@ -141,8 +129,8 @@ impl<'d> Visitor<'d> for DynamicVisitor { } #[cfg(not(feature = "no_index"))] - fn visit_seq>(self, mut seq: A) -> Result { - let mut arr = Array::new(); + fn visit_seq>(self, mut seq: A) -> Result { + let mut arr = crate::Array::new(); while let Some(v) = seq.next_element()? { arr.push(v); @@ -152,8 +140,8 @@ impl<'d> Visitor<'d> for DynamicVisitor { } #[cfg(not(feature = "no_object"))] - fn visit_map>(self, mut map: M) -> Result { - let mut m = Map::new(); + fn visit_map>(self, mut map: M) -> Result { + let mut m = crate::Map::new(); while let Some((k, v)) = map.next_entry::<&str, _>()? { m.insert(k.into(), v); diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 0f79d941..6470e528 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "metadata")] + use crate::module::calc_native_fn_hash; use crate::{calc_fn_hash, Engine, AST}; use serde::{Deserialize, Serialize}; diff --git a/src/serde/mod.rs b/src/serde/mod.rs index f391d694..42b57e6e 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -3,12 +3,10 @@ mod de; mod deserialize; +mod metadata; mod ser; mod serialize; mod str; -#[cfg(feature = "metadata")] -mod metadata; - pub use de::from_dynamic; pub use ser::to_dynamic; diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 5211f219..b415d8fc 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -9,12 +9,6 @@ use std::fmt; #[cfg(feature = "no_std")] use std::prelude::v1::*; -#[cfg(not(feature = "no_index"))] -use crate::Array; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - /// Serializer for [`Dynamic`][crate::Dynamic] which is kept as a reference. struct DynamicSerializer { /// Buffer to hold a temporary key. @@ -332,7 +326,7 @@ impl Serializer for &mut DynamicSerializer { fn serialize_seq(self, _len: Option) -> Result> { #[cfg(not(feature = "no_index"))] - return Ok(DynamicSerializer::new(Array::new().into())); + return Ok(DynamicSerializer::new(crate::Array::new().into())); #[cfg(feature = "no_index")] return Err(EvalAltResult::ErrorMismatchDataType( "".into(), @@ -365,7 +359,7 @@ impl Serializer for &mut DynamicSerializer { #[cfg(not(feature = "no_index"))] return Ok(TupleVariantSerializer { variant: _variant, - array: Array::with_capacity(_len), + array: crate::Array::with_capacity(_len), }); #[cfg(any(feature = "no_object", feature = "no_index"))] return Err(EvalAltResult::ErrorMismatchDataType( @@ -378,7 +372,7 @@ impl Serializer for &mut DynamicSerializer { fn serialize_map(self, _len: Option) -> Result> { #[cfg(not(feature = "no_object"))] - return Ok(DynamicSerializer::new(Map::new().into())); + return Ok(DynamicSerializer::new(crate::Map::new().into())); #[cfg(feature = "no_object")] return Err(EvalAltResult::ErrorMismatchDataType( "".into(), @@ -406,7 +400,7 @@ impl Serializer for &mut DynamicSerializer { #[cfg(not(feature = "no_object"))] return Ok(StructVariantSerializer { variant: _variant, - map: Map::new(), + map: crate::Map::new(), }); #[cfg(feature = "no_object")] return Err(EvalAltResult::ErrorMismatchDataType( @@ -429,7 +423,7 @@ impl SerializeSeq for DynamicSerializer { #[cfg(not(feature = "no_index"))] { let _value = _value.serialize(&mut *self)?; - let arr = self._value.downcast_mut::().unwrap(); + let arr = self._value.downcast_mut::().unwrap(); arr.push(_value); Ok(()) } @@ -467,7 +461,7 @@ impl SerializeTuple for DynamicSerializer { #[cfg(not(feature = "no_index"))] { let _value = _value.serialize(&mut *self)?; - let arr = self._value.downcast_mut::().unwrap(); + let arr = self._value.downcast_mut::().unwrap(); arr.push(_value); Ok(()) } @@ -504,7 +498,7 @@ impl SerializeTupleStruct for DynamicSerializer { #[cfg(not(feature = "no_index"))] { let _value = _value.serialize(&mut *self)?; - let arr = self._value.downcast_mut::().unwrap(); + let arr = self._value.downcast_mut::().unwrap(); arr.push(_value); Ok(()) } @@ -565,7 +559,7 @@ impl SerializeMap for DynamicSerializer { ) })?; let _value = _value.serialize(&mut *self)?; - let map = self._value.downcast_mut::().unwrap(); + let map = self._value.downcast_mut::().unwrap(); map.insert(key.into(), _value); Ok(()) } @@ -590,7 +584,7 @@ impl SerializeMap for DynamicSerializer { EvalAltResult::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE) })?; let _value = _value.serialize(&mut *self)?; - let map = self._value.downcast_mut::().unwrap(); + let map = self._value.downcast_mut::().unwrap(); map.insert(_key.into(), _value); Ok(()) } @@ -628,7 +622,7 @@ impl SerializeStruct for DynamicSerializer { #[cfg(not(feature = "no_object"))] { let _value = _value.serialize(&mut *self)?; - let map = self._value.downcast_mut::().unwrap(); + let map = self._value.downcast_mut::().unwrap(); map.insert(_key.into(), _value); Ok(()) } @@ -657,7 +651,7 @@ impl SerializeStruct for DynamicSerializer { #[cfg(not(any(feature = "no_object", feature = "no_index")))] struct TupleVariantSerializer { variant: &'static str, - array: Array, + array: crate::Array, } #[cfg(not(any(feature = "no_object", feature = "no_index")))] @@ -682,7 +676,7 @@ impl serde::ser::SerializeTupleVariant for TupleVariantSerializer { #[cfg(not(feature = "no_object"))] struct StructVariantSerializer { variant: &'static str, - map: Map, + map: crate::Map, } #[cfg(not(feature = "no_object"))] @@ -707,7 +701,7 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer { #[cfg(not(feature = "no_object"))] fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult { - let mut map = Map::new(); + let mut map = crate::Map::new(); map.insert(variant.into(), value); Ok(map.into()) } diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 91aa892f..8bdcc508 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -19,15 +19,6 @@ use std::{ str::{Chars, FromStr}, }; -#[cfg(not(feature = "no_float"))] -use crate::{ast::FloatWrapper, FLOAT}; - -#[cfg(feature = "decimal")] -use rust_decimal::Decimal; - -#[cfg(not(feature = "no_function"))] -use crate::engine::KEYWORD_IS_DEF_FN; - /// _(internals)_ A type containing commands to control the tokenizer. #[derive(Debug, Clone, Eq, PartialEq, Hash, Copy)] pub struct TokenizerControlBlock { @@ -305,12 +296,12 @@ pub enum Token { /// /// Reserved under the `no_float` feature. #[cfg(not(feature = "no_float"))] - FloatConstant(FloatWrapper), - /// A [`Decimal`] constant. + FloatConstant(crate::ast::FloatWrapper), + /// A [`Decimal`][rust_decimal::Decimal] constant. /// /// Requires the `decimal` feature. #[cfg(feature = "decimal")] - DecimalConstant(Decimal), + DecimalConstant(rust_decimal::Decimal), /// An identifier. Identifier(Box), /// A character constant. @@ -789,7 +780,7 @@ impl Token { } #[cfg(not(feature = "no_function"))] - KEYWORD_IS_DEF_FN => Reserved(syntax.into()), + crate::engine::KEYWORD_IS_DEF_FN => Reserved(syntax.into()), _ => return None, }) @@ -1534,18 +1525,20 @@ fn get_next_token_inner( // If integer parsing is unnecessary, try float instead #[cfg(not(feature = "no_float"))] - let num = - num.or_else(|_| FloatWrapper::from_str(&out).map(Token::FloatConstant)); + let num = num.or_else(|_| { + crate::ast::FloatWrapper::from_str(&out).map(Token::FloatConstant) + }); // Then try decimal #[cfg(feature = "decimal")] - let num = - num.or_else(|_| Decimal::from_str(&out).map(Token::DecimalConstant)); + let num = num.or_else(|_| { + rust_decimal::Decimal::from_str(&out).map(Token::DecimalConstant) + }); // Then try decimal in scientific notation #[cfg(feature = "decimal")] let num = num.or_else(|_| { - Decimal::from_scientific(&out).map(Token::DecimalConstant) + rust_decimal::Decimal::from_scientific(&out).map(Token::DecimalConstant) }); num.unwrap_or_else(|_| { @@ -1990,7 +1983,7 @@ pub fn is_keyword_function(name: impl AsRef) -> bool { | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR => true, #[cfg(not(feature = "no_function"))] - KEYWORD_IS_DEF_FN => true, + crate::engine::KEYWORD_IS_DEF_FN => true, _ => false, } diff --git a/src/types/dynamic.rs b/src/types/dynamic.rs index af4e73e4..9f142049 100644 --- a/src/types/dynamic.rs +++ b/src/types/dynamic.rs @@ -13,23 +13,10 @@ use std::{ str::FromStr, }; -#[cfg(not(feature = "no_float"))] -use crate::{ast::FloatWrapper, FLOAT}; - -#[cfg(feature = "decimal")] -use rust_decimal::Decimal; - -#[cfg(not(feature = "no_index"))] -use crate::{Array, Blob}; - -#[cfg(not(feature = "no_object"))] -use crate::Map; - #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] use std::time::Instant; -use fmt::Debug; #[cfg(not(feature = "no_std"))] #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] use instant::Instant; @@ -190,26 +177,26 @@ pub enum Union { /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] - Float(FloatWrapper, Tag, AccessMode), + Float(crate::ast::FloatWrapper, Tag, AccessMode), /// _(decimal)_ A fixed-precision decimal value. /// Exported under the `decimal` feature only. #[cfg(feature = "decimal")] - Decimal(Box, Tag, AccessMode), + Decimal(Box, Tag, AccessMode), /// An array value. /// /// Not available under `no_index`. #[cfg(not(feature = "no_index"))] - Array(Box, Tag, AccessMode), + Array(Box, Tag, AccessMode), /// An blob (byte array). /// /// Not available under `no_index`. #[cfg(not(feature = "no_index"))] - Blob(Box, Tag, AccessMode), + Blob(Box, Tag, AccessMode), /// An object map value. /// /// Not available under `no_object`. #[cfg(not(feature = "no_object"))] - Map(Box, Tag, AccessMode), + Map(Box, Tag, AccessMode), /// A function pointer. FnPtr(Box, Tag, AccessMode), /// A timestamp value. @@ -411,15 +398,15 @@ impl Dynamic { Union::Char(_, _, _) => TypeId::of::(), Union::Int(_, _, _) => TypeId::of::(), #[cfg(not(feature = "no_float"))] - Union::Float(_, _, _) => TypeId::of::(), + Union::Float(_, _, _) => TypeId::of::(), #[cfg(feature = "decimal")] - Union::Decimal(_, _, _) => TypeId::of::(), + Union::Decimal(_, _, _) => TypeId::of::(), #[cfg(not(feature = "no_index"))] - Union::Array(_, _, _) => TypeId::of::(), + Union::Array(_, _, _) => TypeId::of::(), #[cfg(not(feature = "no_index"))] - Union::Blob(_, _, _) => TypeId::of::(), + Union::Blob(_, _, _) => TypeId::of::(), #[cfg(not(feature = "no_object"))] - Union::Map(_, _, _) => TypeId::of::(), + Union::Map(_, _, _) => TypeId::of::(), Union::FnPtr(_, _, _) => TypeId::of::(), #[cfg(not(feature = "no_std"))] Union::TimeStamp(_, _, _) => TypeId::of::(), @@ -450,7 +437,7 @@ impl Dynamic { Union::Char(_, _, _) => "char", Union::Int(_, _, _) => type_name::(), #[cfg(not(feature = "no_float"))] - Union::Float(_, _, _) => type_name::(), + Union::Float(_, _, _) => type_name::(), #[cfg(feature = "decimal")] Union::Decimal(_, _, _) => "decimal", #[cfg(not(feature = "no_index"))] @@ -582,19 +569,19 @@ pub(crate) fn map_std_type_name(name: &str) -> &str { return "Fn"; } #[cfg(feature = "decimal")] - if name == type_name::() { + if name == type_name::() { return "decimal"; } #[cfg(not(feature = "no_index"))] - if name == type_name::() { + if name == type_name::() { return "array"; } #[cfg(not(feature = "no_index"))] - if name == type_name::() { + if name == type_name::() { return "blob"; } #[cfg(not(feature = "no_object"))] - if name == type_name::() { + if name == type_name::() { return "map"; } #[cfg(not(feature = "no_std"))] @@ -939,14 +926,14 @@ impl Dynamic { pub const fn from_char(value: char) -> Self { Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite)) } - /// Create a new [`Dynamic`] from a [`FLOAT`]. + /// Create a new [`Dynamic`] from a [`FLOAT`][crate::FLOAT]. /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] #[inline(always)] - pub const fn from_float(value: FLOAT) -> Self { + pub const fn from_float(value: crate::FLOAT) -> Self { Self(Union::Float( - FloatWrapper::new_const(value), + crate::ast::FloatWrapper::new_const(value), DEFAULT_TAG_VALUE, ReadWrite, )) @@ -956,7 +943,7 @@ impl Dynamic { /// Exported under the `decimal` feature only. #[cfg(feature = "decimal")] #[inline(always)] - pub fn from_decimal(value: Decimal) -> Self { + pub fn from_decimal(value: rust_decimal::Decimal) -> Self { Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } /// Create a new [`Dynamic`] from an [`Instant`]. @@ -1104,12 +1091,12 @@ impl Dynamic { /// /// # Notes /// - /// Beware that you need to pass in an [`Array`] type for it to be recognized as an [`Array`]. - /// A [`Vec`][Vec] does not get automatically converted to an [`Array`], but will be a generic + /// Beware that you need to pass in an [`Array`][crate::Array] type for it to be recognized as an [`Array`][crate::Array]. + /// A [`Vec`][Vec] does not get automatically converted to an [`Array`][crate::Array], but will be a generic /// restricted trait object instead, because [`Vec`][Vec] is not a supported standard type. /// /// Similarly, passing in a [`HashMap`][std::collections::HashMap] or - /// [`BTreeMap`][std::collections::BTreeMap] will not get a [`Map`] but a trait object. + /// [`BTreeMap`][std::collections::BTreeMap] will not get a [`Map`][crate::Map] but a trait object. /// /// # Examples /// @@ -1143,12 +1130,12 @@ impl Dynamic { return (*val.downcast_ref::().expect(CHECKED)).into(); } #[cfg(not(feature = "no_float"))] - if TypeId::of::() == TypeId::of::() { - return (*val.downcast_ref::().expect(CHECKED)).into(); + if TypeId::of::() == TypeId::of::() { + return (*val.downcast_ref::().expect(CHECKED)).into(); } #[cfg(feature = "decimal")] - if TypeId::of::() == TypeId::of::() { - return (*val.downcast_ref::().expect(CHECKED)).into(); + if TypeId::of::() == TypeId::of::() { + return (*val.downcast_ref::().expect(CHECKED)).into(); } if TypeId::of::() == TypeId::of::() { return (*val.downcast_ref::().expect(CHECKED)).into(); @@ -1176,14 +1163,14 @@ impl Dynamic { }; #[cfg(not(feature = "no_index"))] { - value = match unsafe_try_cast::<_, Array>(value) { + value = match unsafe_try_cast::<_, crate::Array>(value) { Ok(array) => return array.into(), Err(value) => value, }; } #[cfg(not(feature = "no_index"))] { - value = match unsafe_try_cast::<_, Blob>(value) { + value = match unsafe_try_cast::<_, crate::Blob>(value) { Ok(blob) => return Self(Union::Blob(Box::new(blob), DEFAULT_TAG_VALUE, ReadWrite)), Err(value) => value, }; @@ -1191,7 +1178,7 @@ impl Dynamic { #[cfg(not(feature = "no_object"))] { - value = match unsafe_try_cast::<_, Map>(value) { + value = match unsafe_try_cast::<_, crate::Map>(value) { Ok(map) => return map.into(), Err(value) => value, }; @@ -1298,7 +1285,7 @@ impl Dynamic { } #[cfg(not(feature = "no_float"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Float(value, _, _) => unsafe_try_cast(*value).ok(), _ => None, @@ -1306,7 +1293,7 @@ impl Dynamic { } #[cfg(feature = "decimal")] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Decimal(value, _, _) => unsafe_try_cast(*value).ok(), _ => None, @@ -1342,7 +1329,7 @@ impl Dynamic { } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Array(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), _ => None, @@ -1350,7 +1337,7 @@ impl Dynamic { } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Blob(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), _ => None, @@ -1358,7 +1345,7 @@ impl Dynamic { } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Map(value, _, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v), _ => None, @@ -1652,14 +1639,14 @@ impl Dynamic { }; } #[cfg(not(feature = "no_float"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Float(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(feature = "decimal")] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Decimal(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, @@ -1684,21 +1671,21 @@ impl Dynamic { }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Array(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Blob(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, }; } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Map(ref value, _, _) => value.as_ref().as_any().downcast_ref::(), _ => None, @@ -1750,7 +1737,7 @@ impl Dynamic { }; } #[cfg(not(feature = "no_float"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Float(ref mut value, _, _) => { value.as_mut().as_mut_any().downcast_mut::() @@ -1759,7 +1746,7 @@ impl Dynamic { }; } #[cfg(feature = "decimal")] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Decimal(ref mut value, _, _) => { value.as_mut().as_mut_any().downcast_mut::() @@ -1786,7 +1773,7 @@ impl Dynamic { }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Array(ref mut value, _, _) => { value.as_mut().as_mut_any().downcast_mut::() @@ -1795,14 +1782,14 @@ impl Dynamic { }; } #[cfg(not(feature = "no_index"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Blob(ref mut value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), _ => None, }; } #[cfg(not(feature = "no_object"))] - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { return match self.0 { Union::Map(ref mut value, _, _) => value.as_mut().as_mut_any().downcast_mut::(), _ => None, @@ -1864,13 +1851,13 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// Cast the [`Dynamic`] as the system floating-point type [`FLOAT`]. + /// Cast the [`Dynamic`] as the system floating-point type [`FLOAT`][crate::FLOAT]. /// Returns the name of the actual type if the cast fails. /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] #[inline] - pub fn as_float(&self) -> Result { + pub fn as_float(&self) -> Result { match self.0 { Union::Float(n, _, _) => Ok(*n), #[cfg(not(feature = "no_closure"))] @@ -1878,13 +1865,13 @@ impl Dynamic { _ => Err(self.type_name()), } } - /// _(decimal)_ Cast the [`Dynamic`] as a [`Decimal`](https://docs.rs/rust_decimal). + /// _(decimal)_ Cast the [`Dynamic`] as a [`Decimal`][rust_decimal::Decimal]. /// Returns the name of the actual type if the cast fails. /// /// Exported under the `decimal` feature only. #[cfg(feature = "decimal")] #[inline] - pub fn as_decimal(&self) -> Result { + pub fn as_decimal(&self) -> Result { match self.0 { Union::Decimal(ref n, _, _) => Ok(**n), #[cfg(not(feature = "no_closure"))] @@ -1979,23 +1966,23 @@ impl From for Dynamic { } } #[cfg(not(feature = "no_float"))] -impl From for Dynamic { +impl From for Dynamic { #[inline(always)] - fn from(value: FLOAT) -> Self { + fn from(value: crate::FLOAT) -> Self { Self(Union::Float(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_float"))] -impl From> for Dynamic { +impl From> for Dynamic { #[inline(always)] - fn from(value: FloatWrapper) -> Self { + fn from(value: crate::ast::FloatWrapper) -> Self { Self(Union::Float(value, DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(feature = "decimal")] -impl From for Dynamic { +impl From for Dynamic { #[inline(always)] - fn from(value: Decimal) -> Self { + fn from(value: rust_decimal::Decimal) -> Self { Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } @@ -2026,9 +2013,9 @@ impl FromStr for Dynamic { } #[cfg(not(feature = "no_index"))] impl Dynamic { - /// Create a [`Dynamic`] from an [`Array`]. + /// Create a [`Dynamic`] from an [`Array`][crate::Array]. #[inline(always)] - pub(crate) fn from_array(array: Array) -> Self { + pub(crate) fn from_array(array: crate::Array) -> Self { Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } @@ -2067,10 +2054,10 @@ impl std::iter::FromIterator for Dynamic { } #[cfg(not(feature = "no_index"))] impl Dynamic { - /// Convert the [`Dynamic`] into an [`Array`]. + /// Convert the [`Dynamic`] into an [`Array`][crate::Array]. /// Returns the name of the actual type if the cast fails. #[inline(always)] - pub fn into_array(self) -> Result { + pub fn into_array(self) -> Result { match self.0 { Union::Array(a, _, _) => Ok(*a), #[cfg(not(feature = "no_closure"))] @@ -2148,13 +2135,13 @@ impl Dynamic { impl Dynamic { /// Create a [`Dynamic`] from a [`Vec`]. #[inline(always)] - pub fn from_blob(blob: Blob) -> Self { + pub fn from_blob(blob: crate::Blob) -> Self { Self(Union::Blob(Box::new(blob), DEFAULT_TAG_VALUE, ReadWrite)) } /// Convert the [`Dynamic`] into a [`Vec`]. /// Returns the name of the actual type if the cast fails. #[inline(always)] - pub fn into_blob(self) -> Result { + pub fn into_blob(self) -> Result { match self.0 { Union::Blob(a, _, _) => Ok(*a), #[cfg(not(feature = "no_closure"))] @@ -2175,9 +2162,9 @@ impl Dynamic { } #[cfg(not(feature = "no_object"))] impl Dynamic { - /// Create a [`Dynamic`] from a [`Map`]. + /// Create a [`Dynamic`] from a [`Map`][crate::Map]. #[inline(always)] - pub(crate) fn from_map(map: Map) -> Self { + pub(crate) fn from_map(map: crate::Map) -> Self { Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } From 5df2628eeca722e3258b9d95b45fcccfe430b5b0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 6 Dec 2021 21:23:18 +0800 Subject: [PATCH 06/13] Fix book links. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d5680e45..07a221a4 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,8 @@ For those who actually want their own language --------------------------------------------- * Use as a [DSL](https://rhai.rs/book/engine/dsl.html). -* Restrict the language by surgically [disabling keywords and operators](https://rhai.rs/book/engine/disable.html). +* Disable certain [language features](https://rhai.rs/book/engine/options.html#language-features) such as [looping](https://rhai.rs/book/engine/disable-looping.html). +* Further restrict the language by surgically [disabling keywords and operators](https://rhai.rs/book/engine/disable-keywords.html). * Define [custom operators](https://rhai.rs/book/engine/custom-op.html). * Extend the language with [custom syntax](https://rhai.rs/book/engine/custom-syntax.html). From 4421f33b2c3e12bc3fd92c12f56af1ddd7a078a1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 7 Dec 2021 19:03:04 +0800 Subject: [PATCH 07/13] Fix bug in blob's. --- CHANGELOG.md | 9 ++------- src/engine.rs | 2 +- src/packages/blob_basic.rs | 8 ++++---- tests/blobs.rs | 8 ++++++++ 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd6a6e1a..556b22fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,12 @@ Version 1.3.0 Compiler requirement -------------------- -* Minimum compiler version is bumped to 1.51. - -Bug fixes ---------- - -* BLOB's no longer panic when accessed with an out-of-bounds index. +* Minimum compiler version is now 1.51. New features ------------ +* `BLOB` (essentially a byte array) is added as a supported primitive value type parallel to arrays. * New options for `Engine` which allows disabling `if`-expressions, `switch`-expressions, statement expressions, anonymous functions and/or looping (i.e. `while`, `loop`, `do` and `for` statements): * `Engine::set_allow_if_expression` * `Engine::set_allow_switch_expression` @@ -30,7 +26,6 @@ Enhancements * Added `into_array` and `into_typed_array` for `Dynamic`. * Added `FnPtr::call` and `FnPtr::call_within_context` to simplify calling a function pointer. -* BLob's can now be deserialized (using `from_dynamic`) into `Vec` via [`serde_bytes`](https://crates.io/crates/serde_bytes). * A function's hashes are included in its JSON metadata to assist in debugging. Each function's `hashBase` field in the JSON object should map directly to the pre-calculated hash in the function call. * `Expression` now derefs to `Expr`. diff --git a/src/engine.rs b/src/engine.rs index 512aa7b0..47dcade6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -637,7 +637,7 @@ impl<'a> Target<'a> { let index = *index; if index < value.len() { - value[index] = (new_byte & 0x000f) as u8; + value[index] = (new_byte & 0x00ff) as u8; } else { unreachable!("blob index out of bounds: {}", index); } diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index dde3d60f..a0dcd40c 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -48,7 +48,7 @@ mod blob_functions { } let mut blob = Blob::new(); - blob.resize(len, (value & 0x000f) as u8); + blob.resize(len, (value & 0x00ff) as u8); Ok(blob) } #[rhai_fn(name = "len", get = "len", pure)] @@ -57,7 +57,7 @@ mod blob_functions { } #[rhai_fn(name = "push", name = "+=")] pub fn push(blob: &mut Blob, item: INT) { - let item = (item & 0x000f) as u8; + let item = (item & 0x00ff) as u8; blob.push(item); } #[rhai_fn(name = "append", name = "+=")] @@ -82,7 +82,7 @@ mod blob_functions { blob } pub fn insert(blob: &mut Blob, position: INT, item: INT) { - let item = (item & 0x000f) as u8; + let item = (item & 0x00ff) as u8; if blob.is_empty() { blob.push(item); @@ -113,7 +113,7 @@ mod blob_functions { return Ok(()); } - let item = (item & 0x000f) as u8; + let item = (item & 0x00ff) as u8; let _ctx = ctx; // Check if blob will be over max size limit diff --git a/tests/blobs.rs b/tests/blobs.rs index 8f42eb56..f350b26a 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -63,6 +63,14 @@ fn test_blobs() -> Result<(), Box> { engine.eval_with_scope::(&mut orig_scope.clone(), "x + x")?, [1, 2, 3, 1, 2, 3] ); + assert_eq!( + engine.eval_with_scope::(&mut orig_scope.clone(), "x += 999; x")?, + [1, 2, 3, 0xe7] + ); + assert_eq!( + engine.eval_with_scope::(&mut orig_scope.clone(), "x[2] = 999; x")?, + [1, 2, 0xe7] + ); Ok(()) } From d9d44a96838088cb2047017ac5b38135e838a15f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 9 Dec 2021 12:49:12 +0800 Subject: [PATCH 08/13] Add Engine::register_type_with_name_raw. --- CHANGELOG.md | 1 + src/api/register.rs | 22 +++++++++++++++++++--- src/engine.rs | 2 +- src/module/resolvers/mod.rs | 2 +- src/packages/mod.rs | 12 ++++++------ src/types/immutable_string.rs | 3 +-- 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 556b22fb..2b921acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ New features Enhancements ------------ +* Added `Engine::register_type_with_name_raw` to register a custom type based on a fully-qualified type path. * Added `into_array` and `into_typed_array` for `Dynamic`. * Added `FnPtr::call` and `FnPtr::call_within_context` to simplify calling a function pointer. * A function's hashes are included in its JSON metadata to assist in debugging. Each function's `hashBase` field in the JSON object should map directly to the pre-calculated hash in the function call. diff --git a/src/api/register.rs b/src/api/register.rs index 304731b9..32c929d7 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -4,6 +4,7 @@ use crate::func::{FnCallArgs, RegisterNativeFunction, SendSync}; use crate::types::dynamic::Variant; use crate::{ Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, Shared, + SmartString, }; use std::any::{type_name, TypeId}; #[cfg(feature = "no_std")] @@ -146,11 +147,12 @@ impl Engine { /// /// # WARNING - Low Level API /// - /// This function is very low level. It takes a list of [`TypeId`][std::any::TypeId]'s indicating the actual types of the parameters. + /// This function is very low level. It takes a list of [`TypeId`][std::any::TypeId]'s + /// indicating the actual types of the parameters. /// /// ## Arguments /// - /// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][crate::Dynamic], + /// Arguments are simply passed in as a mutable array of [`&mut Dynamic`][crate::Dynamic]. /// The arguments are guaranteed to be of the correct types matching the [`TypeId`][std::any::TypeId]'s. /// /// To access a primary argument value (i.e. cloning is cheap), use: `args[n].as_xxx().unwrap()` @@ -263,9 +265,23 @@ impl Engine { /// ``` #[inline(always)] pub fn register_type_with_name(&mut self, name: &str) -> &mut Self { + self.register_type_with_name_raw(type_name::(), name) + } + /// Register a custom type for use with the [`Engine`], with a pretty-print name + /// for the `type_of` function. The type must implement [`Clone`]. + /// + /// # WARNING - Low Level API + /// + /// This function is low level. + #[inline(always)] + pub fn register_type_with_name_raw( + &mut self, + fully_qualified_type_path: impl Into, + name: impl Into, + ) -> &mut Self { // Add the pretty-print type name into the map self.type_names - .insert(type_name::().into(), Box::new(name.into())); + .insert(fully_qualified_type_path.into(), name.into()); self } /// Register an type iterator for an iterable type with the [`Engine`]. diff --git a/src/engine.rs b/src/engine.rs index 47dcade6..b77050f5 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -936,7 +936,7 @@ pub struct Engine { pub(crate) module_resolver: Option>, /// A map mapping type names to pretty-print names. - pub(crate) type_names: BTreeMap>, + pub(crate) type_names: BTreeMap, /// An empty [`ImmutableString`] for cloning purposes. pub(crate) empty_string: ImmutableString, diff --git a/src/module/resolvers/mod.rs b/src/module/resolvers/mod.rs index 087d8f7c..47911c34 100644 --- a/src/module/resolvers/mod.rs +++ b/src/module/resolvers/mod.rs @@ -31,7 +31,7 @@ pub trait ModuleResolver: SendSync { /// Returns [`None`] (default) if such resolution is not supported /// (e.g. if the module is Rust-based). /// - /// ## Low-Level API + /// # WARNING - Low Level API /// /// Override the default implementation of this method if the module resolver /// serves modules based on compiled Rhai scripts. diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 63bfc5d4..a18a11cf 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -37,10 +37,11 @@ pub use time_basic::BasicTimePackage; /// Trait that all packages must implement. pub trait Package { - /// Register all the functions in a package into a store. - fn init(lib: &mut Module); + /// Initialize the package. + /// Functions should be registered into `module` here. + fn init(module: &mut Module); - /// Retrieve the generic package library from this package. + /// Get a reference to a shared module from this package. #[must_use] fn as_shared_module(&self) -> Shared; } @@ -60,10 +61,10 @@ pub trait Package { /// /// fn add(x: i64, y: i64) -> Result> { Ok(x + y) } /// -/// def_package!(rhai:MyPackage:"My super-duper package", lib, +/// def_package!(rhai:MyPackage:"My super-duper package", module, /// { /// // Load a binary function with all value parameters. -/// lib.set_native_fn("my_add", add); +/// module.set_native_fn("my_add", add); /// }); /// ``` #[macro_export] @@ -76,7 +77,6 @@ macro_rules! def_package { fn as_shared_module(&self) -> $root::Shared<$root::Module> { self.0.clone() } - fn init($lib: &mut $root::Module) { $block } diff --git a/src/types/immutable_string.rs b/src/types/immutable_string.rs index 822223df..8c8dc3eb 100644 --- a/src/types/immutable_string.rs +++ b/src/types/immutable_string.rs @@ -16,8 +16,7 @@ use std::{ /// The system immutable string type. /// -/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`SmartString`][smartstring::SmartString]`>` -/// (or [`Arc`][std::sync::Arc]`<`[`SmartString`][smartstring::SmartString]`>` under the `sync` feature) +/// An [`ImmutableString`] wraps an `Rc` (or `Arc` under the `sync` feature) /// so that it can be simply shared and not cloned. /// /// # Example From 675ddb89f9d1ffa2f50d0dee04461300f013461e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 9 Dec 2021 13:34:51 +0800 Subject: [PATCH 09/13] Add "" and `` in string literals. --- CHANGELOG.md | 1 + src/tokenizer.rs | 9 +++++++++ tests/string.rs | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b921acc..eacb2b1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ New features Enhancements ------------ +* Two double quotes (`""`) in a string literal now maps to `"`; two back-ticks (``` `` ```) in a literal string now maps to `` ` ``. * Added `Engine::register_type_with_name_raw` to register a custom type based on a fully-qualified type path. * Added `into_array` and `into_typed_array` for `Dynamic`. * Added `FnPtr::call` and `FnPtr::call_within_context` to simplify calling a function pointer. diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 8bdcc508..79018371 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1175,6 +1175,15 @@ pub fn parse_string_literal( result.push(next_char) } + // Double wrapper + _ if termination_char == next_char + && escape.is_empty() + && stream.peek_next().map_or(false, |c| c == termination_char) => + { + eat_next(stream, pos); + result.push(termination_char) + } + // Close wrapper _ if termination_char == next_char && escape.is_empty() => { state.is_within_text_terminated_by = None; diff --git a/tests/string.rs b/tests/string.rs index 62f00b86..ac7991f3 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -8,6 +8,10 @@ fn test_string() -> Result<(), Box> { engine.eval::(r#""Test string: \u2764""#)?, "Test string: ❤" ); + assert_eq!( + engine.eval::(r#""Test string: ""\u2764""""#)?, + r#"Test string: "❤""# + ); assert_eq!( engine.eval::("\"Test\rstring: \\u2764\"")?, "Test\rstring: ❤" @@ -24,6 +28,10 @@ fn test_string() -> Result<(), Box> { engine.eval::(" `Test string: \\u2764\nhello,\\nworld!`")?, "Test string: \\u2764\nhello,\\nworld!" ); + assert_eq!( + engine.eval::(r#" `Test string: \\u2764\n``hello``,\\n"world"!`"#)?, + r#"Test string: \\u2764\n`hello`,\\n"world"!"# + ); assert_eq!( engine.eval::(" `\nTest string: \\u2764\nhello,\\nworld!`")?, "Test string: \\u2764\nhello,\\nworld!" From 3a5495a65c2825696610c930c43214af0c78ad74 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 9 Dec 2021 22:54:10 +0800 Subject: [PATCH 10/13] Clarify custom operators. --- src/api/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index 831c9944..a8a2a3c3 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -157,7 +157,7 @@ impl Engine { } /// Register a custom operator with a precedence into the language. /// - /// The operator must be a valid identifier (i.e. it cannot be a symbol). + /// The operator can be a valid identifier, a reserved symbol, a disabled operator or a disabled keyword. /// /// The precedence cannot be zero. /// @@ -169,15 +169,15 @@ impl Engine { /// /// let mut engine = Engine::new(); /// - /// // Register a custom operator called 'foo' and give it + /// // Register a custom operator called '#' and give it /// // a precedence of 160 (i.e. between +|- and *|/). - /// engine.register_custom_operator("foo", 160).expect("should succeed"); + /// engine.register_custom_operator("#", 160).expect("should succeed"); /// - /// // Register a binary function named 'foo' - /// engine.register_fn("foo", |x: i64, y: i64| (x * y) - (x + y)); + /// // Register a binary function named '#' + /// engine.register_fn("#", |x: i64, y: i64| (x * y) - (x + y)); /// /// assert_eq!( - /// engine.eval_expression::("1 + 2 * 3 foo 4 - 5 / 6")?, + /// engine.eval_expression::("1 + 2 * 3 # 4 - 5 / 6")?, /// 15 /// ); /// # Ok(()) From 780c36e675824e446411faf6df31770dda3a3b31 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 12 Dec 2021 12:33:22 +0800 Subject: [PATCH 11/13] Simplify if let. --- CHANGELOG.md | 2 +- src/func/call.rs | 11 ++++++----- src/optimizer.rs | 8 +++----- src/parser.rs | 40 +++++++++++++++++++--------------------- src/tokenizer.rs | 7 +++---- 5 files changed, 32 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eacb2b1d..4192ccc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ Enhancements * Added `Engine::register_type_with_name_raw` to register a custom type based on a fully-qualified type path. * Added `into_array` and `into_typed_array` for `Dynamic`. * Added `FnPtr::call` and `FnPtr::call_within_context` to simplify calling a function pointer. -* A function's hashes are included in its JSON metadata to assist in debugging. Each function's `hashBase` field in the JSON object should map directly to the pre-calculated hash in the function call. +* A function's hashes are included in its JSON metadata to assist in debugging. Each function's `baseHash` field in the JSON object should map directly to the pre-calculated hash in the function call. * `Expression` now derefs to `Expr`. Deprecated and Gated API's diff --git a/src/func/call.rs b/src/func/call.rs index a4f679e9..7db44596 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -577,11 +577,12 @@ impl Engine { } let mut empty_scope; - let scope = if let Some(scope) = _scope { - scope - } else { - empty_scope = Scope::new(); - &mut empty_scope + let scope = match _scope { + Some(scope) => scope, + None => { + empty_scope = Scope::new(); + &mut empty_scope + } }; let result = if _is_method_call { diff --git a/src/optimizer.rs b/src/optimizer.rs index 3dbbc75b..0e8040f7 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -304,11 +304,9 @@ fn optimize_stmt_block( if reduce_return && !options.contains(AST_OPTION_BREAK_OUT) => { state.set_dirty(); - *statements.last_mut().expect(">= 2 elements") = if let Some(expr) = expr { - Stmt::Expr(mem::take(expr)) - } else { - Stmt::Noop(pos) - }; + *statements.last_mut().expect(">= 2 elements") = expr + .as_mut() + .map_or_else(|| Stmt::Noop(pos), |e| Stmt::Expr(mem::take(e))); } // { ...; stmt; noop } -> done [.., ref second_last_stmt, Stmt::Noop(_)] diff --git a/src/parser.rs b/src/parser.rs index 92e36a2b..25bc5272 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1048,19 +1048,18 @@ fn parse_switch( }; let hash = if let Some(expr) = expr { - if let Some(value) = expr.get_literal_value() { - let hasher = &mut get_hasher(); - value.hash(hasher); - let hash = hasher.finish(); + let value = expr.get_literal_value().ok_or_else(|| { + PERR::ExprExpected("a literal".to_string()).into_err(expr.position()) + })?; + let hasher = &mut get_hasher(); + value.hash(hasher); + let hash = hasher.finish(); - if table.contains_key(&hash) { - return Err(PERR::DuplicatedSwitchCase.into_err(expr.position())); - } - - Some(hash) - } else { - return Err(PERR::ExprExpected("a literal".to_string()).into_err(expr.position())); + if table.contains_key(&hash) { + return Err(PERR::DuplicatedSwitchCase.into_err(expr.position())); } + + Some(hash) } else { None }; @@ -1081,11 +1080,12 @@ fn parse_switch( let need_comma = !stmt.is_self_terminated(); - def_stmt = if let Some(hash) = hash { - table.insert(hash, (condition, stmt.into()).into()); - None - } else { - Some(stmt.into()) + def_stmt = match hash { + Some(hash) => { + table.insert(hash, (condition, stmt.into()).into()); + None + } + None => Some(stmt.into()), }; match input.peek().expect(NEVER_ENDS) { @@ -2461,14 +2461,12 @@ fn parse_for( let prev_stack_len = state.stack.len(); - let counter_var = if let Some(name) = counter_name { + let counter_var = counter_name.map(|name| { let name = state.get_identifier(name); let pos = counter_pos.expect("`Some`"); state.stack.push((name.clone(), AccessMode::ReadWrite)); - Some(Ident { name, pos }) - } else { - None - }; + Ident { name, pos } + }); let loop_var = state.get_identifier(name); state.stack.push((loop_var.clone(), AccessMode::ReadWrite)); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 79018371..c039a8dc 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -2223,10 +2223,9 @@ impl<'a> Iterator for TokenIterator<'a> { }; // Run the mapper, if any - let token = if let Some(map_func) = self.token_mapper { - map_func(token, pos, &self.state) - } else { - token + let token = match self.token_mapper { + Some(map_func) => map_func(token, pos, &self.state), + None => token, }; Some((token, pos)) From 6af406bafc4eb194d665f9f4dd120ee458ad7875 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 12 Dec 2021 16:29:54 +0800 Subject: [PATCH 12/13] Add parse/write to blobs. --- CHANGELOG.md | 17 ++- src/packages/blob_basic.rs | 211 +++++++++++++++++++++++++++++++++++-- tests/blobs.rs | 63 +++++++++++ 3 files changed, 272 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4192ccc6..1e07fe18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,19 @@ Rhai Release Notes Version 1.3.0 ============= +This version adds native support for `BLOB`'s (byte arrays), as well as a number of configuration +settings to fine-tun language features. + Compiler requirement -------------------- * Minimum compiler version is now 1.51. +Bug fixes +--------- + +* `from_dynamic` now supports deserializing `Option`. + New features ------------ @@ -40,15 +48,6 @@ Deprecated and Gated API's * `FnPtr::call_dynamic` is deprecated in favor of `FnPtr::call_raw`. -Version 1.2.2 -============= - -Bug fixes ---------- - -* `from_dynamic` now supports deserializing `Option`. - - Version 1.2.1 ============= diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index a0dcd40c..ae7a35cc 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -7,6 +7,9 @@ use crate::{def_package, Blob, Dynamic, EvalAltResult, NativeCallContext, Positi use std::prelude::v1::*; use std::{any::TypeId, mem}; +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; + def_package!(crate:BasicBlobPackage:"Basic BLOB utilities.", lib, { lib.standard = true; @@ -188,10 +191,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { blob.extend(replace.into_iter()); return; @@ -215,10 +218,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { return Blob::new(); } else { @@ -246,10 +249,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { return Blob::new(); } else { @@ -288,10 +291,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { return Blob::new(); } else { @@ -314,10 +317,10 @@ mod blob_functions { } let start = if start < 0 { - let arr_len = blob.len(); + let blob_len = blob.len(); start .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) } else if start as usize >= blob.len() { return mem::take(blob); } else { @@ -351,4 +354,192 @@ mod blob_functions { pub fn not_equals(blob1: &mut Blob, blob2: Blob) -> bool { !equals(blob1, blob2) } + + #[inline] + fn parse_int(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> INT { + if blob.is_empty() || len <= 0 { + return 0; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return 0; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + const INT_BYTES: usize = mem::size_of::(); + + let len = usize::min(len, INT_BYTES); + + let mut buf = [0_u8; INT_BYTES]; + + buf[..len].copy_from_slice(&blob[start..][..len]); + + if is_le { + INT::from_le_bytes(buf) + } else { + INT::from_be_bytes(buf) + } + } + + pub fn parse_le_int(blob: &mut Blob, start: INT, len: INT) -> INT { + parse_int(blob, start, len, true) + } + pub fn parse_be_int(blob: &mut Blob, start: INT, len: INT) -> INT { + parse_int(blob, start, len, false) + } + + #[inline] + fn write_int(blob: &mut Blob, start: INT, len: INT, value: INT, is_le: bool) { + if blob.is_empty() || len <= 0 { + return; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + const INT_BYTES: usize = mem::size_of::(); + + let len = usize::min(len, INT_BYTES); + + let mut buf = [0_u8; INT_BYTES]; + + buf.copy_from_slice(&if is_le { + value.to_le_bytes() + } else { + value.to_be_bytes() + }); + + blob[start..][..len].copy_from_slice(&buf[..len]); + } + #[rhai_fn(name = "write_le")] + pub fn write_le_int(blob: &mut Blob, start: INT, len: INT, value: INT) { + write_int(blob, start, len, value, true) + } + #[rhai_fn(name = "write_be")] + pub fn write_be_int(blob: &mut Blob, start: INT, len: INT, value: INT) { + write_int(blob, start, len, value, false) + } + + #[cfg(not(feature = "no_float"))] + #[inline] + fn parse_float(blob: &mut Blob, start: INT, len: INT, is_le: bool) -> FLOAT { + if blob.is_empty() || len <= 0 { + return 0.0; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return 0.0; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + const FLOAT_BYTES: usize = mem::size_of::(); + + let len = usize::min(len, FLOAT_BYTES); + + let mut buf = [0_u8; FLOAT_BYTES]; + + buf[..len].copy_from_slice(&blob[start..][..len]); + + if is_le { + FLOAT::from_le_bytes(buf) + } else { + FLOAT::from_be_bytes(buf) + } + } + + #[cfg(not(feature = "no_float"))] + pub fn parse_le_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT { + parse_float(blob, start, len, true) + } + #[cfg(not(feature = "no_float"))] + pub fn parse_be_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT { + parse_float(blob, start, len, false) + } + + #[cfg(not(feature = "no_float"))] + #[inline] + fn write_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT, is_le: bool) { + if blob.is_empty() || len <= 0 { + return; + } + let blob_len = blob.len(); + + let start = if start < 0 { + start + .checked_abs() + .map_or(0, |n| blob_len - (n as usize).min(blob_len)) + } else if start as usize >= blob_len { + return; + } else { + start as usize + }; + + let len = if len as usize > blob_len - start { + blob_len - start + } else { + len as usize + }; + + const FLOAT_BYTES: usize = mem::size_of::(); + + let len = usize::min(len, FLOAT_BYTES); + + let mut buf = [0_u8; FLOAT_BYTES]; + + buf.copy_from_slice(&if is_le { + value.to_le_bytes() + } else { + value.to_be_bytes() + }); + + blob[start..][..len].copy_from_slice(&buf[..len]); + } + #[cfg(not(feature = "no_float"))] + #[rhai_fn(name = "write_le")] + pub fn write_le_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) { + write_float(blob, start, len, value, true) + } + #[cfg(not(feature = "no_float"))] + #[rhai_fn(name = "write_be")] + pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) { + write_float(blob, start, len, value, false) + } } diff --git a/tests/blobs.rs b/tests/blobs.rs index f350b26a..b549b8d8 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -74,3 +74,66 @@ fn test_blobs() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_blobs_parse() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,0)" + )?, + 0 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,9)" + )?, + 0x0908070605040302 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_be_int(x,2,10)" + )?, + 0x0203040506070809 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,99)" + )?, + 0x0f0e0d0c0b + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,2)" + )?, + 0x0c0b + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-99,99)" + )?, + 0x0706050403020100 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_be(x, 3, 3, -98765432); parse_be_int(x, 3, 3)" + )?, + 0xffffff0000000000_u64 as i64 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_le(x, 3, 3, -98765432); parse_le_int(x, 3, 3)" + )?, + 0x1cf588 + ); + + Ok(()) +} From 5eaf85254f9e2a56459ee523ed3f1cbde0c640a6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 12 Dec 2021 17:26:15 +0800 Subject: [PATCH 13/13] Fix builds. --- src/packages/array_basic.rs | 17 +------- src/packages/blob_basic.rs | 83 ++++++++++++++----------------------- tests/blobs.rs | 67 +++++++++++++++++++++++++++++- 3 files changed, 99 insertions(+), 68 deletions(-) diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 1b1f55dd..befc3d7d 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -205,22 +205,7 @@ mod array_functions { } #[rhai_fn(name = "extract")] pub fn extract_tail(array: &mut Array, start: INT) -> Array { - if array.is_empty() { - return Array::new(); - } - - let start = if start < 0 { - let arr_len = array.len(); - start - .checked_abs() - .map_or(0, |n| arr_len - (n as usize).min(arr_len)) - } else if start as usize >= array.len() { - return Array::new(); - } else { - start as usize - }; - - array[start..].to_vec() + extract(array, start, INT::MAX) } #[rhai_fn(name = "split")] pub fn split_at(array: &mut Array, start: INT) -> Array { diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index ae7a35cc..8b0380bc 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -189,13 +189,13 @@ mod blob_functions { *blob = replace; return; } + let blob_len = blob.len(); let start = if start < 0 { - let blob_len = blob.len(); start .checked_abs() .map_or(0, |n| blob_len - (n as usize).min(blob_len)) - } else if start as usize >= blob.len() { + } else if start as usize >= blob_len { blob.extend(replace.into_iter()); return; } else { @@ -204,8 +204,8 @@ mod blob_functions { let len = if len < 0 { 0 - } else if len as usize > blob.len() - start { - blob.len() - start + } else if len as usize > blob_len - start { + blob_len - start } else { len as usize }; @@ -216,50 +216,29 @@ mod blob_functions { if blob.is_empty() || len <= 0 { return Blob::new(); } + let blob_len = blob.len(); let start = if start < 0 { - let blob_len = blob.len(); start .checked_abs() .map_or(0, |n| blob_len - (n as usize).min(blob_len)) - } else if start as usize >= blob.len() { + } else if start as usize >= blob_len { return Blob::new(); } else { start as usize }; - let len = if len <= 0 { - 0 - } else if len as usize > blob.len() - start { - blob.len() - start + let len = if len as usize > blob_len - start { + blob_len - start } else { len as usize }; - if len == 0 { - Blob::new() - } else { - blob[start..start + len].to_vec() - } + blob[start..start + len].to_vec() } #[rhai_fn(name = "extract")] pub fn extract_tail(blob: &mut Blob, start: INT) -> Blob { - if blob.is_empty() { - return Blob::new(); - } - - let start = if start < 0 { - let blob_len = blob.len(); - start - .checked_abs() - .map_or(0, |n| blob_len - (n as usize).min(blob_len)) - } else if start as usize >= blob.len() { - return Blob::new(); - } else { - start as usize - }; - - blob[start..].to_vec() + extract(blob, start, INT::MAX) } #[rhai_fn(name = "split")] pub fn split_at(blob: &mut Blob, start: INT) -> Blob { @@ -289,22 +268,20 @@ mod blob_functions { if blob.is_empty() || len <= 0 { return Blob::new(); } + let blob_len = blob.len(); let start = if start < 0 { - let blob_len = blob.len(); start .checked_abs() .map_or(0, |n| blob_len - (n as usize).min(blob_len)) - } else if start as usize >= blob.len() { + } else if start as usize >= blob_len { return Blob::new(); } else { start as usize }; - let len = if len <= 0 { - 0 - } else if len as usize > blob.len() - start { - blob.len() - start + let len = if len as usize > blob_len - start { + blob_len - start } else { len as usize }; @@ -315,22 +292,20 @@ mod blob_functions { if blob.is_empty() || len <= 0 { return Blob::new(); } + let blob_len = blob.len(); let start = if start < 0 { - let blob_len = blob.len(); start .checked_abs() .map_or(0, |n| blob_len - (n as usize).min(blob_len)) - } else if start as usize >= blob.len() { + } else if start as usize >= blob_len { return mem::take(blob); } else { start as usize }; - let len = if len < 0 { - 0 - } else if len as usize > blob.len() - start { - blob.len() - start + let len = if len as usize > blob_len - start { + blob_len - start } else { len as usize }; @@ -340,19 +315,25 @@ mod blob_functions { drained } + pub fn contains(blob: &mut Blob, value: Dynamic) -> bool { + if blob.is_empty() { + return false; + } + + let value = match value.as_int() { + Ok(value) => value as u8, + _ => return false, + }; + + blob.contains(&value) + } #[rhai_fn(name = "==", pure)] pub fn equals(blob1: &mut Blob, blob2: Blob) -> bool { - if blob1.len() != blob2.len() { - false - } else if blob1.is_empty() { - true - } else { - blob1.iter().zip(blob2.iter()).all(|(&v1, &v2)| v1 == v2) - } + &*blob1 == &blob2 } #[rhai_fn(name = "!=", pure)] pub fn not_equals(blob1: &mut Blob, blob2: Blob) -> bool { - !equals(blob1, blob2) + &*blob1 != &blob2 } #[inline] diff --git a/tests/blobs.rs b/tests/blobs.rs index b549b8d8..7036f116 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -75,6 +75,7 @@ fn test_blobs() -> Result<(), Box> { Ok(()) } +#[cfg(not(feature = "only_i32"))] #[test] fn test_blobs_parse() -> Result<(), Box> { let engine = Engine::new(); @@ -125,7 +126,71 @@ fn test_blobs_parse() -> Result<(), Box> { engine.eval::( "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_be(x, 3, 3, -98765432); parse_be_int(x, 3, 3)" )?, - 0xffffff0000000000_u64 as i64 + 0xffffff0000000000_u64 as INT + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_le(x, 3, 3, -98765432); parse_le_int(x, 3, 3)" + )?, + 0x1cf588 + ); + + Ok(()) +} + +#[cfg(feature = "only_i32")] +#[test] +fn test_blobs_parse() -> Result<(), Box> { + let engine = Engine::new(); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,0)" + )?, + 0 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,9)" + )?, + 0x05040302 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_be_int(x,2,10)" + )?, + 0x02030405 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,99)" + )?, + 0x0e0d0c0b + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,2)" + )?, + 0x0c0b + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-99,99)" + )?, + 0x03020100 + ); + + assert_eq!( + engine.eval::( + "let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_be(x, 3, 3, -98765432); parse_be_int(x, 3, 3)" + )?, + 0xfa1cf500_u32 as INT ); assert_eq!(