commit
c11ea90202
@ -23,6 +23,8 @@ New features
|
|||||||
* An integer value can now be indexed to get/set a single bit.
|
* An integer value can now be indexed to get/set a single bit.
|
||||||
* The `bits` method of an integer can be used to iterate through its bits.
|
* The `bits` method of an integer can be used to iterate through its bits.
|
||||||
* New `$bool$`, `$int$`, `$float$` and `$string$` expression types for custom syntax.
|
* New `$bool$`, `$int$`, `$float$` and `$string$` expression types for custom syntax.
|
||||||
|
* New methods `to_hex`, `to_octal` and `to_binary` for integer numbers.
|
||||||
|
* New methods `to_upper`, `to_lower`, `make_upper`, `make_lower` for strings/characters.
|
||||||
|
|
||||||
|
|
||||||
Version 0.20.2
|
Version 0.20.2
|
||||||
|
@ -248,7 +248,6 @@ pub const FN_ANONYMOUS: &str = "anon$";
|
|||||||
pub const OP_EQUALS: &str = "==";
|
pub const OP_EQUALS: &str = "==";
|
||||||
|
|
||||||
/// Standard method function for containment testing.
|
/// Standard method function for containment testing.
|
||||||
///
|
|
||||||
/// The `in` operator is implemented as a call to this method.
|
/// The `in` operator is implemented as a call to this method.
|
||||||
pub const OP_CONTAINS: &str = "contains";
|
pub const OP_CONTAINS: &str = "contains";
|
||||||
|
|
||||||
@ -1233,7 +1232,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let is_ref = target.is_ref();
|
let is_ref_mut = target.is_ref();
|
||||||
|
|
||||||
// Pop the last index value
|
// Pop the last index value
|
||||||
let idx_val = idx_values
|
let idx_val = idx_values
|
||||||
@ -1293,10 +1292,11 @@ impl Engine {
|
|||||||
let hash_set =
|
let hash_set =
|
||||||
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
|
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
|
||||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
||||||
|
let pos = Position::NONE;
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
|
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true,
|
||||||
new_pos, None, level,
|
pos, None, level,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1374,8 +1374,8 @@ impl Engine {
|
|||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
let (mut orig_val, _) = self
|
let (mut orig_val, _) = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
mods, state, lib, getter, hash, args, is_ref, true, *pos, None,
|
mods, state, lib, getter, hash, args, is_ref_mut, true, *pos,
|
||||||
level,
|
None, level,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -1417,7 +1417,8 @@ impl Engine {
|
|||||||
let hash = FnCallHashes::from_native(*hash_set);
|
let hash = FnCallHashes::from_native(*hash_set);
|
||||||
let args = &mut [target.as_mut(), &mut new_val];
|
let args = &mut [target.as_mut(), &mut new_val];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, hash, args, is_ref, true, *pos, None, level,
|
mods, state, lib, setter, hash, args, is_ref_mut, true, *pos, None,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -1425,10 +1426,11 @@ impl Engine {
|
|||||||
let args = &mut [target, &mut name.into(), &mut new_val];
|
let args = &mut [target, &mut name.into(), &mut new_val];
|
||||||
let hash_set =
|
let hash_set =
|
||||||
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
|
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
|
||||||
|
let pos = Position::NONE;
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
|
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true,
|
||||||
*pos, None, level,
|
pos, None, level,
|
||||||
)
|
)
|
||||||
.map_err(
|
.map_err(
|
||||||
|idx_err| match *idx_err {
|
|idx_err| match *idx_err {
|
||||||
@ -1446,7 +1448,8 @@ impl Engine {
|
|||||||
let hash = FnCallHashes::from_native(*hash_get);
|
let hash = FnCallHashes::from_native(*hash_get);
|
||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, getter, hash, args, is_ref, true, *pos, None, level,
|
mods, state, lib, getter, hash, args, is_ref_mut, true, *pos, None,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -1517,7 +1520,7 @@ impl Engine {
|
|||||||
let args = &mut arg_values[..1];
|
let args = &mut arg_values[..1];
|
||||||
let (mut val, updated) = self
|
let (mut val, updated) = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
mods, state, lib, getter, hash_get, args, is_ref, true,
|
mods, state, lib, getter, hash_get, args, is_ref_mut, true,
|
||||||
*pos, None, level,
|
*pos, None, level,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
@ -1563,7 +1566,7 @@ impl Engine {
|
|||||||
let mut arg_values = [target.as_mut(), val];
|
let mut arg_values = [target.as_mut(), val];
|
||||||
let args = &mut arg_values;
|
let args = &mut arg_values;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, hash_set, args, is_ref, true,
|
mods, state, lib, setter, hash_set, args, is_ref_mut, true,
|
||||||
*pos, None, level,
|
*pos, None, level,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
@ -1577,7 +1580,7 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, hash_set, args,
|
mods, state, lib, FN_IDX_SET, hash_set, args,
|
||||||
is_ref, true, *pos, None, level,
|
is_ref_mut, true, *pos, None, level,
|
||||||
)
|
)
|
||||||
.or_else(|idx_err| match *idx_err {
|
.or_else(|idx_err| match *idx_err {
|
||||||
EvalAltResult::ErrorIndexingType(_, _) => {
|
EvalAltResult::ErrorIndexingType(_, _) => {
|
||||||
@ -1972,6 +1975,7 @@ impl Engine {
|
|||||||
_ if indexers => {
|
_ if indexers => {
|
||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
let hash_get = FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_GET, 2));
|
let hash_get = FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_GET, 2));
|
||||||
|
let idx_pos = Position::NONE;
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_GET, hash_get, args, true, true, idx_pos, None, level,
|
mods, state, lib, FN_IDX_GET, hash_get, args, true, true, idx_pos, None, level,
|
||||||
@ -1980,7 +1984,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ => EvalAltResult::ErrorIndexingType(
|
_ => EvalAltResult::ErrorIndexingType(
|
||||||
self.map_type_name(target.type_name()).into(),
|
format!(
|
||||||
|
"{} [{}]",
|
||||||
|
self.map_type_name(target.type_name()),
|
||||||
|
self.map_type_name(idx.type_name())
|
||||||
|
),
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
use crate::dynamic::Variant;
|
use crate::dynamic::Variant;
|
||||||
use crate::engine::{EvalContext, Imports, State};
|
use crate::engine::{EvalContext, Imports, State};
|
||||||
use crate::fn_native::{FnCallArgs, SendSync};
|
use crate::fn_call::FnCallArgs;
|
||||||
|
use crate::fn_native::SendSync;
|
||||||
use crate::fn_register::RegisterNativeFunction;
|
use crate::fn_register::RegisterNativeFunction;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::parse::ParseState;
|
use crate::parse::ParseState;
|
||||||
|
13
src/error.rs
13
src/error.rs
@ -59,8 +59,7 @@ pub enum EvalAltResult {
|
|||||||
/// Bit-field indexing out-of-bounds.
|
/// Bit-field indexing out-of-bounds.
|
||||||
/// Wrapped values are the current number of bits in the bit-field and the index number.
|
/// Wrapped values are the current number of bits in the bit-field and the index number.
|
||||||
ErrorBitFieldBounds(usize, INT, Position),
|
ErrorBitFieldBounds(usize, INT, Position),
|
||||||
/// Trying to index into a type that is not an array, an object map, or a string, and has no
|
/// Trying to index into a type that has no indexer function defined. Wrapped value is the type name.
|
||||||
/// indexer function defined. Wrapped value is the type name.
|
|
||||||
ErrorIndexingType(String, Position),
|
ErrorIndexingType(String, Position),
|
||||||
/// The `for` statement encounters a type that is not an iterator.
|
/// The `for` statement encounters a type that is not an iterator.
|
||||||
ErrorFor(Position),
|
ErrorFor(Position),
|
||||||
@ -101,14 +100,12 @@ impl EvalAltResult {
|
|||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
Self::ErrorSystem(_, s) => s.description(),
|
Self::ErrorSystem(_, s) => s.description(),
|
||||||
Self::ErrorParsing(p, _) => p.desc(),
|
Self::ErrorParsing(p, _) => p.desc(),
|
||||||
Self::ErrorInFunctionCall(_,_, _, _) => "Error in called function",
|
Self::ErrorInFunctionCall(_, _, _, _) => "Error in called function",
|
||||||
Self::ErrorInModule(_, _, _) => "Error in module",
|
Self::ErrorInModule(_, _, _) => "Error in module",
|
||||||
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
||||||
Self::ErrorUnboundThis(_) => "'this' is not bound",
|
Self::ErrorUnboundThis(_) => "'this' is not bound",
|
||||||
Self::ErrorMismatchDataType(_, _, _) => "Data type is incorrect",
|
Self::ErrorMismatchDataType(_, _, _) => "Data type is incorrect",
|
||||||
Self::ErrorIndexingType(_, _) => {
|
Self::ErrorIndexingType(_, _) => "No indexer of the appropriate types defined",
|
||||||
"Indexing can only be performed on an array, an object map, a string, or a type with an indexer function defined"
|
|
||||||
}
|
|
||||||
Self::ErrorArrayBounds(0, _, _) => "Empty array has nothing to access",
|
Self::ErrorArrayBounds(0, _, _) => "Empty array has nothing to access",
|
||||||
Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds",
|
Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds",
|
||||||
Self::ErrorStringBounds(0, _, _) => "Empty string has nothing to index",
|
Self::ErrorStringBounds(0, _, _) => "Empty string has nothing to index",
|
||||||
@ -126,7 +123,7 @@ impl EvalAltResult {
|
|||||||
Self::ErrorTooManyModules(_) => "Too many modules imported",
|
Self::ErrorTooManyModules(_) => "Too many modules imported",
|
||||||
Self::ErrorStackOverflow(_) => "Stack overflow",
|
Self::ErrorStackOverflow(_) => "Stack overflow",
|
||||||
Self::ErrorDataTooLarge(_, _) => "Data size exceeds maximum limit",
|
Self::ErrorDataTooLarge(_, _) => "Data size exceeds maximum limit",
|
||||||
Self::ErrorTerminated(_,_) => "Script terminated.",
|
Self::ErrorTerminated(_, _) => "Script terminated.",
|
||||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||||
Self::LoopBreak(true, _) => "Break statement not inside a loop",
|
Self::LoopBreak(true, _) => "Break statement not inside a loop",
|
||||||
Self::LoopBreak(false, _) => "Continue statement not inside a loop",
|
Self::LoopBreak(false, _) => "Continue statement not inside a loop",
|
||||||
@ -175,7 +172,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
|
|
||||||
Self::ErrorDotExpr(s, _) if !s.is_empty() => f.write_str(s)?,
|
Self::ErrorDotExpr(s, _) if !s.is_empty() => f.write_str(s)?,
|
||||||
|
|
||||||
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for type '{}'", s)?,
|
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for '{}'", s)?,
|
||||||
|
|
||||||
Self::ErrorUnboundThis(_)
|
Self::ErrorUnboundThis(_)
|
||||||
| Self::ErrorFor(_)
|
| Self::ErrorFor(_)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
//! Built-in implementations for common operators.
|
//! Built-in implementations for common operators.
|
||||||
|
|
||||||
use crate::engine::OP_CONTAINS;
|
use crate::engine::OP_CONTAINS;
|
||||||
use crate::fn_native::{FnCallArgs, NativeCallContext};
|
use crate::fn_call::FnCallArgs;
|
||||||
|
use crate::fn_native::NativeCallContext;
|
||||||
use crate::{Dynamic, ImmutableString, RhaiResult, INT};
|
use crate::{Dynamic, ImmutableString, RhaiResult, INT};
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
|
@ -7,7 +7,7 @@ use crate::engine::{
|
|||||||
MAX_DYNAMIC_PARAMETERS,
|
MAX_DYNAMIC_PARAMETERS,
|
||||||
};
|
};
|
||||||
use crate::fn_builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
|
use crate::fn_builtin::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
|
||||||
use crate::fn_native::{FnAny, FnCallArgs};
|
use crate::fn_native::FnAny;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -30,6 +30,9 @@ use std::{
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
|
/// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic].
|
||||||
|
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
||||||
|
|
||||||
/// A type that temporarily stores a mutable reference to a `Dynamic`,
|
/// A type that temporarily stores a mutable reference to a `Dynamic`,
|
||||||
/// replacing it with a cloned copy.
|
/// replacing it with a cloned copy.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -102,13 +105,13 @@ impl Drop for ArgBackup<'_> {
|
|||||||
pub fn ensure_no_data_race(
|
pub fn ensure_no_data_race(
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args: &FnCallArgs,
|
args: &FnCallArgs,
|
||||||
is_ref: bool,
|
is_method_call: bool,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if let Some((n, _)) = args
|
if let Some((n, _)) = args
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.skip(if is_ref { 1 } else { 0 })
|
.skip(if is_method_call { 1 } else { 0 })
|
||||||
.find(|(_, a)| a.is_locked())
|
.find(|(_, a)| a.is_locked())
|
||||||
{
|
{
|
||||||
return EvalAltResult::ErrorDataRace(
|
return EvalAltResult::ErrorDataRace(
|
||||||
@ -298,7 +301,7 @@ impl Engine {
|
|||||||
name: &str,
|
name: &str,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_method_call: bool,
|
||||||
is_op_assign: bool,
|
is_op_assign: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
@ -316,7 +319,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Calling pure function but the first argument is a reference?
|
// Calling pure function but the first argument is a reference?
|
||||||
let mut backup: Option<ArgBackup> = None;
|
let mut backup: Option<ArgBackup> = None;
|
||||||
if is_ref && func.is_pure() && !args.is_empty() {
|
if is_method_call && func.is_pure() && !args.is_empty() {
|
||||||
backup = Some(Default::default());
|
backup = Some(Default::default());
|
||||||
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
|
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
|
||||||
}
|
}
|
||||||
@ -373,7 +376,11 @@ impl Engine {
|
|||||||
assert!(args.len() == 2);
|
assert!(args.len() == 2);
|
||||||
|
|
||||||
EvalAltResult::ErrorIndexingType(
|
EvalAltResult::ErrorIndexingType(
|
||||||
self.map_type_name(args[0].type_name()).to_string(),
|
format!(
|
||||||
|
"{} [{}]",
|
||||||
|
self.map_type_name(args[0].type_name()),
|
||||||
|
self.map_type_name(args[1].type_name())
|
||||||
|
),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
@ -385,7 +392,12 @@ impl Engine {
|
|||||||
assert!(args.len() == 3);
|
assert!(args.len() == 3);
|
||||||
|
|
||||||
EvalAltResult::ErrorIndexingType(
|
EvalAltResult::ErrorIndexingType(
|
||||||
self.map_type_name(args[0].type_name()).to_string(),
|
format!(
|
||||||
|
"{} [{}] = {}",
|
||||||
|
self.map_type_name(args[0].type_name()),
|
||||||
|
self.map_type_name(args[1].type_name()),
|
||||||
|
self.map_type_name(args[2].type_name())
|
||||||
|
),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
@ -620,8 +632,8 @@ impl Engine {
|
|||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref_mut: bool,
|
||||||
_is_method: bool,
|
_is_method_call: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
_capture_scope: Option<Scope>,
|
_capture_scope: Option<Scope>,
|
||||||
_level: usize,
|
_level: usize,
|
||||||
@ -634,7 +646,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
ensure_no_data_race(fn_name, args, is_ref)?;
|
ensure_no_data_race(fn_name, args, is_ref_mut)?;
|
||||||
|
|
||||||
// These may be redirected from method style calls.
|
// These may be redirected from method style calls.
|
||||||
match fn_name {
|
match fn_name {
|
||||||
@ -722,7 +734,7 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = if _is_method {
|
let result = if _is_method_call {
|
||||||
// Method call of script function - map first argument to `this`
|
// Method call of script function - map first argument to `this`
|
||||||
let (first, rest) = args
|
let (first, rest) = args
|
||||||
.split_first_mut()
|
.split_first_mut()
|
||||||
@ -753,7 +765,7 @@ impl Engine {
|
|||||||
// Normal call of script function
|
// Normal call of script function
|
||||||
// The first argument is a reference?
|
// The first argument is a reference?
|
||||||
let mut backup: Option<ArgBackup> = None;
|
let mut backup: Option<ArgBackup> = None;
|
||||||
if is_ref && !args.is_empty() {
|
if is_ref_mut && !args.is_empty() {
|
||||||
backup = Some(Default::default());
|
backup = Some(Default::default());
|
||||||
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
|
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args));
|
||||||
}
|
}
|
||||||
@ -780,7 +792,9 @@ impl Engine {
|
|||||||
|
|
||||||
// Native function call
|
// Native function call
|
||||||
let hash = hashes.native;
|
let hash = hashes.native;
|
||||||
self.call_native_fn(mods, state, lib, fn_name, hash, args, is_ref, false, pos)
|
self.call_native_fn(
|
||||||
|
mods, state, lib, fn_name, hash, args, is_ref_mut, false, pos,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a list of statements with no `this` pointer.
|
/// Evaluate a list of statements with no `this` pointer.
|
||||||
@ -872,7 +886,7 @@ impl Engine {
|
|||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
let is_ref = target.is_ref();
|
let is_ref_mut = target.is_ref();
|
||||||
|
|
||||||
let (result, updated) = match fn_name {
|
let (result, updated) = match fn_name {
|
||||||
KEYWORD_FN_PTR_CALL if target.is::<FnPtr>() => {
|
KEYWORD_FN_PTR_CALL if target.is::<FnPtr>() => {
|
||||||
@ -932,7 +946,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, new_hash, &mut args, is_ref, true, pos, None, level,
|
mods, state, lib, fn_name, new_hash, &mut args, is_ref_mut, true, pos, None,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
KEYWORD_FN_PTR_CURRY => {
|
KEYWORD_FN_PTR_CURRY => {
|
||||||
@ -1004,7 +1019,7 @@ impl Engine {
|
|||||||
args.extend(call_args.iter_mut());
|
args.extend(call_args.iter_mut());
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, &mut args, is_ref, true, pos, None, level,
|
mods, state, lib, fn_name, hash, &mut args, is_ref_mut, true, pos, None, level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}?;
|
}?;
|
||||||
@ -1227,7 +1242,7 @@ impl Engine {
|
|||||||
// Normal function call - except for Fn, curry, call and eval (handled above)
|
// Normal function call - except for Fn, curry, call and eval (handled above)
|
||||||
let mut arg_values = StaticVec::with_capacity(args_expr.len());
|
let mut arg_values = StaticVec::with_capacity(args_expr.len());
|
||||||
let mut args = StaticVec::with_capacity(args_expr.len() + curry.len());
|
let mut args = StaticVec::with_capacity(args_expr.len() + curry.len());
|
||||||
let mut is_ref = false;
|
let mut is_ref_mut = false;
|
||||||
let capture = if capture_scope && !scope.is_empty() {
|
let capture = if capture_scope && !scope.is_empty() {
|
||||||
Some(scope.clone_visible())
|
Some(scope.clone_visible())
|
||||||
} else {
|
} else {
|
||||||
@ -1269,7 +1284,7 @@ impl Engine {
|
|||||||
args.extend(arg_values.iter_mut())
|
args.extend(arg_values.iter_mut())
|
||||||
} else {
|
} else {
|
||||||
// Turn it into a method call only if the object is not shared and not a simple value
|
// Turn it into a method call only if the object is not shared and not a simple value
|
||||||
is_ref = true;
|
is_ref_mut = true;
|
||||||
let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared");
|
let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared");
|
||||||
args.push(obj_ref);
|
args.push(obj_ref);
|
||||||
args.extend(arg_values.iter_mut());
|
args.extend(arg_values.iter_mut());
|
||||||
@ -1288,7 +1303,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, name, hashes, &mut args, is_ref, false, pos, capture, level,
|
mods, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, capture, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
|
187
src/fn_native.rs
187
src/fn_native.rs
@ -2,18 +2,14 @@
|
|||||||
|
|
||||||
use crate::ast::{FnAccess, FnCallHashes};
|
use crate::ast::{FnAccess, FnCallHashes};
|
||||||
use crate::engine::Imports;
|
use crate::engine::Imports;
|
||||||
|
use crate::fn_call::FnCallArgs;
|
||||||
use crate::plugin::PluginFunction;
|
use crate::plugin::PluginFunction;
|
||||||
use crate::token::is_valid_identifier;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, Identifier, Module, Position,
|
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, Module, Position, RhaiResult,
|
||||||
RhaiResult, StaticVec,
|
|
||||||
};
|
};
|
||||||
|
use std::fmt;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
|
||||||
convert::{TryFrom, TryInto},
|
|
||||||
fmt, mem,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
@ -204,12 +200,12 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
pub fn call_fn_dynamic_raw(
|
pub fn call_fn_dynamic_raw(
|
||||||
&self,
|
&self,
|
||||||
fn_name: impl AsRef<str>,
|
fn_name: impl AsRef<str>,
|
||||||
is_method: bool,
|
is_method_call: bool,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let fn_name = fn_name.as_ref();
|
let fn_name = fn_name.as_ref();
|
||||||
|
|
||||||
let hash = if is_method {
|
let hash = if is_method_call {
|
||||||
FnCallHashes::from_script_and_native(
|
FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(fn_name, args.len() - 1),
|
calc_fn_hash(fn_name, args.len() - 1),
|
||||||
calc_fn_hash(fn_name, args.len()),
|
calc_fn_hash(fn_name, args.len()),
|
||||||
@ -226,8 +222,8 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
fn_name,
|
fn_name,
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
is_method,
|
is_method_call,
|
||||||
is_method,
|
is_method_call,
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
None,
|
None,
|
||||||
0,
|
0,
|
||||||
@ -271,175 +267,6 @@ pub fn shared_take<T>(value: Shared<T>) -> T {
|
|||||||
.expect("resource should have no outstanding references")
|
.expect("resource should have no outstanding references")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic].
|
|
||||||
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
|
||||||
|
|
||||||
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
|
||||||
/// to be passed onto a function during a call.
|
|
||||||
#[derive(Debug, Clone, Hash)]
|
|
||||||
pub struct FnPtr(Identifier, StaticVec<Dynamic>);
|
|
||||||
|
|
||||||
impl FnPtr {
|
|
||||||
/// Create a new function pointer.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(name: impl Into<Identifier>) -> Result<Self, Box<EvalAltResult>> {
|
|
||||||
name.into().try_into()
|
|
||||||
}
|
|
||||||
/// Create a new function pointer without checking its parameters.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self {
|
|
||||||
Self(name.into(), curry)
|
|
||||||
}
|
|
||||||
/// Get the name of the function.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn fn_name(&self) -> &str {
|
|
||||||
self.get_fn_name().as_ref()
|
|
||||||
}
|
|
||||||
/// Get the name of the function.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn get_fn_name(&self) -> &Identifier {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
/// Get the underlying data of the function pointer.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub(crate) fn take_data(self) -> (Identifier, StaticVec<Dynamic>) {
|
|
||||||
(self.0, self.1)
|
|
||||||
}
|
|
||||||
/// Get the curried arguments.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn curry(&self) -> &[Dynamic] {
|
|
||||||
self.1.as_ref()
|
|
||||||
}
|
|
||||||
/// Add a new curried argument.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
|
|
||||||
self.1.push(value);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
/// Set curried arguments to the function pointer.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_curry(&mut self, values: impl IntoIterator<Item = Dynamic>) -> &mut Self {
|
|
||||||
self.1 = values.into_iter().collect();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
/// Is the function pointer curried?
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_curried(&self) -> bool {
|
|
||||||
!self.1.is_empty()
|
|
||||||
}
|
|
||||||
/// Get the number of curried arguments.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn num_curried(&self) -> usize {
|
|
||||||
self.1.len()
|
|
||||||
}
|
|
||||||
/// Does the function pointer refer to an anonymous function?
|
|
||||||
///
|
|
||||||
/// Not available under `no_function`.
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_anonymous(&self) -> bool {
|
|
||||||
self.0.starts_with(crate::engine::FN_ANONYMOUS)
|
|
||||||
}
|
|
||||||
/// Call the function pointer with curried arguments (if any).
|
|
||||||
///
|
|
||||||
/// If this function is a script-defined function, it must not be marked private.
|
|
||||||
///
|
|
||||||
/// # WARNING
|
|
||||||
///
|
|
||||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
|
||||||
/// This is to avoid unnecessarily cloning the arguments.
|
|
||||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
|
||||||
/// clone them _before_ calling this function.
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub fn call_dynamic(
|
|
||||||
&self,
|
|
||||||
ctx: &NativeCallContext,
|
|
||||||
this_ptr: Option<&mut Dynamic>,
|
|
||||||
mut arg_values: impl AsMut<[Dynamic]>,
|
|
||||||
) -> RhaiResult {
|
|
||||||
let mut arg_values = arg_values.as_mut();
|
|
||||||
let mut args_data;
|
|
||||||
|
|
||||||
if self.num_curried() > 0 {
|
|
||||||
args_data = StaticVec::with_capacity(self.num_curried() + arg_values.len());
|
|
||||||
args_data.extend(self.curry().iter().cloned());
|
|
||||||
args_data.extend(arg_values.iter_mut().map(mem::take));
|
|
||||||
arg_values = args_data.as_mut();
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_method = this_ptr.is_some();
|
|
||||||
|
|
||||||
let mut args = StaticVec::with_capacity(arg_values.len() + 1);
|
|
||||||
if let Some(obj) = this_ptr {
|
|
||||||
args.push(obj);
|
|
||||||
}
|
|
||||||
args.extend(arg_values.iter_mut());
|
|
||||||
|
|
||||||
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, &mut args)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for FnPtr {
|
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "Fn({})", self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Identifier> for FnPtr {
|
|
||||||
type Error = Box<EvalAltResult>;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_from(value: Identifier) -> Result<Self, Self::Error> {
|
|
||||||
if is_valid_identifier(value.chars()) {
|
|
||||||
Ok(Self(value, Default::default()))
|
|
||||||
} else {
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(value.to_string(), Position::NONE).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
|
||||||
impl TryFrom<crate::ImmutableString> for FnPtr {
|
|
||||||
type Error = Box<EvalAltResult>;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_from(value: crate::ImmutableString) -> Result<Self, Self::Error> {
|
|
||||||
let s: Identifier = value.into();
|
|
||||||
Self::try_from(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<String> for FnPtr {
|
|
||||||
type Error = Box<EvalAltResult>;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
|
||||||
let s: Identifier = value.into();
|
|
||||||
Self::try_from(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for FnPtr {
|
|
||||||
type Error = Box<EvalAltResult>;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
||||||
let s: Identifier = value.into();
|
|
||||||
Self::try_from(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A general function trail object.
|
/// A general function trail object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
pub type FnAny = dyn Fn(NativeCallContext, &mut FnCallArgs) -> RhaiResult;
|
||||||
|
178
src/fn_ptr.rs
Normal file
178
src/fn_ptr.rs
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
//! The `FnPtr` type.
|
||||||
|
|
||||||
|
use crate::token::is_valid_identifier;
|
||||||
|
use crate::{
|
||||||
|
Dynamic, EvalAltResult, Identifier, NativeCallContext, Position, RhaiResult, StaticVec,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "no_std")]
|
||||||
|
use std::prelude::v1::*;
|
||||||
|
use std::{
|
||||||
|
convert::{TryFrom, TryInto},
|
||||||
|
fmt, mem,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
||||||
|
/// to be passed onto a function during a call.
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub struct FnPtr(Identifier, StaticVec<Dynamic>);
|
||||||
|
|
||||||
|
impl FnPtr {
|
||||||
|
/// Create a new function pointer.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(name: impl Into<Identifier>) -> Result<Self, Box<EvalAltResult>> {
|
||||||
|
name.into().try_into()
|
||||||
|
}
|
||||||
|
/// Create a new function pointer without checking its parameters.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self {
|
||||||
|
Self(name.into(), curry)
|
||||||
|
}
|
||||||
|
/// Get the name of the function.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn fn_name(&self) -> &str {
|
||||||
|
self.get_fn_name().as_ref()
|
||||||
|
}
|
||||||
|
/// Get the name of the function.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn get_fn_name(&self) -> &Identifier {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
/// Get the underlying data of the function pointer.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn take_data(self) -> (Identifier, StaticVec<Dynamic>) {
|
||||||
|
(self.0, self.1)
|
||||||
|
}
|
||||||
|
/// Get the curried arguments.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn curry(&self) -> &[Dynamic] {
|
||||||
|
self.1.as_ref()
|
||||||
|
}
|
||||||
|
/// Add a new curried argument.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
|
||||||
|
self.1.push(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Set curried arguments to the function pointer.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_curry(&mut self, values: impl IntoIterator<Item = Dynamic>) -> &mut Self {
|
||||||
|
self.1 = values.into_iter().collect();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Is the function pointer curried?
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_curried(&self) -> bool {
|
||||||
|
!self.1.is_empty()
|
||||||
|
}
|
||||||
|
/// Get the number of curried arguments.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn num_curried(&self) -> usize {
|
||||||
|
self.1.len()
|
||||||
|
}
|
||||||
|
/// Does the function pointer refer to an anonymous function?
|
||||||
|
///
|
||||||
|
/// Not available under `no_function`.
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_anonymous(&self) -> bool {
|
||||||
|
self.0.starts_with(crate::engine::FN_ANONYMOUS)
|
||||||
|
}
|
||||||
|
/// Call the function pointer with curried arguments (if any).
|
||||||
|
///
|
||||||
|
/// If this function is a script-defined function, it must not be marked private.
|
||||||
|
///
|
||||||
|
/// # WARNING
|
||||||
|
///
|
||||||
|
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
||||||
|
/// This is to avoid unnecessarily cloning the arguments.
|
||||||
|
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||||
|
/// clone them _before_ calling this function.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn call_dynamic(
|
||||||
|
&self,
|
||||||
|
ctx: &NativeCallContext,
|
||||||
|
this_ptr: Option<&mut Dynamic>,
|
||||||
|
mut arg_values: impl AsMut<[Dynamic]>,
|
||||||
|
) -> RhaiResult {
|
||||||
|
let mut arg_values = arg_values.as_mut();
|
||||||
|
let mut args_data;
|
||||||
|
|
||||||
|
if self.num_curried() > 0 {
|
||||||
|
args_data = StaticVec::with_capacity(self.num_curried() + arg_values.len());
|
||||||
|
args_data.extend(self.curry().iter().cloned());
|
||||||
|
args_data.extend(arg_values.iter_mut().map(mem::take));
|
||||||
|
arg_values = args_data.as_mut();
|
||||||
|
};
|
||||||
|
|
||||||
|
let is_method = this_ptr.is_some();
|
||||||
|
|
||||||
|
let mut args = StaticVec::with_capacity(arg_values.len() + 1);
|
||||||
|
if let Some(obj) = this_ptr {
|
||||||
|
args.push(obj);
|
||||||
|
}
|
||||||
|
args.extend(arg_values.iter_mut());
|
||||||
|
|
||||||
|
ctx.call_fn_dynamic_raw(self.fn_name(), is_method, &mut args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FnPtr {
|
||||||
|
#[inline(always)]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Fn({})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Identifier> for FnPtr {
|
||||||
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_from(value: Identifier) -> Result<Self, Self::Error> {
|
||||||
|
if is_valid_identifier(value.chars()) {
|
||||||
|
Ok(Self(value, Default::default()))
|
||||||
|
} else {
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(value.to_string(), Position::NONE).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_smartstring"))]
|
||||||
|
impl TryFrom<crate::ImmutableString> for FnPtr {
|
||||||
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_from(value: crate::ImmutableString) -> Result<Self, Self::Error> {
|
||||||
|
let s: Identifier = value.into();
|
||||||
|
Self::try_from(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for FnPtr {
|
||||||
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
|
let s: Identifier = value.into();
|
||||||
|
Self::try_from(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for FnPtr {
|
||||||
|
type Error = Box<EvalAltResult>;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
let s: Identifier = value.into();
|
||||||
|
Self::try_from(s)
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,8 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::dynamic::{DynamicWriteLock, Variant};
|
use crate::dynamic::{DynamicWriteLock, Variant};
|
||||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
use crate::fn_call::FnCallArgs;
|
||||||
|
use crate::fn_native::{CallableFunction, FnAny, SendSync};
|
||||||
use crate::r#unsafe::unsafe_try_cast;
|
use crate::r#unsafe::unsafe_try_cast;
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
use crate::{Dynamic, EvalAltResult, NativeCallContext};
|
use crate::{Dynamic, EvalAltResult, NativeCallContext};
|
||||||
|
42
src/lib.rs
42
src/lib.rs
@ -83,6 +83,7 @@ mod fn_call;
|
|||||||
mod fn_func;
|
mod fn_func;
|
||||||
mod fn_hash;
|
mod fn_hash;
|
||||||
mod fn_native;
|
mod fn_native;
|
||||||
|
mod fn_ptr;
|
||||||
mod fn_register;
|
mod fn_register;
|
||||||
mod immutable_string;
|
mod immutable_string;
|
||||||
mod module;
|
mod module;
|
||||||
@ -132,22 +133,14 @@ pub use dynamic::Dynamic;
|
|||||||
pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS};
|
pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS};
|
||||||
pub use error::EvalAltResult;
|
pub use error::EvalAltResult;
|
||||||
pub use error_parsing::{LexError, ParseError, ParseErrorType};
|
pub use error_parsing::{LexError, ParseError, ParseErrorType};
|
||||||
pub use fn_native::{FnPtr, NativeCallContext};
|
pub use fn_native::NativeCallContext;
|
||||||
|
pub use fn_ptr::FnPtr;
|
||||||
pub use fn_register::RegisterNativeFunction;
|
pub use fn_register::RegisterNativeFunction;
|
||||||
pub use immutable_string::ImmutableString;
|
pub use immutable_string::ImmutableString;
|
||||||
pub use module::{FnNamespace, Module};
|
pub use module::{FnNamespace, Module};
|
||||||
pub use scope::Scope;
|
pub use scope::Scope;
|
||||||
pub use token::Position;
|
pub use token::Position;
|
||||||
|
|
||||||
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
|
||||||
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
|
||||||
pub type Identifier = SmartString;
|
|
||||||
|
|
||||||
/// An identifier in Rhai.
|
|
||||||
#[cfg(feature = "no_smartstring")]
|
|
||||||
pub type Identifier = ImmutableString;
|
|
||||||
|
|
||||||
/// A trait to enable registering Rust functions.
|
/// A trait to enable registering Rust functions.
|
||||||
/// This trait is no longer needed and will be removed in the future.
|
/// This trait is no longer needed and will be removed in the future.
|
||||||
#[deprecated(
|
#[deprecated(
|
||||||
@ -164,6 +157,30 @@ pub trait RegisterFn {}
|
|||||||
)]
|
)]
|
||||||
pub trait RegisterResultFn {}
|
pub trait RegisterResultFn {}
|
||||||
|
|
||||||
|
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
||||||
|
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
||||||
|
#[cfg(not(feature = "internals"))]
|
||||||
|
#[cfg(not(feature = "no_smartstring"))]
|
||||||
|
pub(crate) type Identifier = SmartString;
|
||||||
|
|
||||||
|
/// An identifier in Rhai.
|
||||||
|
#[cfg(not(feature = "internals"))]
|
||||||
|
#[cfg(feature = "no_smartstring")]
|
||||||
|
pub(crate) type Identifier = ImmutableString;
|
||||||
|
|
||||||
|
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
||||||
|
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[cfg(not(feature = "no_smartstring"))]
|
||||||
|
#[deprecated = "this type is volatile and may change"]
|
||||||
|
pub type Identifier = SmartString;
|
||||||
|
|
||||||
|
/// An identifier in Rhai.
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[cfg(feature = "no_smartstring")]
|
||||||
|
#[deprecated = "this type is volatile and may change"]
|
||||||
|
pub type Identifier = ImmutableString;
|
||||||
|
|
||||||
/// Alias to [`Rc`][std::rc::Rc] or [`Arc`][std::sync::Arc] depending on the `sync` feature flag.
|
/// Alias to [`Rc`][std::rc::Rc] or [`Arc`][std::sync::Arc] depending on the `sync` feature flag.
|
||||||
pub use fn_native::Shared;
|
pub use fn_native::Shared;
|
||||||
|
|
||||||
@ -310,17 +327,12 @@ type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
|||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
pub type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
||||||
|
|
||||||
#[cfg(not(feature = "internals"))]
|
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
#[cfg(not(feature = "no_smartstring"))]
|
||||||
pub(crate) type SmartString = smartstring::SmartString<smartstring::Compact>;
|
pub(crate) type SmartString = smartstring::SmartString<smartstring::Compact>;
|
||||||
|
|
||||||
#[cfg(feature = "no_smartstring")]
|
#[cfg(feature = "no_smartstring")]
|
||||||
pub(crate) type SmartString = String;
|
pub(crate) type SmartString = String;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
|
||||||
pub type SmartString = smartstring::SmartString<smartstring::Compact>;
|
|
||||||
|
|
||||||
// Compiler guards against mutually-exclusive feature flags
|
// Compiler guards against mutually-exclusive feature flags
|
||||||
|
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
use crate::ast::{FnAccess, Ident};
|
use crate::ast::{FnAccess, Ident};
|
||||||
use crate::dynamic::Variant;
|
use crate::dynamic::Variant;
|
||||||
use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync};
|
use crate::fn_call::FnCallArgs;
|
||||||
|
use crate::fn_native::{shared_take_or_clone, CallableFunction, IteratorFn, SendSync};
|
||||||
use crate::fn_register::RegisterNativeFunction;
|
use crate::fn_register::RegisterNativeFunction;
|
||||||
use crate::parse::IdentifierBuilder;
|
use crate::parse::IdentifierBuilder;
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, FnPtr};
|
use crate::{def_package, FnPtr, INT};
|
||||||
|
use std::fmt::{Binary, LowerHex, Octal};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -16,6 +17,7 @@ pub const FUNC_TO_DEBUG: &'static str = "to_debug";
|
|||||||
|
|
||||||
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
||||||
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
||||||
|
combine_with_exported_module!(lib, "number_formatting", number_formatting);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register print and debug
|
// Register print and debug
|
||||||
@ -147,3 +149,145 @@ mod print_debug_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod number_formatting {
|
||||||
|
#[rhai_fn(skip)]
|
||||||
|
pub fn to_hex<T: LowerHex>(value: T) -> ImmutableString {
|
||||||
|
format!("{:x}", value).into()
|
||||||
|
}
|
||||||
|
#[rhai_fn(skip)]
|
||||||
|
pub fn to_octal<T: Octal>(value: T) -> ImmutableString {
|
||||||
|
format!("{:o}", value).into()
|
||||||
|
}
|
||||||
|
#[rhai_fn(skip)]
|
||||||
|
pub fn to_binary<T: Binary>(value: T) -> ImmutableString {
|
||||||
|
format!("{:b}", value).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn int_to_hex(value: INT) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn int_to_octal(value: INT) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
#[cfg(not(feature = "only_i64"))]
|
||||||
|
pub mod numbers {
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn u8_to_hex(value: u8) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn u16_to_hex(value: u16) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn u32_to_hex(value: u32) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn u64_to_hex(value: u64) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn i8_to_hex(value: i8) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn i16_to_hex(value: i16) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn i32_to_hex(value: i32) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn i64_to_hex(value: i64) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn u8_to_octal(value: u8) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn u16_to_octal(value: u16) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn u32_to_octal(value: u32) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn u64_to_octal(value: u64) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn i8_to_octal(value: i8) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn i16_to_octal(value: i16) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn i32_to_octal(value: i32) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn i64_to_octal(value: i64) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn u8_to_binary(value: u8) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn u16_to_binary(value: u16) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn u32_to_binary(value: u32) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn u64_to_binary(value: u64) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn i8_to_binary(value: i8) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn i16_to_binary(value: i16) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn i32_to_binary(value: i32) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn i64_to_binary(value: i64) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||||
|
pub mod num_128 {
|
||||||
|
#[rhai_fn(name = "to_hex")]
|
||||||
|
pub fn u128_to_hex(value: u128) -> ImmutableString {
|
||||||
|
to_hex(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_octal")]
|
||||||
|
pub fn i128_to_octal(value: i128) -> ImmutableString {
|
||||||
|
to_octal(value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_binary")]
|
||||||
|
pub fn i128_to_binary(value: i128) -> ImmutableString {
|
||||||
|
to_binary(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -100,6 +100,52 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_upper(string: &str) -> ImmutableString {
|
||||||
|
string.to_uppercase().into()
|
||||||
|
}
|
||||||
|
pub fn make_upper(string: &mut ImmutableString) {
|
||||||
|
*string = to_upper(string);
|
||||||
|
}
|
||||||
|
pub fn to_lower(string: &str) -> ImmutableString {
|
||||||
|
string.to_lowercase().into()
|
||||||
|
}
|
||||||
|
pub fn make_lower(string: &mut ImmutableString) {
|
||||||
|
*string = to_lower(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "to_upper")]
|
||||||
|
pub fn to_upper_char(character: char) -> char {
|
||||||
|
let mut stream = character.to_uppercase();
|
||||||
|
let ch = stream
|
||||||
|
.next()
|
||||||
|
.expect("never fails because there should be at least one character");
|
||||||
|
if stream.next().is_some() {
|
||||||
|
character
|
||||||
|
} else {
|
||||||
|
ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "make_upper")]
|
||||||
|
pub fn make_upper_char(character: &mut char) {
|
||||||
|
*character = to_upper_char(*character)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_lower")]
|
||||||
|
pub fn to_lower_char(character: char) -> char {
|
||||||
|
let mut stream = character.to_lowercase();
|
||||||
|
let ch = stream
|
||||||
|
.next()
|
||||||
|
.expect("never fails because there should be at least one character");
|
||||||
|
if stream.next().is_some() {
|
||||||
|
character
|
||||||
|
} else {
|
||||||
|
ch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "make_lower")]
|
||||||
|
pub fn make_lower_char(character: &mut char) {
|
||||||
|
*character = to_lower_char(*character)
|
||||||
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "index_of")]
|
#[rhai_fn(name = "index_of")]
|
||||||
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
pub fn index_of_char_starting_from(string: &str, character: char, start: INT) -> INT {
|
||||||
let start = if start < 0 {
|
let start = if start < 0 {
|
||||||
|
@ -318,7 +318,7 @@ fn parse_var_name(input: &mut TokenStream) -> Result<(String, Position), ParseEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse ( expr )
|
/// Parse `(` expr `)`
|
||||||
fn parse_paren_expr(
|
fn parse_paren_expr(
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! Module defining macros for developing _plugins_.
|
//! Module defining macros for developing _plugins_.
|
||||||
|
|
||||||
pub use crate::fn_native::{CallableFunction, FnCallArgs};
|
use crate::fn_call::FnCallArgs;
|
||||||
|
pub use crate::fn_native::CallableFunction;
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
|
Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
|
||||||
NativeCallContext, Position,
|
NativeCallContext, Position,
|
||||||
|
@ -122,7 +122,6 @@ impl<'a> Scope<'a> {
|
|||||||
/// assert!(my_scope.is_empty());
|
/// assert!(my_scope.is_empty());
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
|
||||||
pub fn clear(&mut self) -> &mut Self {
|
pub fn clear(&mut self) -> &mut Self {
|
||||||
self.names.clear();
|
self.names.clear();
|
||||||
self.values.clear();
|
self.values.clear();
|
||||||
|
@ -31,7 +31,7 @@ impl Serialize for Dynamic {
|
|||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
#[cfg(not(feature = "f32_float"))]
|
#[cfg(not(feature = "f32_float"))]
|
||||||
Union::Decimal(x, _, _) => {
|
Union::Decimal(ref x, _, _) => {
|
||||||
use rust_decimal::prelude::ToPrimitive;
|
use rust_decimal::prelude::ToPrimitive;
|
||||||
|
|
||||||
if let Some(v) = x.to_f64() {
|
if let Some(v) = x.to_f64() {
|
||||||
@ -42,7 +42,7 @@ impl Serialize for Dynamic {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
Union::Decimal(x, _, _) => {
|
Union::Decimal(ref x, _, _) => {
|
||||||
use rust_decimal::prelude::ToPrimitive;
|
use rust_decimal::prelude::ToPrimitive;
|
||||||
|
|
||||||
if let Some(v) = x.to_f32() {
|
if let Some(v) = x.to_f32() {
|
||||||
|
Loading…
Reference in New Issue
Block a user