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.
* 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
------------

View File

@ -802,14 +802,16 @@ impl Engine {
let module = resolver
.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,
result => Some(result),
})
},
)
.or_else(|| {
Some(
self.module_resolver
.resolve_raw(self, global, &path, path_pos),
.resolve_raw(self, global, scope, &path, path_pos),
)
})
.unwrap_or_else(|| {

View File

@ -2083,9 +2083,10 @@ impl Module {
ast: &crate::AST,
engine: &crate::Engine,
) -> RhaiResultOf<Self> {
let mut scope = scope;
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].
///
@ -2101,13 +2102,12 @@ impl Module {
#[cfg(not(feature = "no_module"))]
pub fn eval_ast_as_new_raw(
engine: &crate::Engine,
scope: crate::Scope,
scope: &mut crate::Scope,
global: &mut crate::eval::GlobalRuntimeState,
ast: &crate::AST,
) -> RhaiResultOf<Self> {
let mut scope = scope;
// Save global state
let orig_scope_len = scope.len();
let orig_imports_len = global.num_imports();
let orig_source = global.source.clone();
@ -2120,7 +2120,7 @@ impl Module {
// Run the script
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
let mut module = Module::new();
@ -2162,7 +2162,9 @@ impl Module {
});
// 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| {
if let Some(fn_ptr) = v.downcast_mut::<crate::FnPtr>() {
fn_ptr.set_encapsulated_environ(Some(environ.clone()));

View File

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

View File

@ -1,6 +1,6 @@
use crate::eval::GlobalRuntimeState;
use crate::func::SendSync;
use crate::{Engine, Position, RhaiResultOf, SharedModule, AST};
use crate::{Engine, Position, RhaiResultOf, Scope, SharedModule, AST};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -27,15 +27,17 @@ pub trait ModuleResolver: SendSync {
pos: Position,
) -> 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
///
/// This function is very low level.
#[allow(unused_variables)]
fn resolve_raw(
&self,
engine: &Engine,
global: &mut GlobalRuntimeState,
scope: &mut Scope,
path: &str,
pos: Position,
) -> RhaiResultOf<SharedModule> {

View File

@ -385,11 +385,22 @@ impl Scope<'_> {
/// ```
#[inline(always)]
pub fn pop(&mut self) -> &mut Self {
self.names.pop().expect("`Scope` must not be empty");
let _ = self.values.pop().expect("`Scope` must not be empty");
self.aliases.pop().expect("`Scope` must not be empty");
self.names.pop().expect("not empty");
let _ = self.values.pop().expect("not empty");
self.aliases.pop().expect("not empty");
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.
///
/// # Example