Complete ImmutableString.
This commit is contained in:
parent
95e67c48bd
commit
b34d5fe3a1
@ -514,9 +514,9 @@ This is useful on some 32-bit targets where using 64-bit integers incur a perfor
|
||||
|
||||
If no floating-point is needed or supported, use the [`no_float`] feature to remove it.
|
||||
|
||||
Strings in Rhai are _immutable_, meaning that they can be shared but not modified. In actual, the `ImmutableString` type
|
||||
is implemented as an `Rc`- or `Arc`-wrapped `String`. Any modification done to a Rhai string will cause the string to be cloned
|
||||
and the modifications made to the copy.
|
||||
[Strings] in Rhai are _immutable_, meaning that they can be shared but not modified. In actual, the `ImmutableString` type
|
||||
is an alias to `Rc<String>` or `Arc<String>` (depending on the [`sync`] feature).
|
||||
Any modification done to a Rhai string will cause the string to be cloned and the modifications made to the copy.
|
||||
|
||||
The `to_string` function converts a standard type into a [string] for display purposes.
|
||||
|
||||
@ -612,6 +612,7 @@ The following conversion traits are implemented for `Dynamic`:
|
||||
* `From<i64>` (`i32` if [`only_i32`])
|
||||
* `From<f64>` (if not [`no_float`])
|
||||
* `From<bool>`
|
||||
* `From<rhai::ImmutableString>`
|
||||
* `From<String>`
|
||||
* `From<char>`
|
||||
* `From<Vec<T>>` (into an [array])
|
||||
|
10
RELEASES.md
10
RELEASES.md
@ -4,8 +4,8 @@ Rhai Release Notes
|
||||
Version 0.14.2
|
||||
==============
|
||||
|
||||
Regression
|
||||
----------
|
||||
Regression fix
|
||||
--------------
|
||||
|
||||
* Do not optimize script with `eval_expression` - it is assumed to be one-off and short.
|
||||
|
||||
@ -18,6 +18,10 @@ Breaking changes
|
||||
* Default maximum limit on levels of nested function calls is fine-tuned and set to a different value.
|
||||
* Some operator functions are now built in (see _Speed enhancements_ below), so they are available even
|
||||
under `Engine::new_raw`.
|
||||
* Strings are now immutable. The type `rhai::ImmutableString` is used instead of `std::string::String`.
|
||||
This is to avoid excessive cloning of strings. All native-Rust functions taking string parameters
|
||||
should switch to `rhai::ImmutableString` (which is either `Rc<String>` or `Arc<String>` depending on
|
||||
whether the `sync` feature is used).
|
||||
|
||||
New features
|
||||
------------
|
||||
@ -35,6 +39,8 @@ Speed enhancements
|
||||
significant speed-up.
|
||||
* Implementations of common operators for standard types are removed from the `ArithmeticPackage` and `LogicPackage`
|
||||
(and therefore the `CorePackage`) because they are now always available, even under `Engine::new_raw`.
|
||||
* Operator-assignment statements (e.g. `+=`) are now handled directly and much faster.
|
||||
* Strings are now _immutable_ and use the `rhai::ImmutableString` type, eliminating large amounts of cloning.
|
||||
|
||||
|
||||
Version 0.14.1
|
||||
|
22
src/any.rs
22
src/any.rs
@ -1,6 +1,5 @@
|
||||
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
||||
|
||||
use crate::fn_native::shared_unwrap;
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||
|
||||
@ -21,9 +20,7 @@ use crate::stdlib::{
|
||||
boxed::Box,
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
rc::Rc,
|
||||
string::String,
|
||||
sync::Arc,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
@ -417,13 +414,10 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Unit(value) => unsafe_try_cast(value),
|
||||
Union::Bool(value) => unsafe_try_cast(value),
|
||||
Union::Str(value) => {
|
||||
if type_id == TypeId::of::<ImmutableString>() {
|
||||
unsafe_try_cast(value)
|
||||
} else {
|
||||
unsafe_try_cast((*value).clone())
|
||||
}
|
||||
Union::Str(value) if type_id == TypeId::of::<ImmutableString>() => {
|
||||
unsafe_try_cast(value)
|
||||
}
|
||||
Union::Str(value) => unsafe_try_cast(value.into_owned()),
|
||||
Union::Char(value) => unsafe_try_cast(value),
|
||||
Union::Int(value) => unsafe_try_cast(value),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@ -464,12 +458,10 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
Union::Unit(value) => unsafe_try_cast(value).unwrap(),
|
||||
Union::Bool(value) => unsafe_try_cast(value).unwrap(),
|
||||
Union::Str(value) => if type_id == TypeId::of::<ImmutableString>() {
|
||||
unsafe_try_cast(value)
|
||||
} else {
|
||||
unsafe_try_cast((*value).clone())
|
||||
Union::Str(value) if type_id == TypeId::of::<ImmutableString>() => {
|
||||
unsafe_try_cast(value).unwrap()
|
||||
}
|
||||
.unwrap(),
|
||||
Union::Str(value) => unsafe_try_cast(value.into_owned()).unwrap(),
|
||||
Union::Char(value) => unsafe_try_cast(value).unwrap(),
|
||||
Union::Int(value) => unsafe_try_cast(value).unwrap(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
@ -588,7 +580,7 @@ impl Dynamic {
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
pub fn take_string(self) -> Result<String, &'static str> {
|
||||
match self.0 {
|
||||
Union::Str(s) => Ok(shared_unwrap(s).unwrap_or_else(|s| (*s).clone())),
|
||||
Union::Str(s) => Ok(s.into_owned()),
|
||||
_ => Err(self.type_name()),
|
||||
}
|
||||
}
|
||||
|
@ -2055,6 +2055,7 @@ fn run_builtin_binary_op(
|
||||
let y = y.downcast_ref::<ImmutableString>().unwrap();
|
||||
|
||||
match op {
|
||||
"+" => return Ok(Some((x + y).into())),
|
||||
"==" => return Ok(Some((x == y).into())),
|
||||
"!=" => return Ok(Some((x != y).into())),
|
||||
">" => return Ok(Some((x > y).into())),
|
||||
|
@ -19,6 +19,8 @@ pub type Shared<T> = Rc<T>;
|
||||
#[cfg(feature = "sync")]
|
||||
pub type Shared<T> = Arc<T>;
|
||||
|
||||
/// Consume a `Shared` resource and return a mutable reference to the wrapped value.
|
||||
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
|
||||
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
{
|
||||
@ -30,14 +32,19 @@ pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shared_unwrap<T: Clone>(value: Shared<T>) -> Result<T, Shared<T>> {
|
||||
/// Consume a `Shared` resource, assuming that it is unique (i.e. not shared).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the resource is shared (i.e. has other outstanding references).
|
||||
pub fn shared_take<T: Clone>(value: Shared<T>) -> T {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
{
|
||||
Rc::try_unwrap(value)
|
||||
Rc::try_unwrap(value).map_err(|_| ()).unwrap()
|
||||
}
|
||||
#[cfg(feature = "sync")]
|
||||
{
|
||||
Arc::try_unwrap(value)
|
||||
Arc::try_unwrap(value).map_err(|_| ()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use crate::fn_native::{CallableFunction, FnAny, FnCallArgs};
|
||||
use crate::parser::FnAccess;
|
||||
use crate::result::EvalAltResult;
|
||||
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, mem};
|
||||
|
||||
/// Trait to register custom functions with the `Engine`.
|
||||
pub trait RegisterFn<FN, ARGS, RET> {
|
||||
|
24
src/lib.rs
24
src/lib.rs
@ -49,18 +49,18 @@
|
||||
//!
|
||||
//! ## Optional features
|
||||
//!
|
||||
//! | Feature | Description |
|
||||
//! | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
//! | `unchecked` | Exclude arithmetic checking (such as overflows and division by zero). Beware that a bad script may panic the entire system! |
|
||||
//! | `no_function` | Disable script-defined functions if not needed. |
|
||||
//! | `no_index` | Disable arrays and indexing features if not needed. |
|
||||
//! | `no_object` | Disable support for custom types and objects. |
|
||||
//! | `no_float` | Disable floating-point numbers and math if not needed. |
|
||||
//! | `no_optimize` | Disable the script optimizer. |
|
||||
//! | `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
|
||||
//! | `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
||||
//! | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||
//! | `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, `Engine`, `Scope` and `AST` are all `Send + Sync`. |
|
||||
//! | Feature | Description |
|
||||
//! | ------------- | ----------------------------------------------------------------------------------------------------------------------------------|
|
||||
//! | `unchecked` | Exclude arithmetic checking (such as overflows and division by zero). Beware that a bad script may panic the entire system! |
|
||||
//! | `no_function` | Disable script-defined functions if not needed. |
|
||||
//! | `no_index` | Disable arrays and indexing features if not needed. |
|
||||
//! | `no_object` | Disable support for custom types and objects. |
|
||||
//! | `no_float` | Disable floating-point numbers and math if not needed. |
|
||||
//! | `no_optimize` | Disable the script optimizer. |
|
||||
//! | `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
|
||||
//! | `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
||||
//! | `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||
//! | `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, `Engine`, `Scope` and `AST` are all `Send + Sync`. |
|
||||
//!
|
||||
//! [Check out the README on GitHub for details on the Rhai language!](https://github.com/jonathandturner/rhai)
|
||||
|
||||
|
@ -269,141 +269,69 @@ macro_rules! reg_op {
|
||||
}
|
||||
|
||||
def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||
// Checked basic arithmetic
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
// reg_op!(lib, "+", add, INT);
|
||||
// reg_op!(lib, "-", sub, INT);
|
||||
// reg_op!(lib, "*", mul, INT);
|
||||
// reg_op!(lib, "/", div, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
// reg_op!(lib, "+", add, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "/", div, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// Checked basic arithmetic
|
||||
reg_op!(lib, "+", add, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "-", sub, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "*", mul, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "/", div, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
// Unchecked basic arithmetic
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
// reg_op!(lib, "+", add_u, INT);
|
||||
// reg_op!(lib, "-", sub_u, INT);
|
||||
// reg_op!(lib, "*", mul_u, INT);
|
||||
// reg_op!(lib, "/", div_u, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
// reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
// Basic arithmetic for floating-point - no need to check
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
// reg_op!(lib, "+", add_u, f32, f64);
|
||||
// reg_op!(lib, "-", sub_u, f32, f64);
|
||||
// reg_op!(lib, "*", mul_u, f32, f64);
|
||||
// reg_op!(lib, "/", div_u, f32, f64);
|
||||
reg_op!(lib, "+", add_u, f32);
|
||||
reg_op!(lib, "-", sub_u, f32);
|
||||
reg_op!(lib, "*", mul_u, f32);
|
||||
reg_op!(lib, "/", div_u, f32);
|
||||
}
|
||||
|
||||
// Bit operations
|
||||
// reg_op!(lib, "|", binary_or, INT);
|
||||
// reg_op!(lib, "&", binary_and, INT);
|
||||
// reg_op!(lib, "^", binary_xor, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
// reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
}
|
||||
|
||||
// Checked bit shifts
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
// reg_op!(lib, "<<", shl, INT);
|
||||
// reg_op!(lib, ">>", shr, INT);
|
||||
// reg_op!(lib, "%", modulo, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
// reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// Checked bit shifts
|
||||
reg_op!(lib, "<<", shl, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, ">>", shr, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "%", modulo, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
// Unchecked bit shifts
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
// reg_op!(lib, "<<", shl_u, INT, INT);
|
||||
// reg_op!(lib, ">>", shr_u, INT, INT);
|
||||
// reg_op!(lib, "%", modulo_u, INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
// reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// Unchecked basic arithmetic
|
||||
reg_op!(lib, "+", add_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "-", sub_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "*", mul_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "/", div_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
// Unchecked bit shifts
|
||||
reg_op!(lib, "<<", shl_u, i64, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, ">>", shr_u, i64, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "%", modulo_u, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
}
|
||||
}
|
||||
|
||||
// Checked power
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
{
|
||||
// lib.set_fn_2("~", pow_i_i);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
lib.set_fn_2("~", pow_f_i);
|
||||
}
|
||||
|
||||
// Unchecked power
|
||||
#[cfg(feature = "unchecked")]
|
||||
{
|
||||
// lib.set_fn_2("~", pow_i_i_u);
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
lib.set_fn_2("~", pow_f_i_u);
|
||||
}
|
||||
|
||||
// Floating-point modulo and power
|
||||
// Basic arithmetic for floating-point - no need to check
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
// reg_op!(lib, "%", modulo_u, f32, f64);
|
||||
reg_op!(lib, "+", add_u, f32);
|
||||
reg_op!(lib, "-", sub_u, f32);
|
||||
reg_op!(lib, "*", mul_u, f32);
|
||||
reg_op!(lib, "/", div_u, f32);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
reg_op!(lib, "|", binary_or, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "&", binary_and, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "^", binary_xor, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
// Checked power
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
lib.set_fn_2("~", pow_f_i);
|
||||
|
||||
// Unchecked power
|
||||
#[cfg(feature = "unchecked")]
|
||||
lib.set_fn_2("~", pow_f_i_u);
|
||||
|
||||
// Floating-point modulo and power
|
||||
reg_op!(lib, "%", modulo_u, f32);
|
||||
// lib.set_fn_2("~", pow_f_f);
|
||||
|
||||
// Floating-point unary
|
||||
reg_unary!(lib, "-", neg_u, f32, f64);
|
||||
reg_unary!(lib, "abs", abs_u, f32, f64);
|
||||
}
|
||||
|
||||
// Checked unary
|
||||
@ -433,11 +361,4 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||
reg_unary!(lib, "abs", abs_u, i8, i16, i32, i64, i128);
|
||||
}
|
||||
}
|
||||
|
||||
// Floating-point unary
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
reg_unary!(lib, "-", neg_u, f32, f64);
|
||||
reg_unary!(lib, "abs", abs_u, f32, f64);
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::def_package;
|
||||
use crate::module::FuncReturn;
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
|
||||
// Comparison operators
|
||||
pub fn lt<T: PartialOrd>(x: T, y: T) -> FuncReturn<bool> {
|
||||
@ -23,12 +22,6 @@ pub fn ne<T: PartialEq>(x: T, y: T) -> FuncReturn<bool> {
|
||||
}
|
||||
|
||||
// Logic operators
|
||||
fn and(x: bool, y: bool) -> FuncReturn<bool> {
|
||||
Ok(x && y)
|
||||
}
|
||||
fn or(x: bool, y: bool) -> FuncReturn<bool> {
|
||||
Ok(x || y)
|
||||
}
|
||||
fn not(x: bool) -> FuncReturn<bool> {
|
||||
Ok(!x)
|
||||
}
|
||||
@ -40,30 +33,9 @@ macro_rules! reg_op {
|
||||
}
|
||||
|
||||
def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||
// reg_op!(lib, "<", lt, INT, char);
|
||||
// reg_op!(lib, "<=", lte, INT, char);
|
||||
// reg_op!(lib, ">", gt, INT, char);
|
||||
// reg_op!(lib, ">=", gte, INT, char);
|
||||
// reg_op!(lib, "==", eq, INT, char, bool, ());
|
||||
// reg_op!(lib, "!=", ne, INT, char, bool, ());
|
||||
|
||||
// Special versions for strings - at least avoid copying the first string
|
||||
// lib.set_fn_2("<", |x: ImmutableString, y: ImmutableString| Ok(*x < y));
|
||||
// lib.set_fn_2("<=", |x: ImmutableString, y: ImmutableString| Ok(*x <= y));
|
||||
// lib.set_fn_2(">", |x: ImmutableString, y: ImmutableString| Ok(*x > y));
|
||||
// lib.set_fn_2(">=", |x: ImmutableString, y: ImmutableString| Ok(*x >= y));
|
||||
// lib.set_fn_2("==", |x: ImmutableString, y: ImmutableString| Ok(*x == y));
|
||||
// lib.set_fn_2("!=", |x: ImmutableString, y: ImmutableString| Ok(*x != y));
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
{
|
||||
// reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
// reg_op!(lib, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, i128, u128);
|
||||
reg_op!(lib, "<", lt, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, "<=", lte, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
reg_op!(lib, ">", gt, i8, u8, i16, u16, i32, u32, u64, i128, u128);
|
||||
@ -74,12 +46,6 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
{
|
||||
// reg_op!(lib, "<", lt, f32, f64);
|
||||
// reg_op!(lib, "<=", lte, f32, f64);
|
||||
// reg_op!(lib, ">", gt, f32, f64);
|
||||
// reg_op!(lib, ">=", gte, f32, f64);
|
||||
// reg_op!(lib, "==", eq, f32, f64);
|
||||
// reg_op!(lib, "!=", ne, f32, f64);
|
||||
reg_op!(lib, "<", lt, f32);
|
||||
reg_op!(lib, "<=", lte, f32);
|
||||
reg_op!(lib, ">", gt, f32);
|
||||
@ -88,12 +54,5 @@ def_package!(crate:LogicPackage:"Logical operators.", lib, {
|
||||
reg_op!(lib, "!=", ne, f32);
|
||||
}
|
||||
|
||||
// `&&` and `||` are treated specially as they short-circuit.
|
||||
// They are implemented as special `Expr` instances, not function calls.
|
||||
//reg_op!(lib, "||", or, bool);
|
||||
//reg_op!(lib, "&&", and, bool);
|
||||
|
||||
// lib.set_fn_2("|", or);
|
||||
// lib.set_fn_2("&", and);
|
||||
lib.set_fn_1("!", not);
|
||||
});
|
||||
|
@ -6,10 +6,7 @@ use crate::engine::Map;
|
||||
use crate::module::FuncReturn;
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
|
||||
use crate::stdlib::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use crate::stdlib::{string::ToString, vec::Vec};
|
||||
|
||||
fn map_get_keys(map: &mut Map) -> FuncReturn<Vec<Dynamic>> {
|
||||
Ok(map.iter().map(|(k, _)| k.to_string().into()).collect())
|
||||
|
@ -4,7 +4,7 @@ use crate::fn_native::{CallableFunction, IteratorFn, Shared};
|
||||
use crate::module::Module;
|
||||
use crate::utils::StaticVec;
|
||||
|
||||
use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec};
|
||||
use crate::stdlib::any::TypeId;
|
||||
|
||||
pub(crate) mod arithmetic;
|
||||
mod array_basic;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::def_package;
|
||||
use crate::engine::{FUNC_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
|
||||
use crate::fn_native::shared_make_mut;
|
||||
use crate::module::FuncReturn;
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
|
||||
@ -13,7 +12,7 @@ use crate::engine::Map;
|
||||
use crate::stdlib::{
|
||||
fmt::{Debug, Display},
|
||||
format,
|
||||
string::{String, ToString},
|
||||
string::ToString,
|
||||
};
|
||||
|
||||
// Register print and debug
|
||||
@ -79,58 +78,9 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
||||
lib.set_fn_1_mut(KEYWORD_DEBUG, format_map);
|
||||
}
|
||||
|
||||
lib.set_fn_2(
|
||||
"+",
|
||||
|s: ImmutableString, ch: char| {
|
||||
if s.is_empty() {
|
||||
return Ok(ch.to_string().into());
|
||||
}
|
||||
|
||||
let mut s = (*s).clone();
|
||||
s.push(ch);
|
||||
Ok(s)
|
||||
},
|
||||
);
|
||||
lib.set_fn_2(
|
||||
"+",
|
||||
|s:ImmutableString, s2:ImmutableString| {
|
||||
if s.is_empty() {
|
||||
return Ok(s2);
|
||||
} else if s2.is_empty() {
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
let mut s = (*s).clone();
|
||||
s.push_str(s2.as_str());
|
||||
Ok(s.into())
|
||||
},
|
||||
);
|
||||
lib.set_fn_2_mut("+=", |s: &mut ImmutableString, ch: char| {
|
||||
shared_make_mut(s).push(ch);
|
||||
Ok(())
|
||||
});
|
||||
lib.set_fn_2_mut("append", |s: &mut ImmutableString, ch: char| {
|
||||
shared_make_mut(s).push(ch);
|
||||
Ok(())
|
||||
});
|
||||
lib.set_fn_2_mut(
|
||||
"+=",
|
||||
|s: &mut ImmutableString, s2: ImmutableString| {
|
||||
if !s2.is_empty() {
|
||||
shared_make_mut(s).push_str(s2.as_str());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
);
|
||||
lib.set_fn_2_mut(
|
||||
"append",
|
||||
|s: &mut ImmutableString, s2: ImmutableString| {
|
||||
if !s2.is_empty() {
|
||||
shared_make_mut(s).push_str(s2.as_str());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
);
|
||||
lib.set_fn_2("+", |s: ImmutableString, ch: char| Ok(s + ch));
|
||||
lib.set_fn_2_mut("+=", |s: &mut ImmutableString, ch: char| { *s += ch; Ok(()) });
|
||||
lib.set_fn_2_mut("append", |s: &mut ImmutableString, ch: char| { *s += ch; Ok(()) });
|
||||
lib.set_fn_2_mut("+=", |s: &mut ImmutableString, s2: ImmutableString| { *s += &s2; Ok(()) });
|
||||
lib.set_fn_2_mut("append", |s: &mut ImmutableString, s2: ImmutableString| { *s += &s2; Ok(()) });
|
||||
});
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::def_package;
|
||||
use crate::fn_native::shared_make_mut;
|
||||
use crate::module::FuncReturn;
|
||||
use crate::parser::{ImmutableString, INT};
|
||||
use crate::utils::StaticVec;
|
||||
@ -48,12 +47,12 @@ fn sub_string(s: ImmutableString, start: INT, len: INT) -> FuncReturn<ImmutableS
|
||||
}
|
||||
fn crop_string(s: &mut ImmutableString, start: INT, len: INT) -> FuncReturn<()> {
|
||||
let offset = if s.is_empty() || len <= 0 {
|
||||
shared_make_mut(s).clear();
|
||||
s.make_mut().clear();
|
||||
return Ok(());
|
||||
} else if start < 0 {
|
||||
0
|
||||
} else if (start as usize) >= s.chars().count() {
|
||||
shared_make_mut(s).clear();
|
||||
s.make_mut().clear();
|
||||
return Ok(());
|
||||
} else {
|
||||
start as usize
|
||||
@ -67,7 +66,7 @@ fn crop_string(s: &mut ImmutableString, start: INT, len: INT) -> FuncReturn<()>
|
||||
len as usize
|
||||
};
|
||||
|
||||
let copy = shared_make_mut(s);
|
||||
let copy = s.make_mut();
|
||||
copy.clear();
|
||||
copy.extend(chars.iter().skip(offset).take(len));
|
||||
|
||||
@ -166,17 +165,17 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
||||
},
|
||||
);
|
||||
lib.set_fn_1_mut("clear", |s: &mut ImmutableString| {
|
||||
shared_make_mut(s).clear();
|
||||
s.make_mut().clear();
|
||||
Ok(())
|
||||
});
|
||||
lib.set_fn_2_mut("append", |s: &mut ImmutableString, ch: char| {
|
||||
shared_make_mut(s).push(ch);
|
||||
s.make_mut().push(ch);
|
||||
Ok(())
|
||||
});
|
||||
lib.set_fn_2_mut(
|
||||
"append",
|
||||
|s: &mut ImmutableString, add: ImmutableString| {
|
||||
shared_make_mut(s).push_str(add.as_str());
|
||||
s.make_mut().push_str(add.as_str());
|
||||
Ok(())
|
||||
}
|
||||
);
|
||||
@ -198,11 +197,11 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
||||
|s: &mut ImmutableString, len: INT| {
|
||||
if len > 0 {
|
||||
let chars: StaticVec<_> = s.chars().collect();
|
||||
let copy = shared_make_mut(s);
|
||||
let copy = s.make_mut();
|
||||
copy.clear();
|
||||
copy.extend(chars.into_iter().take(len as usize));
|
||||
} else {
|
||||
shared_make_mut(s).clear();
|
||||
s.make_mut().clear();
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@ -210,7 +209,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
|
||||
lib.set_fn_3_mut(
|
||||
"pad",
|
||||
|s: &mut ImmutableString, len: INT, ch: char| {
|
||||
let copy = shared_make_mut(s);
|
||||
let copy = s.make_mut();
|
||||
for _ in 0..copy.chars().count() - len as usize {
|
||||
copy.push(ch);
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ use crate::any::{Dynamic, Union};
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{make_getter, make_setter, Engine, FunctionsLib};
|
||||
use crate::error::{LexError, ParseError, ParseErrorType};
|
||||
use crate::fn_native::Shared;
|
||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||
use crate::token::{Position, Token, TokenIterator};
|
||||
@ -49,11 +48,10 @@ pub type INT = i32;
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub type FLOAT = f64;
|
||||
|
||||
/// The system immutable string type.
|
||||
pub type ImmutableString = Shared<String>;
|
||||
|
||||
type PERR = ParseErrorType;
|
||||
|
||||
pub use crate::utils::ImmutableString;
|
||||
|
||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||
///
|
||||
/// Currently, `AST` is neither `Send` nor `Sync`. Turn on the `sync` feature to make it `Send + Sync`.
|
||||
|
276
src/utils.rs
276
src/utils.rs
@ -4,14 +4,18 @@
|
||||
//!
|
||||
//! The `StaticVec` type has some `unsafe` blocks to handle conversions between `MaybeUninit` and regular types.
|
||||
|
||||
use crate::fn_native::{shared_make_mut, shared_take, Shared};
|
||||
|
||||
use crate::stdlib::{
|
||||
any::TypeId,
|
||||
borrow::Borrow,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
iter::FromIterator,
|
||||
mem,
|
||||
mem::MaybeUninit,
|
||||
ops::{Drop, Index, IndexMut},
|
||||
ops::{Add, AddAssign, Deref, Drop, Index, IndexMut},
|
||||
str::FromStr,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
@ -560,3 +564,273 @@ impl<T> From<Vec<T>> for StaticVec<T> {
|
||||
arr
|
||||
}
|
||||
}
|
||||
|
||||
/// The system immutable string type.
|
||||
///
|
||||
/// An `ImmutableString` wraps an `Rc<String>` (or `Arc<String>` under the `sync` feature)
|
||||
/// so that it can be simply shared and not cloned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::ImmutableString;
|
||||
///
|
||||
/// let s1: ImmutableString = "hello".into();
|
||||
///
|
||||
/// // No actual cloning of the string is involved below.
|
||||
/// let s2 = s1.clone();
|
||||
/// let s3 = s2.clone();
|
||||
///
|
||||
/// assert_eq!(s1, s2);
|
||||
///
|
||||
/// // Clones the underlying string (because it is already shared) and extracts it.
|
||||
/// let mut s: String = s1.into_owned();
|
||||
///
|
||||
/// // Changing the clone has no impact on the previously shared version.
|
||||
/// s.push_str(", world!");
|
||||
///
|
||||
/// // The old version still exists.
|
||||
/// assert_eq!(s2, s3);
|
||||
/// assert_eq!(s2.as_str(), "hello");
|
||||
///
|
||||
/// // Not equals!
|
||||
/// assert_ne!(s2.as_str(), s.as_str());
|
||||
/// assert_eq!(s, "hello, world!");
|
||||
/// ```
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
|
||||
pub struct ImmutableString(Shared<String>);
|
||||
|
||||
impl Deref for ImmutableString {
|
||||
type Target = String;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<String> for ImmutableString {
|
||||
fn as_ref(&self) -> &String {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for ImmutableString {
|
||||
fn borrow(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ImmutableString {
|
||||
fn from(value: &str) -> Self {
|
||||
Self(value.to_string().into())
|
||||
}
|
||||
}
|
||||
impl From<String> for ImmutableString {
|
||||
fn from(value: String) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<String>> for ImmutableString {
|
||||
fn from(value: Box<String>) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImmutableString> for String {
|
||||
fn from(value: ImmutableString) -> Self {
|
||||
value.into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ImmutableString {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(s.to_string().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<char> for ImmutableString {
|
||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<String>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().cloned().collect::<String>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<String>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<String> for ImmutableString {
|
||||
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<String>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ImmutableString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.0.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ImmutableString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.0.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self
|
||||
} else if self.is_empty() {
|
||||
rhs
|
||||
} else {
|
||||
self.make_mut().push_str(rhs.0.as_str());
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self.clone()
|
||||
} else if self.is_empty() {
|
||||
rhs.clone()
|
||||
} else {
|
||||
let mut s = self.clone();
|
||||
s.make_mut().push_str(rhs.0.as_str());
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&ImmutableString> for ImmutableString {
|
||||
fn add_assign(&mut self, rhs: &ImmutableString) {
|
||||
if !rhs.is_empty() {
|
||||
if self.is_empty() {
|
||||
self.0 = rhs.0.clone();
|
||||
} else {
|
||||
self.make_mut().push_str(rhs.0.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&str> for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: &str) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self
|
||||
} else {
|
||||
self.make_mut().push_str(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&str> for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
fn add(self, rhs: &str) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self.clone()
|
||||
} else {
|
||||
let mut s = self.clone();
|
||||
s.make_mut().push_str(rhs);
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&str> for ImmutableString {
|
||||
fn add_assign(&mut self, rhs: &str) {
|
||||
if !rhs.is_empty() {
|
||||
self.make_mut().push_str(rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<String> for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: String) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self
|
||||
} else if self.is_empty() {
|
||||
rhs.into()
|
||||
} else {
|
||||
self.make_mut().push_str(&rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<String> for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
fn add(self, rhs: String) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self.clone()
|
||||
} else if self.is_empty() {
|
||||
rhs.into()
|
||||
} else {
|
||||
let mut s = self.clone();
|
||||
s.make_mut().push_str(&rhs);
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<char> for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
fn add(mut self, rhs: char) -> Self::Output {
|
||||
self.make_mut().push(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<char> for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
fn add(self, rhs: char) -> Self::Output {
|
||||
let mut s = self.clone();
|
||||
s.make_mut().push(rhs);
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<char> for ImmutableString {
|
||||
fn add_assign(&mut self, rhs: char) {
|
||||
self.make_mut().push(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl ImmutableString {
|
||||
/// Consume the `ImmutableString` and convert it into a `String`.
|
||||
/// If there are other references to the same string, a cloned copy is returned.
|
||||
pub fn into_owned(mut self) -> String {
|
||||
self.make_mut(); // Make sure it is unique reference
|
||||
shared_take(self.0) // Should succeed
|
||||
}
|
||||
/// Make sure that the `ImmutableString` is unique (i.e. no other outstanding references).
|
||||
/// Then return a mutable reference to the `String`.
|
||||
pub fn make_mut(&mut self) -> &mut String {
|
||||
shared_make_mut(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user