Simplify lifetimes.

This commit is contained in:
Stephen Chung 2021-03-03 22:49:57 +08:00
parent ed568a9395
commit ade290da7e
9 changed files with 81 additions and 92 deletions

View File

@ -21,6 +21,7 @@ Breaking changes
* `Module::update_fn_metadata` input parameter is changed.
* Function keywords (e.g. `type_of`, `eval`, `Fn`) can no longer be overloaded. It is more trouble than worth. To disable these keywords, use `Engine::disable_symbol`.
* `is_def_var` and `is_def_fn` are now reserved keywords.
* `Engine::id` field is removed.
Enhancements
------------

View File

@ -525,18 +525,6 @@ impl State {
pub fn is_global(&self) -> bool {
self.scope_level == 0
}
/// Get the current functions resolution cache.
pub fn fn_resolution_cache(
&self,
) -> Option<
&HashMap<
NonZeroU64,
Option<(CallableFunction, Option<ImmutableString>)>,
StraightHasherBuilder,
>,
> {
self.fn_resolution_caches.last()
}
/// Get a mutable reference to the current functions resolution cache.
pub fn fn_resolution_cache_mut(
&mut self,
@ -617,17 +605,17 @@ pub struct Limits {
/// Context of a script evaluation process.
#[derive(Debug)]
pub struct EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> {
pub(crate) engine: &'e Engine,
pub struct EvalContext<'a, 'x, 'px, 'm, 's, 't, 'pt> {
pub(crate) engine: &'a Engine,
pub(crate) scope: &'x mut Scope<'px>,
pub(crate) mods: &'a mut Imports,
pub(crate) mods: &'m mut Imports,
pub(crate) state: &'s mut State,
pub(crate) lib: &'m [&'m Module],
pub(crate) lib: &'a [&'a Module],
pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>,
pub(crate) level: usize,
}
impl<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> {
impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
/// The current [`Engine`].
#[inline(always)]
pub fn engine(&self) -> &Engine {
@ -710,9 +698,6 @@ impl<'e, 'x, 'px, 'a, 's, 'm, 't, 'pt> EvalContext<'e, 'x, 'px, 'a, 's, 'm, 't,
/// # }
/// ```
pub struct Engine {
/// A unique ID identifying this scripting [`Engine`].
pub id: String,
/// 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.
@ -757,13 +742,9 @@ pub struct Engine {
impl fmt::Debug for Engine {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.id.is_empty() {
write!(f, "Engine({})", self.id)
} else {
f.write_str("Engine")
}
}
}
impl Default for Engine {
#[inline(always)]
@ -819,8 +800,6 @@ impl Engine {
pub fn new() -> Self {
// Create the new scripting Engine
let mut engine = Self {
id: Default::default(),
global_namespace: Default::default(),
global_modules: Default::default(),
global_sub_modules: Default::default(),
@ -886,8 +865,6 @@ impl Engine {
#[inline]
pub fn new_raw() -> Self {
Self {
id: Default::default(),
global_namespace: Default::default(),
global_modules: Default::default(),
global_sub_modules: Default::default(),
@ -934,14 +911,13 @@ impl Engine {
}
/// Search for a module within an imports stack.
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards.
pub fn search_imports(
pub(crate) fn search_imports(
&self,
mods: &Imports,
state: &mut State,
namespace: &NamespaceRef,
) -> Result<Shared<Module>, Box<EvalAltResult>> {
let Ident { name: root, pos } = &namespace[0];
) -> Option<Shared<Module>> {
let root = &namespace[0].name;
// Qualified - check if the root module is directly indexed
let index = if state.always_search {
@ -950,15 +926,14 @@ impl Engine {
namespace.index()
};
Ok(if let Some(index) = index {
if let Some(index) = index {
let offset = mods.len() - index.get();
mods.get(offset).expect("invalid index in Imports")
Some(mods.get(offset).expect("invalid index in Imports"))
} else {
mods.find(root)
.map(|n| mods.get(n).expect("invalid index in Imports"))
.or_else(|| self.global_sub_modules.get(root).cloned())
.ok_or_else(|| EvalAltResult::ErrorModuleNotFound(root.to_string(), *pos))?
})
}
}
/// Search for a variable within the scope or within imports,
@ -976,7 +951,12 @@ impl Engine {
Expr::Variable(v) => match v.as_ref() {
// Qualified variable
(_, Some((hash_var, modules)), Ident { name, pos }) => {
let module = self.search_imports(mods, state, modules)?;
let module = self.search_imports(mods, state, modules).ok_or_else(|| {
EvalAltResult::ErrorModuleNotFound(
modules[0].name.to_string(),
modules[0].pos,
)
})?;
let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
match *err {
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
@ -1068,7 +1048,7 @@ impl Engine {
}
/// Chain-evaluate a dot/index chain.
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn eval_dot_index_chain_helper(
&self,

View File

@ -1028,7 +1028,7 @@ impl Engine {
scripts: &[&str],
optimization_level: OptimizationLevel,
) -> Result<AST, ParseError> {
let stream = self.lex(scripts);
let stream = self.lex_raw(scripts, None);
self.parse(&mut stream.peekable(), scope, optimization_level)
}
/// Read the contents of a file into a string.
@ -1190,9 +1190,9 @@ impl Engine {
.into());
};
let stream = self.lex_with_map(
let stream = self.lex_raw(
&scripts,
if has_null {
Some(if has_null {
|token| match token {
// If `null` is present, make sure `null` is treated as a variable
Token::Reserved(s) if s == "null" => Token::Identifier(s),
@ -1200,7 +1200,7 @@ impl Engine {
}
} else {
|t| t
},
}),
);
let ast =
@ -1283,7 +1283,7 @@ impl Engine {
script: &str,
) -> Result<AST, ParseError> {
let scripts = [script];
let stream = self.lex(&scripts);
let stream = self.lex_raw(&scripts, None);
let mut peekable = stream.peekable();
self.parse_global_expr(&mut peekable, scope, self.optimization_level)
@ -1444,7 +1444,7 @@ impl Engine {
script: &str,
) -> Result<T, Box<EvalAltResult>> {
let scripts = [script];
let stream = self.lex(&scripts);
let stream = self.lex_raw(&scripts, None);
// No need to optimize a lone expression
let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
@ -1585,7 +1585,7 @@ impl Engine {
script: &str,
) -> Result<(), Box<EvalAltResult>> {
let scripts = [script];
let stream = self.lex(&scripts);
let stream = self.lex_raw(&scripts, None);
let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
self.consume_ast_with_scope(scope, &ast)
}

View File

@ -203,7 +203,7 @@ impl Engine {
/// # Examples
///
/// The following will raise an error during parsing because the `if` keyword is disabled
/// and is recognized as a variable name!
/// and is recognized as a reserved symbol!
///
/// ```rust,should_panic
/// # fn main() -> Result<(), rhai::ParseError> {
@ -214,7 +214,7 @@ impl Engine {
/// engine.disable_symbol("if"); // disable the 'if' keyword
///
/// engine.compile("let x = if true { 42 } else { 0 };")?;
/// // ^ 'if' is rejected as a reserved keyword
/// // ^ 'if' is rejected as a reserved symbol
/// # Ok(())
/// # }
/// ```

View File

@ -1357,7 +1357,9 @@ impl Engine {
}
}
let module = self.search_imports(mods, state, namespace)?;
let module = self.search_imports(mods, state, namespace).ok_or_else(|| {
EvalAltResult::ErrorModuleNotFound(namespace[0].name.to_string(), namespace[0].pos)
})?;
// First search in script-defined functions (can override built-in)
let func = match module.get_qualified_fn(hash_script) {

View File

@ -55,20 +55,19 @@ pub type Locked<T> = crate::stdlib::sync::RwLock<T>;
/// Context of a native Rust function call.
#[derive(Debug, Copy, Clone)]
pub struct NativeCallContext<'e, 'n, 's, 'a, 'm> {
engine: &'e Engine,
fn_name: &'n str,
source: Option<&'s str>,
pub(crate) mods: Option<&'a Imports>,
pub(crate) lib: &'m [&'m Module],
pub struct NativeCallContext<'a> {
engine: &'a Engine,
fn_name: &'a str,
source: Option<&'a str>,
mods: Option<&'a Imports>,
lib: &'a [&'a Module],
}
impl<'e, 'n, 's, 'a, 'm, M: AsRef<[&'m Module]> + ?Sized>
From<(&'e Engine, &'n str, Option<&'s str>, &'a Imports, &'m M)>
for NativeCallContext<'e, 'n, 's, 'a, 'm>
impl<'a, M: AsRef<[&'a Module]> + ?Sized>
From<(&'a Engine, &'a str, Option<&'a str>, &'a Imports, &'a M)> for NativeCallContext<'a>
{
#[inline(always)]
fn from(value: (&'e Engine, &'n str, Option<&'s str>, &'a Imports, &'m M)) -> Self {
fn from(value: (&'a Engine, &'a str, Option<&'a str>, &'a Imports, &'a M)) -> Self {
Self {
engine: value.0,
fn_name: value.1,
@ -79,11 +78,11 @@ impl<'e, 'n, 's, 'a, 'm, M: AsRef<[&'m Module]> + ?Sized>
}
}
impl<'e, 'n, 'm, M: AsRef<[&'m Module]> + ?Sized> From<(&'e Engine, &'n str, &'m M)>
for NativeCallContext<'e, 'n, '_, '_, 'm>
impl<'a, M: AsRef<[&'a Module]> + ?Sized> From<(&'a Engine, &'a str, &'a M)>
for NativeCallContext<'a>
{
#[inline(always)]
fn from(value: (&'e Engine, &'n str, &'m M)) -> Self {
fn from(value: (&'a Engine, &'a str, &'a M)) -> Self {
Self {
engine: value.0,
fn_name: value.1,
@ -94,10 +93,10 @@ impl<'e, 'n, 'm, M: AsRef<[&'m Module]> + ?Sized> From<(&'e Engine, &'n str, &'m
}
}
impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
impl<'a> NativeCallContext<'a> {
/// Create a new [`NativeCallContext`].
#[inline(always)]
pub fn new(engine: &'e Engine, fn_name: &'n str, lib: &'m [&'m Module]) -> Self {
pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self {
Self {
engine,
fn_name,
@ -112,11 +111,11 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub fn new_with_all_fields(
engine: &'e Engine,
fn_name: &'n str,
source: &'s Option<&str>,
imports: &'a mut Imports,
lib: &'m [&'m Module],
engine: &'a Engine,
fn_name: &'a str,
source: &'a Option<&str>,
imports: &'a Imports,
lib: &'a [&Module],
) -> Self {
Self {
engine,
@ -147,6 +146,14 @@ impl<'e, 'n, 's, 'a, 'm> NativeCallContext<'e, 'n, 's, 'a, 'm> {
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
self.mods.iter().flat_map(|&m| m.iter())
}
/// Get an iterator over the current set of modules imported via `import` statements.
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub(crate) fn iter_imports_raw(
&self,
) -> impl Iterator<Item = (&ImmutableString, &Shared<Module>)> {
self.mods.iter().flat_map(|&m| m.iter_raw())
}
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
/// Available under the `internals` feature only.
#[cfg(feature = "internals")]

View File

@ -110,15 +110,13 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> Array {
let mut list: Array = Default::default();
ctx.lib
.iter()
ctx.iter_namespaces()
.flat_map(|m| m.iter_script_fn())
.for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into()));
if let Some(mods) = ctx.mods {
mods.iter_raw()
#[cfg(not(feature = "no_module"))]
ctx.iter_imports_raw()
.for_each(|(ns, m)| scan_module(&mut list, &dict, ns.clone(), m.as_ref()));
}
list
}

View File

@ -58,7 +58,7 @@ impl Expression<'_> {
}
}
impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
impl EvalContext<'_, '_, '_, '_, '_, '_, '_> {
/// Evaluate an [expression tree][Expression].
///
/// # WARNING - Low Level API

View File

@ -30,7 +30,7 @@ type LERR = LexError;
const NUM_SEP: char = '_';
/// A stream of tokens.
pub type TokenStream<'a, 't> = Peekable<TokenIterator<'a, 't>>;
pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
/// A location (line number + character position) in the input script.
///
@ -1741,9 +1741,9 @@ impl InputStream for MultiInputsStream<'_> {
}
/// An iterator on a [`Token`] stream.
pub struct TokenIterator<'a, 'e> {
pub struct TokenIterator<'a> {
/// Reference to the scripting `Engine`.
engine: &'e Engine,
engine: &'a Engine,
/// Current state.
state: TokenizeState,
/// Current position.
@ -1754,7 +1754,7 @@ pub struct TokenIterator<'a, 'e> {
map: Option<fn(Token) -> Token>,
}
impl<'a> Iterator for TokenIterator<'a, '_> {
impl<'a> Iterator for TokenIterator<'a> {
type Item = (Token, Position);
fn next(&mut self) -> Option<Self::Item> {
@ -1837,30 +1837,31 @@ impl<'a> Iterator for TokenIterator<'a, '_> {
}
impl Engine {
/// Tokenize an input text stream.
/// _(INTERNALS)_ Tokenize an input text stream.
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[inline(always)]
pub fn lex<'a, 'e>(
&'e self,
input: impl IntoIterator<Item = &'a &'a str>,
) -> TokenIterator<'a, 'e> {
pub fn lex<'a>(&'a self, input: impl IntoIterator<Item = &'a &'a str>) -> TokenIterator<'a> {
self.lex_raw(input, None)
}
/// Tokenize an input text stream with a mapping function.
/// _(INTERNALS)_ Tokenize an input text stream with a mapping function.
/// Exported under the `internals` feature only.
#[cfg(feature = "internals")]
#[inline(always)]
pub fn lex_with_map<'a, 'e>(
&'e self,
pub fn lex_with_map<'a>(
&'a self,
input: impl IntoIterator<Item = &'a &'a str>,
map: fn(Token) -> Token,
) -> TokenIterator<'a, 'e> {
) -> TokenIterator<'a> {
self.lex_raw(input, Some(map))
}
/// Tokenize an input text stream with an optional mapping function.
#[inline]
fn lex_raw<'a, 'e>(
&'e self,
pub(crate) fn lex_raw<'a>(
&'a self,
input: impl IntoIterator<Item = &'a &'a str>,
map: Option<fn(Token) -> Token>,
) -> TokenIterator<'a, 'e> {
) -> TokenIterator<'a> {
TokenIterator {
engine: self,
state: TokenizeState {