Allow access to scope by loaded module.

This commit is contained in:
Stephen Chung 2023-02-13 21:12:17 +08:00
parent 7fc72e8c28
commit 513a1ab435
6 changed files with 50 additions and 29 deletions

View File

@ -13,6 +13,12 @@ Bug fixes
* `x += y` where `x` and `y` are `char` now works correctly. * `x += y` where `x` and `y` are `char` now works correctly.
* Expressions such as `!inside` now parses correctly instead of as `!in` followed by `side`. * Expressions such as `!inside` now parses correctly instead of as `!in` followed by `side`.
Potentially breaking changes
----------------------------
* The trait method `ModuleResolver::resolve_raw` (which is a low-level API) now takes a `&mut Scope` parameter. This is a breaking change because the signature is modified, but this trait method has a default and is rarely called/implemented in practice.
* `Module::eval_ast_as_new_raw` (a low-level API) now takes a `&mut Scope` instead of the `Scope` parameter. This is a breaking change because the `&mut` is now required.
Enhancements Enhancements
------------ ------------

View File

@ -802,14 +802,16 @@ impl Engine {
let module = resolver let module = resolver
.as_ref() .as_ref()
.and_then(|r| match r.resolve_raw(self, global, &path, path_pos) { .and_then(
|r| match r.resolve_raw(self, global, scope, &path, path_pos) {
Err(err) if matches!(*err, ERR::ErrorModuleNotFound(..)) => None, Err(err) if matches!(*err, ERR::ErrorModuleNotFound(..)) => None,
result => Some(result), result => Some(result),
}) },
)
.or_else(|| { .or_else(|| {
Some( Some(
self.module_resolver self.module_resolver
.resolve_raw(self, global, &path, path_pos), .resolve_raw(self, global, scope, &path, path_pos),
) )
}) })
.unwrap_or_else(|| { .unwrap_or_else(|| {

View File

@ -2083,9 +2083,10 @@ impl Module {
ast: &crate::AST, ast: &crate::AST,
engine: &crate::Engine, engine: &crate::Engine,
) -> RhaiResultOf<Self> { ) -> RhaiResultOf<Self> {
let mut scope = scope;
let global = &mut crate::eval::GlobalRuntimeState::new(engine); let global = &mut crate::eval::GlobalRuntimeState::new(engine);
Self::eval_ast_as_new_raw(engine, scope, global, ast) Self::eval_ast_as_new_raw(engine, &mut scope, global, ast)
} }
/// Create a new [`Module`] by evaluating an [`AST`][crate::AST]. /// Create a new [`Module`] by evaluating an [`AST`][crate::AST].
/// ///
@ -2101,13 +2102,12 @@ impl Module {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub fn eval_ast_as_new_raw( pub fn eval_ast_as_new_raw(
engine: &crate::Engine, engine: &crate::Engine,
scope: crate::Scope, scope: &mut crate::Scope,
global: &mut crate::eval::GlobalRuntimeState, global: &mut crate::eval::GlobalRuntimeState,
ast: &crate::AST, ast: &crate::AST,
) -> RhaiResultOf<Self> { ) -> RhaiResultOf<Self> {
let mut scope = scope;
// Save global state // Save global state
let orig_scope_len = scope.len();
let orig_imports_len = global.num_imports(); let orig_imports_len = global.num_imports();
let orig_source = global.source.clone(); let orig_source = global.source.clone();
@ -2120,7 +2120,7 @@ impl Module {
// Run the script // Run the script
let caches = &mut crate::eval::Caches::new(); let caches = &mut crate::eval::Caches::new();
let result = engine.eval_ast_with_scope_raw(global, caches, &mut scope, ast); let result = engine.eval_ast_with_scope_raw(global, caches, scope, ast);
// Create new module // Create new module
let mut module = Module::new(); let mut module = Module::new();
@ -2162,7 +2162,9 @@ impl Module {
}); });
// Variables with an alias left in the scope become module variables // Variables with an alias left in the scope become module variables
for (_name, mut value, mut aliases) in scope { while scope.len() > orig_scope_len {
let (_name, mut value, mut aliases) = scope.pop_entry().expect("not empty");
value.deep_scan(|v| { value.deep_scan(|v| {
if let Some(fn_ptr) = v.downcast_mut::<crate::FnPtr>() { if let Some(fn_ptr) = v.downcast_mut::<crate::FnPtr>() {
fn_ptr.set_encapsulated_environ(Some(environ.clone())); fn_ptr.set_encapsulated_environ(Some(environ.clone()));

View File

@ -290,15 +290,15 @@ impl FileModuleResolver {
fn impl_resolve( fn impl_resolve(
&self, &self,
engine: &Engine, engine: &Engine,
global: Option<&mut GlobalRuntimeState>, global: &mut GlobalRuntimeState,
scope: &mut Scope,
source: Option<&str>, source: Option<&str>,
path: &str, path: &str,
pos: Position, pos: Position,
) -> Result<SharedModule, Box<crate::EvalAltResult>> { ) -> Result<SharedModule, Box<crate::EvalAltResult>> {
// Load relative paths from source if there is no base path specified // Load relative paths from source if there is no base path specified
let source_path = global let source_path = global
.as_ref() .source()
.and_then(|g| g.source())
.or(source) .or(source)
.and_then(|p| Path::new(p).parent()); .and_then(|p| Path::new(p).parent());
@ -321,12 +321,7 @@ impl FileModuleResolver {
ast.set_source(path); ast.set_source(path);
let scope = Scope::new(); let m: Shared<_> = Module::eval_ast_as_new_raw(engine, scope, global, &ast)
let m: Shared<_> = match global {
Some(global) => Module::eval_ast_as_new_raw(engine, scope, global, &ast),
None => Module::eval_ast_as_new(scope, &ast, engine),
}
.map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))? .map_err(|err| Box::new(ERR::ErrorInModule(path.to_string(), err, pos)))?
.into(); .into();
@ -343,10 +338,11 @@ impl ModuleResolver for FileModuleResolver {
&self, &self,
engine: &Engine, engine: &Engine,
global: &mut GlobalRuntimeState, global: &mut GlobalRuntimeState,
scope: &mut Scope,
path: &str, path: &str,
pos: Position, pos: Position,
) -> RhaiResultOf<SharedModule> { ) -> RhaiResultOf<SharedModule> {
self.impl_resolve(engine, Some(global), None, path, pos) self.impl_resolve(engine, global, scope, None, path, pos)
} }
#[inline(always)] #[inline(always)]
@ -357,7 +353,9 @@ impl ModuleResolver for FileModuleResolver {
path: &str, path: &str,
pos: Position, pos: Position,
) -> RhaiResultOf<SharedModule> { ) -> RhaiResultOf<SharedModule> {
self.impl_resolve(engine, None, source, path, pos) let global = &mut GlobalRuntimeState::new(engine);
let scope = &mut Scope::new();
self.impl_resolve(engine, global, scope, source, path, pos)
} }
/// Resolve an `AST` based on a path string. /// Resolve an `AST` based on a path string.

View File

@ -1,6 +1,6 @@
use crate::eval::GlobalRuntimeState; use crate::eval::GlobalRuntimeState;
use crate::func::SendSync; use crate::func::SendSync;
use crate::{Engine, Position, RhaiResultOf, SharedModule, AST}; use crate::{Engine, Position, RhaiResultOf, Scope, SharedModule, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -27,15 +27,17 @@ pub trait ModuleResolver: SendSync {
pos: Position, pos: Position,
) -> RhaiResultOf<SharedModule>; ) -> RhaiResultOf<SharedModule>;
/// Resolve a module based on a path string, given a [`GlobalRuntimeState`]. /// Resolve a module based on a path string, given a [`GlobalRuntimeState`] and the current [`Scope`].
/// ///
/// # WARNING - Low Level API /// # WARNING - Low Level API
/// ///
/// This function is very low level. /// This function is very low level.
#[allow(unused_variables)]
fn resolve_raw( fn resolve_raw(
&self, &self,
engine: &Engine, engine: &Engine,
global: &mut GlobalRuntimeState, global: &mut GlobalRuntimeState,
scope: &mut Scope,
path: &str, path: &str,
pos: Position, pos: Position,
) -> RhaiResultOf<SharedModule> { ) -> RhaiResultOf<SharedModule> {

View File

@ -385,11 +385,22 @@ impl Scope<'_> {
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn pop(&mut self) -> &mut Self { pub fn pop(&mut self) -> &mut Self {
self.names.pop().expect("`Scope` must not be empty"); self.names.pop().expect("not empty");
let _ = self.values.pop().expect("`Scope` must not be empty"); let _ = self.values.pop().expect("not empty");
self.aliases.pop().expect("`Scope` must not be empty"); self.aliases.pop().expect("not empty");
self self
} }
/// Remove the last entry from the [`Scope`] and return it.
#[inline(always)]
pub(crate) fn pop_entry(&mut self) -> Option<(Identifier, Dynamic, Vec<ImmutableString>)> {
self.values.pop().map(|value| {
(
self.names.pop().expect("not empty"),
value,
self.aliases.pop().expect("not empty"),
)
})
}
/// Truncate (rewind) the [`Scope`] to a previous size. /// Truncate (rewind) the [`Scope`] to a previous size.
/// ///
/// # Example /// # Example