commit
bb28ec03a2
@ -25,6 +25,7 @@ Enhancements
|
|||||||
* `Scope::set_value` now takes anything that implements `Into<Cow<str>>`.
|
* `Scope::set_value` now takes anything that implements `Into<Cow<str>>`.
|
||||||
* Added `Scope::is_constant` to check if a variable is constant.
|
* Added `Scope::is_constant` to check if a variable is constant.
|
||||||
* Added `Scope::set_or_push` to add a new variable only if one doesn't already exist.
|
* Added `Scope::set_or_push` to add a new variable only if one doesn't already exist.
|
||||||
|
* `Engine::register_type_XXX` are now available even under `no_object`.
|
||||||
|
|
||||||
|
|
||||||
Version 1.0.2
|
Version 1.0.2
|
||||||
|
10
README.md
10
README.md
@ -20,12 +20,12 @@ Rhai is an embedded scripting language and evaluation engine for Rust that gives
|
|||||||
to add scripting to any application.
|
to add scripting to any application.
|
||||||
|
|
||||||
|
|
||||||
Supported targets and builds
|
Targets and builds
|
||||||
---------------------------
|
------------------
|
||||||
|
|
||||||
* All common CPU targets for Windows, Linux and MacOS.
|
* All CPU and O/S targets supported by Rust, including:
|
||||||
* WebAssembly (WASM)
|
* WebAssembly (WASM)
|
||||||
* `no-std`
|
* `no-std`
|
||||||
* Minimum Rust version 1.49
|
* Minimum Rust version 1.49
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
|||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||||
|
|
|
|
||||||
note: required by a bound in `rhai::Dynamic::from`
|
note: required by a bound in `rhai::Dynamic::from`
|
||||||
--> $DIR/dynamic.rs:1127:30
|
--> $DIR/dynamic.rs:1121:30
|
||||||
|
|
|
|
||||||
1127 | pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
1121 | pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||||
| ^^^^^ required by this bound in `rhai::Dynamic::from`
|
| ^^^^^ required by this bound in `rhai::Dynamic::from`
|
||||||
|
@ -5,7 +5,7 @@ error[E0277]: the trait bound `NonClonable: Clone` is not satisfied
|
|||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClonable`
|
||||||
|
|
|
|
||||||
note: required by a bound in `rhai::Dynamic::from`
|
note: required by a bound in `rhai::Dynamic::from`
|
||||||
--> $DIR/dynamic.rs:1127:30
|
--> $DIR/dynamic.rs:1121:30
|
||||||
|
|
|
|
||||||
1127 | pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
1121 | pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||||
| ^^^^^ required by this bound in `rhai::Dynamic::from`
|
| ^^^^^ required by this bound in `rhai::Dynamic::from`
|
||||||
|
14
src/ast.rs
14
src/ast.rs
@ -1490,8 +1490,7 @@ impl Stmt {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
path.pop()
|
path.pop().expect("`path` contains current node");
|
||||||
.expect("never fails because `path` always contains the current node");
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -1566,7 +1565,7 @@ impl OpAssignment<'_> {
|
|||||||
pub fn new(op: Token) -> Self {
|
pub fn new(op: Token) -> Self {
|
||||||
let op_raw = op
|
let op_raw = op
|
||||||
.map_op_assignment()
|
.map_op_assignment()
|
||||||
.expect("never fails because token must be an op-assignment operator")
|
.expect("token is op-assignment operator")
|
||||||
.literal_syntax();
|
.literal_syntax();
|
||||||
let op_assignment = op.literal_syntax();
|
let op_assignment = op.literal_syntax();
|
||||||
|
|
||||||
@ -2018,7 +2017,7 @@ impl Expr {
|
|||||||
let mut arr = Array::with_capacity(x.len());
|
let mut arr = Array::with_capacity(x.len());
|
||||||
arr.extend(x.iter().map(|v| {
|
arr.extend(x.iter().map(|v| {
|
||||||
v.get_literal_value()
|
v.get_literal_value()
|
||||||
.expect("never fails because a constant array always has a constant value")
|
.expect("constant array has constant value")
|
||||||
}));
|
}));
|
||||||
Dynamic::from_array(arr)
|
Dynamic::from_array(arr)
|
||||||
}
|
}
|
||||||
@ -2028,9 +2027,9 @@ impl Expr {
|
|||||||
let mut map = x.1.clone();
|
let mut map = x.1.clone();
|
||||||
x.0.iter().for_each(|(k, v)| {
|
x.0.iter().for_each(|(k, v)| {
|
||||||
*map.get_mut(k.name.as_str())
|
*map.get_mut(k.name.as_str())
|
||||||
.expect("never fails because the template should contain all the keys") = v
|
.expect("template contains all keys") = v
|
||||||
.get_literal_value()
|
.get_literal_value()
|
||||||
.expect("never fails because a constant map always has a constant value")
|
.expect("constant map has constant value")
|
||||||
});
|
});
|
||||||
Dynamic::from_map(map)
|
Dynamic::from_map(map)
|
||||||
}
|
}
|
||||||
@ -2309,8 +2308,7 @@ impl Expr {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
path.pop()
|
path.pop().expect("`path` contains current node");
|
||||||
.expect("never fails because `path` always contains the current node");
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -154,8 +154,8 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
|
|||||||
|
|
||||||
/// Definition of a custom syntax definition.
|
/// Definition of a custom syntax definition.
|
||||||
pub struct CustomSyntax {
|
pub struct CustomSyntax {
|
||||||
/// A parsing function to return the next keyword in a custom syntax based on the
|
/// A parsing function to return the next token in a custom syntax based on the
|
||||||
/// keywords parsed so far.
|
/// symbols parsed so far.
|
||||||
pub parse: Box<FnCustomSyntaxParse>,
|
pub parse: Box<FnCustomSyntaxParse>,
|
||||||
/// Custom syntax implementation function.
|
/// Custom syntax implementation function.
|
||||||
pub func: Shared<FnCustomSyntaxEval>,
|
pub func: Shared<FnCustomSyntaxEval>,
|
||||||
@ -166,10 +166,17 @@ pub struct CustomSyntax {
|
|||||||
impl Engine {
|
impl Engine {
|
||||||
/// Register a custom syntax with the [`Engine`].
|
/// Register a custom syntax with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// * `keywords` holds a slice of strings that define the custom syntax.
|
/// * `symbols` holds a slice of strings that define the custom syntax.
|
||||||
/// * `scope_may_be_changed` specifies variables _may_ be added/removed by this custom syntax.
|
/// * `scope_may_be_changed` specifies variables _may_ be added/removed by this custom syntax.
|
||||||
/// * `func` is the implementation function.
|
/// * `func` is the implementation function.
|
||||||
///
|
///
|
||||||
|
/// ## Note on `symbols`
|
||||||
|
///
|
||||||
|
/// * Whitespaces around symbols are stripped.
|
||||||
|
/// * Symbols that are all-whitespace or empty are ignored.
|
||||||
|
/// * If `symbols` does not contain at least one valid token, then the custom syntax registration
|
||||||
|
/// is simply ignored.
|
||||||
|
///
|
||||||
/// ## Note on `scope_may_be_changed`
|
/// ## Note on `scope_may_be_changed`
|
||||||
///
|
///
|
||||||
/// If `scope_may_be_changed` is `true`, then _size_ of the current [`Scope`][crate::Scope]
|
/// If `scope_may_be_changed` is `true`, then _size_ of the current [`Scope`][crate::Scope]
|
||||||
@ -185,7 +192,7 @@ impl Engine {
|
|||||||
/// does NOT count, so `false` should be passed.
|
/// does NOT count, so `false` should be passed.
|
||||||
pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
|
pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
keywords: &[S],
|
symbols: &[S],
|
||||||
scope_may_be_changed: bool,
|
scope_may_be_changed: bool,
|
||||||
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
|
||||||
) -> Result<&mut Self, ParseError> {
|
) -> Result<&mut Self, ParseError> {
|
||||||
@ -193,10 +200,10 @@ impl Engine {
|
|||||||
|
|
||||||
let mut segments: StaticVec<ImmutableString> = Default::default();
|
let mut segments: StaticVec<ImmutableString> = Default::default();
|
||||||
|
|
||||||
for s in keywords {
|
for s in symbols {
|
||||||
let s = s.as_ref().trim();
|
let s = s.as_ref().trim();
|
||||||
|
|
||||||
// Skip empty keywords
|
// Skip empty symbols
|
||||||
if s.is_empty() {
|
if s.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -220,10 +227,10 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||||
// Standard or reserved keyword/symbol not in first position
|
// Standard or reserved keyword/symbol not in first position
|
||||||
s if !segments.is_empty() && token.is_some() => {
|
_ if !segments.is_empty() && token.is_some() => {
|
||||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||||
if (self.disabled_symbols.contains(s)
|
if (self.disabled_symbols.contains(s)
|
||||||
|| matches!(token, Some(Token::Reserved(_))))
|
|| token.map_or(false, |v| v.is_reserved()))
|
||||||
&& !self.custom_keywords.contains_key(s)
|
&& !self.custom_keywords.contains_key(s)
|
||||||
{
|
{
|
||||||
self.custom_keywords.insert(s.into(), None);
|
self.custom_keywords.insert(s.into(), None);
|
||||||
@ -273,7 +280,7 @@ impl Engine {
|
|||||||
segments.push(seg);
|
segments.push(seg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the syntax has no keywords, just ignore the registration
|
// If the syntax has no symbols, just ignore the registration
|
||||||
if segments.is_empty() {
|
if segments.is_empty() {
|
||||||
return Ok(self);
|
return Ok(self);
|
||||||
}
|
}
|
||||||
@ -307,8 +314,8 @@ impl Engine {
|
|||||||
/// * `parse` is the parsing function.
|
/// * `parse` is the parsing function.
|
||||||
/// * `func` is the implementation function.
|
/// * `func` is the implementation function.
|
||||||
///
|
///
|
||||||
/// All custom keywords must be manually registered via [`Engine::register_custom_operator`].
|
/// All custom keywords used as symbols must be manually registered via [`Engine::register_custom_operator`].
|
||||||
/// Otherwise, custom keywords won't be recognized.
|
/// Otherwise, they won't be recognized.
|
||||||
pub fn register_custom_syntax_raw(
|
pub fn register_custom_syntax_raw(
|
||||||
&mut self,
|
&mut self,
|
||||||
key: impl Into<Identifier>,
|
key: impl Into<Identifier>,
|
||||||
|
@ -34,8 +34,8 @@ use fmt::Debug;
|
|||||||
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
|
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
|
|
||||||
/// The message: never fails because the type was checked
|
/// The message: data type was checked
|
||||||
const CHECKED: &str = "never fails because the type was checked";
|
const CHECKED: &str = "data type was checked";
|
||||||
|
|
||||||
mod private {
|
mod private {
|
||||||
use crate::fn_native::SendSync;
|
use crate::fn_native::SendSync;
|
||||||
@ -259,9 +259,7 @@ impl<'d, T: Any + Clone> Deref for DynamicReadLock<'d, T> {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
DynamicReadLockInner::Reference(ref reference) => *reference,
|
DynamicReadLockInner::Reference(ref reference) => *reference,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicReadLockInner::Guard(ref guard) => guard.downcast_ref().expect(
|
DynamicReadLockInner::Guard(ref guard) => guard.downcast_ref().expect(CHECKED),
|
||||||
"never fails because the read guard was created after checking the data type",
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,9 +300,7 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
DynamicWriteLockInner::Reference(ref reference) => *reference,
|
DynamicWriteLockInner::Reference(ref reference) => *reference,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicWriteLockInner::Guard(ref guard) => guard.downcast_ref().expect(
|
DynamicWriteLockInner::Guard(ref guard) => guard.downcast_ref().expect(CHECKED),
|
||||||
"never fails because the write guard was created after checking the data type",
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -315,9 +311,7 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
DynamicWriteLockInner::Reference(ref mut reference) => *reference,
|
DynamicWriteLockInner::Reference(ref mut reference) => *reference,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
DynamicWriteLockInner::Guard(ref mut guard) => guard.downcast_mut().expect(
|
DynamicWriteLockInner::Guard(ref mut guard) => guard.downcast_mut().expect(CHECKED),
|
||||||
"never fails because the write guard was created after checking the data type",
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1261,7 +1255,7 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// let x = Dynamic::from(42_u32);
|
/// let x = Dynamic::from(42_u32);
|
||||||
///
|
///
|
||||||
/// assert_eq!(x.try_cast::<u32>().unwrap(), 42);
|
/// assert_eq!(x.try_cast::<u32>().expect("x should be u32"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
137
src/engine.rs
137
src/engine.rs
@ -501,7 +501,7 @@ impl<'a> Target<'a> {
|
|||||||
|
|
||||||
let value = &mut *value
|
let value = &mut *value
|
||||||
.write_lock::<crate::INT>()
|
.write_lock::<crate::INT>()
|
||||||
.expect("never fails because `BitField` always holds an `INT`");
|
.expect("`BitField` holds `INT`");
|
||||||
|
|
||||||
let index = *index;
|
let index = *index;
|
||||||
|
|
||||||
@ -529,7 +529,7 @@ impl<'a> Target<'a> {
|
|||||||
|
|
||||||
let s = &mut *s
|
let s = &mut *s
|
||||||
.write_lock::<ImmutableString>()
|
.write_lock::<ImmutableString>()
|
||||||
.expect("never fails because `StringChar` always holds an `ImmutableString`");
|
.expect("`StringChar` holds `ImmutableString`");
|
||||||
|
|
||||||
let index = *index;
|
let index = *index;
|
||||||
|
|
||||||
@ -553,9 +553,7 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
|||||||
// Cloning is cheap for a shared value
|
// Cloning is cheap for a shared value
|
||||||
let container = value.clone();
|
let container = value.clone();
|
||||||
return Self::LockGuard((
|
return Self::LockGuard((
|
||||||
value
|
value.write_lock::<Dynamic>().expect("cast to `Dynamic`"),
|
||||||
.write_lock::<Dynamic>()
|
|
||||||
.expect("never fails when casting to `Dynamic`"),
|
|
||||||
container,
|
container,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -701,9 +699,9 @@ impl EvalState {
|
|||||||
// Push a new function resolution cache if the stack is empty
|
// Push a new function resolution cache if the stack is empty
|
||||||
self.fn_resolution_caches.push(Default::default());
|
self.fn_resolution_caches.push(Default::default());
|
||||||
}
|
}
|
||||||
self.fn_resolution_caches.last_mut().expect(
|
self.fn_resolution_caches
|
||||||
"never fails because there is at least one function resolution cache by this point",
|
.last_mut()
|
||||||
)
|
.expect("at least one function resolution cache")
|
||||||
}
|
}
|
||||||
/// Push an empty function resolution cache onto the stack and make it current.
|
/// Push an empty function resolution cache onto the stack and make it current.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -720,7 +718,7 @@ impl EvalState {
|
|||||||
pub fn pop_fn_resolution_cache(&mut self) {
|
pub fn pop_fn_resolution_cache(&mut self) {
|
||||||
self.fn_resolution_caches
|
self.fn_resolution_caches
|
||||||
.pop()
|
.pop()
|
||||||
.expect("there should be at least one function resolution cache");
|
.expect("at least one function resolution cache");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,8 +898,6 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub struct Engine {
|
pub struct Engine {
|
||||||
/// A module containing all functions directly loaded into the Engine.
|
|
||||||
pub(crate) global_namespace: Module,
|
|
||||||
/// A collection of all modules loaded into the global namespace of the Engine.
|
/// A collection of all modules loaded into the global namespace of the Engine.
|
||||||
pub(crate) global_modules: StaticVec<Shared<Module>>,
|
pub(crate) global_modules: StaticVec<Shared<Module>>,
|
||||||
/// A collection of all sub-modules directly loaded into the Engine.
|
/// A collection of all sub-modules directly loaded into the Engine.
|
||||||
@ -1036,7 +1032,6 @@ impl Engine {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_raw() -> Self {
|
pub fn new_raw() -> Self {
|
||||||
let mut engine = Self {
|
let mut engine = Self {
|
||||||
global_namespace: Default::default(),
|
|
||||||
global_modules: Default::default(),
|
global_modules: Default::default(),
|
||||||
global_sub_modules: Default::default(),
|
global_sub_modules: Default::default(),
|
||||||
|
|
||||||
@ -1063,7 +1058,10 @@ impl Engine {
|
|||||||
limits: Default::default(),
|
limits: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
engine.global_namespace.internal = true;
|
// Add the global namespace module
|
||||||
|
let mut global_namespace = Module::new();
|
||||||
|
global_namespace.internal = true;
|
||||||
|
engine.global_modules.push(global_namespace.into());
|
||||||
|
|
||||||
engine
|
engine
|
||||||
}
|
}
|
||||||
@ -1088,16 +1086,10 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
let offset = mods.len() - index.get();
|
let offset = mods.len() - index.get();
|
||||||
Some(
|
Some(mods.get(offset).expect("offset within range"))
|
||||||
mods.get(offset)
|
|
||||||
.expect("never fails because offset should be within range"),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
mods.find(root)
|
mods.find(root)
|
||||||
.map(|n| {
|
.map(|n| mods.get(n).expect("index is valid"))
|
||||||
mods.get(n)
|
|
||||||
.expect("never fails because the index came from `find`")
|
|
||||||
})
|
|
||||||
.or_else(|| self.global_sub_modules.get(root).cloned())
|
.or_else(|| self.global_sub_modules.get(root).cloned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1191,8 +1183,7 @@ impl Engine {
|
|||||||
level: 0,
|
level: 0,
|
||||||
};
|
};
|
||||||
match resolve_var(
|
match resolve_var(
|
||||||
expr.get_variable_name(true)
|
expr.get_variable_name(true).expect("`expr` is `Variable`"),
|
||||||
.expect("`expr` should be `Variable`"),
|
|
||||||
index,
|
index,
|
||||||
&context,
|
&context,
|
||||||
) {
|
) {
|
||||||
@ -1209,9 +1200,7 @@ impl Engine {
|
|||||||
scope.len() - index
|
scope.len() - index
|
||||||
} else {
|
} else {
|
||||||
// Find the variable in the scope
|
// Find the variable in the scope
|
||||||
let var_name = expr
|
let var_name = expr.get_variable_name(true).expect("`expr` is `Variable`");
|
||||||
.get_variable_name(true)
|
|
||||||
.expect("`expr` should be `Variable`");
|
|
||||||
scope
|
scope
|
||||||
.get_index(var_name)
|
.get_index(var_name)
|
||||||
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))?
|
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))?
|
||||||
@ -1245,9 +1234,7 @@ impl Engine {
|
|||||||
let _terminate_chaining = terminate_chaining;
|
let _terminate_chaining = terminate_chaining;
|
||||||
|
|
||||||
// Pop the last index value
|
// Pop the last index value
|
||||||
let idx_val = idx_values
|
let idx_val = idx_values.pop().expect("index chain is never empty");
|
||||||
.pop()
|
|
||||||
.expect("never fails because an index chain is never empty");
|
|
||||||
|
|
||||||
match chain_type {
|
match chain_type {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1255,7 +1242,7 @@ impl Engine {
|
|||||||
let pos = rhs.position();
|
let pos = rhs.position();
|
||||||
let idx_val = idx_val
|
let idx_val = idx_val
|
||||||
.into_index_value()
|
.into_index_value()
|
||||||
.expect("never fails because `chain_type` is `ChainType::Index`");
|
.expect("`chain_type` is `ChainType::Index`");
|
||||||
|
|
||||||
match rhs {
|
match rhs {
|
||||||
// xxx[idx].expr... | xxx[idx][expr]...
|
// xxx[idx].expr... | xxx[idx][expr]...
|
||||||
@ -1277,7 +1264,7 @@ impl Engine {
|
|||||||
// xxx[rhs] op= new_val
|
// xxx[rhs] op= new_val
|
||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
let ((new_val, new_pos), (op_info, op_pos)) =
|
let ((new_val, new_pos), (op_info, op_pos)) =
|
||||||
new_val.expect("never fails because `new_val` is `Some`");
|
new_val.expect("`new_val` is `Some`");
|
||||||
let mut idx_val_for_setter = idx_val.clone();
|
let mut idx_val_for_setter = idx_val.clone();
|
||||||
|
|
||||||
let try_setter = match self.get_indexed_mut(
|
let try_setter = match self.get_indexed_mut(
|
||||||
@ -1333,7 +1320,7 @@ impl Engine {
|
|||||||
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
||||||
let call_args = &mut idx_val
|
let call_args = &mut idx_val
|
||||||
.into_fn_call_args()
|
.into_fn_call_args()
|
||||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
.expect("`chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
||||||
self.make_method_call(
|
self.make_method_call(
|
||||||
mods, state, lib, name, *hashes, target, call_args, *pos, level,
|
mods, state, lib, name, *hashes, target, call_args, *pos, level,
|
||||||
)
|
)
|
||||||
@ -1350,7 +1337,7 @@ impl Engine {
|
|||||||
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
|
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
|
||||||
let (name, pos) = &x.2;
|
let (name, pos) = &x.2;
|
||||||
let ((new_val, new_pos), (op_info, op_pos)) =
|
let ((new_val, new_pos), (op_info, op_pos)) =
|
||||||
new_val.expect("never fails because `new_val` is `Some`");
|
new_val.expect("`new_val` is `Some`");
|
||||||
let index = name.into();
|
let index = name.into();
|
||||||
{
|
{
|
||||||
let val_target = &mut self.get_indexed_mut(
|
let val_target = &mut self.get_indexed_mut(
|
||||||
@ -1378,7 +1365,7 @@ impl Engine {
|
|||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref();
|
let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref();
|
||||||
let ((mut new_val, new_pos), (op_info, op_pos)) =
|
let ((mut new_val, new_pos), (op_info, op_pos)) =
|
||||||
new_val.expect("never fails because `new_val` is `Some`");
|
new_val.expect("`new_val` is `Some`");
|
||||||
|
|
||||||
if op_info.is_some() {
|
if op_info.is_some() {
|
||||||
let hash = FnCallHashes::from_native(*hash_get);
|
let hash = FnCallHashes::from_native(*hash_get);
|
||||||
@ -1498,9 +1485,9 @@ impl Engine {
|
|||||||
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
||||||
Expr::FnCall(ref x, pos) if !x.is_qualified() => {
|
Expr::FnCall(ref x, pos) if !x.is_qualified() => {
|
||||||
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
||||||
let call_args = &mut idx_val
|
let call_args = &mut idx_val.into_fn_call_args().expect(
|
||||||
.into_fn_call_args()
|
"`chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`",
|
||||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
);
|
||||||
let (val, _) = self.make_method_call(
|
let (val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hashes, target, call_args, pos, level,
|
mods, state, lib, name, *hashes, target, call_args, pos, level,
|
||||||
)?;
|
)?;
|
||||||
@ -1620,9 +1607,9 @@ impl Engine {
|
|||||||
Expr::FnCall(ref f, pos) if !f.is_qualified() => {
|
Expr::FnCall(ref f, pos) if !f.is_qualified() => {
|
||||||
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
||||||
let rhs_chain = match_chaining_type(rhs);
|
let rhs_chain = match_chaining_type(rhs);
|
||||||
let args = &mut idx_val
|
let args = &mut idx_val.into_fn_call_args().expect(
|
||||||
.into_fn_call_args()
|
"`chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`",
|
||||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
);
|
||||||
let (mut val, _) = self.make_method_call(
|
let (mut val, _) = self.make_method_call(
|
||||||
mods, state, lib, name, *hashes, target, args, pos, level,
|
mods, state, lib, name, *hashes, target, args, pos, level,
|
||||||
)?;
|
)?;
|
||||||
@ -2117,7 +2104,7 @@ impl Engine {
|
|||||||
for (Ident { name: key, .. }, expr) in &x.0 {
|
for (Ident { name: key, .. }, expr) in &x.0 {
|
||||||
let value_ref = map
|
let value_ref = map
|
||||||
.get_mut(key.as_str())
|
.get_mut(key.as_str())
|
||||||
.expect("never fails because the template should contain all the keys");
|
.expect("template contains all keys");
|
||||||
*value_ref = self
|
*value_ref = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2135,9 +2122,7 @@ impl Engine {
|
|||||||
constants,
|
constants,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace
|
let namespace = namespace.as_ref().expect("qualified function call");
|
||||||
.as_ref()
|
|
||||||
.expect("never fails because function call is qualified");
|
|
||||||
let hash = hashes.native;
|
let hash = hashes.native;
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash,
|
scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash,
|
||||||
@ -2192,12 +2177,14 @@ impl Engine {
|
|||||||
|
|
||||||
Expr::Custom(custom, _) => {
|
Expr::Custom(custom, _) => {
|
||||||
let expressions: StaticVec<_> = custom.keywords.iter().map(Into::into).collect();
|
let expressions: StaticVec<_> = custom.keywords.iter().map(Into::into).collect();
|
||||||
let key_token = custom.tokens.first().expect(
|
let key_token = custom
|
||||||
"never fails because a custom syntax stream must contain at least one token",
|
.tokens
|
||||||
);
|
.first()
|
||||||
|
.expect("custom syntax stream contains at least one token");
|
||||||
let custom_def = self
|
let custom_def = self
|
||||||
.custom_syntax.get(key_token)
|
.custom_syntax
|
||||||
.expect("never fails because the custom syntax leading token should match with definition");
|
.get(key_token)
|
||||||
|
.expect("custom syntax leading token matches with definition");
|
||||||
let mut context = EvalContext {
|
let mut context = EvalContext {
|
||||||
engine: self,
|
engine: self,
|
||||||
scope,
|
scope,
|
||||||
@ -2329,9 +2316,7 @@ impl Engine {
|
|||||||
let target_is_shared = false;
|
let target_is_shared = false;
|
||||||
|
|
||||||
if target_is_shared {
|
if target_is_shared {
|
||||||
lock_guard = target
|
lock_guard = target.write_lock::<Dynamic>().expect("cast to `Dynamic`");
|
||||||
.write_lock::<Dynamic>()
|
|
||||||
.expect("never fails when casting to `Dynamic`");
|
|
||||||
lhs_ptr_inner = &mut *lock_guard;
|
lhs_ptr_inner = &mut *lock_guard;
|
||||||
} else {
|
} else {
|
||||||
lhs_ptr_inner = &mut *target;
|
lhs_ptr_inner = &mut *target;
|
||||||
@ -2403,7 +2388,7 @@ impl Engine {
|
|||||||
|
|
||||||
let var_name = lhs_expr
|
let var_name = lhs_expr
|
||||||
.get_variable_name(false)
|
.get_variable_name(false)
|
||||||
.expect("never fails because `lhs_ptr` is a `Variable`s");
|
.expect("`lhs_ptr` is `Variable`");
|
||||||
|
|
||||||
if !lhs_ptr.is_ref() {
|
if !lhs_ptr.is_ref() {
|
||||||
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
|
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
|
||||||
@ -2633,13 +2618,9 @@ impl Engine {
|
|||||||
// 3) Imported modules - functions marked with global namespace
|
// 3) Imported modules - functions marked with global namespace
|
||||||
// 4) Global sub-modules - functions marked with global namespace
|
// 4) Global sub-modules - functions marked with global namespace
|
||||||
let func = self
|
let func = self
|
||||||
.global_namespace
|
.global_modules
|
||||||
.get_iter(iter_type)
|
.iter()
|
||||||
.or_else(|| {
|
.find_map(|m| m.get_iter(iter_type))
|
||||||
self.global_modules
|
|
||||||
.iter()
|
|
||||||
.find_map(|m| m.get_iter(iter_type))
|
|
||||||
})
|
|
||||||
.or_else(|| mods.get_iter(iter_type))
|
.or_else(|| mods.get_iter(iter_type))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self.global_sub_modules
|
self.global_sub_modules
|
||||||
@ -2667,10 +2648,7 @@ impl Engine {
|
|||||||
if x > INT::MAX as usize {
|
if x > INT::MAX as usize {
|
||||||
return EvalAltResult::ErrorArithmetic(
|
return EvalAltResult::ErrorArithmetic(
|
||||||
format!("for-loop counter overflow: {}", x),
|
format!("for-loop counter overflow: {}", x),
|
||||||
counter
|
counter.as_ref().expect("`counter` is `Some`").pos,
|
||||||
.as_ref()
|
|
||||||
.expect("never fails because `counter` is `Some`")
|
|
||||||
.pos,
|
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -2678,7 +2656,7 @@ impl Engine {
|
|||||||
let mut counter_var = scope
|
let mut counter_var = scope
|
||||||
.get_mut_by_index(c)
|
.get_mut_by_index(c)
|
||||||
.write_lock::<INT>()
|
.write_lock::<INT>()
|
||||||
.expect("never fails because the counter always holds an `INT`");
|
.expect("counter holds `INT`");
|
||||||
*counter_var = x as INT;
|
*counter_var = x as INT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2691,9 +2669,7 @@ impl Engine {
|
|||||||
let loop_var_is_shared = false;
|
let loop_var_is_shared = false;
|
||||||
|
|
||||||
if loop_var_is_shared {
|
if loop_var_is_shared {
|
||||||
let mut value_ref = loop_var
|
let mut value_ref = loop_var.write_lock().expect("cast to `Dynamic`");
|
||||||
.write_lock()
|
|
||||||
.expect("never fails when casting to `Dynamic`");
|
|
||||||
*value_ref = value;
|
*value_ref = value;
|
||||||
} else {
|
} else {
|
||||||
*loop_var = value;
|
*loop_var = value;
|
||||||
@ -2744,9 +2720,7 @@ impl Engine {
|
|||||||
constants,
|
constants,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace
|
let namespace = namespace.as_ref().expect("qualified function call");
|
||||||
.as_ref()
|
|
||||||
.expect("never fails because function call is qualified");
|
|
||||||
let hash = hashes.native;
|
let hash = hashes.native;
|
||||||
self.make_qualified_function_call(
|
self.make_qualified_function_call(
|
||||||
scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash,
|
scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash,
|
||||||
@ -2805,11 +2779,16 @@ impl Engine {
|
|||||||
if err_pos.is_none() {
|
if err_pos.is_none() {
|
||||||
// No position info
|
// No position info
|
||||||
} else {
|
} else {
|
||||||
let line = err_pos.line().expect("never fails because a non-NONE `Position` always has a line number") as INT;
|
let line = err_pos
|
||||||
|
.line()
|
||||||
|
.expect("non-NONE `Position` has line number")
|
||||||
|
as INT;
|
||||||
let position = if err_pos.is_beginning_of_line() {
|
let position = if err_pos.is_beginning_of_line() {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
err_pos.position().expect("never fails because a non-NONE `Position` always has a character position")
|
err_pos
|
||||||
|
.position()
|
||||||
|
.expect("non-NONE `Position` has character position")
|
||||||
} as INT;
|
} as INT;
|
||||||
err_map.insert("line".into(), line.into());
|
err_map.insert("line".into(), line.into());
|
||||||
err_map.insert("position".into(), position.into());
|
err_map.insert("position".into(), position.into());
|
||||||
@ -2893,10 +2872,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
|
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
|
||||||
let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) {
|
let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) {
|
||||||
match mods
|
match mods.get_mut(index).expect("index is valid") {
|
||||||
.get_mut(index)
|
|
||||||
.expect("never fails because the index came from `find`")
|
|
||||||
{
|
|
||||||
m if m.internal => Some(m),
|
m if m.internal => Some(m),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -2905,15 +2881,12 @@ impl Engine {
|
|||||||
let mut global = Module::new();
|
let mut global = Module::new();
|
||||||
global.internal = true;
|
global.internal = true;
|
||||||
mods.push(KEYWORD_GLOBAL, global);
|
mods.push(KEYWORD_GLOBAL, global);
|
||||||
Some(
|
Some(mods.get_mut(mods.len() - 1).expect("global module exists"))
|
||||||
mods.get_mut(mods.len() - 1)
|
|
||||||
.expect("never fails because the global module was just added"),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(global) = global {
|
if let Some(global) = global {
|
||||||
Shared::get_mut(global)
|
Shared::get_mut(global)
|
||||||
.expect("never fails because the global module is never shared")
|
.expect("global module is not shared")
|
||||||
.set_var(name.clone(), value.clone());
|
.set_var(name.clone(), value.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,26 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
/// Engine public API
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
|
/// Get the global namespace module (which is the last module in `global_modules`).
|
||||||
|
#[inline(always)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn global_namespace(&self) -> &Module {
|
||||||
|
self.global_modules
|
||||||
|
.last()
|
||||||
|
.expect("global_modules contains at least one module")
|
||||||
|
}
|
||||||
|
/// Get a mutable reference to the global namespace module
|
||||||
|
/// (which is the last module in `global_modules`).
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn global_namespace_mut(&mut self) -> &mut Module {
|
||||||
|
Shared::get_mut(
|
||||||
|
self.global_modules
|
||||||
|
.last_mut()
|
||||||
|
.expect("global_modules contains at least one module"),
|
||||||
|
)
|
||||||
|
.expect("global namespace module is never shared")
|
||||||
|
}
|
||||||
/// Register a custom function with the [`Engine`].
|
/// Register a custom function with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -75,7 +93,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "metadata"))]
|
#[cfg(not(feature = "metadata"))]
|
||||||
let param_type_names: Option<[&str; 0]> = None;
|
let param_type_names: Option<[&str; 0]> = None;
|
||||||
|
|
||||||
self.global_namespace.set_fn(
|
self.global_namespace_mut().set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
@ -133,7 +151,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "metadata"))]
|
#[cfg(not(feature = "metadata"))]
|
||||||
let param_type_names: Option<[&str; 0]> = None;
|
let param_type_names: Option<[&str; 0]> = None;
|
||||||
|
|
||||||
self.global_namespace.set_fn(
|
self.global_namespace_mut().set_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
@ -172,7 +190,7 @@ impl Engine {
|
|||||||
N: AsRef<str> + Into<Identifier>,
|
N: AsRef<str> + Into<Identifier>,
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
{
|
{
|
||||||
self.global_namespace.set_raw_fn(
|
self.global_namespace_mut().set_raw_fn(
|
||||||
name,
|
name,
|
||||||
FnNamespace::Global,
|
FnNamespace::Global,
|
||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
@ -184,8 +202,6 @@ impl Engine {
|
|||||||
/// Register a custom type for use with the [`Engine`].
|
/// Register a custom type for use with the [`Engine`].
|
||||||
/// The type must implement [`Clone`].
|
/// The type must implement [`Clone`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_object`.
|
|
||||||
///
|
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -195,9 +211,8 @@ impl Engine {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
///
|
/// fn update(&mut self, offset: i64) { self.field += offset; }
|
||||||
/// fn update(&mut self, offset: i64) { self.field += offset; }
|
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
@ -212,6 +227,7 @@ impl Engine {
|
|||||||
/// // Use `register_fn` to register methods on the type.
|
/// // Use `register_fn` to register methods on the type.
|
||||||
/// .register_fn("update", TestStruct::update);
|
/// .register_fn("update", TestStruct::update);
|
||||||
///
|
///
|
||||||
|
/// # #[cfg(not(feature = "no_object"))]
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// engine.eval::<TestStruct>("let x = new_ts(); x.update(41); x")?,
|
/// engine.eval::<TestStruct>("let x = new_ts(); x.update(41); x")?,
|
||||||
/// TestStruct { field: 42 }
|
/// TestStruct { field: 42 }
|
||||||
@ -219,7 +235,6 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_type<T: Variant + Clone>(&mut self) -> &mut Self {
|
pub fn register_type<T: Variant + Clone>(&mut self) -> &mut Self {
|
||||||
self.register_type_with_name::<T>(type_name::<T>())
|
self.register_type_with_name::<T>(type_name::<T>())
|
||||||
@ -227,8 +242,6 @@ impl Engine {
|
|||||||
/// Register a custom type for use with the [`Engine`], with a pretty-print 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`].
|
/// for the `type_of` function. The type must implement [`Clone`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_object`.
|
|
||||||
///
|
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -266,7 +279,6 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) -> &mut Self {
|
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) -> &mut Self {
|
||||||
// Add the pretty-print type name into the map
|
// Add the pretty-print type name into the map
|
||||||
@ -282,7 +294,7 @@ impl Engine {
|
|||||||
T: Variant + Clone + IntoIterator,
|
T: Variant + Clone + IntoIterator,
|
||||||
<T as IntoIterator>::Item: Variant + Clone,
|
<T as IntoIterator>::Item: Variant + Clone,
|
||||||
{
|
{
|
||||||
self.global_namespace.set_iterable::<T>();
|
self.global_namespace_mut().set_iterable::<T>();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Register a getter function for a member of a registered type with the [`Engine`].
|
/// Register a getter function for a member of a registered type with the [`Engine`].
|
||||||
@ -300,8 +312,7 @@ impl Engine {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self) -> i64 { self.field }
|
/// fn get_field(&mut self) -> i64 { self.field }
|
||||||
/// }
|
/// }
|
||||||
@ -349,7 +360,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self) -> Result<i64, Box<EvalAltResult>> {
|
/// fn get_field(&mut self) -> Result<i64, Box<EvalAltResult>> {
|
||||||
/// Ok(self.field)
|
/// Ok(self.field)
|
||||||
@ -392,9 +402,8 @@ impl Engine {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
///
|
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
||||||
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
@ -442,7 +451,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
///
|
|
||||||
/// fn set_field(&mut self, new_val: i64) -> Result<(), Box<EvalAltResult>> {
|
/// fn set_field(&mut self, new_val: i64) -> Result<(), Box<EvalAltResult>> {
|
||||||
/// self.field = new_val;
|
/// self.field = new_val;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
@ -492,12 +500,10 @@ impl Engine {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { field: 1 } }
|
/// fn new() -> Self { Self { field: 1 } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self) -> i64 { self.field }
|
/// fn get_field(&mut self) -> i64 { self.field }
|
||||||
///
|
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
||||||
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
@ -549,7 +555,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
||||||
/// }
|
/// }
|
||||||
@ -623,7 +628,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> Result<i64, Box<EvalAltResult>> {
|
/// fn get_field(&mut self, index: i64) -> Result<i64, Box<EvalAltResult>> {
|
||||||
/// Ok(self.fields[index as usize])
|
/// Ok(self.fields[index as usize])
|
||||||
@ -697,7 +701,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
///
|
|
||||||
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -771,7 +774,6 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
///
|
|
||||||
/// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box<EvalAltResult>> {
|
/// fn set_field(&mut self, index: i64, value: i64) -> Result<(), Box<EvalAltResult>> {
|
||||||
/// self.fields[index as usize] = value;
|
/// self.fields[index as usize] = value;
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
@ -847,11 +849,9 @@ impl Engine {
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// impl TestStruct {
|
/// impl TestStruct {
|
||||||
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
/// fn new() -> Self { Self { fields: vec![1, 2, 3, 4, 5] } }
|
||||||
///
|
|
||||||
/// // Even a getter must start with `&mut self` and not `&self`.
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
||||||
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
||||||
///
|
|
||||||
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
/// fn set_field(&mut self, index: i64, value: i64) { self.fields[index as usize] = value; }
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
@ -957,14 +957,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut iter = name.as_ref().splitn(2, separator.as_ref());
|
let mut iter = name.as_ref().splitn(2, separator.as_ref());
|
||||||
let sub_module = iter
|
let sub_module = iter.next().expect("name contains separator").trim();
|
||||||
.next()
|
let remainder = iter.next().expect("name contains separator").trim();
|
||||||
.expect("never fails because the name contains a separator")
|
|
||||||
.trim();
|
|
||||||
let remainder = iter
|
|
||||||
.next()
|
|
||||||
.expect("never fails because the name contains a separator")
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
if !root.contains_key(sub_module) {
|
if !root.contains_key(sub_module) {
|
||||||
let mut m: Module = Default::default();
|
let mut m: Module = Default::default();
|
||||||
@ -974,7 +968,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
let m = root
|
let m = root
|
||||||
.remove(sub_module)
|
.remove(sub_module)
|
||||||
.expect("never fails because the root contains the sub-module");
|
.expect("root contains the sub-module");
|
||||||
let mut m = crate::fn_native::shared_take_or_clone(m);
|
let mut m = crate::fn_native::shared_take_or_clone(m);
|
||||||
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
||||||
m.build_index();
|
m.build_index();
|
||||||
@ -1079,19 +1073,18 @@ impl Engine {
|
|||||||
resolver: &StaticModuleResolver,
|
resolver: &StaticModuleResolver,
|
||||||
imports: &mut BTreeSet<Identifier>,
|
imports: &mut BTreeSet<Identifier>,
|
||||||
) {
|
) {
|
||||||
ast.walk(&mut |path| match path
|
ast.walk(
|
||||||
.last()
|
&mut |path| match path.last().expect("`path` contains the current node") {
|
||||||
.expect("never fails because `path` always contains the current node")
|
// Collect all `import` statements with a string constant path
|
||||||
{
|
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
|
||||||
// Collect all `import` statements with a string constant path
|
if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>
|
||||||
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
|
{
|
||||||
if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>
|
imports.insert(s.clone().into());
|
||||||
{
|
true
|
||||||
imports.insert(s.clone().into());
|
}
|
||||||
true
|
_ => true,
|
||||||
}
|
},
|
||||||
_ => true,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ast = self.compile_scripts_with_scope(scope, &[script])?;
|
let mut ast = self.compile_scripts_with_scope(scope, &[script])?;
|
||||||
@ -1335,12 +1328,12 @@ impl Engine {
|
|||||||
/// true)?;
|
/// true)?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(map.len(), 4);
|
/// assert_eq!(map.len(), 4);
|
||||||
/// assert_eq!(map["a"].as_int().unwrap(), 123);
|
/// assert_eq!(map["a"].as_int().expect("a should exist"), 123);
|
||||||
/// assert_eq!(map["b"].as_int().unwrap(), 42);
|
/// assert_eq!(map["b"].as_int().expect("b should exist"), 42);
|
||||||
/// assert!(map["d"].is::<()>());
|
/// assert!(map["d"].is::<()>());
|
||||||
///
|
///
|
||||||
/// let c = map["c"].read_lock::<Map>().unwrap();
|
/// let c = map["c"].read_lock::<Map>().expect("c should exist");
|
||||||
/// assert_eq!(c["x"].as_bool().unwrap(), false);
|
/// assert_eq!(c["x"].as_bool().expect("x should be bool"), false);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
@ -1933,7 +1926,7 @@ impl Engine {
|
|||||||
/// let mut value: Dynamic = 1_i64.into();
|
/// let mut value: Dynamic = 1_i64.into();
|
||||||
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "action", Some(&mut value), [ 41_i64.into() ])?;
|
/// let result = engine.call_fn_dynamic(&mut scope, &ast, true, "action", Some(&mut value), [ 41_i64.into() ])?;
|
||||||
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
/// // ^^^^^^^^^^^^^^^^ binding the 'this' pointer
|
||||||
/// assert_eq!(value.as_int().unwrap(), 42);
|
/// assert_eq!(value.as_int().expect("value should be INT"), 42);
|
||||||
/// # }
|
/// # }
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
@ -2038,7 +2031,7 @@ impl Engine {
|
|||||||
.map(|f| {
|
.map(|f| {
|
||||||
f.func
|
f.func
|
||||||
.get_script_fn_def()
|
.get_script_fn_def()
|
||||||
.expect("never fails because the function is scripted")
|
.expect("scripted function")
|
||||||
.clone()
|
.clone()
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -2062,7 +2055,7 @@ impl Engine {
|
|||||||
pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> {
|
pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> {
|
||||||
let mut signatures: Vec<_> = Default::default();
|
let mut signatures: Vec<_> = Default::default();
|
||||||
|
|
||||||
signatures.extend(self.global_namespace.gen_fn_signatures());
|
signatures.extend(self.global_namespace().gen_fn_signatures());
|
||||||
|
|
||||||
self.global_sub_modules.iter().for_each(|(name, m)| {
|
self.global_sub_modules.iter().for_each(|(name, m)| {
|
||||||
signatures.extend(m.gen_fn_signatures().map(|f| format!("{}::{}", name, f)))
|
signatures.extend(m.gen_fn_signatures().map(|f| format!("{}::{}", name, f)))
|
||||||
@ -2072,6 +2065,7 @@ impl Engine {
|
|||||||
signatures.extend(
|
signatures.extend(
|
||||||
self.global_modules
|
self.global_modules
|
||||||
.iter()
|
.iter()
|
||||||
|
.take(self.global_modules.len() - 1)
|
||||||
.flat_map(|m| m.gen_fn_signatures()),
|
.flat_map(|m| m.gen_fn_signatures()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// // Register a custom operator called 'foo' and give it
|
/// // Register a custom operator called 'foo' and give it
|
||||||
/// // a precedence of 160 (i.e. between +|- and *|/).
|
/// // a precedence of 160 (i.e. between +|- and *|/).
|
||||||
/// engine.register_custom_operator("foo", 160).unwrap();
|
/// engine.register_custom_operator("foo", 160).expect("should succeed");
|
||||||
///
|
///
|
||||||
/// // Register a binary function named 'foo'
|
/// // Register a binary function named 'foo'
|
||||||
/// engine.register_fn("foo", |x: i64, y: i64| (x * y) - (x + y));
|
/// engine.register_fn("foo", |x: i64, y: i64| (x * y) - (x + y));
|
||||||
|
@ -303,7 +303,7 @@ impl EvalAltResult {
|
|||||||
format!("{:?}", self)
|
format!("{:?}", self)
|
||||||
.split('(')
|
.split('(')
|
||||||
.next()
|
.next()
|
||||||
.expect("never fails because the debug format of an error is `ErrorXXX(...)`")
|
.expect("debug format of error is `ErrorXXX(...)`")
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ use crate::FLOAT;
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
use rust_decimal::Decimal;
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
/// The message: never fails because this is built-in code and the type is already checked
|
/// The message: data type was checked
|
||||||
const BUILTIN: &str = "never fails because this is built-in code and the type is already checked";
|
const BUILTIN: &str = "data type was checked";
|
||||||
|
|
||||||
/// Is the type a numeric type?
|
/// Is the type a numeric type?
|
||||||
#[inline]
|
#[inline]
|
||||||
|
147
src/fn_call.rs
147
src/fn_call.rs
@ -190,17 +190,39 @@ impl Engine {
|
|||||||
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let func = lib.iter().find_map(|m| m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
|
let func = lib
|
||||||
func, source: m.id_raw().cloned()
|
.iter()
|
||||||
})).or_else(|| self.global_namespace.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
|
.find_map(|m| {
|
||||||
func, source: None
|
m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
|
||||||
})).or_else(|| self.global_modules.iter().find_map(|m| m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
|
func,
|
||||||
func, source: m.id_raw().cloned()
|
source: m.id_raw().cloned(),
|
||||||
}))).or_else(|| mods.get_fn(hash).map(|(func, source)| FnResolutionCacheEntry {
|
})
|
||||||
func: func.clone(), source: source.cloned()
|
})
|
||||||
})).or_else(|| self.global_sub_modules.values().find_map(|m| m.get_qualified_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
|
.or_else(|| {
|
||||||
func, source: m.id_raw().cloned()
|
self.global_modules.iter().find_map(|m| {
|
||||||
})));
|
m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
|
||||||
|
func,
|
||||||
|
source: m.id_raw().cloned(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
mods.get_fn(hash)
|
||||||
|
.map(|(func, source)| FnResolutionCacheEntry {
|
||||||
|
func: func.clone(),
|
||||||
|
source: source.cloned(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
self.global_sub_modules.values().find_map(|m| {
|
||||||
|
m.get_qualified_fn(hash).cloned().map(|func| {
|
||||||
|
FnResolutionCacheEntry {
|
||||||
|
func,
|
||||||
|
source: m.id_raw().cloned(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
match func {
|
match func {
|
||||||
// Specific version found
|
// Specific version found
|
||||||
@ -215,17 +237,26 @@ impl Engine {
|
|||||||
return args.and_then(|args| {
|
return args.and_then(|args| {
|
||||||
if !is_op_assignment {
|
if !is_op_assignment {
|
||||||
get_builtin_binary_op_fn(fn_name, &args[0], &args[1]).map(|f| {
|
get_builtin_binary_op_fn(fn_name, &args[0], &args[1]).map(|f| {
|
||||||
FnResolutionCacheEntry { func: CallableFunction::from_method(
|
FnResolutionCacheEntry {
|
||||||
Box::new(f) as Box<FnAny>
|
func: CallableFunction::from_method(
|
||||||
), source: None }
|
Box::new(f) as Box<FnAny>
|
||||||
|
),
|
||||||
|
source: None,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let (first, second) = args.split_first()
|
let (first, second) = args
|
||||||
.expect("never fails because an op-assignment must have two arguments");
|
.split_first()
|
||||||
|
.expect("op-assignment has two arguments");
|
||||||
|
|
||||||
get_builtin_op_assignment_fn(fn_name, *first, second[0]).map(|f| FnResolutionCacheEntry {
|
get_builtin_op_assignment_fn(fn_name, *first, second[0]).map(
|
||||||
func: CallableFunction::from_method(Box::new(f) as Box<FnAny>), source: None
|
|f| FnResolutionCacheEntry {
|
||||||
})
|
func: CallableFunction::from_method(
|
||||||
|
Box::new(f) as Box<FnAny>
|
||||||
|
),
|
||||||
|
source: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.map(Box::new)
|
.map(Box::new)
|
||||||
});
|
});
|
||||||
@ -234,17 +265,19 @@ impl Engine {
|
|||||||
// Try all permutations with `Dynamic` wildcards
|
// Try all permutations with `Dynamic` wildcards
|
||||||
None => {
|
None => {
|
||||||
let hash_params = calc_fn_params_hash(
|
let hash_params = calc_fn_params_hash(
|
||||||
args.as_ref().expect("never fails because there are no permutations if there are no arguments")
|
args.as_ref()
|
||||||
.iter().enumerate().map(|(i, a)|
|
.expect("no permutations if no arguments")
|
||||||
{
|
.iter()
|
||||||
let mask = 1usize << (num_args - i - 1);
|
.enumerate()
|
||||||
if bitmask & mask != 0 {
|
.map(|(i, a)| {
|
||||||
// Replace with `Dynamic`
|
let mask = 1usize << (num_args - i - 1);
|
||||||
TypeId::of::<Dynamic>()
|
if bitmask & mask != 0 {
|
||||||
} else {
|
// Replace with `Dynamic`
|
||||||
a.type_id()
|
TypeId::of::<Dynamic>()
|
||||||
}
|
} else {
|
||||||
}),
|
a.type_id()
|
||||||
|
}
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
hash = combine_hashes(hash_script, hash_params);
|
hash = combine_hashes(hash_script, hash_params);
|
||||||
|
|
||||||
@ -290,7 +323,10 @@ impl Engine {
|
|||||||
let mut backup: Option<ArgBackup> = None;
|
let mut backup: Option<ArgBackup> = None;
|
||||||
if is_method_call && 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().unwrap().change_first_arg_to_copy(args);
|
backup
|
||||||
|
.as_mut()
|
||||||
|
.expect("`backup` is `Some`")
|
||||||
|
.change_first_arg_to_copy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run external function
|
// Run external function
|
||||||
@ -301,12 +337,10 @@ impl Engine {
|
|||||||
|
|
||||||
let result = if func.is_plugin_fn() {
|
let result = if func.is_plugin_fn() {
|
||||||
func.get_plugin_fn()
|
func.get_plugin_fn()
|
||||||
.expect("never fails because the function is a plugin")
|
.expect("plugin function")
|
||||||
.call((self, name, source, mods, lib).into(), args)
|
.call((self, name, source, mods, lib).into(), args)
|
||||||
} else {
|
} else {
|
||||||
let func = func
|
let func = func.get_native_fn().expect("native function");
|
||||||
.get_native_fn()
|
|
||||||
.expect("never fails because the function is native");
|
|
||||||
func((self, name, source, mods, lib).into(), args)
|
func((self, name, source, mods, lib).into(), args)
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -579,8 +613,6 @@ impl Engine {
|
|||||||
|
|
||||||
// First check script-defined functions
|
// First check script-defined functions
|
||||||
let result = lib.iter().any(|&m| m.contains_fn(hash_script))
|
let result = lib.iter().any(|&m| m.contains_fn(hash_script))
|
||||||
// Then check registered functions
|
|
||||||
|| self.global_namespace.contains_fn(hash_script)
|
|
||||||
// Then check packages
|
// Then check packages
|
||||||
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script))
|
|| self.global_modules.iter().any(|m| m.contains_fn(hash_script))
|
||||||
// Then check imported modules
|
// Then check imported modules
|
||||||
@ -644,10 +676,8 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
let fn_name = args[0]
|
let fn_name = args[0]
|
||||||
.read_lock::<ImmutableString>()
|
.read_lock::<ImmutableString>()
|
||||||
.expect("never fails because `args[0]` is `FnPtr`");
|
.expect("`args[0]` is `FnPtr`");
|
||||||
let num_params = args[1]
|
let num_params = args[1].as_int().expect("`args[1]` is `INT`");
|
||||||
.as_int()
|
|
||||||
.expect("never fails because `args[1]` is `INT`");
|
|
||||||
|
|
||||||
return Ok((
|
return Ok((
|
||||||
if num_params < 0 {
|
if num_params < 0 {
|
||||||
@ -691,9 +721,7 @@ impl Engine {
|
|||||||
// Script function call
|
// Script function call
|
||||||
assert!(func.is_script());
|
assert!(func.is_script());
|
||||||
|
|
||||||
let func = func
|
let func = func.get_script_fn_def().expect("scripted function");
|
||||||
.get_script_fn_def()
|
|
||||||
.expect("never fails because the function is scripted");
|
|
||||||
|
|
||||||
if func.body.is_empty() {
|
if func.body.is_empty() {
|
||||||
return Ok((Dynamic::UNIT, false));
|
return Ok((Dynamic::UNIT, false));
|
||||||
@ -719,7 +747,7 @@ impl Engine {
|
|||||||
// 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()
|
||||||
.expect("never fails because a method call always has a first parameter");
|
.expect("method call has first parameter");
|
||||||
|
|
||||||
let orig_source = state.source.take();
|
let orig_source = state.source.take();
|
||||||
state.source = source;
|
state.source = source;
|
||||||
@ -748,7 +776,10 @@ impl Engine {
|
|||||||
let mut backup: Option<ArgBackup> = None;
|
let mut backup: Option<ArgBackup> = None;
|
||||||
if is_ref_mut && !args.is_empty() {
|
if is_ref_mut && !args.is_empty() {
|
||||||
backup = Some(Default::default());
|
backup = Some(Default::default());
|
||||||
backup.as_mut().unwrap().change_first_arg_to_copy(args);
|
backup
|
||||||
|
.as_mut()
|
||||||
|
.expect("`backup` is `Some`")
|
||||||
|
.change_first_arg_to_copy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_source = state.source.take();
|
let orig_source = state.source.take();
|
||||||
@ -871,9 +902,7 @@ impl Engine {
|
|||||||
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>() => {
|
||||||
// FnPtr call
|
// FnPtr call
|
||||||
let fn_ptr = target
|
let fn_ptr = target.read_lock::<FnPtr>().expect("`obj` is `FnPtr`");
|
||||||
.read_lock::<FnPtr>()
|
|
||||||
.expect("never fails because `obj` is `FnPtr`");
|
|
||||||
// Redirect function name
|
// Redirect function name
|
||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||||
@ -938,9 +967,7 @@ impl Engine {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = target
|
let fn_ptr = target.read_lock::<FnPtr>().expect("`obj` is `FnPtr`");
|
||||||
.read_lock::<FnPtr>()
|
|
||||||
.expect("never fails because `obj` is `FnPtr`");
|
|
||||||
|
|
||||||
// Curry call
|
// Curry call
|
||||||
Ok((
|
Ok((
|
||||||
@ -1264,7 +1291,7 @@ impl Engine {
|
|||||||
} 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_mut = 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("`target` is reference");
|
||||||
args.push(obj_ref);
|
args.push(obj_ref);
|
||||||
args.extend(arg_values.iter_mut());
|
args.extend(arg_values.iter_mut());
|
||||||
}
|
}
|
||||||
@ -1345,9 +1372,9 @@ impl Engine {
|
|||||||
// 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
|
||||||
let (first, rest) = arg_values
|
let (first, rest) = arg_values
|
||||||
.split_first_mut()
|
.split_first_mut()
|
||||||
.expect("never fails because the arguments list is not empty");
|
.expect("arguments list is not empty");
|
||||||
first_arg_value = Some(first);
|
first_arg_value = Some(first);
|
||||||
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("`target` is reference");
|
||||||
args.push(obj_ref);
|
args.push(obj_ref);
|
||||||
args.extend(rest.iter_mut());
|
args.extend(rest.iter_mut());
|
||||||
}
|
}
|
||||||
@ -1393,9 +1420,7 @@ impl Engine {
|
|||||||
match func {
|
match func {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Some(f) if f.is_script() => {
|
Some(f) if f.is_script() => {
|
||||||
let fn_def = f
|
let fn_def = f.get_script_fn_def().expect("scripted function");
|
||||||
.get_script_fn_def()
|
|
||||||
.expect("never fails because the function is scripted");
|
|
||||||
|
|
||||||
if fn_def.body.is_empty() {
|
if fn_def.body.is_empty() {
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
@ -1419,15 +1444,13 @@ impl Engine {
|
|||||||
|
|
||||||
Some(f) if f.is_plugin_fn() => f
|
Some(f) if f.is_plugin_fn() => f
|
||||||
.get_plugin_fn()
|
.get_plugin_fn()
|
||||||
.expect("never fails because the function is a plugin")
|
.expect("plugin function")
|
||||||
.clone()
|
.clone()
|
||||||
.call((self, fn_name, module.id(), &*mods, lib).into(), &mut args)
|
.call((self, fn_name, module.id(), &*mods, lib).into(), &mut args)
|
||||||
.map_err(|err| err.fill_position(pos)),
|
.map_err(|err| err.fill_position(pos)),
|
||||||
|
|
||||||
Some(f) if f.is_native() => {
|
Some(f) if f.is_native() => {
|
||||||
let func = f
|
let func = f.get_native_fn().expect("native function");
|
||||||
.get_native_fn()
|
|
||||||
.expect("never fails because the function is native");
|
|
||||||
func((self, fn_name, module.id(), &*mods, lib).into(), &mut args)
|
func((self, fn_name, module.id(), &*mods, lib).into(), &mut args)
|
||||||
.map_err(|err| err.fill_position(pos))
|
.map_err(|err| err.fill_position(pos))
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
|||||||
pub fn shared_take<T>(value: Shared<T>) -> T {
|
pub fn shared_take<T>(value: Shared<T>) -> T {
|
||||||
shared_try_take(value)
|
shared_try_take(value)
|
||||||
.ok()
|
.ok()
|
||||||
.expect("resource should have no outstanding references")
|
.expect("no outstanding references")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A general function trail object.
|
/// A general function trail object.
|
||||||
|
@ -34,8 +34,7 @@ pub struct Mut<T>(T);
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
|
pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
|
||||||
// Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data.
|
// Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data.
|
||||||
data.write_lock::<T>()
|
data.write_lock::<T>().expect("data type was checked")
|
||||||
.expect("never fails because the type was checked")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dereference into value.
|
/// Dereference into value.
|
||||||
@ -47,15 +46,15 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
|||||||
data.flatten_in_place();
|
data.flatten_in_place();
|
||||||
let ref_str = data
|
let ref_str = data
|
||||||
.as_str_ref()
|
.as_str_ref()
|
||||||
.expect("never fails because argument passed by value should not be shared");
|
.expect("argument passed by value is not shared");
|
||||||
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
||||||
ref_t.clone()
|
ref_t.clone()
|
||||||
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
||||||
let value = mem::take(data)
|
let value = mem::take(data)
|
||||||
.into_string()
|
.into_string()
|
||||||
.expect("never fails because the type was checked");
|
.expect("data type was checked");
|
||||||
unsafe_try_cast(value).expect("never fails because the type was checked")
|
unsafe_try_cast(value).expect("data type was checked")
|
||||||
} else {
|
} else {
|
||||||
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
||||||
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
||||||
@ -131,7 +130,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
$($let $par = ($clone)(_drain.next().expect("arguments list is fixed")); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
let r = self($($arg),*);
|
let r = self($($arg),*);
|
||||||
@ -159,7 +158,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
$($let $par = ($clone)(_drain.next().expect("arguments list is fixed")); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
let r = self(ctx, $($arg),*);
|
let r = self(ctx, $($arg),*);
|
||||||
@ -187,7 +186,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
$($let $par = ($clone)(_drain.next().expect("arguments list is fixed")); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
self($($arg),*).map(Dynamic::from)
|
self($($arg),*).map(Dynamic::from)
|
||||||
@ -212,7 +211,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
$($let $par = ($clone)(_drain.next().expect("arguments list is fixed")); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
self(ctx, $($arg),*).map(Dynamic::from)
|
self(ctx, $($arg),*).map(Dynamic::from)
|
||||||
|
@ -238,7 +238,7 @@ impl Module {
|
|||||||
/// # use rhai::Module;
|
/// # use rhai::Module;
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -400,7 +400,7 @@ impl Module {
|
|||||||
/// # use rhai::Module;
|
/// # use rhai::Module;
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -416,7 +416,7 @@ impl Module {
|
|||||||
/// # use rhai::Module;
|
/// # use rhai::Module;
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
|
/// assert_eq!(module.get_var("answer").expect("answer should exist").cast::<i64>(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -434,7 +434,7 @@ impl Module {
|
|||||||
/// # use rhai::Module;
|
/// # use rhai::Module;
|
||||||
/// let mut module = Module::new();
|
/// let mut module = Module::new();
|
||||||
/// module.set_var("answer", 42_i64);
|
/// module.set_var("answer", 42_i64);
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_var(
|
pub fn set_var(
|
||||||
@ -1337,9 +1337,7 @@ impl Module {
|
|||||||
f.access,
|
f.access,
|
||||||
f.name.as_str(),
|
f.name.as_str(),
|
||||||
f.params,
|
f.params,
|
||||||
f.func
|
f.func.get_script_fn_def().expect("scripted function"),
|
||||||
.get_script_fn_def()
|
|
||||||
.expect("never fails because the function is scripted"),
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1407,7 +1405,7 @@ impl Module {
|
|||||||
/// let ast = engine.compile("let answer = 42; export answer;")?;
|
/// let ast = engine.compile("let answer = 42; export answer;")?;
|
||||||
/// let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
|
/// let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
|
||||||
/// assert!(module.contains_var("answer"));
|
/// assert!(module.contains_var("answer"));
|
||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").expect("answer should exist"), 42);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
@ -1431,9 +1429,7 @@ impl Module {
|
|||||||
match aliases.len() {
|
match aliases.len() {
|
||||||
0 => (),
|
0 => (),
|
||||||
1 => {
|
1 => {
|
||||||
let alias = aliases
|
let alias = aliases.pop().expect("list has one item");
|
||||||
.pop()
|
|
||||||
.expect("never fails because the list has one item");
|
|
||||||
module.set_var(alias, value);
|
module.set_var(alias, value);
|
||||||
}
|
}
|
||||||
_ => aliases.into_iter().for_each(|alias| {
|
_ => aliases.into_iter().for_each(|alias| {
|
||||||
@ -1465,7 +1461,7 @@ impl Module {
|
|||||||
let mut func = f
|
let mut func = f
|
||||||
.func
|
.func
|
||||||
.get_script_fn_def()
|
.get_script_fn_def()
|
||||||
.expect("never fails because the function is scripted")
|
.expect("scripted function")
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.clone();
|
.clone();
|
||||||
func.lib = Some(ast.shared_lib());
|
func.lib = Some(ast.shared_lib());
|
||||||
|
@ -148,10 +148,8 @@ impl<'a> OptimizerState<'a> {
|
|||||||
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
|
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
|
||||||
let hash = combine_hashes(hash_script, hash_params);
|
let hash = combine_hashes(hash_script, hash_params);
|
||||||
|
|
||||||
// First check registered functions
|
// First check packages
|
||||||
self.engine.global_namespace.contains_fn(hash)
|
self.engine.global_modules.iter().any(|m| m.contains_fn(hash))
|
||||||
// Then check packages
|
|
||||||
|| self.engine.global_modules.iter().any(|m| m.contains_fn(hash))
|
|
||||||
// Then check sub-modules
|
// Then check sub-modules
|
||||||
|| self.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
|
|| self.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
|
||||||
}
|
}
|
||||||
@ -282,18 +280,23 @@ fn optimize_stmt_block(
|
|||||||
if reduce_return && !last_stmt.returns_value() =>
|
if reduce_return && !last_stmt.returns_value() =>
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
statements.pop().unwrap();
|
statements
|
||||||
|
.pop()
|
||||||
|
.expect("`statements` contains at least two elements");
|
||||||
}
|
}
|
||||||
// { ...; return val; } -> { ...; val }
|
// { ...; return val; } -> { ...; val }
|
||||||
[.., Stmt::Return(crate::ast::ReturnType::Return, ref mut expr, pos)]
|
[.., Stmt::Return(crate::ast::ReturnType::Return, ref mut expr, pos)]
|
||||||
if reduce_return =>
|
if reduce_return =>
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*statements.last_mut().unwrap() = if let Some(expr) = expr {
|
*statements
|
||||||
Stmt::Expr(mem::take(expr))
|
.last_mut()
|
||||||
} else {
|
.expect("`statements` contains at least two elements") =
|
||||||
Stmt::Noop(pos)
|
if let Some(expr) = expr {
|
||||||
};
|
Stmt::Expr(mem::take(expr))
|
||||||
|
} else {
|
||||||
|
Stmt::Noop(pos)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// { ...; stmt; noop } -> done
|
// { ...; stmt; noop } -> done
|
||||||
[.., ref second_last_stmt, Stmt::Noop(_)]
|
[.., ref second_last_stmt, Stmt::Noop(_)]
|
||||||
@ -308,9 +311,14 @@ fn optimize_stmt_block(
|
|||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if second_last_stmt.returns_value() {
|
if second_last_stmt.returns_value() {
|
||||||
*statements.last_mut().unwrap() = Stmt::Noop(last_stmt.position());
|
*statements
|
||||||
|
.last_mut()
|
||||||
|
.expect("`statements` contains at least two elements") =
|
||||||
|
Stmt::Noop(last_stmt.position());
|
||||||
} else {
|
} else {
|
||||||
statements.pop().unwrap();
|
statements
|
||||||
|
.pop()
|
||||||
|
.expect("`statements` contains at least two elements");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
@ -328,18 +336,24 @@ fn optimize_stmt_block(
|
|||||||
if reduce_return =>
|
if reduce_return =>
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
statements.pop().unwrap();
|
statements
|
||||||
|
.pop()
|
||||||
|
.expect("`statements` contains at least two elements");
|
||||||
}
|
}
|
||||||
// { ...; return pure_val; } -> { ... }
|
// { ...; return pure_val; } -> { ... }
|
||||||
[.., Stmt::Return(crate::ast::ReturnType::Return, Some(ref expr), _)]
|
[.., Stmt::Return(crate::ast::ReturnType::Return, Some(ref expr), _)]
|
||||||
if reduce_return && expr.is_pure() =>
|
if reduce_return && expr.is_pure() =>
|
||||||
{
|
{
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
statements.pop().unwrap();
|
statements
|
||||||
|
.pop()
|
||||||
|
.expect("`statements` contains at least two elements");
|
||||||
}
|
}
|
||||||
[.., ref last_stmt] if is_pure(last_stmt) => {
|
[.., ref last_stmt] if is_pure(last_stmt) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
statements.pop().unwrap();
|
statements
|
||||||
|
.pop()
|
||||||
|
.expect("`statements` contains at least one element");
|
||||||
}
|
}
|
||||||
_ => break,
|
_ => break,
|
||||||
}
|
}
|
||||||
@ -381,14 +395,18 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
match x.2 {
|
match x.2 {
|
||||||
Expr::FnCall(ref mut x2, _) => {
|
Expr::FnCall(ref mut x2, _) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let op = Token::lookup_from_syntax(&x2.name).unwrap();
|
let op = Token::lookup_from_syntax(&x2.name).expect("`x2` is operator");
|
||||||
let op_assignment = op.make_op_assignment().unwrap();
|
let op_assignment = op.make_op_assignment().expect("`op` is operator");
|
||||||
x.1 = Some(OpAssignment::new(op_assignment));
|
x.1 = Some(OpAssignment::new(op_assignment));
|
||||||
|
|
||||||
let value = mem::take(&mut x2.args[1]);
|
let value = mem::take(&mut x2.args[1]);
|
||||||
|
|
||||||
if let Expr::Stack(slot, pos) = value {
|
if let Expr::Stack(slot, pos) = value {
|
||||||
let value = mem::take(x2.constants.get_mut(slot).unwrap());
|
let value = mem::take(
|
||||||
|
x2.constants
|
||||||
|
.get_mut(slot)
|
||||||
|
.expect("`constants[slot]` is valid"),
|
||||||
|
);
|
||||||
x.2 = Expr::from_dynamic(value, pos);
|
x.2 = Expr::from_dynamic(value, pos);
|
||||||
} else {
|
} else {
|
||||||
x.2 = value;
|
x.2 = value;
|
||||||
@ -459,7 +477,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
|
|
||||||
// switch const { ... }
|
// switch const { ... }
|
||||||
Stmt::Switch(match_expr, x, pos) if match_expr.is_constant() => {
|
Stmt::Switch(match_expr, x, pos) if match_expr.is_constant() => {
|
||||||
let value = match_expr.get_literal_value().unwrap();
|
let value = match_expr
|
||||||
|
.get_literal_value()
|
||||||
|
.expect("`match_expr` is constant");
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
value.hash(hasher);
|
value.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
@ -782,13 +802,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < s.chars().count() => {
|
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < s.chars().count() => {
|
||||||
// String literal indexing - get the character
|
// String literal indexing - get the character
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::CharConstant(s.chars().nth(*i as usize).unwrap(), *pos);
|
*expr = Expr::CharConstant(s.chars().nth(*i as usize).expect("character position is valid"), *pos);
|
||||||
}
|
}
|
||||||
// string[-int]
|
// string[-int]
|
||||||
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= s.chars().count()).unwrap_or(false) => {
|
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= s.chars().count()).unwrap_or(false) => {
|
||||||
// String literal indexing - get the character
|
// String literal indexing - get the character
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::CharConstant(s.chars().rev().nth(i.abs() as usize - 1).unwrap(), *pos);
|
*expr = Expr::CharConstant(s.chars().rev().nth(i.abs() as usize - 1).expect("character position is valid"), *pos);
|
||||||
}
|
}
|
||||||
// var[rhs]
|
// var[rhs]
|
||||||
(Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state, true),
|
(Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state, true),
|
||||||
@ -855,7 +875,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(_, _) if expr.is_constant() => {
|
Expr::Array(_, _) if expr.is_constant() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position());
|
*expr = Expr::DynamicConstant(expr.get_literal_value().expect("`expr` is constant").into(), expr.position());
|
||||||
}
|
}
|
||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -864,7 +884,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(_, _) if expr.is_constant() => {
|
Expr::Map(_, _) if expr.is_constant() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position());
|
*expr = Expr::DynamicConstant(expr.get_literal_value().expect("`expr` is constant").into(), expr.position());
|
||||||
}
|
}
|
||||||
// #{ key:value, .. }
|
// #{ key:value, .. }
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -935,7 +955,10 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
if let Some(fn_name) = fn_name {
|
if let Some(fn_name) = fn_name {
|
||||||
if fn_name.is::<ImmutableString>() {
|
if fn_name.is::<ImmutableString>() {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let fn_ptr = FnPtr::new_unchecked(fn_name.as_str_ref().unwrap().into(), Default::default());
|
let fn_ptr = FnPtr::new_unchecked(
|
||||||
|
fn_name.as_str_ref().expect("`fn_name` is `ImmutableString`").into(),
|
||||||
|
Default::default()
|
||||||
|
);
|
||||||
*expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), *pos);
|
*expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), *pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -955,7 +978,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
=> {
|
=> {
|
||||||
let arg_values = &mut x.args.iter().map(|e| match e {
|
let arg_values = &mut x.args.iter().map(|e| match e {
|
||||||
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
||||||
_ => e.get_literal_value().unwrap()
|
_ => e.get_literal_value().expect("`e` is constant")
|
||||||
}).collect::<StaticVec<_>>();
|
}).collect::<StaticVec<_>>();
|
||||||
|
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
@ -969,7 +992,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|f| {
|
||||||
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
let ctx = (state.engine, x.name.as_ref(), state.lib).into();
|
||||||
let (first, second) = arg_values.split_first_mut().unwrap();
|
let (first, second) = arg_values.split_first_mut().expect("`arg_values` is not empty");
|
||||||
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
|
(f)(ctx, &mut [ first, &mut second[0] ]).ok()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -1009,7 +1032,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
if !has_script_fn {
|
if !has_script_fn {
|
||||||
let arg_values = &mut x.args.iter().map(|e| match e {
|
let arg_values = &mut x.args.iter().map(|e| match e {
|
||||||
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
Expr::Stack(slot, _) => x.constants[*slot].clone(),
|
||||||
_ => e.get_literal_value().unwrap()
|
_ => e.get_literal_value().expect("`e` is constant")
|
||||||
}).collect::<StaticVec<_>>();
|
}).collect::<StaticVec<_>>();
|
||||||
|
|
||||||
let result = match x.name.as_str() {
|
let result = match x.name.as_str() {
|
||||||
@ -1044,7 +1067,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
|
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
*expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos);
|
*expr = Expr::from_dynamic(state.find_constant(&x.2).expect("constant exists").clone(), *pos);
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
|||||||
namespace: Option<Identifier>,
|
namespace: Option<Identifier>,
|
||||||
f: &ScriptFnDef,
|
f: &ScriptFnDef,
|
||||||
) -> Map {
|
) -> Map {
|
||||||
const DICT: &str = "never fails because the dictionary is pre-filled with all the keys";
|
const DICT: &str = "key exists";
|
||||||
|
|
||||||
let mut map = Map::new();
|
let mut map = Map::new();
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ pub fn print_with_func(
|
|||||||
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
||||||
Ok(result) if result.is::<crate::ImmutableString>() => result
|
Ok(result) if result.is::<crate::ImmutableString>() => result
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.expect("never fails as the result is `ImmutableString`"),
|
.expect("result is `ImmutableString`"),
|
||||||
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||||
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
||||||
}
|
}
|
||||||
|
@ -116,9 +116,7 @@ mod string_functions {
|
|||||||
#[rhai_fn(name = "to_upper")]
|
#[rhai_fn(name = "to_upper")]
|
||||||
pub fn to_upper_char(character: char) -> char {
|
pub fn to_upper_char(character: char) -> char {
|
||||||
let mut stream = character.to_uppercase();
|
let mut stream = character.to_uppercase();
|
||||||
let ch = stream
|
let ch = stream.next().expect("at least one character");
|
||||||
.next()
|
|
||||||
.expect("never fails because there should be at least one character");
|
|
||||||
if stream.next().is_some() {
|
if stream.next().is_some() {
|
||||||
character
|
character
|
||||||
} else {
|
} else {
|
||||||
@ -134,7 +132,7 @@ mod string_functions {
|
|||||||
let mut stream = character.to_lowercase();
|
let mut stream = character.to_lowercase();
|
||||||
let ch = stream
|
let ch = stream
|
||||||
.next()
|
.next()
|
||||||
.expect("never fails because there should be at least one character");
|
.expect("there should be at least one character");
|
||||||
if stream.next().is_some() {
|
if stream.next().is_some() {
|
||||||
character
|
character
|
||||||
} else {
|
} else {
|
||||||
|
31
src/parse.rs
31
src/parse.rs
@ -38,8 +38,8 @@ type FunctionsLib = BTreeMap<u64, Shared<ScriptFnDef>>;
|
|||||||
/// Invalid variable name that acts as a search barrier in a [`Scope`].
|
/// Invalid variable name that acts as a search barrier in a [`Scope`].
|
||||||
const SCOPE_SEARCH_BARRIER_MARKER: &str = "$BARRIER$";
|
const SCOPE_SEARCH_BARRIER_MARKER: &str = "$BARRIER$";
|
||||||
|
|
||||||
/// The message: never fails because `TokenStream` never ends
|
/// The message: `TokenStream` never ends
|
||||||
const NEVER_ENDS: &str = "never fails because `TokenStream` never ends";
|
const NEVER_ENDS: &str = "`TokenStream` never ends";
|
||||||
|
|
||||||
/// A factory of identifiers from text strings.
|
/// A factory of identifiers from text strings.
|
||||||
///
|
///
|
||||||
@ -1602,9 +1602,7 @@ fn make_assignment_stmt(
|
|||||||
let index = i.map_or_else(
|
let index = i.map_or_else(
|
||||||
|| {
|
|| {
|
||||||
index
|
index
|
||||||
.expect(
|
.expect("the long index is `Some` when the short index is `None`")
|
||||||
"never fails because the long index is `Some` when the short index is `None`",
|
|
||||||
)
|
|
||||||
.get()
|
.get()
|
||||||
},
|
},
|
||||||
|n| n.get() as usize,
|
|n| n.get() as usize,
|
||||||
@ -1695,8 +1693,9 @@ fn make_dot_expr(
|
|||||||
}
|
}
|
||||||
// lhs.module::id - syntax error
|
// lhs.module::id - syntax error
|
||||||
(_, Expr::Variable(_, _, x)) => {
|
(_, Expr::Variable(_, _, x)) => {
|
||||||
return Err(PERR::PropertyExpected
|
return Err(
|
||||||
.into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos))
|
PERR::PropertyExpected.into_err(x.1.expect("the namespace is `Some`").0[0].pos)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// lhs.prop
|
// lhs.prop
|
||||||
(lhs, prop @ Expr::Property(_)) => {
|
(lhs, prop @ Expr::Property(_)) => {
|
||||||
@ -1901,12 +1900,8 @@ fn parse_binary_op(
|
|||||||
| Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
| Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
||||||
|
|
||||||
Token::Or => {
|
Token::Or => {
|
||||||
let rhs = args
|
let rhs = args.pop().expect("`||` has two arguments");
|
||||||
.pop()
|
let current_lhs = args.pop().expect("`||` has two arguments");
|
||||||
.expect("never fails because `||` has two arguments");
|
|
||||||
let current_lhs = args
|
|
||||||
.pop()
|
|
||||||
.expect("never fails because `||` has two arguments");
|
|
||||||
Expr::Or(
|
Expr::Or(
|
||||||
BinaryExpr {
|
BinaryExpr {
|
||||||
lhs: current_lhs.ensure_bool_expr()?,
|
lhs: current_lhs.ensure_bool_expr()?,
|
||||||
@ -1917,12 +1912,8 @@ fn parse_binary_op(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Token::And => {
|
Token::And => {
|
||||||
let rhs = args
|
let rhs = args.pop().expect("`&&` has two arguments");
|
||||||
.pop()
|
let current_lhs = args.pop().expect("`&&` has two arguments");
|
||||||
.expect("never fails because `&&` has two arguments");
|
|
||||||
let current_lhs = args
|
|
||||||
.pop()
|
|
||||||
.expect("never fails because `&&` has two arguments");
|
|
||||||
Expr::And(
|
Expr::And(
|
||||||
BinaryExpr {
|
BinaryExpr {
|
||||||
lhs: current_lhs.ensure_bool_expr()?,
|
lhs: current_lhs.ensure_bool_expr()?,
|
||||||
@ -2391,7 +2382,7 @@ fn parse_for(
|
|||||||
},
|
},
|
||||||
counter_var.map(|name| Ident {
|
counter_var.map(|name| Ident {
|
||||||
name,
|
name,
|
||||||
pos: counter_pos.expect("never fails because `counter_var` is `Some`"),
|
pos: counter_pos.expect("`counter_var` is `Some`"),
|
||||||
}),
|
}),
|
||||||
body.into(),
|
body.into(),
|
||||||
)),
|
)),
|
||||||
|
55
src/scope.rs
55
src/scope.rs
@ -32,8 +32,8 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
|
|||||||
///
|
///
|
||||||
/// assert_eq!(engine.eval_with_scope::<i64>(&mut my_scope, "x + 1")?, 42);
|
/// assert_eq!(engine.eval_with_scope::<i64>(&mut my_scope, "x + 1")?, 42);
|
||||||
///
|
///
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 41);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 41);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("z").unwrap(), 0);
|
/// assert_eq!(my_scope.get_value::<i64>("z").expect("z should exist"), 0);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
@ -85,7 +85,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -163,7 +163,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push(
|
pub fn push(
|
||||||
@ -183,7 +183,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push_dynamic("x", Dynamic::from(42_i64));
|
/// my_scope.push_dynamic("x", Dynamic::from(42_i64));
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
|
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
|
||||||
@ -202,7 +202,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push_constant("x", 42_i64);
|
/// my_scope.push_constant("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_constant(
|
pub fn push_constant(
|
||||||
@ -225,7 +225,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push_constant_dynamic("x", Dynamic::from(42_i64));
|
/// my_scope.push_constant_dynamic("x", Dynamic::from(42_i64));
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_constant_dynamic(
|
pub fn push_constant_dynamic(
|
||||||
@ -327,7 +327,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -377,15 +377,15 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.set_or_push("x", 42_i64);
|
/// my_scope.set_or_push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
/// assert_eq!(my_scope.len(), 1);
|
/// assert_eq!(my_scope.len(), 1);
|
||||||
///
|
///
|
||||||
/// my_scope.set_or_push("x", 0_i64);
|
/// my_scope.set_or_push("x", 0_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 0);
|
||||||
/// assert_eq!(my_scope.len(), 1);
|
/// assert_eq!(my_scope.len(), 1);
|
||||||
///
|
///
|
||||||
/// my_scope.set_or_push("y", 123_i64);
|
/// my_scope.set_or_push("y", 123_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("y").unwrap(), 123);
|
/// assert_eq!(my_scope.get_value::<i64>("y").expect("y should exist"), 123);
|
||||||
/// assert_eq!(my_scope.len(), 2);
|
/// assert_eq!(my_scope.len(), 2);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -399,10 +399,7 @@ impl<'a> Scope<'a> {
|
|||||||
self.push(name, value);
|
self.push(name, value);
|
||||||
}
|
}
|
||||||
Some((index, AccessMode::ReadWrite)) => {
|
Some((index, AccessMode::ReadWrite)) => {
|
||||||
let value_ref = self
|
let value_ref = self.values.get_mut(index).expect("index is valid");
|
||||||
.values
|
|
||||||
.get_mut(index)
|
|
||||||
.expect("never fails because the index is returned by `get_index`");
|
|
||||||
*value_ref = Dynamic::from(value);
|
*value_ref = Dynamic::from(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,10 +422,10 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
///
|
///
|
||||||
/// my_scope.set_value("x", 0_i64);
|
/// my_scope.set_value("x", 0_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 0);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_value(
|
pub fn set_value(
|
||||||
@ -442,10 +439,7 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()),
|
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()),
|
||||||
Some((index, AccessMode::ReadWrite)) => {
|
Some((index, AccessMode::ReadWrite)) => {
|
||||||
let value_ref = self
|
let value_ref = self.values.get_mut(index).expect("index is valid");
|
||||||
.values
|
|
||||||
.get_mut(index)
|
|
||||||
.expect("never fails because the index is returned by `get_index`");
|
|
||||||
*value_ref = Dynamic::from(value);
|
*value_ref = Dynamic::from(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,12 +458,12 @@ impl<'a> Scope<'a> {
|
|||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// my_scope.push("x", 42_i64);
|
/// my_scope.push("x", 42_i64);
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||||
///
|
///
|
||||||
/// let ptr = my_scope.get_mut("x").unwrap();
|
/// let ptr = my_scope.get_mut("x").expect("x should exist");
|
||||||
/// *ptr = 123_i64.into();
|
/// *ptr = 123_i64.into();
|
||||||
///
|
///
|
||||||
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 123);
|
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 123);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -488,9 +482,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
|
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
|
||||||
self.values
|
self.values.get_mut(index).expect("index is out of bounds")
|
||||||
.get_mut(index)
|
|
||||||
.expect("never fails unless the index is out of bounds")
|
|
||||||
}
|
}
|
||||||
/// Update the access type of an entry in the [`Scope`].
|
/// Update the access type of an entry in the [`Scope`].
|
||||||
///
|
///
|
||||||
@ -500,10 +492,7 @@ impl<'a> Scope<'a> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
||||||
let (_, aliases) = self
|
let (_, aliases) = self.names.get_mut(index).expect("index is out of bounds");
|
||||||
.names
|
|
||||||
.get_mut(index)
|
|
||||||
.expect("never fails unless the index is out of bounds");
|
|
||||||
match aliases {
|
match aliases {
|
||||||
None => {
|
None => {
|
||||||
let mut list = StaticVec::new();
|
let mut list = StaticVec::new();
|
||||||
@ -563,12 +552,12 @@ impl<'a> Scope<'a> {
|
|||||||
///
|
///
|
||||||
/// let mut iter = my_scope.iter();
|
/// let mut iter = my_scope.iter();
|
||||||
///
|
///
|
||||||
/// let (name, is_constant, value) = iter.next().unwrap();
|
/// let (name, is_constant, value) = iter.next().expect("value should exist");
|
||||||
/// assert_eq!(name, "x");
|
/// assert_eq!(name, "x");
|
||||||
/// assert!(!is_constant);
|
/// assert!(!is_constant);
|
||||||
/// assert_eq!(value.cast::<i64>(), 42);
|
/// assert_eq!(value.cast::<i64>(), 42);
|
||||||
///
|
///
|
||||||
/// let (name, is_constant, value) = iter.next().unwrap();
|
/// let (name, is_constant, value) = iter.next().expect("value should exist");
|
||||||
/// assert_eq!(name, "foo");
|
/// assert_eq!(name, "foo");
|
||||||
/// assert!(is_constant);
|
/// assert!(is_constant);
|
||||||
/// assert_eq!(value.cast::<String>(), "hello");
|
/// assert_eq!(value.cast::<String>(), "hello");
|
||||||
|
@ -568,7 +568,7 @@ where
|
|||||||
) -> Result<V::Value, Box<EvalAltResult>> {
|
) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
// Deserialize each value item coming out of the iterator.
|
// Deserialize each value item coming out of the iterator.
|
||||||
seed.deserialize(&mut DynamicDeserializer::from_dynamic(
|
seed.deserialize(&mut DynamicDeserializer::from_dynamic(
|
||||||
self.values.next().unwrap(),
|
self.values.next().expect("value should exist"),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,21 +53,27 @@ struct FnParam {
|
|||||||
|
|
||||||
impl PartialOrd for FnParam {
|
impl PartialOrd for FnParam {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(match self.name.partial_cmp(&other.name).unwrap() {
|
Some(
|
||||||
Ordering::Less => Ordering::Less,
|
match self
|
||||||
Ordering::Greater => Ordering::Greater,
|
.name
|
||||||
Ordering::Equal => match (self.typ.is_none(), other.typ.is_none()) {
|
.partial_cmp(&other.name)
|
||||||
(true, true) => Ordering::Equal,
|
.expect("String::partial_cmp should succeed")
|
||||||
(true, false) => Ordering::Greater,
|
{
|
||||||
(false, true) => Ordering::Less,
|
Ordering::Less => Ordering::Less,
|
||||||
(false, false) => self
|
Ordering::Greater => Ordering::Greater,
|
||||||
.typ
|
Ordering::Equal => match (self.typ.is_none(), other.typ.is_none()) {
|
||||||
.as_ref()
|
(true, true) => Ordering::Equal,
|
||||||
.unwrap()
|
(true, false) => Ordering::Greater,
|
||||||
.partial_cmp(other.typ.as_ref().unwrap())
|
(false, true) => Ordering::Less,
|
||||||
.unwrap(),
|
(false, false) => self
|
||||||
|
.typ
|
||||||
|
.as_ref()
|
||||||
|
.expect("`typ` is not `None`")
|
||||||
|
.partial_cmp(other.typ.as_ref().expect("`typ` is not `None`"))
|
||||||
|
.expect("String::partial_cmp should succeed"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +163,7 @@ impl From<&crate::module::FuncInfo> for FnMetadata {
|
|||||||
{
|
{
|
||||||
info.func
|
info.func
|
||||||
.get_script_fn_def()
|
.get_script_fn_def()
|
||||||
.expect("never fails because the function is scripted")
|
.expect("scripted function")
|
||||||
.comments
|
.comments
|
||||||
.to_vec()
|
.to_vec()
|
||||||
}
|
}
|
||||||
@ -238,6 +244,7 @@ impl Engine {
|
|||||||
if include_global {
|
if include_global {
|
||||||
self.global_modules
|
self.global_modules
|
||||||
.iter()
|
.iter()
|
||||||
|
.take(self.global_modules.len() - 1)
|
||||||
.flat_map(|m| m.iter_fn())
|
.flat_map(|m| m.iter_fn())
|
||||||
.for_each(|f| global.functions.push(f.into()));
|
.for_each(|f| global.functions.push(f.into()));
|
||||||
}
|
}
|
||||||
@ -246,7 +253,7 @@ impl Engine {
|
|||||||
global.modules.insert(name.to_string(), m.as_ref().into());
|
global.modules.insert(name.to_string(), m.as_ref().into());
|
||||||
});
|
});
|
||||||
|
|
||||||
self.global_namespace
|
self.global_namespace()
|
||||||
.iter_fn()
|
.iter_fn()
|
||||||
.for_each(|f| global.functions.push(f.into()));
|
.for_each(|f| global.functions.push(f.into()));
|
||||||
|
|
||||||
|
28
src/token.rs
28
src/token.rs
@ -1207,7 +1207,7 @@ pub fn parse_string_literal(
|
|||||||
{
|
{
|
||||||
let start_position = start
|
let start_position = start
|
||||||
.position()
|
.position()
|
||||||
.expect("never fails because the string must have a starting position");
|
.expect("string must have starting position");
|
||||||
skip_whitespace_until = start_position + 1;
|
skip_whitespace_until = start_position + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1229,9 +1229,7 @@ pub fn parse_string_literal(
|
|||||||
// Whitespace to skip
|
// Whitespace to skip
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
_ if next_char.is_whitespace()
|
_ if next_char.is_whitespace()
|
||||||
&& pos
|
&& pos.position().expect("character must have position")
|
||||||
.position()
|
|
||||||
.expect("never fails because a character must have a position")
|
|
||||||
< skip_whitespace_until => {}
|
< skip_whitespace_until => {}
|
||||||
|
|
||||||
// All other characters
|
// All other characters
|
||||||
@ -1379,16 +1377,12 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
let return_comment = return_comment
|
let return_comment =
|
||||||
|| is_doc_comment(
|
return_comment || is_doc_comment(comment.as_ref().expect("`include_comments` is true"));
|
||||||
comment
|
|
||||||
.as_ref()
|
|
||||||
.expect("never fails because `include_comments` is true"),
|
|
||||||
);
|
|
||||||
|
|
||||||
if return_comment {
|
if return_comment {
|
||||||
return Some((
|
return Some((
|
||||||
Token::Comment(comment.expect("never fails because `return_comment` is true")),
|
Token::Comment(comment.expect("`return_comment` is true")),
|
||||||
start_pos,
|
start_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -1440,7 +1434,7 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
|
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
|
||||||
'.' => {
|
'.' => {
|
||||||
stream.get_next().expect("never fails because it is `.`");
|
stream.get_next().expect("it is `.`");
|
||||||
|
|
||||||
// Check if followed by digits or something that cannot start a property name
|
// Check if followed by digits or something that cannot start a property name
|
||||||
match stream.peek_next().unwrap_or('\0') {
|
match stream.peek_next().unwrap_or('\0') {
|
||||||
@ -1474,7 +1468,7 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
'e' => {
|
'e' => {
|
||||||
stream.get_next().expect("never fails it is `e`");
|
stream.get_next().expect("it is `e`");
|
||||||
|
|
||||||
// Check if followed by digits or +/-
|
// Check if followed by digits or +/-
|
||||||
match stream.peek_next().unwrap_or('\0') {
|
match stream.peek_next().unwrap_or('\0') {
|
||||||
@ -1487,11 +1481,7 @@ fn get_next_token_inner(
|
|||||||
'+' | '-' => {
|
'+' | '-' => {
|
||||||
result.push(next_char);
|
result.push(next_char);
|
||||||
pos.advance();
|
pos.advance();
|
||||||
result.push(
|
result.push(stream.get_next().expect("it is `+` or `-`"));
|
||||||
stream
|
|
||||||
.get_next()
|
|
||||||
.expect("never fails because it is `+` or `-`"),
|
|
||||||
);
|
|
||||||
pos.advance();
|
pos.advance();
|
||||||
}
|
}
|
||||||
// Not a floating-point number
|
// Not a floating-point number
|
||||||
@ -1639,7 +1629,7 @@ fn get_next_token_inner(
|
|||||||
|(err, err_pos)| (Token::LexError(err), err_pos),
|
|(err, err_pos)| (Token::LexError(err), err_pos),
|
||||||
|(result, _)| {
|
|(result, _)| {
|
||||||
let mut chars = result.chars();
|
let mut chars = result.chars();
|
||||||
let first = chars.next().unwrap();
|
let first = chars.next().expect("`chars` is not empty");
|
||||||
|
|
||||||
if chars.next().is_some() {
|
if chars.next().is_some() {
|
||||||
(Token::LexError(LERR::MalformedChar(result)), start_pos)
|
(Token::LexError(LERR::MalformedChar(result)), start_pos)
|
||||||
|
Loading…
Reference in New Issue
Block a user