Merge branch 'master' into plugins
This commit is contained in:
commit
95490adf0b
99
README.md
99
README.md
@ -268,6 +268,7 @@ let ast = engine.compile_file("hello_world.rhai".into())?;
|
|||||||
### Calling Rhai functions from Rust
|
### Calling Rhai functions from Rust
|
||||||
|
|
||||||
Rhai also allows working _backwards_ from the other direction - i.e. calling a Rhai-scripted function from Rust via `call_fn`.
|
Rhai also allows working _backwards_ from the other direction - i.e. calling a Rhai-scripted function from Rust via `call_fn`.
|
||||||
|
Functions declared with `private` are hidden and cannot be called from Rust (see also [modules]).
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Define functions in a script.
|
// Define functions in a script.
|
||||||
@ -287,6 +288,11 @@ let ast = engine.compile(true,
|
|||||||
fn hello() {
|
fn hello() {
|
||||||
42
|
42
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this one is private and cannot be called by 'call_fn'
|
||||||
|
private hidden() {
|
||||||
|
throw "you shouldn't see me!";
|
||||||
|
}
|
||||||
")?;
|
")?;
|
||||||
|
|
||||||
// A custom scope can also contain any variables/constants available to the functions
|
// A custom scope can also contain any variables/constants available to the functions
|
||||||
@ -300,11 +306,15 @@ let result: i64 = engine.call_fn(&mut scope, &ast, "hello", ( String::from("abc"
|
|||||||
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
// put arguments in a tuple
|
// put arguments in a tuple
|
||||||
|
|
||||||
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", (123_i64,) )?
|
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", (123_i64,) )?;
|
||||||
// ^^^^^^^^^^ tuple of one
|
// ^^^^^^^^^^ tuple of one
|
||||||
|
|
||||||
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?
|
let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?;
|
||||||
// ^^ unit = tuple of zero
|
// ^^ unit = tuple of zero
|
||||||
|
|
||||||
|
// The following call will return a function-not-found error because
|
||||||
|
// 'hidden' is declared with 'private'.
|
||||||
|
let result: () = engine.call_fn(&mut scope, &ast, "hidden", ())?;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Creating Rust anonymous functions from Rhai script
|
### Creating Rust anonymous functions from Rhai script
|
||||||
@ -2044,10 +2054,34 @@ 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
|
Rhai allows organizing code (functions and variables) into _modules_.
|
||||||
with `export` statements that _exports_ certain global variables and functions as contents of the module.
|
Modules can be disabled via the [`no_module`] feature.
|
||||||
|
|
||||||
Everything exported as part of a module is constant and read-only.
|
### Exporting variables and functions
|
||||||
|
|
||||||
|
A module is a single script (or pre-compiled `AST`) containing global variables and functions.
|
||||||
|
The `export` statement, which can only be at global level, exposes selected variables as members of a module.
|
||||||
|
Variables not exported are private and invisible to the outside.
|
||||||
|
All functions are automatically exported, unless it is prefixed with `private`.
|
||||||
|
Functions declared `private` are invisible to the outside.
|
||||||
|
|
||||||
|
Everything exported from a module is **constant** (**read-only**).
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// This is a module script.
|
||||||
|
|
||||||
|
fn inc(x) { x + 1 } // public function
|
||||||
|
|
||||||
|
private fn foo() {} // private function - invisible to outside
|
||||||
|
|
||||||
|
let private = 123; // variable not exported - invisible to outside
|
||||||
|
let x = 42; // this will be exported below
|
||||||
|
|
||||||
|
export x; // the variable 'x' is exported under its own name
|
||||||
|
|
||||||
|
export x as answer; // the variable 'x' is exported under the alias 'answer'
|
||||||
|
// another script can load this module and access 'x' as 'module::answer'
|
||||||
|
```
|
||||||
|
|
||||||
### Importing modules
|
### Importing modules
|
||||||
|
|
||||||
@ -2080,7 +2114,7 @@ crypto::encrypt(others); // <- this causes a run-time error because the 'cryp
|
|||||||
|
|
||||||
### Creating custom modules from Rust
|
### Creating custom modules from Rust
|
||||||
|
|
||||||
To load a custom module into an [`Engine`], first create a `Module` type, add variables/functions into it,
|
To load a custom module (written in Rust) into an [`Engine`], first create a `Module` type, add variables/functions into it,
|
||||||
then finally push it into a custom [`Scope`]. This has the equivalent effect of putting an `import` statement
|
then finally push it into a custom [`Scope`]. This has the equivalent effect of putting an `import` statement
|
||||||
at the beginning of any script run.
|
at the beginning of any script run.
|
||||||
|
|
||||||
@ -2105,6 +2139,55 @@ engine.eval_expression_with_scope::<i64>(&scope, "question::answer + 1")? == 42;
|
|||||||
engine.eval_expression_with_scope::<i64>(&scope, "question::inc(question::answer)")? == 42;
|
engine.eval_expression_with_scope::<i64>(&scope, "question::inc(question::answer)")? == 42;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Creating a module from an `AST`
|
||||||
|
|
||||||
|
It is easy to convert a pre-compiled `AST` into a module, just use `Module::eval_ast_as_new`.
|
||||||
|
Don't forget the `export` statement, otherwise there will be nothing inside the module!
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use rhai::{Engine, Module};
|
||||||
|
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
// Compile a script into an 'AST'
|
||||||
|
let ast = engine.compile(r#"
|
||||||
|
// Functions become module functions
|
||||||
|
fn calc(x) {
|
||||||
|
x + 1
|
||||||
|
}
|
||||||
|
fn add_len(x, y) {
|
||||||
|
x + y.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imported modules can become sub-modules
|
||||||
|
import "another module" as extra;
|
||||||
|
|
||||||
|
// Variables defined at global level can become module variables
|
||||||
|
const x = 123;
|
||||||
|
let foo = 41;
|
||||||
|
let hello;
|
||||||
|
|
||||||
|
// Variable values become constant module variable values
|
||||||
|
foo = calc(foo);
|
||||||
|
hello = "hello, " + foo + " worlds!";
|
||||||
|
|
||||||
|
// Finally, export the variables and modules
|
||||||
|
export
|
||||||
|
x as abc, // aliased variable name
|
||||||
|
foo,
|
||||||
|
hello,
|
||||||
|
extra as foobar; // export sub-module
|
||||||
|
"#)?;
|
||||||
|
|
||||||
|
// Convert the 'AST' into a module, using the 'Engine' to evaluate it first
|
||||||
|
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:
|
||||||
|
// - sub-module: 'foobar' (renamed from 'extra')
|
||||||
|
// - functions: 'calc', 'add_len'
|
||||||
|
// - variables: 'abc' (renamed from 'x'), 'foo', 'hello'
|
||||||
|
```
|
||||||
|
|
||||||
### Module resolvers
|
### Module resolvers
|
||||||
|
|
||||||
When encountering an `import` statement, Rhai attempts to _resolve_ the module based on the path string.
|
When encountering an `import` statement, Rhai attempts to _resolve_ the module based on the path string.
|
||||||
@ -2115,8 +2198,8 @@ which simply loads a script file based on the path (with `.rhai` extension attac
|
|||||||
Built-in module resolvers are grouped under the `rhai::module_resolvers` module namespace.
|
Built-in module resolvers are grouped under the `rhai::module_resolvers` module namespace.
|
||||||
|
|
||||||
| Module Resolver | Description |
|
| Module Resolver | Description |
|
||||||
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `FileModuleResolver` | The default module resolution service, not available under the [`no_std`] feature. Loads a script file (based off the current directory) with `.rhai` extension.<br/>The base directory can be changed via the `FileModuleResolver::new_with_path()` constructor function. |
|
| `FileModuleResolver` | The default module resolution service, not available under the [`no_std`] feature. Loads a script file (based off the current directory) with `.rhai` extension.<br/>The base directory can be changed via the `FileModuleResolver::new_with_path()` constructor function.<br/>`FileModuleResolver::create_module()` loads a script file and returns a module. |
|
||||||
| `StaticModuleResolver` | Loads modules that are statically added. This can be used when the [`no_std`] feature is turned on. |
|
| `StaticModuleResolver` | Loads modules that are statically added. This can be used when the [`no_std`] feature is turned on. |
|
||||||
|
|
||||||
An [`Engine`]'s module resolver is set via a call to `set_module_resolver`:
|
An [`Engine`]'s module resolver is set via a call to `set_module_resolver`:
|
||||||
|
56
src/any.rs
56
src/any.rs
@ -18,7 +18,7 @@ use crate::stdlib::{
|
|||||||
any::{type_name, Any, TypeId},
|
any::{type_name, Any, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt,
|
fmt, mem, ptr,
|
||||||
string::String,
|
string::String,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
@ -38,6 +38,9 @@ pub trait Variant: Any {
|
|||||||
/// Convert this `Variant` trait object to `&mut dyn Any`.
|
/// Convert this `Variant` trait object to `&mut dyn Any`.
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any;
|
fn as_mut_any(&mut self) -> &mut dyn Any;
|
||||||
|
|
||||||
|
/// Convert this `Variant` trait object to an `Any` trait object.
|
||||||
|
fn as_box_any(self: Box<Self>) -> Box<dyn Any>;
|
||||||
|
|
||||||
/// Get the name of this type.
|
/// Get the name of this type.
|
||||||
fn type_name(&self) -> &'static str;
|
fn type_name(&self) -> &'static str;
|
||||||
|
|
||||||
@ -60,6 +63,9 @@ impl<T: Any + Clone> Variant for T {
|
|||||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
fn as_mut_any(&mut self) -> &mut dyn Any {
|
||||||
self as &mut dyn Any
|
self as &mut dyn Any
|
||||||
}
|
}
|
||||||
|
fn as_box_any(self: Box<Self>) -> Box<dyn Any> {
|
||||||
|
self as Box<dyn Any>
|
||||||
|
}
|
||||||
fn type_name(&self) -> &'static str {
|
fn type_name(&self) -> &'static str {
|
||||||
type_name::<T>()
|
type_name::<T>()
|
||||||
}
|
}
|
||||||
@ -86,6 +92,9 @@ pub trait Variant: Any + Send + Sync {
|
|||||||
/// Convert this `Variant` trait object to `&mut dyn Any`.
|
/// Convert this `Variant` trait object to `&mut dyn Any`.
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any;
|
fn as_mut_any(&mut self) -> &mut dyn Any;
|
||||||
|
|
||||||
|
/// Convert this `Variant` trait object to an `Any` trait object.
|
||||||
|
fn as_box_any(self) -> Box<dyn Any>;
|
||||||
|
|
||||||
/// Get the name of this type.
|
/// Get the name of this type.
|
||||||
fn type_name(&self) -> &'static str;
|
fn type_name(&self) -> &'static str;
|
||||||
|
|
||||||
@ -108,6 +117,9 @@ impl<T: Any + Clone + Send + Sync> Variant for T {
|
|||||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
fn as_mut_any(&mut self) -> &mut dyn Any {
|
||||||
self as &mut dyn Any
|
self as &mut dyn Any
|
||||||
}
|
}
|
||||||
|
fn as_box_any(self: Box<Self>) -> Box<dyn Any> {
|
||||||
|
self as Box<dyn Any>
|
||||||
|
}
|
||||||
fn type_name(&self) -> &'static str {
|
fn type_name(&self) -> &'static str {
|
||||||
type_name::<T>()
|
type_name::<T>()
|
||||||
}
|
}
|
||||||
@ -284,6 +296,22 @@ impl Default for Dynamic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cast a type into another type.
|
||||||
|
fn try_cast<A: Any, B: Any>(a: A) -> Option<B> {
|
||||||
|
if TypeId::of::<B>() == a.type_id() {
|
||||||
|
// SAFETY: Just checked we have the right type. We explicitly forget the
|
||||||
|
// value immediately after moving out, removing any chance of a destructor
|
||||||
|
// running or value otherwise being used again.
|
||||||
|
unsafe {
|
||||||
|
let ret: B = ptr::read(&a as *const _ as *const B);
|
||||||
|
mem::forget(a);
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Cast a Boxed type into another type.
|
/// Cast a Boxed type into another type.
|
||||||
fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
|
fn cast_box<X: Variant, T: Variant>(item: Box<X>) -> Result<Box<T>, Box<X>> {
|
||||||
// Only allow casting to the exact same type
|
// Only allow casting to the exact same type
|
||||||
@ -388,26 +416,26 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(x.try_cast::<u32>().unwrap(), 42);
|
/// assert_eq!(x.try_cast::<u32>().unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn try_cast<T: Variant + Clone>(self) -> Option<T> {
|
pub fn try_cast<T: Variant>(self) -> Option<T> {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||||
return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v);
|
return cast_box::<_, T>(Box::new(self)).ok().map(|v| *v);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
|
Union::Unit(value) => try_cast(value),
|
||||||
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
|
Union::Bool(value) => try_cast(value),
|
||||||
Union::Str(value) => cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::Str(value) => cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
|
Union::Char(value) => try_cast(value),
|
||||||
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
|
Union::Int(value) => try_cast(value),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().cloned(),
|
Union::Float(value) => try_cast(value),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(value) => cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::Array(value) => cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::Map(value) => cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Union::Module(value) => cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::Module(value) => cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
Union::Variant(value) => value.as_any().downcast_ref::<T>().cloned(),
|
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,20 +463,20 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
|
Union::Unit(value) => try_cast(value).unwrap(),
|
||||||
Union::Bool(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
|
Union::Bool(value) => try_cast(value).unwrap(),
|
||||||
Union::Str(value) => *cast_box::<_, T>(value).unwrap(),
|
Union::Str(value) => *cast_box::<_, T>(value).unwrap(),
|
||||||
Union::Char(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
|
Union::Char(value) => try_cast(value).unwrap(),
|
||||||
Union::Int(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
|
Union::Int(value) => try_cast(value).unwrap(),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(ref value) => (value as &dyn Any).downcast_ref::<T>().unwrap().clone(),
|
Union::Float(value) => try_cast(value).unwrap(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(value) => *cast_box::<_, T>(value).unwrap(),
|
Union::Array(value) => *cast_box::<_, T>(value).unwrap(),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => *cast_box::<_, T>(value).unwrap(),
|
Union::Map(value) => *cast_box::<_, T>(value).unwrap(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Union::Module(value) => *cast_box::<_, T>(value).unwrap(),
|
Union::Module(value) => *cast_box::<_, T>(value).unwrap(),
|
||||||
Union::Variant(value) => value.as_any().downcast_ref::<T>().unwrap().clone(),
|
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
18
src/api.rs
18
src/api.rs
@ -168,7 +168,9 @@ impl Engine {
|
|||||||
/// Register an iterator adapter for a type with the `Engine`.
|
/// Register an iterator adapter for a type with the `Engine`.
|
||||||
/// This is an advanced feature.
|
/// This is an advanced feature.
|
||||||
pub fn register_iterator<T: Variant + Clone, F: IteratorCallback>(&mut self, f: F) {
|
pub fn register_iterator<T: Variant + Clone, F: IteratorCallback>(&mut self, f: F) {
|
||||||
self.type_iterators.insert(TypeId::of::<T>(), Box::new(f));
|
self.base_package
|
||||||
|
.type_iterators
|
||||||
|
.insert(TypeId::of::<T>(), Box::new(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a getter function for a member of a registered type with the `Engine`.
|
/// Register a getter function for a member of a registered type with the `Engine`.
|
||||||
@ -867,12 +869,12 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let mut state = State::new();
|
let mut state = State::new(ast.fn_lib());
|
||||||
|
|
||||||
ast.statements()
|
ast.statements()
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, &mut state, ast.fn_lib(), stmt, 0)
|
self.eval_stmt(scope, &mut state, stmt, 0)
|
||||||
})
|
})
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
EvalAltResult::Return(out, _) => Ok(out),
|
EvalAltResult::Return(out, _) => Ok(out),
|
||||||
@ -932,12 +934,12 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
ast: &AST,
|
ast: &AST,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut state = State::new();
|
let mut state = State::new(ast.fn_lib());
|
||||||
|
|
||||||
ast.statements()
|
ast.statements()
|
||||||
.iter()
|
.iter()
|
||||||
.try_fold(().into(), |_, stmt| {
|
.try_fold(().into(), |_, stmt| {
|
||||||
self.eval_stmt(scope, &mut state, ast.fn_lib(), stmt, 0)
|
self.eval_stmt(scope, &mut state, stmt, 0)
|
||||||
})
|
})
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -997,10 +999,12 @@ impl Engine {
|
|||||||
let pos = Position::none();
|
let pos = Position::none();
|
||||||
|
|
||||||
let fn_def = fn_lib
|
let fn_def = fn_lib
|
||||||
.get_function(name, args.len())
|
.get_function_by_signature(name, args.len(), true)
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
|
||||||
|
|
||||||
let result = self.call_fn_from_lib(Some(scope), fn_lib, fn_def, &mut args, pos, 0)?;
|
let state = State::new(fn_lib);
|
||||||
|
|
||||||
|
let result = self.call_script_fn(Some(scope), &state, fn_def, &mut args, pos, 0)?;
|
||||||
|
|
||||||
let return_type = self.map_type_name(result.type_name());
|
let return_type = self.map_type_name(result.type_name());
|
||||||
|
|
||||||
|
811
src/engine.rs
811
src/engine.rs
File diff suppressed because it is too large
Load Diff
30
src/error.rs
30
src/error.rs
@ -5,7 +5,7 @@ use crate::token::Position;
|
|||||||
use crate::stdlib::{boxed::Box, char, error::Error, fmt, string::String};
|
use crate::stdlib::{boxed::Box, char, error::Error, fmt, string::String};
|
||||||
|
|
||||||
/// Error when tokenizing the script text.
|
/// Error when tokenizing the script text.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||||
pub enum LexError {
|
pub enum LexError {
|
||||||
/// An unexpected character is encountered when tokenizing the script text.
|
/// An unexpected character is encountered when tokenizing the script text.
|
||||||
UnexpectedChar(char),
|
UnexpectedChar(char),
|
||||||
@ -44,7 +44,7 @@ impl fmt::Display for LexError {
|
|||||||
/// Some errors never appear when certain features are turned on.
|
/// Some errors never appear when certain features are turned on.
|
||||||
/// They still exist so that the application can turn features on and off without going through
|
/// They still exist so that the application can turn features on and off without going through
|
||||||
/// massive code changes to remove/add back enum variants in match statements.
|
/// massive code changes to remove/add back enum variants in match statements.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||||
pub enum ParseErrorType {
|
pub enum ParseErrorType {
|
||||||
/// Error in the script text. Wrapped value is the error message.
|
/// Error in the script text. Wrapped value is the error message.
|
||||||
BadInput(String),
|
BadInput(String),
|
||||||
@ -98,8 +98,16 @@ pub enum ParseErrorType {
|
|||||||
///
|
///
|
||||||
/// Never appears under the `no_function` feature.
|
/// Never appears under the `no_function` feature.
|
||||||
FnMissingBody(String),
|
FnMissingBody(String),
|
||||||
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
/// An export statement has duplicated names.
|
||||||
AssignmentToInvalidLHS,
|
///
|
||||||
|
/// Never appears under the `no_module` feature.
|
||||||
|
DuplicatedExport(String),
|
||||||
|
/// Export statement not at global level.
|
||||||
|
///
|
||||||
|
/// Never appears under the `no_module` feature.
|
||||||
|
WrongExport,
|
||||||
|
/// Assignment to a copy of a value.
|
||||||
|
AssignmentToCopy,
|
||||||
/// Assignment to an a constant variable.
|
/// Assignment to an a constant variable.
|
||||||
AssignmentToConstant(String),
|
AssignmentToConstant(String),
|
||||||
/// Break statement not inside a loop.
|
/// Break statement not inside a loop.
|
||||||
@ -114,7 +122,7 @@ impl ParseErrorType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Error when parsing a script.
|
/// Error when parsing a script.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
pub struct ParseError(pub(crate) ParseErrorType, pub(crate) Position);
|
pub struct ParseError(pub(crate) ParseErrorType, pub(crate) Position);
|
||||||
|
|
||||||
impl ParseError {
|
impl ParseError {
|
||||||
@ -147,8 +155,10 @@ impl ParseError {
|
|||||||
ParseErrorType::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
ParseErrorType::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
||||||
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
ParseErrorType::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||||
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
ParseErrorType::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||||
ParseErrorType::AssignmentToInvalidLHS => "Cannot assign to this expression",
|
ParseErrorType::DuplicatedExport(_) => "Duplicated variable/function in export statement",
|
||||||
ParseErrorType::AssignmentToConstant(_) => "Cannot assign to a constant variable.",
|
ParseErrorType::WrongExport => "Export statement can only appear at global level",
|
||||||
|
ParseErrorType::AssignmentToCopy => "Only a copy of the value is change with this assignment",
|
||||||
|
ParseErrorType::AssignmentToConstant(_) => "Cannot assign to a constant value.",
|
||||||
ParseErrorType::LoopBreak => "Break statement should only be used inside a loop"
|
ParseErrorType::LoopBreak => "Break statement should only be used inside a loop"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -193,6 +203,12 @@ impl fmt::Display for ParseError {
|
|||||||
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)?
|
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParseErrorType::DuplicatedExport(s) => write!(
|
||||||
|
f,
|
||||||
|
"Duplicated variable/function '{}' in export statement",
|
||||||
|
s
|
||||||
|
)?,
|
||||||
|
|
||||||
ParseErrorType::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s)?,
|
ParseErrorType::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s)?,
|
||||||
|
|
||||||
ParseErrorType::AssignmentToConstant(s) if s.is_empty() => {
|
ParseErrorType::AssignmentToConstant(s) if s.is_empty() => {
|
||||||
|
@ -8,7 +8,7 @@ use crate::result::EvalAltResult;
|
|||||||
use crate::token::Position;
|
use crate::token::Position;
|
||||||
use crate::utils::calc_fn_spec;
|
use crate::utils::calc_fn_spec;
|
||||||
|
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::ToString};
|
use crate::stdlib::{any::TypeId, boxed::Box, iter::empty, mem, string::ToString};
|
||||||
|
|
||||||
/// A trait to register custom plugins with the `Engine`.
|
/// A trait to register custom plugins with the `Engine`.
|
||||||
///
|
///
|
||||||
@ -279,8 +279,8 @@ macro_rules! def_register {
|
|||||||
fn register_fn(&mut self, name: &str, f: FN) {
|
fn register_fn(&mut self, name: &str, f: FN) {
|
||||||
let fn_name = name.to_string();
|
let fn_name = name.to_string();
|
||||||
let func = make_func!(fn_name : f : map_dynamic ; $($par => $clone),*);
|
let func = make_func!(fn_name : f : map_dynamic ; $($par => $clone),*);
|
||||||
let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||||
self.functions.insert(hash, Box::new(func));
|
self.base_package.functions.insert(hash, Box::new(func));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,8 +297,8 @@ macro_rules! def_register {
|
|||||||
fn register_dynamic_fn(&mut self, name: &str, f: FN) {
|
fn register_dynamic_fn(&mut self, name: &str, f: FN) {
|
||||||
let fn_name = name.to_string();
|
let fn_name = name.to_string();
|
||||||
let func = make_func!(fn_name : f : map_identity ; $($par => $clone),*);
|
let func = make_func!(fn_name : f : map_identity ; $($par => $clone),*);
|
||||||
let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||||
self.functions.insert(hash, Box::new(func));
|
self.base_package.functions.insert(hash, Box::new(func));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,8 +316,8 @@ macro_rules! def_register {
|
|||||||
fn register_result_fn(&mut self, name: &str, f: FN) {
|
fn register_result_fn(&mut self, name: &str, f: FN) {
|
||||||
let fn_name = name.to_string();
|
let fn_name = name.to_string();
|
||||||
let func = make_func!(fn_name : f : map_result ; $($par => $clone),*);
|
let func = make_func!(fn_name : f : map_result ; $($par => $clone),*);
|
||||||
let hash = calc_fn_spec(name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
let hash = calc_fn_spec(empty(), name, [$(TypeId::of::<$par>()),*].iter().cloned());
|
||||||
self.functions.insert(hash, Box::new(func));
|
self.base_package.functions.insert(hash, Box::new(func));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
372
src/module.rs
372
src/module.rs
@ -3,23 +3,27 @@
|
|||||||
|
|
||||||
use crate::any::{Dynamic, Variant};
|
use crate::any::{Dynamic, Variant};
|
||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib};
|
use crate::engine::{Engine, FnAny, FnCallArgs, FunctionsLib, NativeFunction, ScriptedFunction};
|
||||||
use crate::parser::{FnDef, AST};
|
use crate::parser::{FnAccess, FnDef, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::{Position, Token};
|
||||||
use crate::token::Token;
|
use crate::utils::{StaticVec, EMPTY_TYPE_ID};
|
||||||
use crate::utils::StaticVec;
|
|
||||||
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt, mem,
|
fmt,
|
||||||
|
iter::{empty, repeat},
|
||||||
|
mem,
|
||||||
|
num::NonZeroUsize,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
vec,
|
||||||
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A trait that encapsulates a module resolution service.
|
/// A trait that encapsulates a module resolution service.
|
||||||
@ -28,6 +32,7 @@ pub trait ModuleResolver {
|
|||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
scope: Scope,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Module, Box<EvalAltResult>>;
|
) -> Result<Module, Box<EvalAltResult>>;
|
||||||
@ -44,18 +49,24 @@ type FuncReturn<T> = Result<T, Box<EvalAltResult>>;
|
|||||||
pub struct Module {
|
pub struct Module {
|
||||||
/// Sub-modules.
|
/// Sub-modules.
|
||||||
modules: HashMap<String, Module>,
|
modules: HashMap<String, Module>,
|
||||||
/// Module variables, including sub-modules.
|
|
||||||
|
/// Module variables.
|
||||||
variables: HashMap<String, Dynamic>,
|
variables: HashMap<String, Dynamic>,
|
||||||
|
|
||||||
|
/// Flattened collection of all module variables, including those in sub-modules.
|
||||||
|
all_variables: HashMap<u64, Dynamic>,
|
||||||
|
|
||||||
/// External Rust functions.
|
/// External Rust functions.
|
||||||
#[cfg(not(feature = "sync"))]
|
functions: HashMap<u64, (String, FnAccess, Vec<TypeId>, NativeFunction)>,
|
||||||
functions: HashMap<u64, Rc<Box<FnAny>>>,
|
|
||||||
/// External Rust functions.
|
/// Flattened collection of all external Rust functions, including those in sub-modules.
|
||||||
#[cfg(feature = "sync")]
|
all_functions: HashMap<u64, NativeFunction>,
|
||||||
functions: HashMap<u64, Arc<Box<FnAny>>>,
|
|
||||||
|
|
||||||
/// Script-defined functions.
|
/// Script-defined functions.
|
||||||
fn_lib: FunctionsLib,
|
fn_lib: FunctionsLib,
|
||||||
|
|
||||||
|
/// Flattened collection of all script-defined functions, including those in sub-modules.
|
||||||
|
all_fn_lib: FunctionsLib,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Module {
|
impl fmt::Debug for Module {
|
||||||
@ -113,7 +124,7 @@ impl Module {
|
|||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||||
self.get_var(name).and_then(|v| v.try_cast::<T>())
|
self.get_var(name).and_then(Dynamic::try_cast::<T>)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a module variable as a `Dynamic`.
|
/// Get a module variable as a `Dynamic`.
|
||||||
@ -131,11 +142,6 @@ impl Module {
|
|||||||
self.variables.get(name).cloned()
|
self.variables.get(name).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a module variable.
|
|
||||||
pub fn get_var_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
|
||||||
self.variables.get_mut(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set a variable into the module.
|
/// Set a variable into the module.
|
||||||
///
|
///
|
||||||
/// If there is an existing variable of the same name, it is replaced.
|
/// If there is an existing variable of the same name, it is replaced.
|
||||||
@ -154,16 +160,17 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a modules-qualified variable.
|
/// Get a mutable reference to a modules-qualified variable.
|
||||||
|
///
|
||||||
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
pub(crate) fn get_qualified_var_mut(
|
pub(crate) fn get_qualified_var_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
modules: &StaticVec<(String, Position)>,
|
hash: u64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
) -> Result<&mut Dynamic, Box<EvalAltResult>> {
|
||||||
Ok(self
|
self.all_variables
|
||||||
.get_qualified_module_mut(modules)?
|
.get_mut(&hash)
|
||||||
.get_var_mut(name)
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.to_string(), pos)))
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorVariableNotFound(name.into(), pos)))?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does a sub-module exist in the module?
|
/// Does a sub-module exist in the module?
|
||||||
@ -232,26 +239,6 @@ impl Module {
|
|||||||
self.modules.insert(name.into(), sub_module.into());
|
self.modules.insert(name.into(), sub_module.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable reference to a modules chain.
|
|
||||||
/// The first module is always skipped and assumed to be the same as `self`.
|
|
||||||
pub(crate) fn get_qualified_module_mut(
|
|
||||||
&mut self,
|
|
||||||
modules: &StaticVec<(String, Position)>,
|
|
||||||
) -> Result<&mut Module, Box<EvalAltResult>> {
|
|
||||||
let mut drain = modules.iter();
|
|
||||||
drain.next().unwrap(); // Skip first module
|
|
||||||
|
|
||||||
let mut module = self;
|
|
||||||
|
|
||||||
for (id, id_pos) in drain {
|
|
||||||
module = module
|
|
||||||
.get_sub_module_mut(id)
|
|
||||||
.ok_or_else(|| Box::new(EvalAltResult::ErrorModuleNotFound(id.into(), *id_pos)))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(module)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Does the particular Rust function exist in the module?
|
/// Does the particular Rust function exist in the module?
|
||||||
///
|
///
|
||||||
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
@ -273,13 +260,21 @@ impl Module {
|
|||||||
/// Set a Rust function into the module, returning a hash key.
|
/// Set a Rust function into the module, returning a hash key.
|
||||||
///
|
///
|
||||||
/// If there is an existing Rust function of the same hash, it is replaced.
|
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||||
pub fn set_fn(&mut self, fn_name: &str, params: &[TypeId], func: Box<FnAny>) -> u64 {
|
pub fn set_fn(
|
||||||
let hash = calc_fn_hash(fn_name, params.iter().cloned());
|
&mut self,
|
||||||
|
fn_name: String,
|
||||||
|
access: FnAccess,
|
||||||
|
params: Vec<TypeId>,
|
||||||
|
func: Box<FnAny>,
|
||||||
|
) -> u64 {
|
||||||
|
let hash = calc_fn_hash(empty(), &fn_name, params.iter().cloned());
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
self.functions.insert(hash, Rc::new(func));
|
self.functions
|
||||||
|
.insert(hash, (fn_name, access, params, Rc::new(func)));
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
self.functions.insert(hash, Arc::new(func));
|
self.functions
|
||||||
|
.insert(hash, (fn_name, access, params, Arc::new(func)));
|
||||||
|
|
||||||
hash
|
hash
|
||||||
}
|
}
|
||||||
@ -297,9 +292,9 @@ impl Module {
|
|||||||
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
/// let hash = module.set_fn_0("calc", || Ok(42_i64));
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_0<T: Into<Dynamic>>(
|
pub fn set_fn_0<K: Into<String>, T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn() -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn() -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
@ -308,8 +303,8 @@ impl Module {
|
|||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = &[];
|
let arg_types = vec![];
|
||||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
/// Set a Rust function taking one parameter into the module, returning a hash key.
|
||||||
@ -325,9 +320,9 @@ impl Module {
|
|||||||
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
/// let hash = module.set_fn_1("calc", |x: i64| Ok(x + 1));
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_1<A: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_1<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
@ -336,8 +331,8 @@ impl Module {
|
|||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = &[TypeId::of::<A>()];
|
let arg_types = vec![TypeId::of::<A>()];
|
||||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
/// Set a Rust function taking one mutable parameter into the module, returning a hash key.
|
||||||
@ -353,9 +348,9 @@ impl Module {
|
|||||||
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
/// let hash = module.set_fn_1_mut("calc", |x: &mut i64| { *x += 1; Ok(*x) });
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_1_mut<A: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_1_mut<K: Into<String>, A: Variant + Clone, T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
@ -364,8 +359,8 @@ impl Module {
|
|||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = &[TypeId::of::<A>()];
|
let arg_types = vec![TypeId::of::<A>()];
|
||||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
/// Set a Rust function taking two parameters into the module, returning a hash key.
|
||||||
@ -383,9 +378,9 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_2<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_2<K: Into<String>, A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
@ -397,8 +392,8 @@ impl Module {
|
|||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
/// Set a Rust function taking two parameters (the first one mutable) into the module,
|
||||||
@ -415,9 +410,14 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_2_mut<A: Variant + Clone, B: Variant + Clone, T: Into<Dynamic>>(
|
pub fn set_fn_2_mut<
|
||||||
|
K: Into<String>,
|
||||||
|
A: Variant + Clone,
|
||||||
|
B: Variant + Clone,
|
||||||
|
T: Into<Dynamic>,
|
||||||
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
@ -429,8 +429,8 @@ impl Module {
|
|||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>()];
|
||||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
/// Set a Rust function taking three parameters into the module, returning a hash key.
|
||||||
@ -449,13 +449,14 @@ impl Module {
|
|||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_3<
|
pub fn set_fn_3<
|
||||||
|
K: Into<String>,
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
C: Variant + Clone,
|
C: Variant + Clone,
|
||||||
T: Into<Dynamic>,
|
T: Into<Dynamic>,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(A, B, C) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
@ -468,8 +469,8 @@ impl Module {
|
|||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
/// Set a Rust function taking three parameters (the first one mutable) into the module,
|
||||||
@ -489,13 +490,14 @@ impl Module {
|
|||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn set_fn_3_mut<
|
pub fn set_fn_3_mut<
|
||||||
|
K: Into<String>,
|
||||||
A: Variant + Clone,
|
A: Variant + Clone,
|
||||||
B: Variant + Clone,
|
B: Variant + Clone,
|
||||||
C: Variant + Clone,
|
C: Variant + Clone,
|
||||||
T: Into<Dynamic>,
|
T: Into<Dynamic>,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: K,
|
||||||
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
#[cfg(not(feature = "sync"))] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + 'static,
|
||||||
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
#[cfg(feature = "sync")] func: impl Fn(&mut A, B, C) -> FuncReturn<T> + Send + Sync + 'static,
|
||||||
) -> u64 {
|
) -> u64 {
|
||||||
@ -508,8 +510,8 @@ impl Module {
|
|||||||
.map(|v| v.into())
|
.map(|v| v.into())
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
};
|
};
|
||||||
let arg_types = &[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
let arg_types = vec![TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()];
|
||||||
self.set_fn(fn_name, arg_types, Box::new(f))
|
self.set_fn(fn_name.into(), FnAccess::Public, arg_types, Box::new(f))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a Rust function.
|
/// Get a Rust function.
|
||||||
@ -527,7 +529,7 @@ impl Module {
|
|||||||
/// assert!(module.get_fn(hash).is_some());
|
/// assert!(module.get_fn(hash).is_some());
|
||||||
/// ```
|
/// ```
|
||||||
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
|
pub fn get_fn(&self, hash: u64) -> Option<&Box<FnAny>> {
|
||||||
self.functions.get(&hash).map(|v| v.as_ref())
|
self.functions.get(&hash).map(|(_, _, _, v)| v.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a modules-qualified function.
|
/// Get a modules-qualified function.
|
||||||
@ -538,24 +540,12 @@ impl Module {
|
|||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
modules: &StaticVec<(String, Position)>,
|
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<&Box<FnAny>, Box<EvalAltResult>> {
|
) -> Result<&Box<FnAny>, Box<EvalAltResult>> {
|
||||||
Ok(self
|
self.all_functions
|
||||||
.get_qualified_module_mut(modules)?
|
.get(&hash)
|
||||||
.get_fn(hash)
|
.map(|f| f.as_ref())
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))
|
||||||
let mut fn_name: String = Default::default();
|
|
||||||
|
|
||||||
modules.iter().for_each(|(n, _)| {
|
|
||||||
fn_name.push_str(n);
|
|
||||||
fn_name.push_str(Token::DoubleColon.syntax().as_ref());
|
|
||||||
});
|
|
||||||
|
|
||||||
fn_name.push_str(name);
|
|
||||||
|
|
||||||
Box::new(EvalAltResult::ErrorFunctionNotFound(fn_name, pos))
|
|
||||||
})?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the script-defined functions.
|
/// Get the script-defined functions.
|
||||||
@ -572,17 +562,11 @@ impl Module {
|
|||||||
&self.fn_lib
|
&self.fn_lib
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a modules-qualified functions library.
|
/// Get a modules-qualified script-defined functions.
|
||||||
pub(crate) fn get_qualified_fn_lib(
|
///
|
||||||
&mut self,
|
/// The `u64` hash is calculated by the function `crate::calc_fn_hash`.
|
||||||
name: &str,
|
pub(crate) fn get_qualified_scripted_fn(&mut self, hash: u64) -> Option<&FnDef> {
|
||||||
args: usize,
|
self.all_fn_lib.get_function(hash)
|
||||||
modules: &StaticVec<(String, Position)>,
|
|
||||||
) -> Result<Option<&FnDef>, Box<EvalAltResult>> {
|
|
||||||
Ok(self
|
|
||||||
.get_qualified_module_mut(modules)?
|
|
||||||
.fn_lib
|
|
||||||
.get_function(name, args))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new `Module` by evaluating an `AST`.
|
/// Create a new `Module` by evaluating an `AST`.
|
||||||
@ -591,20 +575,17 @@ 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 ast = engine.compile("let answer = 42;")?;
|
/// let ast = engine.compile("let answer = 42; export answer;")?;
|
||||||
/// let module = Module::eval_ast_as_new(&ast, &engine)?;
|
/// let module = Module::eval_ast_as_new(Scope::new(), &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(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn eval_ast_as_new(ast: &AST, engine: &Engine) -> FuncReturn<Self> {
|
pub fn eval_ast_as_new(mut scope: Scope, ast: &AST, engine: &Engine) -> FuncReturn<Self> {
|
||||||
// Use new scope
|
|
||||||
let mut scope = Scope::new();
|
|
||||||
|
|
||||||
// Run the script
|
// Run the script
|
||||||
engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
|
engine.eval_ast_with_scope_raw(&mut scope, &ast)?;
|
||||||
|
|
||||||
@ -613,19 +594,21 @@ impl Module {
|
|||||||
|
|
||||||
scope.into_iter().for_each(
|
scope.into_iter().for_each(
|
||||||
|ScopeEntry {
|
|ScopeEntry {
|
||||||
name, typ, value, ..
|
typ, value, alias, ..
|
||||||
}| {
|
}| {
|
||||||
match typ {
|
match typ {
|
||||||
// Variables left in the scope become module variables
|
// Variables with an alias left in the scope become module variables
|
||||||
ScopeEntryType::Normal | ScopeEntryType::Constant => {
|
ScopeEntryType::Normal | ScopeEntryType::Constant if alias.is_some() => {
|
||||||
module.variables.insert(name.into_owned(), value);
|
module.variables.insert(*alias.unwrap(), value);
|
||||||
}
|
}
|
||||||
// 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
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -634,6 +617,87 @@ impl Module {
|
|||||||
|
|
||||||
Ok(module)
|
Ok(module)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Scan through all the sub-modules in the `Module` build an index of all
|
||||||
|
/// variables and external Rust functions via hashing.
|
||||||
|
pub(crate) fn index_all_sub_modules(&mut self) {
|
||||||
|
// Collect a particular module.
|
||||||
|
fn index_module<'a>(
|
||||||
|
module: &'a mut Module,
|
||||||
|
qualifiers: &mut Vec<&'a str>,
|
||||||
|
variables: &mut Vec<(u64, Dynamic)>,
|
||||||
|
functions: &mut Vec<(u64, NativeFunction)>,
|
||||||
|
fn_lib: &mut Vec<(u64, ScriptedFunction)>,
|
||||||
|
) {
|
||||||
|
for (name, m) in module.modules.iter_mut() {
|
||||||
|
// Index all the sub-modules first.
|
||||||
|
qualifiers.push(name);
|
||||||
|
index_module(m, qualifiers, variables, functions, fn_lib);
|
||||||
|
qualifiers.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index all variables
|
||||||
|
for (var_name, value) in module.variables.iter() {
|
||||||
|
// Qualifiers + variable name
|
||||||
|
let hash = calc_fn_hash(qualifiers.iter().map(|v| *v), var_name, empty());
|
||||||
|
variables.push((hash, value.clone()));
|
||||||
|
}
|
||||||
|
// Index all Rust functions
|
||||||
|
for (fn_name, access, params, func) in module.functions.values() {
|
||||||
|
match access {
|
||||||
|
// Private functions are not exported
|
||||||
|
FnAccess::Private => continue,
|
||||||
|
FnAccess::Public => (),
|
||||||
|
}
|
||||||
|
// Rust functions are indexed in two steps:
|
||||||
|
// 1) Calculate a hash in a similar manner to script-defined functions,
|
||||||
|
// i.e. qualifiers + function name + dummy parameter types (one for each parameter).
|
||||||
|
let hash_fn_def = calc_fn_hash(
|
||||||
|
qualifiers.iter().map(|v| *v),
|
||||||
|
fn_name,
|
||||||
|
repeat(EMPTY_TYPE_ID()).take(params.len()),
|
||||||
|
);
|
||||||
|
// 2) Calculate a second hash with no qualifiers, empty function name, and
|
||||||
|
// the actual list of parameter `TypeId`'.s
|
||||||
|
let hash_fn_args = calc_fn_hash(empty(), "", params.iter().cloned());
|
||||||
|
// 3) The final hash is the XOR of the two hashes.
|
||||||
|
let hash = hash_fn_def ^ hash_fn_args;
|
||||||
|
|
||||||
|
functions.push((hash, func.clone()));
|
||||||
|
}
|
||||||
|
// Index all script-defined functions
|
||||||
|
for fn_def in module.fn_lib.values() {
|
||||||
|
match fn_def.access {
|
||||||
|
// Private functions are not exported
|
||||||
|
FnAccess::Private => continue,
|
||||||
|
FnAccess::Public => (),
|
||||||
|
}
|
||||||
|
// Qualifiers + function name + placeholders (one for each parameter)
|
||||||
|
let hash = calc_fn_hash(
|
||||||
|
qualifiers.iter().map(|v| *v),
|
||||||
|
&fn_def.name,
|
||||||
|
repeat(EMPTY_TYPE_ID()).take(fn_def.params.len()),
|
||||||
|
);
|
||||||
|
fn_lib.push((hash, fn_def.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut variables = Vec::new();
|
||||||
|
let mut functions = Vec::new();
|
||||||
|
let mut fn_lib = Vec::new();
|
||||||
|
|
||||||
|
index_module(
|
||||||
|
self,
|
||||||
|
&mut vec!["root"],
|
||||||
|
&mut variables,
|
||||||
|
&mut functions,
|
||||||
|
&mut fn_lib,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.all_variables = variables.into_iter().collect();
|
||||||
|
self.all_functions = functions.into_iter().collect();
|
||||||
|
self.all_fn_lib = fn_lib.into();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Re-export module resolvers.
|
/// Re-export module resolvers.
|
||||||
@ -669,12 +733,18 @@ mod file {
|
|||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
/// engine.set_module_resolver(Some(resolver));
|
/// engine.set_module_resolver(Some(resolver));
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct FileModuleResolver {
|
pub struct FileModuleResolver {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
extension: String,
|
extension: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for FileModuleResolver {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new_with_path(PathBuf::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FileModuleResolver {
|
impl FileModuleResolver {
|
||||||
/// Create a new `FileModuleResolver` with a specific base path.
|
/// Create a new `FileModuleResolver` with a specific base path.
|
||||||
///
|
///
|
||||||
@ -740,12 +810,23 @@ mod file {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a `Module` from a file path.
|
||||||
|
pub fn create_module<P: Into<PathBuf>>(
|
||||||
|
&self,
|
||||||
|
engine: &Engine,
|
||||||
|
scope: Scope,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
self.resolve(engine, scope, path, Default::default())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleResolver for FileModuleResolver {
|
impl ModuleResolver for FileModuleResolver {
|
||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
scope: Scope,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Module, Box<EvalAltResult>> {
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
@ -759,12 +840,70 @@ mod file {
|
|||||||
.compile_file(file_path)
|
.compile_file(file_path)
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))?;
|
.map_err(|err| EvalAltResult::set_position(err, pos))?;
|
||||||
|
|
||||||
Module::eval_ast_as_new(&ast, engine)
|
Module::eval_ast_as_new(scope, &ast, engine)
|
||||||
.map_err(|err| EvalAltResult::set_position(err, pos))
|
.map_err(|err| EvalAltResult::set_position(err, pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A chain of module names to qualify a variable or function call.
|
||||||
|
/// A `u64` hash key is kept for quick search purposes.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
#[derive(Clone, Hash, Default)]
|
||||||
|
pub struct ModuleRef(StaticVec<(String, Position)>, Option<NonZeroUsize>);
|
||||||
|
|
||||||
|
impl fmt::Debug for ModuleRef {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.0, f)?;
|
||||||
|
|
||||||
|
if let Some(index) = self.1 {
|
||||||
|
write!(f, " -> {}", index)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ModuleRef {
|
||||||
|
type Target = StaticVec<(String, Position)>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for ModuleRef {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ModuleRef {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
for (m, _) in self.0.iter() {
|
||||||
|
write!(f, "{}{}", m, Token::DoubleColon.syntax())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StaticVec<(String, Position)>> for ModuleRef {
|
||||||
|
fn from(modules: StaticVec<(String, Position)>) -> Self {
|
||||||
|
Self(modules, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleRef {
|
||||||
|
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
pub(crate) fn set_index(&mut self, index: Option<NonZeroUsize>) {
|
||||||
|
self.1 = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Static module resolver.
|
/// Static module resolver.
|
||||||
mod stat {
|
mod stat {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -828,6 +967,7 @@ mod stat {
|
|||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
_: &Engine,
|
_: &Engine,
|
||||||
|
_: Scope,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Module, Box<EvalAltResult>> {
|
) -> Result<Module, Box<EvalAltResult>> {
|
||||||
|
323
src/optimize.rs
323
src/optimize.rs
@ -4,7 +4,7 @@ use crate::engine::{
|
|||||||
Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
|
Engine, FnAny, FnCallArgs, FunctionsLib, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT,
|
||||||
KEYWORD_TYPE_OF,
|
KEYWORD_TYPE_OF,
|
||||||
};
|
};
|
||||||
use crate::packages::PackageLibrary;
|
use crate::packages::{PackageStore, PackagesCollection};
|
||||||
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
|
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
|
||||||
@ -13,6 +13,7 @@ use crate::token::Position;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
iter::empty,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -110,23 +111,18 @@ impl<'a> State<'a> {
|
|||||||
|
|
||||||
/// Call a registered function
|
/// Call a registered function
|
||||||
fn call_fn(
|
fn call_fn(
|
||||||
packages: &Vec<PackageLibrary>,
|
packages: &PackagesCollection,
|
||||||
functions: &HashMap<u64, Box<FnAny>>,
|
base_package: &PackageStore,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
) -> Result<Option<Dynamic>, Box<EvalAltResult>> {
|
||||||
// Search built-in's and external functions
|
// Search built-in's and external functions
|
||||||
let hash = calc_fn_hash(fn_name, args.iter().map(|a| a.type_id()));
|
let hash = calc_fn_hash(empty(), fn_name, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
functions
|
base_package
|
||||||
.get(&hash)
|
.get_function(hash)
|
||||||
.or_else(|| {
|
.or_else(|| packages.get_function(hash))
|
||||||
packages
|
|
||||||
.iter()
|
|
||||||
.find(|p| p.functions.contains_key(&hash))
|
|
||||||
.and_then(|p| p.functions.get(&hash))
|
|
||||||
})
|
|
||||||
.map(|func| func(args, pos))
|
.map(|func| func(args, pos))
|
||||||
.transpose()
|
.transpose()
|
||||||
}
|
}
|
||||||
@ -135,63 +131,63 @@ fn call_fn(
|
|||||||
fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -> Stmt {
|
fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -> Stmt {
|
||||||
match stmt {
|
match stmt {
|
||||||
// if expr { Noop }
|
// if expr { Noop }
|
||||||
Stmt::IfThenElse(expr, if_block, None) if matches!(*if_block, Stmt::Noop(_)) => {
|
Stmt::IfThenElse(x) if matches!(x.1, Stmt::Noop(_)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
let pos = expr.position();
|
let pos = x.0.position();
|
||||||
let expr = optimize_expr(*expr, state);
|
let expr = optimize_expr(x.0, state);
|
||||||
|
|
||||||
if preserve_result {
|
if preserve_result {
|
||||||
// -> { expr, Noop }
|
// -> { expr, Noop }
|
||||||
Stmt::Block(vec![Stmt::Expr(Box::new(expr)), *if_block], pos)
|
Stmt::Block(Box::new((vec![Stmt::Expr(Box::new(expr)), x.1], pos)))
|
||||||
} else {
|
} else {
|
||||||
// -> expr
|
// -> expr
|
||||||
Stmt::Expr(Box::new(expr))
|
Stmt::Expr(Box::new(expr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// if expr { if_block }
|
// if expr { if_block }
|
||||||
Stmt::IfThenElse(expr, if_block, None) => match *expr {
|
Stmt::IfThenElse(x) if x.2.is_none() => match x.0 {
|
||||||
// if false { if_block } -> Noop
|
// if false { if_block } -> Noop
|
||||||
Expr::False(pos) => {
|
Expr::False(pos) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Stmt::Noop(pos)
|
Stmt::Noop(pos)
|
||||||
}
|
}
|
||||||
// if true { if_block } -> if_block
|
// if true { if_block } -> if_block
|
||||||
Expr::True(_) => optimize_stmt(*if_block, state, true),
|
Expr::True(_) => optimize_stmt(x.1, state, true),
|
||||||
// if expr { if_block }
|
// if expr { if_block }
|
||||||
expr => Stmt::IfThenElse(
|
expr => Stmt::IfThenElse(Box::new((
|
||||||
Box::new(optimize_expr(expr, state)),
|
optimize_expr(expr, state),
|
||||||
Box::new(optimize_stmt(*if_block, state, true)),
|
optimize_stmt(x.1, state, true),
|
||||||
None,
|
None,
|
||||||
),
|
))),
|
||||||
},
|
},
|
||||||
// if expr { if_block } else { else_block }
|
// if expr { if_block } else { else_block }
|
||||||
Stmt::IfThenElse(expr, if_block, Some(else_block)) => match *expr {
|
Stmt::IfThenElse(x) if x.2.is_some() => match x.0 {
|
||||||
// if false { if_block } else { else_block } -> else_block
|
// if false { if_block } else { else_block } -> else_block
|
||||||
Expr::False(_) => optimize_stmt(*else_block, state, true),
|
Expr::False(_) => optimize_stmt(x.2.unwrap(), state, true),
|
||||||
// if true { if_block } else { else_block } -> if_block
|
// if true { if_block } else { else_block } -> if_block
|
||||||
Expr::True(_) => optimize_stmt(*if_block, state, true),
|
Expr::True(_) => optimize_stmt(x.1, state, true),
|
||||||
// if expr { if_block } else { else_block }
|
// if expr { if_block } else { else_block }
|
||||||
expr => Stmt::IfThenElse(
|
expr => Stmt::IfThenElse(Box::new((
|
||||||
Box::new(optimize_expr(expr, state)),
|
optimize_expr(expr, state),
|
||||||
Box::new(optimize_stmt(*if_block, state, true)),
|
optimize_stmt(x.1, state, true),
|
||||||
match optimize_stmt(*else_block, state, true) {
|
match optimize_stmt(x.2.unwrap(), state, true) {
|
||||||
Stmt::Noop(_) => None, // Noop -> no else block
|
Stmt::Noop(_) => None, // Noop -> no else block
|
||||||
stmt => Some(Box::new(stmt)),
|
stmt => Some(stmt),
|
||||||
},
|
},
|
||||||
),
|
))),
|
||||||
},
|
},
|
||||||
// while expr { block }
|
// while expr { block }
|
||||||
Stmt::While(expr, block) => match *expr {
|
Stmt::While(x) => match x.0 {
|
||||||
// while false { block } -> Noop
|
// while false { block } -> Noop
|
||||||
Expr::False(pos) => {
|
Expr::False(pos) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Stmt::Noop(pos)
|
Stmt::Noop(pos)
|
||||||
}
|
}
|
||||||
// while true { block } -> loop { block }
|
// while true { block } -> loop { block }
|
||||||
Expr::True(_) => Stmt::Loop(Box::new(optimize_stmt(*block, state, false))),
|
Expr::True(_) => Stmt::Loop(Box::new(optimize_stmt(x.1, state, false))),
|
||||||
// while expr { block }
|
// while expr { block }
|
||||||
expr => match optimize_stmt(*block, state, false) {
|
expr => match optimize_stmt(x.1, state, false) {
|
||||||
// while expr { break; } -> { expr; }
|
// while expr { break; } -> { expr; }
|
||||||
Stmt::Break(pos) => {
|
Stmt::Break(pos) => {
|
||||||
// Only a single break statement - turn into running the guard expression once
|
// Only a single break statement - turn into running the guard expression once
|
||||||
@ -200,10 +196,10 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
if preserve_result {
|
if preserve_result {
|
||||||
statements.push(Stmt::Noop(pos))
|
statements.push(Stmt::Noop(pos))
|
||||||
}
|
}
|
||||||
Stmt::Block(statements, pos)
|
Stmt::Block(Box::new((statements, pos)))
|
||||||
}
|
}
|
||||||
// while expr { block }
|
// while expr { block }
|
||||||
stmt => Stmt::While(Box::new(optimize_expr(expr, state)), Box::new(stmt)),
|
stmt => Stmt::While(Box::new((optimize_expr(expr, state), stmt))),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// loop { block }
|
// loop { block }
|
||||||
@ -218,31 +214,33 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
stmt => Stmt::Loop(Box::new(stmt)),
|
stmt => Stmt::Loop(Box::new(stmt)),
|
||||||
},
|
},
|
||||||
// for id in expr { block }
|
// for id in expr { block }
|
||||||
Stmt::For(id, expr, block) => Stmt::For(
|
Stmt::For(x) => Stmt::For(Box::new((
|
||||||
id,
|
x.0,
|
||||||
Box::new(optimize_expr(*expr, state)),
|
optimize_expr(x.1, state),
|
||||||
Box::new(optimize_stmt(*block, state, false)),
|
optimize_stmt(x.2, state, false),
|
||||||
),
|
))),
|
||||||
// let id = expr;
|
// let id = expr;
|
||||||
Stmt::Let(id, Some(expr), pos) => {
|
Stmt::Let(x) if x.1.is_some() => {
|
||||||
Stmt::Let(id, Some(Box::new(optimize_expr(*expr, state))), pos)
|
Stmt::Let(Box::new((x.0, Some(optimize_expr(x.1.unwrap(), state)))))
|
||||||
}
|
}
|
||||||
// let id;
|
// let id;
|
||||||
Stmt::Let(_, None, _) => stmt,
|
stmt @ Stmt::Let(_) => stmt,
|
||||||
// import expr as id;
|
// import expr as id;
|
||||||
Stmt::Import(expr, id, pos) => Stmt::Import(Box::new(optimize_expr(*expr, state)), id, pos),
|
Stmt::Import(x) => Stmt::Import(Box::new((optimize_expr(x.0, state), x.1))),
|
||||||
// { block }
|
// { block }
|
||||||
Stmt::Block(block, pos) => {
|
Stmt::Block(x) => {
|
||||||
let orig_len = block.len(); // Original number of statements in the block, for change detection
|
let orig_len = x.0.len(); // Original number of statements in the block, for change detection
|
||||||
let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later
|
let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later
|
||||||
|
let pos = x.1;
|
||||||
|
|
||||||
// Optimize each statement in the block
|
// Optimize each statement in the block
|
||||||
let mut result: Vec<_> = block
|
let mut result: Vec<_> =
|
||||||
.into_iter()
|
x.0.into_iter()
|
||||||
.map(|stmt| match stmt {
|
.map(|stmt| match stmt {
|
||||||
// Add constant into the state
|
// Add constant into the state
|
||||||
Stmt::Const(name, value, pos) => {
|
Stmt::Const(v) => {
|
||||||
state.push_constant(&name, *value);
|
let ((name, pos), expr) = *v;
|
||||||
|
state.push_constant(&name, expr);
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Stmt::Noop(pos) // No need to keep constants
|
Stmt::Noop(pos) // No need to keep constants
|
||||||
}
|
}
|
||||||
@ -267,9 +265,9 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
|
|
||||||
while let Some(expr) = result.pop() {
|
while let Some(expr) = result.pop() {
|
||||||
match expr {
|
match expr {
|
||||||
Stmt::Let(_, None, _) => removed = true,
|
Stmt::Let(x) if x.1.is_none() => removed = true,
|
||||||
Stmt::Let(_, Some(val_expr), _) => removed = val_expr.is_pure(),
|
Stmt::Let(x) if x.1.is_some() => removed = x.1.unwrap().is_pure(),
|
||||||
Stmt::Import(expr, _, _) => removed = expr.is_pure(),
|
Stmt::Import(x) => removed = x.0.is_pure(),
|
||||||
_ => {
|
_ => {
|
||||||
result.push(expr);
|
result.push(expr);
|
||||||
break;
|
break;
|
||||||
@ -301,7 +299,7 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
match stmt {
|
match stmt {
|
||||||
Stmt::ReturnWithVal(_, _, _) | Stmt::Break(_) => {
|
Stmt::ReturnWithVal(_) | Stmt::Break(_) => {
|
||||||
dead_code = true;
|
dead_code = true;
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -325,20 +323,20 @@ fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -
|
|||||||
Stmt::Noop(pos)
|
Stmt::Noop(pos)
|
||||||
}
|
}
|
||||||
// Only one let/import statement - leave it alone
|
// Only one let/import statement - leave it alone
|
||||||
[Stmt::Let(_, _, _)] | [Stmt::Import(_, _, _)] => Stmt::Block(result, pos),
|
[Stmt::Let(_)] | [Stmt::Import(_)] => Stmt::Block(Box::new((result, pos))),
|
||||||
// Only one statement - promote
|
// Only one statement - promote
|
||||||
[_] => {
|
[_] => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
result.remove(0)
|
result.remove(0)
|
||||||
}
|
}
|
||||||
_ => Stmt::Block(result, pos),
|
_ => Stmt::Block(Box::new((result, pos))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// expr;
|
// expr;
|
||||||
Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))),
|
Stmt::Expr(expr) => Stmt::Expr(Box::new(optimize_expr(*expr, state))),
|
||||||
// return expr;
|
// return expr;
|
||||||
Stmt::ReturnWithVal(Some(expr), is_return, pos) => {
|
Stmt::ReturnWithVal(x) if x.1.is_some() => {
|
||||||
Stmt::ReturnWithVal(Some(Box::new(optimize_expr(*expr, state))), is_return, pos)
|
Stmt::ReturnWithVal(Box::new((x.0, Some(optimize_expr(x.1.unwrap(), state)))))
|
||||||
}
|
}
|
||||||
// All other statements - skip
|
// All other statements - skip
|
||||||
stmt => stmt,
|
stmt => stmt,
|
||||||
@ -352,11 +350,11 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
// ( stmt )
|
// ( stmt )
|
||||||
Expr::Stmt(stmt, pos) => match optimize_stmt(*stmt, state, true) {
|
Expr::Stmt(x) => match optimize_stmt(x.0, state, true) {
|
||||||
// ( Noop ) -> ()
|
// ( Noop ) -> ()
|
||||||
Stmt::Noop(_) => {
|
Stmt::Noop(_) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Expr::Unit(pos)
|
Expr::Unit(x.1)
|
||||||
}
|
}
|
||||||
// ( expr ) -> expr
|
// ( expr ) -> expr
|
||||||
Stmt::Expr(expr) => {
|
Stmt::Expr(expr) => {
|
||||||
@ -364,150 +362,128 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
*expr
|
*expr
|
||||||
}
|
}
|
||||||
// ( stmt )
|
// ( stmt )
|
||||||
stmt => Expr::Stmt(Box::new(stmt), pos),
|
stmt => Expr::Stmt(Box::new((stmt, x.1))),
|
||||||
},
|
},
|
||||||
// id = expr
|
// id = expr
|
||||||
Expr::Assignment(id, expr, pos) => match *expr {
|
Expr::Assignment(x) => match x.1 {
|
||||||
//id = id2 = expr2
|
//id = id2 = expr2
|
||||||
Expr::Assignment(id2, expr2, pos2) => match (*id, *id2) {
|
Expr::Assignment(x2) => match (x.0, x2.0) {
|
||||||
// var = var = expr2 -> var = expr2
|
// var = var = expr2 -> var = expr2
|
||||||
(Expr::Variable(var, None, sp, _), Expr::Variable(var2, None, sp2, _))
|
(Expr::Variable(a), Expr::Variable(b))
|
||||||
if var == var2 && sp == sp2 =>
|
if a.1.is_none() && b.1.is_none() && a.0 == b.0 && a.3 == b.3 =>
|
||||||
{
|
{
|
||||||
// Assignment to the same variable - fold
|
// Assignment to the same variable - fold
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Expr::Assignment(Box::new(Expr::Variable(var, None, sp, pos)), Box::new(optimize_expr(*expr2, state)), pos)
|
Expr::Assignment(Box::new((Expr::Variable(a), optimize_expr(x2.1, state), x.2)))
|
||||||
}
|
}
|
||||||
// id1 = id2 = expr2
|
// id1 = id2 = expr2
|
||||||
(id1, id2) => Expr::Assignment(
|
(id1, id2) => {
|
||||||
Box::new(id1),
|
Expr::Assignment(Box::new((
|
||||||
Box::new(Expr::Assignment(
|
id1, Expr::Assignment(Box::new((id2, optimize_expr(x2.1, state), x2.2))), x.2,
|
||||||
Box::new(id2),
|
)))
|
||||||
Box::new(optimize_expr(*expr2, state)),
|
}
|
||||||
pos2,
|
|
||||||
)),
|
|
||||||
pos,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
// id = expr
|
// id = expr
|
||||||
expr => Expr::Assignment(id, Box::new(optimize_expr(expr, state)), pos),
|
expr => Expr::Assignment(Box::new((x.0, optimize_expr(expr, state), x.2))),
|
||||||
},
|
},
|
||||||
|
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(lhs, rhs, pos) => match (*lhs, *rhs) {
|
Expr::Dot(x) => match (x.0, x.1) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(items, pos), Expr::Property(s, _)) if items.iter().all(|(_, x, _)| x.is_pure()) => {
|
(Expr::Map(m), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
items.into_iter().find(|(name, _, _)| name == &s)
|
let pos = m.1;
|
||||||
.map(|(_, expr, _)| expr.set_position(pos))
|
m.0.into_iter().find(|((name, _), _)| name == &p.0)
|
||||||
|
.map(|(_, expr)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// lhs.rhs
|
// lhs.rhs
|
||||||
(lhs, rhs) => Expr::Dot(
|
(lhs, rhs) => Expr::Dot(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2)))
|
||||||
Box::new(optimize_expr(lhs, state)),
|
|
||||||
Box::new(optimize_expr(rhs, state)),
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lhs[rhs]
|
// lhs[rhs]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(lhs, rhs, pos) => match (*lhs, *rhs) {
|
Expr::Index(x) => match (x.0, x.1) {
|
||||||
// array[int]
|
// array[int]
|
||||||
(Expr::Array(mut items, pos), Expr::IntegerConstant(i, _))
|
(Expr::Array(mut a), Expr::IntegerConstant(i))
|
||||||
if i >= 0 && (i as usize) < items.len() && items.iter().all(Expr::is_pure) =>
|
if i.0 >= 0 && (i.0 as usize) < a.0.len() && a.0.iter().all(Expr::is_pure) =>
|
||||||
{
|
{
|
||||||
// Array literal where everything is pure - promote the indexed item.
|
// Array literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
items.remove(i as usize).set_position(pos)
|
a.0.remove(i.0 as usize).set_position(a.1)
|
||||||
}
|
}
|
||||||
// map[string]
|
// map[string]
|
||||||
(Expr::Map(items, pos), Expr::StringConstant(s, _)) if items.iter().all(|(_, x, _)| x.is_pure()) => {
|
(Expr::Map(m), Expr::StringConstant(s)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
items.into_iter().find(|(name, _, _)| name == &s)
|
let pos = m.1;
|
||||||
.map(|(_, expr, _)| expr.set_position(pos))
|
m.0.into_iter().find(|((name, _), _)| name == &s.0)
|
||||||
|
.map(|(_, expr)| expr.set_position(pos))
|
||||||
.unwrap_or_else(|| Expr::Unit(pos))
|
.unwrap_or_else(|| Expr::Unit(pos))
|
||||||
}
|
}
|
||||||
// string[int]
|
// string[int]
|
||||||
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if i >= 0 && (i as usize) < s.chars().count() => {
|
(Expr::StringConstant(s), Expr::IntegerConstant(i)) if i.0 >= 0 && (i.0 as usize) < s.0.chars().count() => {
|
||||||
// String literal indexing - get the character
|
// String literal indexing - get the character
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
Expr::CharConstant(s.chars().nth(i as usize).expect("should get char"), pos)
|
Expr::CharConstant(Box::new((s.0.chars().nth(i.0 as usize).expect("should get char"), s.1)))
|
||||||
}
|
}
|
||||||
// lhs[rhs]
|
// lhs[rhs]
|
||||||
(lhs, rhs) => Expr::Index(
|
(lhs, rhs) => Expr::Index(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
|
||||||
Box::new(optimize_expr(lhs, state)),
|
|
||||||
Box::new(optimize_expr(rhs, state)),
|
|
||||||
pos,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Array(items, pos) => Expr::Array(items
|
Expr::Array(a) => Expr::Array(Box::new((a.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|expr| optimize_expr(expr, state))
|
.map(|expr| optimize_expr(expr, state))
|
||||||
.collect(), pos),
|
.collect(), a.1))),
|
||||||
// [ items .. ]
|
// [ items .. ]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Map(items, pos) => Expr::Map(items
|
Expr::Map(m) => Expr::Map(Box::new((m.0
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(key, expr, pos)| (key, optimize_expr(expr, state), pos))
|
.map(|((key, pos), expr)| ((key, pos), optimize_expr(expr, state)))
|
||||||
.collect(), pos),
|
.collect(), m.1))),
|
||||||
// lhs in rhs
|
// lhs in rhs
|
||||||
Expr::In(lhs, rhs, pos) => match (*lhs, *rhs) {
|
Expr::In(x) => match (x.0, x.1) {
|
||||||
// "xxx" in "xxxxx"
|
// "xxx" in "xxxxx"
|
||||||
(Expr::StringConstant(lhs, pos), Expr::StringConstant(rhs, _)) => {
|
(Expr::StringConstant(a), Expr::StringConstant(b)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if rhs.contains(&lhs) {
|
if b.0.contains(&a.0) { Expr::True(a.1) } else { Expr::False(a.1) }
|
||||||
Expr::True(pos)
|
|
||||||
} else {
|
|
||||||
Expr::False(pos)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 'x' in "xxxxx"
|
// 'x' in "xxxxx"
|
||||||
(Expr::CharConstant(lhs, pos), Expr::StringConstant(rhs, _)) => {
|
(Expr::CharConstant(a), Expr::StringConstant(b)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if rhs.contains(&lhs.to_string()) {
|
if b.0.contains(a.0) { Expr::True(a.1) } else { Expr::False(a.1) }
|
||||||
Expr::True(pos)
|
|
||||||
} else {
|
|
||||||
Expr::False(pos)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// "xxx" in #{...}
|
// "xxx" in #{...}
|
||||||
(Expr::StringConstant(lhs, pos), Expr::Map(items, _)) => {
|
(Expr::StringConstant(a), Expr::Map(b)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
if items.iter().find(|(name, _, _)| name == &lhs).is_some() {
|
if b.0.iter().find(|((name, _), _)| name == &a.0).is_some() {
|
||||||
Expr::True(pos)
|
Expr::True(a.1)
|
||||||
} else {
|
} else {
|
||||||
Expr::False(pos)
|
Expr::False(a.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 'x' in #{...}
|
// 'x' in #{...}
|
||||||
(Expr::CharConstant(lhs, pos), Expr::Map(items, _)) => {
|
(Expr::CharConstant(a), Expr::Map(b)) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
let lhs = lhs.to_string();
|
let ch = a.0.to_string();
|
||||||
|
|
||||||
if items.iter().find(|(name, _, _)| name == &lhs).is_some() {
|
if b.0.iter().find(|((name, _), _)| name == &ch).is_some() {
|
||||||
Expr::True(pos)
|
Expr::True(a.1)
|
||||||
} else {
|
} else {
|
||||||
Expr::False(pos)
|
Expr::False(a.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// lhs in rhs
|
// lhs in rhs
|
||||||
(lhs, rhs) => Expr::In(
|
(lhs, rhs) => Expr::In(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
|
||||||
Box::new(optimize_expr(lhs, state)),
|
|
||||||
Box::new(optimize_expr(rhs, state)),
|
|
||||||
pos
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
// lhs && rhs
|
// lhs && rhs
|
||||||
Expr::And(lhs, rhs, pos) => match (*lhs, *rhs) {
|
Expr::And(x) => match (x.0, x.1) {
|
||||||
// true && rhs -> rhs
|
// true && rhs -> rhs
|
||||||
(Expr::True(_), rhs) => {
|
(Expr::True(_), rhs) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
@ -524,14 +500,10 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
optimize_expr(lhs, state)
|
optimize_expr(lhs, state)
|
||||||
}
|
}
|
||||||
// lhs && rhs
|
// lhs && rhs
|
||||||
(lhs, rhs) => Expr::And(
|
(lhs, rhs) => Expr::And(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
|
||||||
Box::new(optimize_expr(lhs, state)),
|
|
||||||
Box::new(optimize_expr(rhs, state)),
|
|
||||||
pos
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
// lhs || rhs
|
// lhs || rhs
|
||||||
Expr::Or(lhs, rhs, pos) => match (*lhs, *rhs) {
|
Expr::Or(x) => match (x.0, x.1) {
|
||||||
// false || rhs -> rhs
|
// false || rhs -> rhs
|
||||||
(Expr::False(_), rhs) => {
|
(Expr::False(_), rhs) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
@ -548,22 +520,28 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
optimize_expr(lhs, state)
|
optimize_expr(lhs, state)
|
||||||
}
|
}
|
||||||
// lhs || rhs
|
// lhs || rhs
|
||||||
(lhs, rhs) => Expr::Or(Box::new(optimize_expr(lhs, state)), Box::new(optimize_expr(rhs, state)), pos),
|
(lhs, rhs) => Expr::Or(Box::new((optimize_expr(lhs, state), optimize_expr(rhs, state), x.2))),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FnCall(id, None, args, def_value, pos) if DONT_EVAL_KEYWORDS.contains(&id.as_ref().as_ref())=>
|
Expr::FnCall(mut x) if DONT_EVAL_KEYWORDS.contains(&(x.0).0.as_ref())=> {
|
||||||
Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
|
Expr::FnCall(x)
|
||||||
|
}
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
Expr::FnCall(id, None, args, def_value, pos)
|
Expr::FnCall(mut x)
|
||||||
if state.optimization_level == OptimizationLevel::Full // full optimizations
|
if x.1.is_none() // Non-qualified
|
||||||
&& args.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
&& state.optimization_level == OptimizationLevel::Full // full optimizations
|
||||||
|
&& x.3.iter().all(|expr| expr.is_constant()) // all arguments are constants
|
||||||
=> {
|
=> {
|
||||||
|
let ((name, pos), _, _, args, def_value) = x.as_mut();
|
||||||
|
|
||||||
// First search in script-defined functions (can override built-in)
|
// First search in script-defined functions (can override built-in)
|
||||||
if state.fn_lib.iter().find(|(name, len)| name == id.as_ref() && *len == args.len()).is_some() {
|
if state.fn_lib.iter().find(|(id, len)| *id == name && *len == args.len()).is_some() {
|
||||||
// A script-defined function overrides the built-in function - do not make the call
|
// A script-defined function overrides the built-in function - do not make the call
|
||||||
return Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos);
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
|
return Expr::FnCall(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
let mut arg_values: Vec<_> = args.iter().map(Expr::get_constant_value).collect();
|
||||||
@ -571,13 +549,13 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
|
|
||||||
// Save the typename of the first argument if it is `type_of()`
|
// Save the typename of the first argument if it is `type_of()`
|
||||||
// This is to avoid `call_args` being passed into the closure
|
// This is to avoid `call_args` being passed into the closure
|
||||||
let arg_for_type_of = if *id == KEYWORD_TYPE_OF && call_args.len() == 1 {
|
let arg_for_type_of = if name == KEYWORD_TYPE_OF && call_args.len() == 1 {
|
||||||
state.engine.map_type_name(call_args[0].type_name())
|
state.engine.map_type_name(call_args[0].type_name())
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
|
|
||||||
call_fn(&state.engine.packages, &state.engine.functions, &id, &mut call_args, pos).ok()
|
call_fn(&state.engine.packages, &state.engine.base_package, name, &mut call_args, *pos).ok()
|
||||||
.and_then(|result|
|
.and_then(|result|
|
||||||
result.or_else(|| {
|
result.or_else(|| {
|
||||||
if !arg_for_type_of.is_empty() {
|
if !arg_for_type_of.is_empty() {
|
||||||
@ -585,25 +563,29 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
|
|||||||
Some(arg_for_type_of.to_string().into())
|
Some(arg_for_type_of.to_string().into())
|
||||||
} else {
|
} else {
|
||||||
// Otherwise use the default value, if any
|
// Otherwise use the default value, if any
|
||||||
def_value.clone().map(|v| *v)
|
def_value.clone()
|
||||||
}
|
}
|
||||||
}).and_then(|result| map_dynamic_to_expr(result, pos))
|
}).and_then(|result| map_dynamic_to_expr(result, *pos))
|
||||||
.map(|expr| {
|
.map(|expr| {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
expr
|
expr
|
||||||
})
|
})
|
||||||
).unwrap_or_else(||
|
).unwrap_or_else(|| {
|
||||||
// Optimize function call arguments
|
// Optimize function call arguments
|
||||||
Expr::FnCall(id, None, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos)
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
)
|
Expr::FnCall(x)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// id(args ..) -> optimize function call arguments
|
// id(args ..) -> optimize function call arguments
|
||||||
Expr::FnCall(id, modules, args, def_value, pos) =>
|
Expr::FnCall(mut x) => {
|
||||||
Expr::FnCall(id, modules, Box::new(args.into_iter().map(|a| optimize_expr(a, state)).collect()), def_value, pos),
|
x.3 = x.3.into_iter().map(|a| optimize_expr(a, state)).collect();
|
||||||
|
Expr::FnCall(x)
|
||||||
|
}
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(name, None, _, pos) if state.contains_constant(&name) => {
|
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&(x.0).0) => {
|
||||||
|
let (name, pos) = x.0;
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
@ -660,17 +642,18 @@ fn optimize<'a>(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, stmt)| {
|
.map(|(i, stmt)| {
|
||||||
match stmt {
|
match &stmt {
|
||||||
Stmt::Const(ref name, ref value, _) => {
|
Stmt::Const(v) => {
|
||||||
// Load constants
|
// Load constants
|
||||||
state.push_constant(name.as_ref(), value.as_ref().clone());
|
let ((name, _), expr) = v.as_ref();
|
||||||
|
state.push_constant(&name, expr.clone());
|
||||||
stmt // Keep it in the global scope
|
stmt // Keep it in the global scope
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Keep all variable declarations at this level
|
// Keep all variable declarations at this level
|
||||||
// and always keep the last return value
|
// and always keep the last return value
|
||||||
let keep = match stmt {
|
let keep = match stmt {
|
||||||
Stmt::Let(_, _, _) | Stmt::Import(_, _, _) => true,
|
Stmt::Let(_) | Stmt::Import(_) => true,
|
||||||
_ => i == num_statements - 1,
|
_ => i == num_statements - 1,
|
||||||
};
|
};
|
||||||
optimize_stmt(stmt, &mut state, keep)
|
optimize_stmt(stmt, &mut state, keep)
|
||||||
@ -731,19 +714,25 @@ pub fn optimize_into_ast(
|
|||||||
|
|
||||||
// Optimize the function body
|
// Optimize the function body
|
||||||
let mut body =
|
let mut body =
|
||||||
optimize(vec![*fn_def.body], engine, &Scope::new(), &fn_lib, level);
|
optimize(vec![fn_def.body], engine, &Scope::new(), &fn_lib, level);
|
||||||
|
|
||||||
// {} -> Noop
|
// {} -> Noop
|
||||||
fn_def.body = Box::new(match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
fn_def.body = match body.pop().unwrap_or_else(|| Stmt::Noop(pos)) {
|
||||||
// { return val; } -> val
|
// { return val; } -> val
|
||||||
Stmt::ReturnWithVal(Some(val), ReturnType::Return, _) => Stmt::Expr(val),
|
Stmt::ReturnWithVal(x)
|
||||||
|
if x.1.is_some() && (x.0).0 == ReturnType::Return =>
|
||||||
|
{
|
||||||
|
Stmt::Expr(Box::new(x.1.unwrap()))
|
||||||
|
}
|
||||||
// { return; } -> ()
|
// { return; } -> ()
|
||||||
Stmt::ReturnWithVal(None, ReturnType::Return, pos) => {
|
Stmt::ReturnWithVal(x)
|
||||||
Stmt::Expr(Box::new(Expr::Unit(pos)))
|
if x.1.is_none() && (x.0).0 == ReturnType::Return =>
|
||||||
|
{
|
||||||
|
Stmt::Expr(Box::new(Expr::Unit((x.0).1)))
|
||||||
}
|
}
|
||||||
// All others
|
// All others
|
||||||
stmt => stmt,
|
stmt => stmt,
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
fn_def
|
fn_def
|
||||||
})
|
})
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::engine::{FnAny, IteratorFn};
|
use crate::engine::{FnAny, IteratorFn};
|
||||||
|
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc};
|
use crate::stdlib::{any::TypeId, boxed::Box, collections::HashMap, rc::Rc, sync::Arc, vec::Vec};
|
||||||
|
|
||||||
mod arithmetic;
|
mod arithmetic;
|
||||||
mod array_basic;
|
mod array_basic;
|
||||||
@ -34,11 +34,10 @@ pub use time_basic::BasicTimePackage;
|
|||||||
|
|
||||||
pub use utils::*;
|
pub use utils::*;
|
||||||
|
|
||||||
|
const NUM_NATIVE_FUNCTIONS: usize = 512;
|
||||||
|
|
||||||
/// Trait that all packages must implement.
|
/// Trait that all packages must implement.
|
||||||
pub trait Package {
|
pub trait Package {
|
||||||
/// Create a new instance of a package.
|
|
||||||
fn new() -> Self;
|
|
||||||
|
|
||||||
/// Register all the functions in a package into a store.
|
/// Register all the functions in a package into a store.
|
||||||
fn init(lib: &mut PackageStore);
|
fn init(lib: &mut PackageStore);
|
||||||
|
|
||||||
@ -47,7 +46,6 @@ pub trait Package {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Type to store all functions in the package.
|
/// Type to store all functions in the package.
|
||||||
#[derive(Default)]
|
|
||||||
pub struct PackageStore {
|
pub struct PackageStore {
|
||||||
/// All functions, keyed by a hash created from the function name and parameter types.
|
/// All functions, keyed by a hash created from the function name and parameter types.
|
||||||
pub functions: HashMap<u64, Box<FnAny>>,
|
pub functions: HashMap<u64, Box<FnAny>>,
|
||||||
@ -61,6 +59,31 @@ impl PackageStore {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
/// Does the specified function hash key exist in the `PackageStore`?
|
||||||
|
pub fn contains_function(&self, hash: u64) -> bool {
|
||||||
|
self.functions.contains_key(&hash)
|
||||||
|
}
|
||||||
|
/// Get specified function via its hash key.
|
||||||
|
pub fn get_function(&self, hash: u64) -> Option<&Box<FnAny>> {
|
||||||
|
self.functions.get(&hash)
|
||||||
|
}
|
||||||
|
/// Does the specified TypeId iterator exist in the `PackageStore`?
|
||||||
|
pub fn contains_iterator(&self, id: TypeId) -> bool {
|
||||||
|
self.type_iterators.contains_key(&id)
|
||||||
|
}
|
||||||
|
/// Get the specified TypeId iterator.
|
||||||
|
pub fn get_iterator(&self, id: TypeId) -> Option<&Box<IteratorFn>> {
|
||||||
|
self.type_iterators.get(&id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PackageStore {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
functions: HashMap::with_capacity(NUM_NATIVE_FUNCTIONS),
|
||||||
|
type_iterators: HashMap::with_capacity(4),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type which `Rc`-wraps a `PackageStore` to facilitate sharing library instances.
|
/// Type which `Rc`-wraps a `PackageStore` to facilitate sharing library instances.
|
||||||
@ -70,3 +93,43 @@ pub type PackageLibrary = Rc<PackageStore>;
|
|||||||
/// Type which `Arc`-wraps a `PackageStore` to facilitate sharing library instances.
|
/// Type which `Arc`-wraps a `PackageStore` to facilitate sharing library instances.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type PackageLibrary = Arc<PackageStore>;
|
pub type PackageLibrary = Arc<PackageStore>;
|
||||||
|
|
||||||
|
/// Type containing a collection of `PackageLibrary` instances.
|
||||||
|
/// All function and type iterator keys in the loaded packages are indexed for fast access.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub(crate) struct PackagesCollection {
|
||||||
|
/// Collection of `PackageLibrary` instances.
|
||||||
|
packages: Vec<PackageLibrary>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackagesCollection {
|
||||||
|
/// Add a `PackageLibrary` into the `PackagesCollection`.
|
||||||
|
pub fn push(&mut self, package: PackageLibrary) {
|
||||||
|
// Later packages override previous ones.
|
||||||
|
self.packages.insert(0, package);
|
||||||
|
}
|
||||||
|
/// Does the specified function hash key exist in the `PackagesCollection`?
|
||||||
|
pub fn contains_function(&self, hash: u64) -> bool {
|
||||||
|
self.packages.iter().any(|p| p.contains_function(hash))
|
||||||
|
}
|
||||||
|
/// Get specified function via its hash key.
|
||||||
|
pub fn get_function(&self, hash: u64) -> Option<&Box<FnAny>> {
|
||||||
|
self.packages
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.get_function(hash))
|
||||||
|
.find(|f| f.is_some())
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
/// Does the specified TypeId iterator exist in the `PackagesCollection`?
|
||||||
|
pub fn contains_iterator(&self, id: TypeId) -> bool {
|
||||||
|
self.packages.iter().any(|p| p.contains_iterator(id))
|
||||||
|
}
|
||||||
|
/// Get the specified TypeId iterator.
|
||||||
|
pub fn get_iterator(&self, id: TypeId) -> Option<&Box<IteratorFn>> {
|
||||||
|
self.packages
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.get_iterator(id))
|
||||||
|
.find(|f| f.is_some())
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,8 @@ use crate::token::Position;
|
|||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
|
iter::empty,
|
||||||
|
mem,
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,12 +44,6 @@ macro_rules! def_package {
|
|||||||
pub struct $package($root::packages::PackageLibrary);
|
pub struct $package($root::packages::PackageLibrary);
|
||||||
|
|
||||||
impl $root::packages::Package for $package {
|
impl $root::packages::Package for $package {
|
||||||
fn new() -> Self {
|
|
||||||
let mut pkg = $root::packages::PackageStore::new();
|
|
||||||
Self::init(&mut pkg);
|
|
||||||
Self(pkg.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get(&self) -> $root::packages::PackageLibrary {
|
fn get(&self) -> $root::packages::PackageLibrary {
|
||||||
self.0.clone()
|
self.0.clone()
|
||||||
}
|
}
|
||||||
@ -56,6 +52,14 @@ macro_rules! def_package {
|
|||||||
$block
|
$block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl $package {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut pkg = $root::packages::PackageStore::new();
|
||||||
|
<Self as $root::packages::Package>::init(&mut pkg);
|
||||||
|
Self(pkg.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +118,7 @@ pub fn reg_none<R>(
|
|||||||
+ Sync
|
+ Sync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) {
|
) {
|
||||||
let hash = calc_fn_hash(fn_name, ([] as [TypeId; 0]).iter().cloned());
|
let hash = calc_fn_hash(empty(), fn_name, ([] as [TypeId; 0]).iter().cloned());
|
||||||
|
|
||||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
check_num_args(fn_name, 0, args, pos)?;
|
check_num_args(fn_name, 0, args, pos)?;
|
||||||
@ -164,15 +168,15 @@ pub fn reg_unary<T: Variant + Clone, R>(
|
|||||||
) {
|
) {
|
||||||
//println!("register {}({})", fn_name, crate::std::any::type_name::<T>());
|
//println!("register {}({})", fn_name, crate::std::any::type_name::<T>());
|
||||||
|
|
||||||
let hash = calc_fn_hash(fn_name, [TypeId::of::<T>()].iter().cloned());
|
let hash = calc_fn_hash(empty(), fn_name, [TypeId::of::<T>()].iter().cloned());
|
||||||
|
|
||||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
check_num_args(fn_name, 1, args, pos)?;
|
check_num_args(fn_name, 1, args, pos)?;
|
||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
let x: &mut T = drain.next().unwrap().downcast_mut().unwrap();
|
let x = mem::take(*drain.next().unwrap()).cast::<T>();
|
||||||
|
|
||||||
let r = func(x.clone());
|
let r = func(x);
|
||||||
map_result(r, pos)
|
map_result(r, pos)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -224,7 +228,7 @@ pub fn reg_unary_mut<T: Variant + Clone, R>(
|
|||||||
) {
|
) {
|
||||||
//println!("register {}(&mut {})", fn_name, crate::std::any::type_name::<T>());
|
//println!("register {}(&mut {})", fn_name, crate::std::any::type_name::<T>());
|
||||||
|
|
||||||
let hash = calc_fn_hash(fn_name, [TypeId::of::<T>()].iter().cloned());
|
let hash = calc_fn_hash(empty(), fn_name, [TypeId::of::<T>()].iter().cloned());
|
||||||
|
|
||||||
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
let f = Box::new(move |args: &mut FnCallArgs, pos: Position| {
|
||||||
check_num_args(fn_name, 1, args, pos)?;
|
check_num_args(fn_name, 1, args, pos)?;
|
||||||
@ -278,6 +282,7 @@ pub fn reg_binary<A: Variant + Clone, B: Variant + Clone, R>(
|
|||||||
//println!("register {}({}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
//println!("register {}({}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
||||||
|
|
||||||
let hash = calc_fn_hash(
|
let hash = calc_fn_hash(
|
||||||
|
empty(),
|
||||||
fn_name,
|
fn_name,
|
||||||
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||||
);
|
);
|
||||||
@ -286,10 +291,10 @@ pub fn reg_binary<A: Variant + Clone, B: Variant + Clone, R>(
|
|||||||
check_num_args(fn_name, 2, args, pos)?;
|
check_num_args(fn_name, 2, args, pos)?;
|
||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
let x = mem::take(*drain.next().unwrap()).cast::<A>();
|
||||||
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
let y = mem::take(*drain.next().unwrap()).cast::<B>();
|
||||||
|
|
||||||
let r = func(x.clone(), y.clone());
|
let r = func(x, y);
|
||||||
map_result(r, pos)
|
map_result(r, pos)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -342,6 +347,7 @@ pub fn reg_binary_mut<A: Variant + Clone, B: Variant + Clone, R>(
|
|||||||
//println!("register {}(&mut {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
//println!("register {}(&mut {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>());
|
||||||
|
|
||||||
let hash = calc_fn_hash(
|
let hash = calc_fn_hash(
|
||||||
|
empty(),
|
||||||
fn_name,
|
fn_name,
|
||||||
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
[TypeId::of::<A>(), TypeId::of::<B>()].iter().cloned(),
|
||||||
);
|
);
|
||||||
@ -351,9 +357,9 @@ pub fn reg_binary_mut<A: Variant + Clone, B: Variant + Clone, R>(
|
|||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
let y = mem::take(*drain.next().unwrap()).cast::<B>();
|
||||||
|
|
||||||
let r = func(x, y.clone());
|
let r = func(x, y);
|
||||||
map_result(r, pos)
|
map_result(r, pos)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -380,6 +386,7 @@ pub fn reg_trinary<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R
|
|||||||
//println!("register {}({}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
|
//println!("register {}({}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
|
||||||
|
|
||||||
let hash = calc_fn_hash(
|
let hash = calc_fn_hash(
|
||||||
|
empty(),
|
||||||
fn_name,
|
fn_name,
|
||||||
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||||
.iter()
|
.iter()
|
||||||
@ -390,11 +397,11 @@ pub fn reg_trinary<A: Variant + Clone, B: Variant + Clone, C: Variant + Clone, R
|
|||||||
check_num_args(fn_name, 3, args, pos)?;
|
check_num_args(fn_name, 3, args, pos)?;
|
||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
let x = mem::take(*drain.next().unwrap()).cast::<A>();
|
||||||
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
let y = mem::take(*drain.next().unwrap()).cast::<B>();
|
||||||
let z: &mut C = drain.next().unwrap().downcast_mut().unwrap();
|
let z = mem::take(*drain.next().unwrap()).cast::<C>();
|
||||||
|
|
||||||
let r = func(x.clone(), y.clone(), z.clone());
|
let r = func(x, y, z);
|
||||||
map_result(r, pos)
|
map_result(r, pos)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -421,6 +428,7 @@ pub fn reg_trinary_mut<A: Variant + Clone, B: Variant + Clone, C: Variant + Clon
|
|||||||
//println!("register {}(&mut {}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
|
//println!("register {}(&mut {}, {}, {})", fn_name, crate::std::any::type_name::<A>(), crate::std::any::type_name::<B>(), crate::std::any::type_name::<C>());
|
||||||
|
|
||||||
let hash = calc_fn_hash(
|
let hash = calc_fn_hash(
|
||||||
|
empty(),
|
||||||
fn_name,
|
fn_name,
|
||||||
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
[TypeId::of::<A>(), TypeId::of::<B>(), TypeId::of::<C>()]
|
||||||
.iter()
|
.iter()
|
||||||
@ -432,10 +440,10 @@ pub fn reg_trinary_mut<A: Variant + Clone, B: Variant + Clone, C: Variant + Clon
|
|||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
let x: &mut A = drain.next().unwrap().downcast_mut().unwrap();
|
||||||
let y: &mut B = drain.next().unwrap().downcast_mut().unwrap();
|
let y = mem::take(*drain.next().unwrap()).cast::<B>();
|
||||||
let z: &mut C = drain.next().unwrap().downcast_mut().unwrap();
|
let z = mem::take(*drain.next().unwrap()).cast::<C>();
|
||||||
|
|
||||||
let r = func(x, y.clone(), z.clone());
|
let r = func(x, y, z);
|
||||||
map_result(r, pos)
|
map_result(r, pos)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
1239
src/parser.rs
1239
src/parser.rs
File diff suppressed because it is too large
Load Diff
23
src/scope.rs
23
src/scope.rs
@ -7,7 +7,7 @@ use crate::token::Position;
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
|
|
||||||
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec, vec::Vec};
|
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec::Vec};
|
||||||
|
|
||||||
/// Type of an entry in the Scope.
|
/// Type of an entry in the Scope.
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||||
@ -30,6 +30,8 @@ pub struct Entry<'a> {
|
|||||||
pub typ: EntryType,
|
pub typ: EntryType,
|
||||||
/// Current value of the entry.
|
/// Current value of the entry.
|
||||||
pub value: Dynamic,
|
pub value: Dynamic,
|
||||||
|
/// Alias of the entry.
|
||||||
|
pub alias: Option<Box<String>>,
|
||||||
/// A constant expression if the initial value matches one of the recognized types.
|
/// A constant expression if the initial value matches one of the recognized types.
|
||||||
pub expr: Option<Box<Expr>>,
|
pub expr: Option<Box<Expr>>,
|
||||||
}
|
}
|
||||||
@ -175,7 +177,9 @@ impl<'a> Scope<'a> {
|
|||||||
///
|
///
|
||||||
/// Modules are used for accessing member variables, functions and plugins under a namespace.
|
/// Modules are used for accessing member variables, functions and plugins under a namespace.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, value: Module) {
|
pub fn push_module<K: Into<Cow<'a, str>>>(&mut self, name: K, mut value: Module) {
|
||||||
|
value.index_all_sub_modules();
|
||||||
|
|
||||||
self.push_dynamic_value(
|
self.push_dynamic_value(
|
||||||
name,
|
name,
|
||||||
EntryType::Module,
|
EntryType::Module,
|
||||||
@ -246,6 +250,7 @@ impl<'a> Scope<'a> {
|
|||||||
self.0.push(Entry {
|
self.0.push(Entry {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
typ: entry_type,
|
typ: entry_type,
|
||||||
|
alias: None,
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
expr,
|
expr,
|
||||||
});
|
});
|
||||||
@ -410,16 +415,15 @@ impl<'a> Scope<'a> {
|
|||||||
/// Get a mutable reference to an entry in the Scope.
|
/// Get a mutable reference to an entry in the Scope.
|
||||||
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
||||||
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||||
|
|
||||||
// assert_ne!(
|
|
||||||
// entry.typ,
|
|
||||||
// EntryType::Constant,
|
|
||||||
// "get mut of constant entry"
|
|
||||||
// );
|
|
||||||
|
|
||||||
(&mut entry.value, entry.typ)
|
(&mut entry.value, entry.typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the access type of an entry in the Scope.
|
||||||
|
pub(crate) fn set_entry_alias(&mut self, index: usize, alias: String) {
|
||||||
|
let entry = self.0.get_mut(index).expect("invalid index in Scope");
|
||||||
|
entry.alias = Some(Box::new(alias));
|
||||||
|
}
|
||||||
|
|
||||||
/// Get an iterator to entries in the Scope.
|
/// Get an iterator to entries in the Scope.
|
||||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
|
pub(crate) fn into_iter(self) -> impl Iterator<Item = Entry<'a>> {
|
||||||
self.0.into_iter()
|
self.0.into_iter()
|
||||||
@ -437,6 +441,7 @@ impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<
|
|||||||
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
.extend(iter.into_iter().map(|(name, typ, value)| Entry {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
typ,
|
typ,
|
||||||
|
alias: None,
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
expr: None,
|
expr: None,
|
||||||
}));
|
}));
|
||||||
|
@ -196,6 +196,7 @@ pub enum Token {
|
|||||||
XOrAssign,
|
XOrAssign,
|
||||||
ModuloAssign,
|
ModuloAssign,
|
||||||
PowerOfAssign,
|
PowerOfAssign,
|
||||||
|
Private,
|
||||||
Import,
|
Import,
|
||||||
Export,
|
Export,
|
||||||
As,
|
As,
|
||||||
@ -205,14 +206,14 @@ pub enum Token {
|
|||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
/// Get the syntax of the token.
|
/// Get the syntax of the token.
|
||||||
pub fn syntax(&self) -> Cow<str> {
|
pub fn syntax(&self) -> Cow<'static, str> {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
IntegerConstant(i) => i.to_string().into(),
|
IntegerConstant(i) => i.to_string().into(),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
FloatConstant(f) => f.to_string().into(),
|
FloatConstant(f) => f.to_string().into(),
|
||||||
Identifier(s) => s.into(),
|
Identifier(s) => s.clone().into(),
|
||||||
CharConstant(c) => c.to_string().into(),
|
CharConstant(c) => c.to_string().into(),
|
||||||
LexError(err) => err.to_string().into(),
|
LexError(err) => err.to_string().into(),
|
||||||
|
|
||||||
@ -279,6 +280,7 @@ impl Token {
|
|||||||
ModuloAssign => "%=",
|
ModuloAssign => "%=",
|
||||||
PowerOf => "~",
|
PowerOf => "~",
|
||||||
PowerOfAssign => "~=",
|
PowerOfAssign => "~=",
|
||||||
|
Private => "private",
|
||||||
Import => "import",
|
Import => "import",
|
||||||
Export => "export",
|
Export => "export",
|
||||||
As => "as",
|
As => "as",
|
||||||
@ -750,6 +752,7 @@ impl<'a> TokenIterator<'a> {
|
|||||||
"throw" => Token::Throw,
|
"throw" => Token::Throw,
|
||||||
"for" => Token::For,
|
"for" => Token::For,
|
||||||
"in" => Token::In,
|
"in" => Token::In,
|
||||||
|
"private" => Token::Private,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
"import" => Token::Import,
|
"import" => Token::Import,
|
||||||
|
46
src/utils.rs
46
src/utils.rs
@ -14,30 +14,32 @@ use crate::stdlib::collections::hash_map::DefaultHasher;
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use ahash::AHasher;
|
use ahash::AHasher;
|
||||||
|
|
||||||
/// Calculate a `u64` hash key from a function name and parameter types.
|
pub fn EMPTY_TYPE_ID() -> TypeId {
|
||||||
///
|
TypeId::of::<()>()
|
||||||
/// Parameter types are passed in via `TypeId` values from an iterator
|
|
||||||
/// which can come from any source.
|
|
||||||
pub fn calc_fn_spec(fn_name: &str, params: impl Iterator<Item = TypeId>) -> u64 {
|
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
let mut s: AHasher = Default::default();
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
let mut s = DefaultHasher::new();
|
|
||||||
|
|
||||||
s.write(fn_name.as_bytes());
|
|
||||||
params.for_each(|t| t.hash(&mut s));
|
|
||||||
s.finish()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a `u64` hash key from a function name and number of parameters (without regard to types).
|
/// Calculate a `u64` hash key from a module-qualified function name and parameter types.
|
||||||
pub(crate) fn calc_fn_def(fn_name: &str, num_params: usize) -> u64 {
|
///
|
||||||
|
/// Module names are passed in via `&str` references from an iterator.
|
||||||
|
/// Parameter types are passed in via `TypeId` values from an iterator.
|
||||||
|
///
|
||||||
|
/// ### Note
|
||||||
|
///
|
||||||
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
|
pub fn calc_fn_spec<'a>(
|
||||||
|
modules: impl Iterator<Item = &'a str>,
|
||||||
|
fn_name: &str,
|
||||||
|
params: impl Iterator<Item = TypeId>,
|
||||||
|
) -> u64 {
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
let mut s: AHasher = Default::default();
|
let mut s: AHasher = Default::default();
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
let mut s = DefaultHasher::new();
|
let mut s = DefaultHasher::new();
|
||||||
|
|
||||||
|
// We always skip the first module
|
||||||
|
modules.skip(1).for_each(|m| m.hash(&mut s));
|
||||||
s.write(fn_name.as_bytes());
|
s.write(fn_name.as_bytes());
|
||||||
s.write_usize(num_params);
|
params.for_each(|t| t.hash(&mut s));
|
||||||
s.finish()
|
s.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +47,7 @@ pub(crate) fn calc_fn_def(fn_name: &str, num_params: usize) -> u64 {
|
|||||||
///
|
///
|
||||||
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
/// This is essentially a knock-off of the [`staticvec`](https://crates.io/crates/staticvec) crate.
|
||||||
/// This simplified implementation here is to avoid pulling in another crate.
|
/// This simplified implementation here is to avoid pulling in another crate.
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Hash, Default)]
|
||||||
pub struct StaticVec<T: Default + Clone> {
|
pub struct StaticVec<T: Default + Clone> {
|
||||||
/// Total number of values held.
|
/// Total number of values held.
|
||||||
len: usize,
|
len: usize,
|
||||||
@ -117,6 +119,16 @@ impl<T: Default + Clone> StaticVec<T> {
|
|||||||
|
|
||||||
self.list[..num].iter().chain(self.more.iter())
|
self.list[..num].iter().chain(self.more.iter())
|
||||||
}
|
}
|
||||||
|
/// Get a mutable iterator to entries in the `StaticVec`.
|
||||||
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||||
|
let num = if self.len >= self.list.len() {
|
||||||
|
self.list.len()
|
||||||
|
} else {
|
||||||
|
self.len
|
||||||
|
};
|
||||||
|
|
||||||
|
self.list[..num].iter_mut().chain(self.more.iter_mut())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Default + Clone + fmt::Debug> fmt::Debug for StaticVec<T> {
|
impl<T: Default + Clone + fmt::Debug> fmt::Debug for StaticVec<T> {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, ParseErrorType, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -8,13 +8,13 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<INT>("const x = 123; x = 42;").expect_err("expects error"),
|
*engine.eval::<INT>("const x = 123; x = 42;").expect_err("expects error"),
|
||||||
EvalAltResult::ErrorAssignmentToConstant(var, _) if var == "x"
|
EvalAltResult::ErrorParsing(err) if err.error_type() == &ParseErrorType::AssignmentToConstant("x".to_string())
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
*engine.eval::<INT>("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
||||||
EvalAltResult::ErrorAssignmentToConstant(var, _) if var == "x"
|
EvalAltResult::ErrorParsing(err) if err.error_type() == &ParseErrorType::AssignmentToConstant("x".to_string())
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -18,7 +18,7 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let mut sub_module2 = Module::new();
|
let mut sub_module2 = Module::new();
|
||||||
sub_module2.set_var("answer", 41 as INT);
|
sub_module2.set_var("answer", 41 as INT);
|
||||||
let hash = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
|
let hash_inc = sub_module2.set_fn_1("inc", |x: INT| Ok(x + 1));
|
||||||
|
|
||||||
sub_module.set_sub_module("universe", sub_module2);
|
sub_module.set_sub_module("universe", sub_module2);
|
||||||
module.set_sub_module("life", sub_module);
|
module.set_sub_module("life", sub_module);
|
||||||
@ -30,11 +30,11 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let m2 = m.get_sub_module("universe").unwrap();
|
let m2 = m.get_sub_module("universe").unwrap();
|
||||||
|
|
||||||
assert!(m2.contains_var("answer"));
|
assert!(m2.contains_var("answer"));
|
||||||
assert!(m2.contains_fn(hash));
|
assert!(m2.contains_fn(hash_inc));
|
||||||
|
|
||||||
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let engine = Engine::new();
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
scope.push_module("question", module);
|
scope.push_module("question", module);
|
||||||
@ -81,3 +81,86 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
let mut resolver = rhai::module_resolvers::StaticModuleResolver::new();
|
||||||
|
let mut sub_module = Module::new();
|
||||||
|
sub_module.set_var("foo", true);
|
||||||
|
resolver.insert("another module".to_string(), sub_module);
|
||||||
|
|
||||||
|
engine.set_module_resolver(Some(resolver));
|
||||||
|
|
||||||
|
let ast = engine.compile(
|
||||||
|
r#"
|
||||||
|
// Functions become module functions
|
||||||
|
fn calc(x) {
|
||||||
|
x + 1
|
||||||
|
}
|
||||||
|
fn add_len(x, y) {
|
||||||
|
x + y.len()
|
||||||
|
}
|
||||||
|
private fn hidden() {
|
||||||
|
throw "you shouldn't see me!";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imported modules become sub-modules
|
||||||
|
import "another module" as extra;
|
||||||
|
|
||||||
|
// Variables defined at global level become module variables
|
||||||
|
const x = 123;
|
||||||
|
let foo = 41;
|
||||||
|
let hello;
|
||||||
|
|
||||||
|
// Final variable values become constant module variable values
|
||||||
|
foo = calc(foo);
|
||||||
|
hello = "hello, " + foo + " worlds!";
|
||||||
|
|
||||||
|
export
|
||||||
|
x as abc,
|
||||||
|
foo,
|
||||||
|
hello,
|
||||||
|
extra as foobar;
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?;
|
||||||
|
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
scope.push_module("testing", module);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::abc")?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::foo")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
assert!(engine.eval_expression_with_scope::<bool>(&mut scope, "testing::foobar::foo")?);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_expression_with_scope::<String>(&mut scope, "testing::hello")?,
|
||||||
|
"hello, 42 worlds!"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_expression_with_scope::<INT>(&mut scope, "testing::calc(999)")?,
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_expression_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
"testing::add_len(testing::foo, testing::hello)"
|
||||||
|
)?,
|
||||||
|
59
|
||||||
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.eval_expression_with_scope::<()>(&mut scope, "testing::hidden()")
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "hidden"
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user