Pre-calculate index for module-qualified calls.
This commit is contained in:
parent
eb52bfa28a
commit
e50fcc385f
@ -2158,7 +2158,7 @@ let ast = engine.compile(r#"
|
|||||||
"#)?;
|
"#)?;
|
||||||
|
|
||||||
// Convert the 'AST' into a module, using the 'Engine' to evaluate it first
|
// Convert the 'AST' into a module, using the 'Engine' to evaluate it first
|
||||||
let module = Module::eval_ast_as_new(&ast, &engine)?;
|
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
|
||||||
|
|
||||||
// 'module' now can be loaded into a custom 'Scope' for future use. It contains:
|
// 'module' now can be loaded into a custom 'Scope' for future use. It contains:
|
||||||
// - sub-module: 'extra'
|
// - sub-module: 'extra'
|
||||||
|
@ -434,7 +434,7 @@ fn default_print(s: &str) {
|
|||||||
fn search_scope<'a>(
|
fn search_scope<'a>(
|
||||||
scope: &'a mut Scope,
|
scope: &'a mut Scope,
|
||||||
name: &str,
|
name: &str,
|
||||||
#[cfg(not(feature = "no_module"))] mut modules: &Option<Box<ModuleRef>>,
|
#[cfg(not(feature = "no_module"))] modules: &Option<Box<ModuleRef>>,
|
||||||
#[cfg(feature = "no_module")] _: &Option<ModuleRef>,
|
#[cfg(feature = "no_module")] _: &Option<ModuleRef>,
|
||||||
index: Option<NonZeroUsize>,
|
index: Option<NonZeroUsize>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
@ -444,7 +444,7 @@ fn search_scope<'a>(
|
|||||||
if let Some(modules) = modules {
|
if let Some(modules) = modules {
|
||||||
let (id, root_pos) = modules.get(0);
|
let (id, root_pos) = modules.get(0);
|
||||||
|
|
||||||
let module = if let Some(index) = index {
|
let module = if let Some(index) = modules.index() {
|
||||||
scope
|
scope
|
||||||
.get_mut(scope.len() - index.get())
|
.get_mut(scope.len() - index.get())
|
||||||
.0
|
.0
|
||||||
@ -1369,9 +1369,17 @@ impl Engine {
|
|||||||
|
|
||||||
let (id, root_pos) = modules.get(0); // First module
|
let (id, root_pos) = modules.get(0); // First module
|
||||||
|
|
||||||
let module = scope.find_module(id).ok_or_else(|| {
|
let module = if let Some(index) = modules.index() {
|
||||||
|
scope
|
||||||
|
.get_mut(scope.len() - index.get())
|
||||||
|
.0
|
||||||
|
.downcast_mut::<Module>()
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
scope.find_module(id).ok_or_else(|| {
|
||||||
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
|
Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *root_pos))
|
||||||
})?;
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if let Some(fn_def) = module.get_qualified_scripted_fn(modules.key()) {
|
if let Some(fn_def) = module.get_qualified_scripted_fn(modules.key()) {
|
||||||
@ -1623,9 +1631,8 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
if let Some(resolver) = self.module_resolver.as_ref() {
|
if let Some(resolver) = self.module_resolver.as_ref() {
|
||||||
// Use an empty scope to create a module
|
// Use an empty scope to create a module
|
||||||
let mut mod_scope = Scope::new();
|
|
||||||
let module =
|
let module =
|
||||||
resolver.resolve(self, mod_scope, &path, expr.position())?;
|
resolver.resolve(self, Scope::new(), &path, expr.position())?;
|
||||||
|
|
||||||
// TODO - avoid copying module name in inner block?
|
// TODO - avoid copying module name in inner block?
|
||||||
let mod_name = name.as_ref().clone();
|
let mod_name = name.as_ref().clone();
|
||||||
@ -1649,19 +1656,19 @@ impl Engine {
|
|||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
|
||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
match scope.get_index(id) {
|
if let Some(index) = scope
|
||||||
Some((index, ScopeEntryType::Normal))
|
.get_index(id)
|
||||||
| Some((index, ScopeEntryType::Constant)) => {
|
.map(|(i, _)| i)
|
||||||
|
.or_else(|| scope.get_module_index(id))
|
||||||
|
{
|
||||||
let alias = rename
|
let alias = rename
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|(n, _)| n.clone())
|
.map(|(n, _)| n.clone())
|
||||||
.unwrap_or_else(|| id.clone());
|
.unwrap_or_else(|| id.clone());
|
||||||
|
|
||||||
scope.set_entry_alias(index, alias);
|
scope.set_entry_alias(index, alias);
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
Some((_, ScopeEntryType::Module)) => unreachable!(),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
return Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
return Err(Box::new(EvalAltResult::ErrorVariableNotFound(
|
||||||
|
@ -17,6 +17,7 @@ use crate::stdlib::{
|
|||||||
fmt,
|
fmt,
|
||||||
iter::{empty, repeat},
|
iter::{empty, repeat},
|
||||||
mem,
|
mem,
|
||||||
|
num::NonZeroUsize,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
@ -568,12 +569,11 @@ impl Module {
|
|||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||||
/// use rhai::{Engine, Module};
|
/// use rhai::{Engine, Module, Scope};
|
||||||
///
|
///
|
||||||
/// let engine = Engine::new();
|
/// let engine = Engine::new();
|
||||||
/// let mut scope = Scope::new();
|
/// let ast = engine.compile("let answer = 42; export answer;")?;
|
||||||
/// let ast = engine.compile("let answer = 42;")?;
|
/// let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
|
||||||
/// let module = Module::eval_ast_as_new(scope, &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").unwrap(), 42);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
@ -599,14 +599,14 @@ impl Module {
|
|||||||
ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => {
|
ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => {
|
||||||
module.variables.insert(*alias.unwrap(), value);
|
module.variables.insert(*alias.unwrap(), value);
|
||||||
}
|
}
|
||||||
// Variables with no alias are private and not exported
|
|
||||||
ScopeEntryType::Normal | ScopeEntryType::Constant => (),
|
|
||||||
// Modules left in the scope become sub-modules
|
// Modules left in the scope become sub-modules
|
||||||
ScopeEntryType::Module => {
|
ScopeEntryType::Module if alias.is_some() => {
|
||||||
module
|
module
|
||||||
.modules
|
.modules
|
||||||
.insert(name.into_owned(), value.cast::<Module>());
|
.insert(*alias.unwrap(), value.cast::<Module>());
|
||||||
}
|
}
|
||||||
|
// Variables and modules with no alias are private and not exported
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -830,14 +830,18 @@ mod file {
|
|||||||
/// A `StaticVec` is used because most module-level access contains only one level,
|
/// A `StaticVec` is used because most module-level access contains only one level,
|
||||||
/// and it is wasteful to always allocate a `Vec` with one element.
|
/// and it is wasteful to always allocate a `Vec` with one element.
|
||||||
#[derive(Clone, Hash, Default)]
|
#[derive(Clone, Hash, Default)]
|
||||||
pub struct ModuleRef(StaticVec<(String, Position)>, u64);
|
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>, u64);
|
||||||
|
|
||||||
impl fmt::Debug for ModuleRef {
|
impl fmt::Debug for ModuleRef {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.0, f)?;
|
fmt::Debug::fmt(&self.0, f)?;
|
||||||
|
|
||||||
if self.1 > 0 {
|
if self.2 > 0 {
|
||||||
write!(f, " -> {:0>16x}", self.1)
|
if let Some(index) = self.1 {
|
||||||
|
write!(f, " -> {},{:0>16x}", index, self.2)
|
||||||
|
} else {
|
||||||
|
write!(f, " -> {:0>16x}", self.2)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -869,16 +873,22 @@ impl fmt::Display for ModuleRef {
|
|||||||
|
|
||||||
impl From<StaticVec<(String, Position)>> for ModuleRef {
|
impl From<StaticVec<(String, Position)>> for ModuleRef {
|
||||||
fn from(modules: StaticVec<(String, Position)>) -> Self {
|
fn from(modules: StaticVec<(String, Position)>) -> Self {
|
||||||
Self(modules, 0)
|
Self(modules, None, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleRef {
|
impl ModuleRef {
|
||||||
pub fn key(&self) -> u64 {
|
pub(crate) fn key(&self) -> u64 {
|
||||||
|
self.2
|
||||||
|
}
|
||||||
|
pub(crate) fn set_key(&mut self, key: u64) {
|
||||||
|
self.2 = key
|
||||||
|
}
|
||||||
|
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
||||||
self.1
|
self.1
|
||||||
}
|
}
|
||||||
pub fn set_key(&mut self, key: u64) {
|
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
|
||||||
self.1 = key
|
self.1 = index
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -719,6 +719,7 @@ fn parse_call_expr<'a>(
|
|||||||
// Calculate hash
|
// Calculate hash
|
||||||
let hash = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
|
let hash = calc_fn_hash(modules.iter().map(|(m, _)| m.as_str()), &id, empty());
|
||||||
modules.set_key(hash);
|
modules.set_key(hash);
|
||||||
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
@ -751,6 +752,7 @@ fn parse_call_expr<'a>(
|
|||||||
repeat(EMPTY_TYPE_ID()).take(args.len()),
|
repeat(EMPTY_TYPE_ID()).take(args.len()),
|
||||||
);
|
);
|
||||||
modules.set_key(hash);
|
modules.set_key(hash);
|
||||||
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Ok(Expr::FnCall(
|
return Ok(Expr::FnCall(
|
||||||
@ -1147,14 +1149,12 @@ fn parse_primary<'a>(
|
|||||||
}
|
}
|
||||||
// module access
|
// module access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(Expr::Variable(id, mut modules, mut index, pos), Token::DoubleColon) => {
|
(Expr::Variable(id, mut modules, 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 {
|
||||||
modules.push((*id, pos));
|
modules.push((*id, pos));
|
||||||
} else {
|
} else {
|
||||||
index = stack.find_module(id.as_ref());
|
|
||||||
|
|
||||||
let mut m: ModuleRef = Default::default();
|
let mut m: ModuleRef = Default::default();
|
||||||
m.push((*id, pos));
|
m.push((*id, pos));
|
||||||
modules = Some(Box::new(m));
|
modules = Some(Box::new(m));
|
||||||
@ -1181,6 +1181,7 @@ fn parse_primary<'a>(
|
|||||||
Expr::Variable(id, Some(modules), _, _) => {
|
Expr::Variable(id, Some(modules), _, _) => {
|
||||||
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
|
let hash = calc_fn_hash(modules.iter().map(|(v, _)| v.as_str()), id, empty());
|
||||||
modules.set_key(hash);
|
modules.set_key(hash);
|
||||||
|
modules.set_index(stack.find_module(&modules.get(0).0));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -2131,7 +2132,8 @@ fn parse_stmt<'a>(
|
|||||||
Token::LeftBrace => parse_block(input, stack, breakable, allow_stmt_expr),
|
Token::LeftBrace => parse_block(input, stack, breakable, allow_stmt_expr),
|
||||||
|
|
||||||
// fn ...
|
// fn ...
|
||||||
Token::Fn => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
Token::Fn if !is_global => Err(PERR::WrongFnDefinition.into_err(*pos)),
|
||||||
|
Token::Fn => unreachable!(),
|
||||||
|
|
||||||
Token::If => parse_if(input, stack, breakable, allow_stmt_expr),
|
Token::If => parse_if(input, stack, breakable, allow_stmt_expr),
|
||||||
Token::While => parse_while(input, stack, allow_stmt_expr),
|
Token::While => parse_while(input, stack, allow_stmt_expr),
|
||||||
|
@ -114,23 +114,25 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
|
|||||||
// Final variable values become constant module variable values
|
// Final variable values become constant module variable values
|
||||||
foo = calc(foo);
|
foo = calc(foo);
|
||||||
hello = "hello, " + foo + " worlds!";
|
hello = "hello, " + foo + " worlds!";
|
||||||
|
|
||||||
|
export x as abc, foo, hello, extra as foobar;
|
||||||
"#,
|
"#,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let module = Module::eval_ast_as_new(&ast, &engine)?;
|
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
|
||||||
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
scope.push_module("testing", module);
|
scope.push_module("testing", module);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::x")?,
|
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::abc")?,
|
||||||
123
|
123
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::foo")?,
|
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::foo")?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
assert!(engine.eval_expression_with_scope::<bool>(&mut scope, "testing::extra::foo")?);
|
assert!(engine.eval_expression_with_scope::<bool>(&mut scope, "testing::foobar::foo")?);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_expression_with_scope::<String>(&mut scope, "testing::hello")?,
|
engine.eval_expression_with_scope::<String>(&mut scope, "testing::hello")?,
|
||||||
"hello, 42 worlds!"
|
"hello, 42 worlds!"
|
||||||
|
Loading…
Reference in New Issue
Block a user