Refine modules.
This commit is contained in:
parent
2bdd174f16
commit
64036f69ca
34
README.md
34
README.md
@ -1977,8 +1977,8 @@ When embedding Rhai into an application, it is usually necessary to trap `print`
|
|||||||
(for logging into a tracking log, for example).
|
(for logging into a tracking log, for example).
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Any function or closure that takes an &str argument can be used to override
|
// Any function or closure that takes an '&str' argument can be used to override
|
||||||
// print and debug
|
// 'print' and 'debug'
|
||||||
engine.on_print(|x| println!("hello: {}", x));
|
engine.on_print(|x| println!("hello: {}", x));
|
||||||
engine.on_debug(|x| println!("DEBUG: {}", x));
|
engine.on_debug(|x| println!("DEBUG: {}", x));
|
||||||
|
|
||||||
@ -2007,14 +2007,36 @@ Using external modules
|
|||||||
[module]: #using-external-modules
|
[module]: #using-external-modules
|
||||||
[modules]: #using-external-modules
|
[modules]: #using-external-modules
|
||||||
|
|
||||||
|
Rhai allows organizing code (functions and variables) into _modules_. A module is a single script file
|
||||||
|
with `export` statements that _exports_ certain global variables and functions as contents of the module.
|
||||||
|
|
||||||
|
Everything exported as part of a module is constant and read-only.
|
||||||
|
|
||||||
|
A module can be _imported_ via the `import` statement, and its members accessed via '`::`' similar to C++.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
import "crypto" as crypto; // Import an external script file as a module
|
import "crypto" as crypto; // import the script file 'crypto.rhai' as a module
|
||||||
|
|
||||||
crypto::encrypt(secret); // Use functions defined under the module via '::'
|
crypto::encrypt(secret); // use functions defined under the module via '::'
|
||||||
|
|
||||||
print(crypto::status); // Module variables are constants
|
print(crypto::status); // module variables are constants
|
||||||
|
|
||||||
crypto::hash::sha256(key); // Sub-modules are also supported
|
crypto::hash::sha256(key); // sub-modules are also supported
|
||||||
|
```
|
||||||
|
|
||||||
|
`import` statements are _scoped_, meaning that they are only accessible inside the scope that they're imported.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let mod = "crypto";
|
||||||
|
|
||||||
|
if secured { // new block scope
|
||||||
|
import mod as crypto; // import module (the path needs not be a constant string)
|
||||||
|
|
||||||
|
crypto::encrypt(key); // use a function in the module
|
||||||
|
} // the module disappears at the end of the block scope
|
||||||
|
|
||||||
|
crypto::encrypt(others); // <- this causes a run-time error because the 'crypto' module
|
||||||
|
// is no longer available!
|
||||||
```
|
```
|
||||||
|
|
||||||
Script optimization
|
Script optimization
|
||||||
|
12
src/any.rs
12
src/any.rs
@ -1,6 +1,6 @@
|
|||||||
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
||||||
|
|
||||||
use crate::engine::{Array, Map};
|
use crate::engine::{Array, Map, SubScope};
|
||||||
use crate::parser::INT;
|
use crate::parser::INT;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -135,6 +135,7 @@ pub enum Union {
|
|||||||
Float(FLOAT),
|
Float(FLOAT),
|
||||||
Array(Box<Array>),
|
Array(Box<Array>),
|
||||||
Map(Box<Map>),
|
Map(Box<Map>),
|
||||||
|
SubScope(Box<SubScope>),
|
||||||
Variant(Box<Box<dyn Variant>>),
|
Variant(Box<Box<dyn Variant>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +166,7 @@ impl Dynamic {
|
|||||||
Union::Float(_) => TypeId::of::<FLOAT>(),
|
Union::Float(_) => TypeId::of::<FLOAT>(),
|
||||||
Union::Array(_) => TypeId::of::<Array>(),
|
Union::Array(_) => TypeId::of::<Array>(),
|
||||||
Union::Map(_) => TypeId::of::<Map>(),
|
Union::Map(_) => TypeId::of::<Map>(),
|
||||||
|
Union::SubScope(_) => TypeId::of::<SubScope>(),
|
||||||
Union::Variant(value) => (***value).type_id(),
|
Union::Variant(value) => (***value).type_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,6 +183,7 @@ impl Dynamic {
|
|||||||
Union::Float(_) => type_name::<FLOAT>(),
|
Union::Float(_) => type_name::<FLOAT>(),
|
||||||
Union::Array(_) => "array",
|
Union::Array(_) => "array",
|
||||||
Union::Map(_) => "map",
|
Union::Map(_) => "map",
|
||||||
|
Union::SubScope(_) => "sub-scope",
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::Variant(value) if value.is::<Instant>() => "timestamp",
|
Union::Variant(value) if value.is::<Instant>() => "timestamp",
|
||||||
@ -201,6 +204,7 @@ impl fmt::Display for Dynamic {
|
|||||||
Union::Float(value) => write!(f, "{}", value),
|
Union::Float(value) => write!(f, "{}", value),
|
||||||
Union::Array(value) => write!(f, "{:?}", value),
|
Union::Array(value) => write!(f, "{:?}", value),
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
|
Union::SubScope(value) => write!(f, "#{:?}", value),
|
||||||
Union::Variant(_) => write!(f, "?"),
|
Union::Variant(_) => write!(f, "?"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,6 +222,7 @@ impl fmt::Debug for Dynamic {
|
|||||||
Union::Float(value) => write!(f, "{:?}", value),
|
Union::Float(value) => write!(f, "{:?}", value),
|
||||||
Union::Array(value) => write!(f, "{:?}", value),
|
Union::Array(value) => write!(f, "{:?}", value),
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
|
Union::SubScope(value) => write!(f, "#{:?}", value),
|
||||||
Union::Variant(_) => write!(f, "<dynamic>"),
|
Union::Variant(_) => write!(f, "<dynamic>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -235,6 +240,7 @@ impl Clone for Dynamic {
|
|||||||
Union::Float(value) => Self(Union::Float(value)),
|
Union::Float(value) => Self(Union::Float(value)),
|
||||||
Union::Array(ref value) => Self(Union::Array(value.clone())),
|
Union::Array(ref value) => Self(Union::Array(value.clone())),
|
||||||
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
||||||
|
Union::SubScope(ref value) => Self(Union::SubScope(value.clone())),
|
||||||
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,6 +369,7 @@ impl Dynamic {
|
|||||||
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
|
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
|
||||||
Union::Array(value) => cast_box::<_, T>(value).ok(),
|
Union::Array(value) => cast_box::<_, T>(value).ok(),
|
||||||
Union::Map(value) => cast_box::<_, T>(value).ok(),
|
Union::Map(value) => cast_box::<_, T>(value).ok(),
|
||||||
|
Union::SubScope(value) => cast_box::<_, T>(value).ok(),
|
||||||
Union::Variant(value) => value.as_any().downcast_ref::<T>().cloned(),
|
Union::Variant(value) => value.as_any().downcast_ref::<T>().cloned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -400,6 +407,7 @@ impl Dynamic {
|
|||||||
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
|
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
|
||||||
Union::Array(value) => cast_box::<_, T>(value).unwrap(),
|
Union::Array(value) => cast_box::<_, T>(value).unwrap(),
|
||||||
Union::Map(value) => cast_box::<_, T>(value).unwrap(),
|
Union::Map(value) => cast_box::<_, T>(value).unwrap(),
|
||||||
|
Union::SubScope(value) => cast_box::<_, T>(value).unwrap(),
|
||||||
Union::Variant(value) => value.as_any().downcast_ref::<T>().unwrap().clone(),
|
Union::Variant(value) => value.as_any().downcast_ref::<T>().unwrap().clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,6 +430,7 @@ impl Dynamic {
|
|||||||
Union::Float(value) => (value as &dyn Any).downcast_ref::<T>(),
|
Union::Float(value) => (value as &dyn Any).downcast_ref::<T>(),
|
||||||
Union::Array(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
|
Union::Array(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
|
||||||
Union::Map(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
|
Union::Map(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
|
||||||
|
Union::SubScope(value) => (value.as_ref() as &dyn Any).downcast_ref::<T>(),
|
||||||
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -444,6 +453,7 @@ impl Dynamic {
|
|||||||
Union::Float(value) => (value as &mut dyn Any).downcast_mut::<T>(),
|
Union::Float(value) => (value as &mut dyn Any).downcast_mut::<T>(),
|
||||||
Union::Array(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
|
Union::Array(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
|
||||||
Union::Map(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
|
Union::Map(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
|
||||||
|
Union::SubScope(value) => (value.as_mut() as &mut dyn Any).downcast_mut::<T>(),
|
||||||
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
148
src/engine.rs
148
src/engine.rs
@ -18,6 +18,7 @@ use crate::stdlib::{
|
|||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
iter::once,
|
iter::once,
|
||||||
mem,
|
mem,
|
||||||
|
num::NonZeroUsize,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
@ -41,6 +42,32 @@ pub type Array = Vec<Dynamic>;
|
|||||||
/// Not available under the `no_object` feature.
|
/// Not available under the `no_object` feature.
|
||||||
pub type Map = HashMap<String, Dynamic>;
|
pub type Map = HashMap<String, Dynamic>;
|
||||||
|
|
||||||
|
/// A sub-scope - basically an imported module namespace.
|
||||||
|
///
|
||||||
|
/// Not available under the `no_import` feature.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SubScope(HashMap<String, Dynamic>);
|
||||||
|
|
||||||
|
impl SubScope {
|
||||||
|
/// Create a new sub-scope.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self(HashMap::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for SubScope {
|
||||||
|
type Target = HashMap<String, Dynamic>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for SubScope {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
||||||
|
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
@ -472,39 +499,68 @@ fn default_print(s: &str) {
|
|||||||
println!("{}", s);
|
println!("{}", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for a variable within the scope, returning its value and index inside the Scope
|
/// Search for a variable within the scope
|
||||||
fn search_scope<'a>(
|
fn search_scope_variables<'a>(
|
||||||
scope: &'a mut Scope,
|
scope: &'a mut Scope,
|
||||||
name: &str,
|
name: &str,
|
||||||
modules: Option<&Box<StaticVec<(String, Position)>>>,
|
index: Option<NonZeroUsize>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||||
if let Some(modules) = modules {
|
let index = if let Some(index) = index {
|
||||||
let (id, root_pos) = modules.get(0); // First module
|
scope.len() - index.get()
|
||||||
let mut sub_scope = scope
|
|
||||||
.find_sub_scope(id)
|
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))?;
|
|
||||||
|
|
||||||
for x in 1..modules.len() {
|
|
||||||
let (id, id_pos) = modules.get(x);
|
|
||||||
|
|
||||||
sub_scope = sub_scope
|
|
||||||
.get_mut(id)
|
|
||||||
.unwrap()
|
|
||||||
.downcast_mut::<Map>()
|
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *id_pos)))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub_scope
|
|
||||||
.get_mut(name)
|
|
||||||
.map(|v| (v, ScopeEntryType::Constant))
|
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))
|
|
||||||
} else {
|
} else {
|
||||||
let (index, _) = scope
|
scope
|
||||||
.get_index(name)
|
.get_index(name)
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?;
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?
|
||||||
|
.0
|
||||||
|
};
|
||||||
|
|
||||||
Ok(scope.get_mut(index))
|
Ok(scope.get_mut(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search for a sub-scope within the scope
|
||||||
|
fn search_scope_modules<'a>(
|
||||||
|
scope: &'a mut Scope,
|
||||||
|
name: &str,
|
||||||
|
modules: &Box<StaticVec<(String, Position)>>,
|
||||||
|
index: Option<NonZeroUsize>,
|
||||||
|
pos: Position,
|
||||||
|
) -> Result<(&'a mut Dynamic, ScopeEntryType), Box<EvalAltResult>> {
|
||||||
|
let (id, root_pos) = modules.get(0); // First module
|
||||||
|
|
||||||
|
let mut sub_scope = if let Some(index) = index {
|
||||||
|
scope
|
||||||
|
.get_mut(scope.len() - index.get())
|
||||||
|
.0
|
||||||
|
.downcast_mut::<SubScope>()
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
scope
|
||||||
|
.find_sub_scope(id)
|
||||||
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos)))?
|
||||||
|
};
|
||||||
|
|
||||||
|
for x in 1..modules.len() {
|
||||||
|
let (id, id_pos) = modules.get(x);
|
||||||
|
|
||||||
|
sub_scope = sub_scope
|
||||||
|
.get_mut(id)
|
||||||
|
.and_then(|v| v.downcast_mut::<SubScope>())
|
||||||
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *id_pos)))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = sub_scope
|
||||||
|
.get_mut(name)
|
||||||
|
.map(|v| (v, ScopeEntryType::Constant))
|
||||||
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?;
|
||||||
|
|
||||||
|
if result.0.is::<SubScope>() {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
|
name.into(),
|
||||||
|
pos,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,20 +1029,24 @@ impl Engine {
|
|||||||
match dot_lhs {
|
match dot_lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(id, modules, index, pos) => {
|
Expr::Variable(id, modules, index, pos) => {
|
||||||
let (target, typ) = match index {
|
let index = if state.always_search { None } else { *index };
|
||||||
Some(i) if !state.always_search => scope.get_mut(scope.len() - i.get()),
|
|
||||||
_ => search_scope(scope, id, modules.as_ref(), *pos)?,
|
let (target, typ) = if let Some(modules) = modules {
|
||||||
|
search_scope_modules(scope, id, modules, index, *pos)?
|
||||||
|
} else {
|
||||||
|
search_scope_variables(scope, id, index, *pos)?
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
match typ {
|
match typ {
|
||||||
|
ScopeEntryType::SubScope => unreachable!(),
|
||||||
ScopeEntryType::Constant if new_val.is_some() => {
|
ScopeEntryType::Constant if new_val.is_some() => {
|
||||||
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
return Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||||
id.to_string(),
|
id.to_string(),
|
||||||
*pos,
|
*pos,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
_ => (),
|
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_ptr = target.into();
|
let this_ptr = target.into();
|
||||||
@ -1206,11 +1266,16 @@ impl Engine {
|
|||||||
Expr::FloatConstant(f, _) => Ok((*f).into()),
|
Expr::FloatConstant(f, _) => Ok((*f).into()),
|
||||||
Expr::StringConstant(s, _) => Ok(s.to_string().into()),
|
Expr::StringConstant(s, _) => Ok(s.to_string().into()),
|
||||||
Expr::CharConstant(c, _) => Ok((*c).into()),
|
Expr::CharConstant(c, _) => Ok((*c).into()),
|
||||||
Expr::Variable(_, None, Some(index), _) if !state.always_search => {
|
Expr::Variable(id, modules, index, pos) => {
|
||||||
Ok(scope.get_mut(scope.len() - index.get()).0.clone())
|
let index = if state.always_search { None } else { *index };
|
||||||
}
|
|
||||||
Expr::Variable(id, modules, _, pos) => {
|
let val = if let Some(modules) = modules {
|
||||||
search_scope(scope, id, modules.as_ref(), *pos).map(|(v, _)| v.clone())
|
search_scope_modules(scope, id, modules, index, *pos)?
|
||||||
|
} else {
|
||||||
|
search_scope_variables(scope, id, index, *pos)?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(val.0.clone())
|
||||||
}
|
}
|
||||||
Expr::Property(_, _) => unreachable!(),
|
Expr::Property(_, _) => unreachable!(),
|
||||||
|
|
||||||
@ -1223,8 +1288,15 @@ impl Engine {
|
|||||||
|
|
||||||
match lhs.as_ref() {
|
match lhs.as_ref() {
|
||||||
// name = rhs
|
// name = rhs
|
||||||
Expr::Variable(name, modules, _, pos) => {
|
Expr::Variable(name, modules, index, pos) => {
|
||||||
match search_scope(scope, name, modules.as_ref(), *pos)? {
|
let index = if state.always_search { None } else { *index };
|
||||||
|
let val = if let Some(modules) = modules {
|
||||||
|
search_scope_modules(scope, name, modules, index, *pos)?
|
||||||
|
} else {
|
||||||
|
search_scope_variables(scope, name, index, *pos)?
|
||||||
|
};
|
||||||
|
|
||||||
|
match val {
|
||||||
(_, ScopeEntryType::Constant) => Err(Box::new(
|
(_, ScopeEntryType::Constant) => Err(Box::new(
|
||||||
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos),
|
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), *op_pos),
|
||||||
)),
|
)),
|
||||||
@ -1562,7 +1634,7 @@ impl Engine {
|
|||||||
.eval_expr(scope, state, fn_lib, expr, level)?
|
.eval_expr(scope, state, fn_lib, expr, level)?
|
||||||
.try_cast::<String>()
|
.try_cast::<String>()
|
||||||
{
|
{
|
||||||
let mut module = Map::new();
|
let mut module = SubScope::new();
|
||||||
module.insert("kitty".to_string(), "foo".to_string().into());
|
module.insert("kitty".to_string(), "foo".to_string().into());
|
||||||
module.insert("path".to_string(), path.into());
|
module.insert("path".to_string(), path.into());
|
||||||
|
|
||||||
|
@ -103,6 +103,9 @@ pub use engine::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub use engine::Map;
|
pub use engine::Map;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
pub use engine::SubScope;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub use parser::FLOAT;
|
pub use parser::FLOAT;
|
||||||
|
|
||||||
|
@ -1070,9 +1070,9 @@ fn parse_primary<'a>(
|
|||||||
(Expr::Property(id, pos), Token::LeftParen) => {
|
(Expr::Property(id, pos), Token::LeftParen) => {
|
||||||
parse_call_expr(input, stack, id, None, pos, allow_stmt_expr)?
|
parse_call_expr(input, stack, id, None, pos, allow_stmt_expr)?
|
||||||
}
|
}
|
||||||
// moduled
|
// module access
|
||||||
#[cfg(not(feature = "no_import"))]
|
#[cfg(not(feature = "no_import"))]
|
||||||
(Expr::Variable(id, mut modules, _, pos), Token::DoubleColon) => {
|
(Expr::Variable(id, mut modules, mut index, pos), Token::DoubleColon) => {
|
||||||
match input.next().unwrap() {
|
match input.next().unwrap() {
|
||||||
(Token::Identifier(id2), pos2) => {
|
(Token::Identifier(id2), pos2) => {
|
||||||
if let Some(ref mut modules) = modules {
|
if let Some(ref mut modules) = modules {
|
||||||
@ -1081,10 +1081,11 @@ fn parse_primary<'a>(
|
|||||||
let mut vec = StaticVec::new();
|
let mut vec = StaticVec::new();
|
||||||
vec.push((*id, pos));
|
vec.push((*id, pos));
|
||||||
modules = Some(Box::new(vec));
|
modules = Some(Box::new(vec));
|
||||||
|
|
||||||
|
let root = modules.as_ref().unwrap().get(0);
|
||||||
|
index = stack.find_sub_scope(&root.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let root = modules.as_ref().unwrap().get(0);
|
|
||||||
let index = stack.find_sub_scope(&root.0);
|
|
||||||
Expr::Variable(Box::new(id2), modules, index, pos2)
|
Expr::Variable(Box::new(id2), modules, index, pos2)
|
||||||
}
|
}
|
||||||
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
||||||
@ -1828,6 +1829,7 @@ fn parse_import<'a>(
|
|||||||
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
(_, pos) => return Err(PERR::VariableExpected.into_err(pos)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
stack.push((name.clone(), ScopeEntryType::SubScope));
|
||||||
Ok(Stmt::Import(Box::new(expr), Box::new(name), pos))
|
Ok(Stmt::Import(Box::new(expr), Box::new(name), pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
src/scope.rs
21
src/scope.rs
@ -1,7 +1,7 @@
|
|||||||
//! Module that defines the `Scope` type representing a function call-stack scope.
|
//! Module that defines the `Scope` type representing a function call-stack scope.
|
||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Union, Variant};
|
||||||
use crate::engine::Map;
|
use crate::engine::SubScope;
|
||||||
use crate::parser::{map_dynamic_to_expr, Expr};
|
use crate::parser::{map_dynamic_to_expr, Expr};
|
||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
|
|
||||||
@ -175,11 +175,11 @@ impl<'a> Scope<'a> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rhai::{Scope, Map};
|
/// use rhai::{Scope, SubScope};
|
||||||
///
|
///
|
||||||
/// let mut my_scope = Scope::new();
|
/// let mut my_scope = Scope::new();
|
||||||
///
|
///
|
||||||
/// let mut sub_scope = Map::new();
|
/// let mut sub_scope = SubScope::new();
|
||||||
/// sub_scope.insert("x".to_string(), 42_i64.into());
|
/// sub_scope.insert("x".to_string(), 42_i64.into());
|
||||||
///
|
///
|
||||||
/// my_scope.push_sub_scope("my_plugin", sub_scope);
|
/// my_scope.push_sub_scope("my_plugin", sub_scope);
|
||||||
@ -187,8 +187,13 @@ impl<'a> Scope<'a> {
|
|||||||
/// let s = my_scope.find_sub_scope("my_plugin").unwrap();
|
/// let s = my_scope.find_sub_scope("my_plugin").unwrap();
|
||||||
/// assert_eq!(*s.get("x").unwrap().downcast_ref::<i64>().unwrap(), 42);
|
/// assert_eq!(*s.get("x").unwrap().downcast_ref::<i64>().unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn push_sub_scope<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Map) {
|
pub fn push_sub_scope<K: Into<Cow<'a, str>>>(&mut self, name: K, value: SubScope) {
|
||||||
self.push_dynamic_value(name, EntryType::SubScope, value.into(), true);
|
self.push_dynamic_value(
|
||||||
|
name,
|
||||||
|
EntryType::SubScope,
|
||||||
|
Dynamic(Union::SubScope(Box::new(value))),
|
||||||
|
true,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add (push) a new constant to the Scope.
|
/// Add (push) a new constant to the Scope.
|
||||||
@ -352,9 +357,9 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Find a sub-scope in the Scope, starting from the last entry.
|
/// Find a sub-scope in the Scope, starting from the last entry.
|
||||||
pub fn find_sub_scope(&mut self, name: &str) -> Option<&mut Map> {
|
pub fn find_sub_scope(&mut self, name: &str) -> Option<&mut SubScope> {
|
||||||
let index = self.get_sub_scope_index(name)?;
|
let index = self.get_sub_scope_index(name)?;
|
||||||
self.get_mut(index).0.downcast_mut()
|
self.get_mut(index).0.downcast_mut::<SubScope>()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of an entry in the Scope, starting from the last.
|
/// Get the value of an entry in the Scope, starting from the last.
|
||||||
|
15
tests/modules.rs
Normal file
15
tests/modules.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use rhai::{EvalAltResult, Scope, SubScope, INT};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_import"))]
|
||||||
|
fn test_sub_scope() {
|
||||||
|
let mut my_scope = Scope::new();
|
||||||
|
|
||||||
|
let mut sub_scope = SubScope::new();
|
||||||
|
sub_scope.insert("x".to_string(), (42 as INT).into());
|
||||||
|
|
||||||
|
my_scope.push_sub_scope("my_plugin", sub_scope);
|
||||||
|
|
||||||
|
let s = my_scope.find_sub_scope("my_plugin").unwrap();
|
||||||
|
assert_eq!(*s.get("x").unwrap().downcast_ref::<INT>().unwrap(), 42);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user