Merge pull request #387 from schungx/master
Fix property setter op-assignment bug.
This commit is contained in:
commit
fa63f2a0ae
27
CHANGELOG.md
27
CHANGELOG.md
@ -1,6 +1,27 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
Version 0.19.16
|
||||||
|
===============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Property setter op-assignments now work properly.
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `ModuleResolver` trait methods take an additional parameter `source_path` that contains the path of the current environment. This is to facilitate loading other script files always from the current directory.
|
||||||
|
* `FileModuleResolver` now resolves relative paths under the source path if there is no base path set.
|
||||||
|
* `FileModuleResolver::base_path` now returns `Option<&str>` which is `None` if there is no base path set.
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
* `FileModuleResolver` resolves relative paths under the parent path (i.e. the path holding the script that does the loading). This allows seamless cross-loading of scripts from a directory hierarchy instead of having all relative paths load from the current working directory.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.15
|
Version 0.19.15
|
||||||
===============
|
===============
|
||||||
|
|
||||||
@ -16,12 +37,12 @@ an object map is small.
|
|||||||
`HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break
|
`HashMap` and `BTreeMap` have almost identical public API's so this change is unlikely to break
|
||||||
existing code.
|
existing code.
|
||||||
|
|
||||||
[`SmartString`](https://crates.io/crates/smartstring) is used to store identifiers (which tends to
|
[`SmartString`](https://crates.io/crates/smartstring) is used to store identifiers (which tend to
|
||||||
be short, fewer than 23 characters, and ASCII-based) because they can usually be stored inline.
|
be short, fewer than 23 characters, and ASCII-based) because they can usually be stored inline.
|
||||||
`Map` keys now also use [`SmartString`](https://crates.io/crates/smartstring).
|
`Map` keys now also use [`SmartString`](https://crates.io/crates/smartstring).
|
||||||
|
|
||||||
In addition, there is now support for line continuation in strings (put `\` at the end of line) as
|
In addition, there is now support for line continuation in strings (put `\` at the end of line) as
|
||||||
well as multi-line literal strings (wrapped by back-ticks: <code>\`...\`</code>).
|
well as multi-line literal strings (wrapped by back-ticks: `` `...` ``).
|
||||||
|
|
||||||
Finally, all function signature/metadata methods are now grouped under the umbrella `metadata` feature.
|
Finally, all function signature/metadata methods are now grouped under the umbrella `metadata` feature.
|
||||||
This avoids spending precious resources maintaining metadata for functions for the vast majority of
|
This avoids spending precious resources maintaining metadata for functions for the vast majority of
|
||||||
@ -45,7 +66,7 @@ Breaking changes
|
|||||||
* The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate.
|
* The _reflections_ API such as `Engine::gen_fn_signatures`, `Module::update_fn_metadata` etc. are put under the `metadata` feature gate.
|
||||||
* The shebang `#!` is now a reserved symbol.
|
* The shebang `#!` is now a reserved symbol.
|
||||||
* Shebangs at the very beginning of script files are skipped when loading them.
|
* Shebangs at the very beginning of script files are skipped when loading them.
|
||||||
* [`smartstring`](https://crates.io/crates/smartstring) is used for identifiers by default. Currently, a PR branch is pulled because it breaks on `no-std` builds. The official crate will be used once `smartstring` is fixed to support `no-std`.
|
* [`SmartString`](https://crates.io/crates/smartstring) is used for identifiers by default. Currently, a PR branch is pulled for `no-std` builds. The official crate will be used once `SmartString` is fixed to support `no-std`.
|
||||||
* `Map` is now an alias to `BTreeMap<SmartString, Dynamic>` instead of `HashMap` because most object maps hold few properties.
|
* `Map` is now an alias to `BTreeMap<SmartString, Dynamic>` instead of `HashMap` because most object maps hold few properties.
|
||||||
* `EvalAltResult::FnWrongDefinition` is renamed `WrongFnDefinition` for consistency.
|
* `EvalAltResult::FnWrongDefinition` is renamed `WrongFnDefinition` for consistency.
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ members = [".", "codegen"]
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "0.19.15"
|
version = "0.19.16"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
|
@ -3,6 +3,7 @@ Rhai - Embedded Scripting for Rust
|
|||||||
|
|
||||||
![GitHub last commit](https://img.shields.io/github/last-commit/rhaiscript/rhai?logo=github)
|
![GitHub last commit](https://img.shields.io/github/last-commit/rhaiscript/rhai?logo=github)
|
||||||
[![Build Status](https://github.com/rhaiscript/rhai/workflows/Build/badge.svg)](https://github.com/rhaiscript/rhai/actions)
|
[![Build Status](https://github.com/rhaiscript/rhai/workflows/Build/badge.svg)](https://github.com/rhaiscript/rhai/actions)
|
||||||
|
[![stars](https://img.shields.io/github/stars/rhaiscript/rhai?logo=github)](https://github.com/rhaiscript/rhai)
|
||||||
[![license](https://img.shields.io/crates/l/rhai)](https://github.com/license/rhaiscript/rhai)
|
[![license](https://img.shields.io/crates/l/rhai)](https://github.com/license/rhaiscript/rhai)
|
||||||
[![crates.io](https://img.shields.io/crates/v/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
[![crates.io](https://img.shields.io/crates/v/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
||||||
[![crates.io](https://img.shields.io/crates/d/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
[![crates.io](https://img.shields.io/crates/d/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
||||||
@ -34,8 +35,8 @@ Standard features
|
|||||||
* Freely pass Rust values into a script as [variables](https://rhai.rs/book/language/variables.html)/[constants](https://rhai.rs/book/language/constants.html) via an external [`Scope`](https://rhai.rs/book/rust/scope.html) - all clonable Rust types are supported; no need to implement any special trait. Or tap directly into the [variable resolution process](https://rhai.rs/book/engine/var.html).
|
* Freely pass Rust values into a script as [variables](https://rhai.rs/book/language/variables.html)/[constants](https://rhai.rs/book/language/constants.html) via an external [`Scope`](https://rhai.rs/book/rust/scope.html) - all clonable Rust types are supported; no need to implement any special trait. Or tap directly into the [variable resolution process](https://rhai.rs/book/engine/var.html).
|
||||||
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, [integers](https://rhai.rs/book/language/numbers.html), [floating-point numbers](https://rhai.rs/book/language/numbers.html) (including [`Decimal`](https://crates.io/crates/rust_decimal)), [strings](https://rhai.rs/book/language/strings-chars.html), [Unicode characters](https://rhai.rs/book/language/strings-chars.html), [arrays](https://rhai.rs/book/language/arrays.html) and [object maps](https://rhai.rs/book/language/object-maps.html).
|
* Built-in support for most common [data types](https://rhai.rs/book/language/values-and-types.html) including booleans, [integers](https://rhai.rs/book/language/numbers.html), [floating-point numbers](https://rhai.rs/book/language/numbers.html) (including [`Decimal`](https://crates.io/crates/rust_decimal)), [strings](https://rhai.rs/book/language/strings-chars.html), [Unicode characters](https://rhai.rs/book/language/strings-chars.html), [arrays](https://rhai.rs/book/language/arrays.html) and [object maps](https://rhai.rs/book/language/object-maps.html).
|
||||||
* Easily [call a script-defined function](https://rhai.rs/book/engine/call-fn.html) from Rust.
|
* Easily [call a script-defined function](https://rhai.rs/book/engine/call-fn.html) from Rust.
|
||||||
* Relatively little `unsafe` code (yes there are some for performance reasons).
|
* Relatively little `unsafe` code (yes there are some for performance reasons), and `unsafe` code is only used for type casting, never to get around the borrow checker.
|
||||||
* Few dependencies (currently only [`smallvec`](https://crates.io/crates/smallvec), [`num-traits`](https://crates.io/crates/num-traits), [`ahash`](https://crates.io/crates/ahash)) and [`smartstring`](https://crates.io/crates/smartstring)).
|
* Few dependencies - currently only [`smallvec`](https://crates.io/crates/smallvec), [`num-traits`](https://crates.io/crates/num-traits), [`ahash`](https://crates.io/crates/ahash) and [`smartstring`](https://crates.io/crates/smartstring).
|
||||||
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
|
* Re-entrant scripting engine can be made `Send + Sync` (via the `sync` feature).
|
||||||
* Compile once to [AST](https://rhai.rs/book/engine/compile.html) form for repeated evaluations.
|
* Compile once to [AST](https://rhai.rs/book/engine/compile.html) form for repeated evaluations.
|
||||||
* Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts).
|
* Scripts are [optimized](https://rhai.rs/book/engine/optimize.html) (useful for template-based machine-generated scripts).
|
||||||
|
@ -16,7 +16,7 @@ default = []
|
|||||||
metadata = []
|
metadata = []
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rhai = { path = "..", version = "^0.19.15" }
|
rhai = { path = "..", version = "0.19" }
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// This script calculates the n-th Fibonacci number using a really dumb algorithm
|
// This script calculates the n-th Fibonacci number using a really dumb algorithm
|
||||||
// to test the speed of the scripting engine.
|
// to test the speed of the scripting engine.
|
||||||
|
|
||||||
const target = 30;
|
const target = 28;
|
||||||
|
|
||||||
fn fib(n) {
|
fn fib(n) {
|
||||||
if n < 2 {
|
if n < 2 {
|
||||||
@ -11,16 +11,20 @@ fn fib(n) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print("Running Fibonacci(28) x 5 times...");
|
||||||
print("Ready... Go!");
|
print("Ready... Go!");
|
||||||
|
|
||||||
|
let result;
|
||||||
let now = timestamp();
|
let now = timestamp();
|
||||||
|
|
||||||
let result = fib(target);
|
for n in range(0, 5) {
|
||||||
|
result = fib(target);
|
||||||
|
}
|
||||||
|
|
||||||
print("Finished. Run time = " + now.elapsed + " seconds.");
|
print("Finished. Run time = " + now.elapsed + " seconds.");
|
||||||
|
|
||||||
print("Fibonacci number #" + target + " = " + result);
|
print("Fibonacci number #" + target + " = " + result);
|
||||||
|
|
||||||
if result != 832_040 {
|
if result != 317_811 {
|
||||||
print("The answer is WRONG! Should be 832,040!");
|
print("The answer is WRONG! Should be 317,811!");
|
||||||
}
|
}
|
@ -1,3 +1,3 @@
|
|||||||
import "scripts/loop";
|
import "loop";
|
||||||
|
|
||||||
print("Module test!");
|
print("Module test!");
|
||||||
|
@ -4,6 +4,7 @@ use std::{
|
|||||||
env,
|
env,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{stdin, stdout, Read, Write},
|
io::{stdin, stdout, Read, Write},
|
||||||
|
path::Path,
|
||||||
process::exit,
|
process::exit,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -64,28 +65,39 @@ fn main() {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
{
|
{
|
||||||
// Set a file module resolver without caching
|
|
||||||
let mut resolver = rhai::module_resolvers::FileModuleResolver::new();
|
|
||||||
resolver.enable_cache(false);
|
|
||||||
engine.set_module_resolver(resolver);
|
|
||||||
|
|
||||||
// Load init scripts
|
// Load init scripts
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
let mut has_init_scripts = false;
|
let mut has_init_scripts = false;
|
||||||
|
|
||||||
for filename in env::args().skip(1) {
|
for filename in env::args().skip(1) {
|
||||||
|
let filename = match Path::new(&filename).canonicalize() {
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error script file path: {}\n{}", filename, err);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Ok(f) => f,
|
||||||
|
};
|
||||||
|
|
||||||
contents.clear();
|
contents.clear();
|
||||||
|
|
||||||
let mut f = match File::open(&filename) {
|
let mut f = match File::open(&filename) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Error reading script file: {}\n{}", filename, err);
|
eprintln!(
|
||||||
|
"Error reading script file: {}\n{}",
|
||||||
|
filename.to_string_lossy(),
|
||||||
|
err
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = f.read_to_string(&mut contents) {
|
if let Err(err) = f.read_to_string(&mut contents) {
|
||||||
println!("Error reading script file: {}\n{}", filename, err);
|
println!(
|
||||||
|
"Error reading script file: {}\n{}",
|
||||||
|
filename.to_string_lossy(),
|
||||||
|
err
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,10 +105,12 @@ fn main() {
|
|||||||
.compile(&contents)
|
.compile(&contents)
|
||||||
.map_err(|err| err.into())
|
.map_err(|err| err.into())
|
||||||
.and_then(|mut ast| {
|
.and_then(|mut ast| {
|
||||||
ast.set_source(&filename);
|
ast.set_source(filename.to_string_lossy());
|
||||||
Module::eval_ast_as_new(Default::default(), &ast, &engine)
|
Module::eval_ast_as_new(Default::default(), &ast, &engine)
|
||||||
}) {
|
}) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
let filename = filename.to_string_lossy();
|
||||||
|
|
||||||
eprintln!("{:=<1$}", "", filename.len());
|
eprintln!("{:=<1$}", "", filename.len());
|
||||||
eprintln!("{}", filename);
|
eprintln!("{}", filename);
|
||||||
eprintln!("{:=<1$}", "", filename.len());
|
eprintln!("{:=<1$}", "", filename.len());
|
||||||
@ -112,7 +126,7 @@ fn main() {
|
|||||||
|
|
||||||
has_init_scripts = true;
|
has_init_scripts = true;
|
||||||
|
|
||||||
println!("Script '{}' loaded.", filename);
|
println!("Script '{}' loaded.", filename.to_string_lossy());
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_init_scripts {
|
if has_init_scripts {
|
||||||
@ -124,17 +138,26 @@ fn main() {
|
|||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
engine.set_optimization_level(rhai::OptimizationLevel::None);
|
engine.set_optimization_level(rhai::OptimizationLevel::None);
|
||||||
|
|
||||||
|
// Set a file module resolver without caching
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
{
|
||||||
|
let mut resolver = rhai::module_resolvers::FileModuleResolver::new();
|
||||||
|
resolver.enable_cache(false);
|
||||||
|
engine.set_module_resolver(resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make Engine immutable
|
||||||
|
let engine = engine;
|
||||||
|
|
||||||
|
// Create scope
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
// REPL loop
|
||||||
let mut input = String::new();
|
let mut input = String::new();
|
||||||
let mut main_ast: AST = Default::default();
|
let mut main_ast: AST = Default::default();
|
||||||
let mut ast_u: AST = Default::default();
|
let mut ast_u: AST = Default::default();
|
||||||
let mut ast: AST = Default::default();
|
let mut ast: AST = Default::default();
|
||||||
|
|
||||||
// Make Engine immutable
|
|
||||||
let engine = engine;
|
|
||||||
|
|
||||||
// REPL loop
|
|
||||||
'main_loop: loop {
|
'main_loop: loop {
|
||||||
print!("rhai-repl> ");
|
print!("rhai-repl> ");
|
||||||
stdout().flush().expect("couldn't flush stdout");
|
stdout().flush().expect("couldn't flush stdout");
|
||||||
|
@ -3,7 +3,7 @@ use rhai::{Engine, EvalAltResult, Position};
|
|||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
use rhai::OptimizationLevel;
|
use rhai::OptimizationLevel;
|
||||||
|
|
||||||
use std::{env, fs::File, io::Read, process::exit};
|
use std::{env, fs::File, io::Read, path::Path, process::exit};
|
||||||
|
|
||||||
fn eprint_error(input: &str, mut err: EvalAltResult) {
|
fn eprint_error(input: &str, mut err: EvalAltResult) {
|
||||||
fn eprint_line(lines: &[&str], pos: Position, err_msg: &str) {
|
fn eprint_line(lines: &[&str], pos: Position, err_msg: &str) {
|
||||||
@ -38,6 +38,14 @@ fn main() {
|
|||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
|
|
||||||
for filename in env::args().skip(1) {
|
for filename in env::args().skip(1) {
|
||||||
|
let filename = match Path::new(&filename).canonicalize() {
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error script file path: {}\n{}", filename, err);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Ok(f) => f,
|
||||||
|
};
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
@ -45,7 +53,11 @@ fn main() {
|
|||||||
|
|
||||||
let mut f = match File::open(&filename) {
|
let mut f = match File::open(&filename) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!("Error reading script file: {}\n{}", filename, err);
|
eprintln!(
|
||||||
|
"Error reading script file: {}\n{}",
|
||||||
|
filename.to_string_lossy(),
|
||||||
|
err
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
@ -54,7 +66,11 @@ fn main() {
|
|||||||
contents.clear();
|
contents.clear();
|
||||||
|
|
||||||
if let Err(err) = f.read_to_string(&mut contents) {
|
if let Err(err) = f.read_to_string(&mut contents) {
|
||||||
eprintln!("Error reading script file: {}\n{}", filename, err);
|
eprintln!(
|
||||||
|
"Error reading script file: {}\n{}",
|
||||||
|
filename.to_string_lossy(),
|
||||||
|
err
|
||||||
|
);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +81,16 @@ fn main() {
|
|||||||
&contents[..]
|
&contents[..]
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = engine.consume(contents) {
|
if let Err(err) = engine
|
||||||
|
.compile(contents)
|
||||||
|
.map_err(|err| Box::new(err.into()) as Box<EvalAltResult>)
|
||||||
|
.and_then(|mut ast| {
|
||||||
|
ast.set_source(filename.to_string_lossy());
|
||||||
|
engine.consume_ast(&ast)
|
||||||
|
})
|
||||||
|
{
|
||||||
|
let filename = filename.to_string_lossy();
|
||||||
|
|
||||||
eprintln!("{:=<1$}", "", filename.len());
|
eprintln!("{:=<1$}", "", filename.len());
|
||||||
eprintln!("{}", filename);
|
eprintln!("{}", filename);
|
||||||
eprintln!("{:=<1$}", "", filename.len());
|
eprintln!("{:=<1$}", "", filename.len());
|
||||||
|
@ -645,7 +645,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
|
|||||||
self.mods.iter()
|
self.mods.iter()
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
||||||
/// Available under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -658,7 +658,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
|
|||||||
self.lib.iter().cloned()
|
self.lib.iter().cloned()
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ The current set of namespaces containing definitions of all script-defined functions.
|
/// _(INTERNALS)_ The current set of namespaces containing definitions of all script-defined functions.
|
||||||
/// Available under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn namespaces(&self) -> &[&Module] {
|
pub fn namespaces(&self) -> &[&Module] {
|
||||||
@ -712,12 +712,12 @@ pub struct Engine {
|
|||||||
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
|
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
|
||||||
|
|
||||||
/// A map mapping type names to pretty-print names.
|
/// A map mapping type names to pretty-print names.
|
||||||
pub(crate) type_names: BTreeMap<String, String>,
|
pub(crate) type_names: BTreeMap<Identifier, Identifier>,
|
||||||
|
|
||||||
/// A set of symbols to disable.
|
/// A set of symbols to disable.
|
||||||
pub(crate) disabled_symbols: BTreeSet<String>,
|
pub(crate) disabled_symbols: BTreeSet<Identifier>,
|
||||||
/// A map containing custom keywords and precedence to recognize.
|
/// A map containing custom keywords and precedence to recognize.
|
||||||
pub(crate) custom_keywords: BTreeMap<String, Option<Precedence>>,
|
pub(crate) custom_keywords: BTreeMap<Identifier, Option<Precedence>>,
|
||||||
/// Custom syntax.
|
/// Custom syntax.
|
||||||
pub(crate) custom_syntax: BTreeMap<Identifier, CustomSyntax>,
|
pub(crate) custom_syntax: BTreeMap<Identifier, CustomSyntax>,
|
||||||
/// Callback closure for resolving variable access.
|
/// Callback closure for resolving variable access.
|
||||||
@ -1205,12 +1205,28 @@ impl Engine {
|
|||||||
|
|
||||||
Ok((val.take_or_clone(), false))
|
Ok((val.take_or_clone(), false))
|
||||||
}
|
}
|
||||||
// xxx.id = ???
|
// xxx.id op= ???
|
||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let (_, (setter, hash_set), Ident { pos, .. }) = x.as_ref();
|
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
||||||
|
x.as_ref();
|
||||||
|
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||||
|
|
||||||
|
if op_info.is_some() {
|
||||||
|
let hash = FnCallHash::from_native(*hash_get);
|
||||||
|
let mut args = [target.as_mut()];
|
||||||
|
let (mut orig_val, _) = self.exec_fn_call(
|
||||||
|
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos,
|
||||||
|
None, level,
|
||||||
|
)?;
|
||||||
|
let obj_ptr = (&mut orig_val).into();
|
||||||
|
self.eval_op_assignment(
|
||||||
|
mods, state, lib, op_info, op_pos, obj_ptr, new_val, new_pos,
|
||||||
|
)?;
|
||||||
|
new_val = orig_val;
|
||||||
|
}
|
||||||
|
|
||||||
let hash = FnCallHash::from_native(*hash_set);
|
let hash = FnCallHash::from_native(*hash_set);
|
||||||
let mut new_val = new_val;
|
let mut args = [target.as_mut(), &mut new_val];
|
||||||
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
|
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
|
||||||
level,
|
level,
|
||||||
@ -2433,19 +2449,22 @@ impl Engine {
|
|||||||
{
|
{
|
||||||
use crate::ModuleResolver;
|
use crate::ModuleResolver;
|
||||||
|
|
||||||
|
let source = state.source.as_ref().map(|s| s.as_str());
|
||||||
let expr_pos = expr.position();
|
let expr_pos = expr.position();
|
||||||
|
|
||||||
let module = state
|
let module = state
|
||||||
.resolver
|
.resolver
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|r| match r.resolve(self, &path, expr_pos) {
|
.and_then(|r| match r.resolve(self, source, &path, expr_pos) {
|
||||||
Ok(m) => return Some(Ok(m)),
|
Ok(m) => return Some(Ok(m)),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorModuleNotFound(_, _) => None,
|
EvalAltResult::ErrorModuleNotFound(_, _) => None,
|
||||||
_ => return Some(Err(err)),
|
_ => return Some(Err(err)),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| self.module_resolver.resolve(self, &path, expr_pos))?;
|
.unwrap_or_else(|| {
|
||||||
|
self.module_resolver.resolve(self, source, &path, expr_pos)
|
||||||
|
})?;
|
||||||
|
|
||||||
if let Some(name) = export.as_ref().map(|x| x.name.clone()) {
|
if let Some(name) = export.as_ref().map(|x| x.name.clone()) {
|
||||||
if !module.is_indexed() {
|
if !module.is_indexed() {
|
||||||
@ -2650,7 +2669,7 @@ impl Engine {
|
|||||||
pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
|
pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
|
||||||
self.type_names
|
self.type_names
|
||||||
.get(name)
|
.get(name)
|
||||||
.map(String::as_str)
|
.map(|s| s.as_str())
|
||||||
.unwrap_or_else(|| map_std_type_name(name))
|
.unwrap_or_else(|| map_std_type_name(name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,10 +317,10 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_get<T: Variant + Clone, U: Variant + Clone>(
|
pub fn register_get<T: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
get_fn: impl Fn(&mut T) -> U + SendSync + 'static,
|
get_fn: impl Fn(&mut T) -> V + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_getter;
|
use crate::engine::make_getter;
|
||||||
self.register_fn(&make_getter(name), get_fn)
|
self.register_fn(&make_getter(name), get_fn)
|
||||||
@ -364,10 +364,10 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_get_result<T: Variant + Clone, U: Variant + Clone>(
|
pub fn register_get_result<T: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
get_fn: impl Fn(&mut T) -> Result<U, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut T) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_getter;
|
use crate::engine::make_getter;
|
||||||
self.register_result_fn(&make_getter(name), get_fn)
|
self.register_result_fn(&make_getter(name), get_fn)
|
||||||
@ -410,10 +410,10 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_set<T: Variant + Clone, U: Variant + Clone>(
|
pub fn register_set<T: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
set_fn: impl Fn(&mut T, U) + SendSync + 'static,
|
set_fn: impl Fn(&mut T, V) + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_setter;
|
use crate::engine::make_setter;
|
||||||
self.register_fn(&make_setter(name), set_fn)
|
self.register_fn(&make_setter(name), set_fn)
|
||||||
@ -459,13 +459,13 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_set_result<T: Variant + Clone, U: Variant + Clone>(
|
pub fn register_set_result<T: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
set_fn: impl Fn(&mut T, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
use crate::engine::make_setter;
|
use crate::engine::make_setter;
|
||||||
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: U| {
|
self.register_result_fn(&make_setter(name), move |obj: &mut T, value: V| {
|
||||||
set_fn(obj, value)
|
set_fn(obj, value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -510,11 +510,11 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_get_set<T: Variant + Clone, U: Variant + Clone>(
|
pub fn register_get_set<T: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
get_fn: impl Fn(&mut T) -> U + SendSync + 'static,
|
get_fn: impl Fn(&mut T) -> V + SendSync + 'static,
|
||||||
set_fn: impl Fn(&mut T, U) + SendSync + 'static,
|
set_fn: impl Fn(&mut T, V) + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_get(name, get_fn).register_set(name, set_fn)
|
self.register_get(name, get_fn).register_set(name, set_fn)
|
||||||
}
|
}
|
||||||
@ -562,9 +562,9 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
|
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
@ -631,10 +631,10 @@ impl Engine {
|
|||||||
pub fn register_indexer_get_result<
|
pub fn register_indexer_get_result<
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
U: Variant + Clone,
|
V: Variant + Clone,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> Result<U, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
@ -696,9 +696,9 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
|
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
set_fn: impl Fn(&mut T, X, U) + SendSync + 'static,
|
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
@ -766,10 +766,10 @@ impl Engine {
|
|||||||
pub fn register_indexer_set_result<
|
pub fn register_indexer_set_result<
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
X: Variant + Clone,
|
X: Variant + Clone,
|
||||||
U: Variant + Clone,
|
V: Variant + Clone,
|
||||||
>(
|
>(
|
||||||
&mut self,
|
&mut self,
|
||||||
set_fn: impl Fn(&mut T, X, U) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
@ -787,7 +787,7 @@ impl Engine {
|
|||||||
|
|
||||||
self.register_result_fn(
|
self.register_result_fn(
|
||||||
crate::engine::FN_IDX_SET,
|
crate::engine::FN_IDX_SET,
|
||||||
move |obj: &mut T, index: X, value: U| set_fn(obj, index, value),
|
move |obj: &mut T, index: X, value: V| set_fn(obj, index, value),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`].
|
/// Short-hand for register both index getter and setter functions for a custom type with the [`Engine`].
|
||||||
@ -834,10 +834,10 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, U: Variant + Clone>(
|
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> U + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
||||||
set_fn: impl Fn(&mut T, X, U) -> () + SendSync + 'static,
|
set_fn: impl Fn(&mut T, X, V) -> () + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.register_indexer_get(get_fn)
|
self.register_indexer_get(get_fn)
|
||||||
.register_indexer_set(set_fn)
|
.register_indexer_set(set_fn)
|
||||||
@ -1070,7 +1070,7 @@ impl Engine {
|
|||||||
|
|
||||||
match self
|
match self
|
||||||
.module_resolver
|
.module_resolver
|
||||||
.resolve_ast(self, &path, Position::NONE)
|
.resolve_ast(self, None, &path, Position::NONE)
|
||||||
{
|
{
|
||||||
Some(Ok(module_ast)) => {
|
Some(Ok(module_ast)) => {
|
||||||
collect_imports(&module_ast, &mut resolver, &mut imports)
|
collect_imports(&module_ast, &mut resolver, &mut imports)
|
||||||
@ -1081,6 +1081,7 @@ impl Engine {
|
|||||||
|
|
||||||
let module = shared_take_or_clone(self.module_resolver.resolve(
|
let module = shared_take_or_clone(self.module_resolver.resolve(
|
||||||
self,
|
self,
|
||||||
|
None,
|
||||||
&path,
|
&path,
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
)?);
|
)?);
|
||||||
@ -1977,7 +1978,7 @@ impl Engine {
|
|||||||
crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level)
|
crate::optimize::optimize_into_ast(self, scope, stmt.into_vec(), lib, optimization_level)
|
||||||
}
|
}
|
||||||
/// Generate a list of all registered functions.
|
/// Generate a list of all registered functions.
|
||||||
/// Available under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
///
|
///
|
||||||
/// Functions from the following sources are included, in order:
|
/// Functions from the following sources are included, in order:
|
||||||
/// 1) Functions registered into the global namespace
|
/// 1) Functions registered into the global namespace
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//! Configuration settings for [`Engine`].
|
//! Configuration settings for [`Engine`].
|
||||||
|
|
||||||
use crate::engine::Precedence;
|
|
||||||
use crate::stdlib::{format, string::String};
|
use crate::stdlib::{format, string::String};
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::Engine;
|
use crate::Engine;
|
||||||
|
use crate::{engine::Precedence, Identifier};
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
use crate::stdlib::num::{NonZeroU64, NonZeroUsize};
|
use crate::stdlib::num::{NonZeroU64, NonZeroUsize};
|
||||||
@ -236,7 +236,7 @@ impl Engine {
|
|||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn disable_symbol(&mut self, symbol: impl Into<String>) -> &mut Self {
|
pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
|
||||||
self.disabled_symbols.insert(symbol.into());
|
self.disabled_symbols.insert(symbol.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -270,7 +270,7 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
pub fn register_custom_operator(
|
pub fn register_custom_operator(
|
||||||
&mut self,
|
&mut self,
|
||||||
keyword: impl AsRef<str> + Into<String>,
|
keyword: impl AsRef<str> + Into<Identifier>,
|
||||||
precedence: u8,
|
precedence: u8,
|
||||||
) -> Result<&mut Self, String> {
|
) -> Result<&mut Self, String> {
|
||||||
let precedence = Precedence::new(precedence);
|
let precedence = Precedence::new(precedence);
|
||||||
|
@ -100,7 +100,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ Create a new [`NativeCallContext`].
|
/// _(INTERNALS)_ Create a new [`NativeCallContext`].
|
||||||
/// Available under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -150,7 +150,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
self.mods.iter().flat_map(|&m| m.iter_raw())
|
self.mods.iter().flat_map(|&m| m.iter_raw())
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
||||||
/// Available under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -163,7 +163,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
self.lib.iter().cloned()
|
self.lib.iter().cloned()
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ The current set of namespaces containing definitions of all script-defined functions.
|
/// _(INTERNALS)_ The current set of namespaces containing definitions of all script-defined functions.
|
||||||
/// Available under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn namespaces(&self) -> &[&Module] {
|
pub fn namespaces(&self) -> &[&Module] {
|
||||||
|
@ -60,15 +60,15 @@ pub trait RegisterNativeFunction<Args, Result> {
|
|||||||
/// Get the type ID's of this function's parameters.
|
/// Get the type ID's of this function's parameters.
|
||||||
fn param_types() -> Box<[TypeId]>;
|
fn param_types() -> Box<[TypeId]>;
|
||||||
/// Get the type names of this function's parameters.
|
/// Get the type names of this function's parameters.
|
||||||
/// Available under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
fn param_names() -> Box<[&'static str]>;
|
fn param_names() -> Box<[&'static str]>;
|
||||||
/// Get the type ID of this function's return value.
|
/// Get the type ID of this function's return value.
|
||||||
/// Available under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
fn return_type() -> TypeId;
|
fn return_type() -> TypeId;
|
||||||
/// Get the type name of this function's return value.
|
/// Get the type name of this function's return value.
|
||||||
/// Available under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
fn return_type_name() -> &'static str;
|
fn return_type_name() -> &'static str;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ pub struct FuncInfo {
|
|||||||
|
|
||||||
impl FuncInfo {
|
impl FuncInfo {
|
||||||
/// Generate a signature of the function.
|
/// Generate a signature of the function.
|
||||||
/// Available under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub fn gen_signature(&self) -> String {
|
pub fn gen_signature(&self) -> String {
|
||||||
let mut sig = format!("{}(", self.name);
|
let mut sig = format!("{}(", self.name);
|
||||||
@ -363,7 +363,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate signatures for all the non-private functions in the [`Module`].
|
/// Generate signatures for all the non-private functions in the [`Module`].
|
||||||
/// Available under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
|
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
|
||||||
@ -605,7 +605,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update the metadata (parameter names/types and return type) of a registered function.
|
/// Update the metadata (parameter names/types and return type) of a registered function.
|
||||||
/// Available under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
///
|
///
|
||||||
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
|
||||||
///
|
///
|
||||||
|
@ -111,11 +111,12 @@ impl ModuleResolver for ModuleResolversCollection {
|
|||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
source_path: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
||||||
for resolver in self.0.iter() {
|
for resolver in self.0.iter() {
|
||||||
match resolver.resolve(engine, path, pos) {
|
match resolver.resolve(engine, source_path, path, pos) {
|
||||||
Ok(module) => return Ok(module),
|
Ok(module) => return Ok(module),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
EvalAltResult::ErrorModuleNotFound(_, _) => continue,
|
EvalAltResult::ErrorModuleNotFound(_, _) => continue,
|
||||||
|
@ -40,6 +40,7 @@ impl ModuleResolver for DummyModuleResolver {
|
|||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
_: &Engine,
|
_: &Engine,
|
||||||
|
_: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
||||||
|
@ -3,9 +3,10 @@ use crate::stdlib::{
|
|||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
io::Error as IoError,
|
io::Error as IoError,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
string::String,
|
|
||||||
};
|
};
|
||||||
use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
use crate::{Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, Shared};
|
||||||
|
|
||||||
|
pub const RHAI_SCRIPT_EXTENSION: &str = "rhai";
|
||||||
|
|
||||||
/// A [module][Module] resolution service that loads [module][Module] script files from the file system.
|
/// A [module][Module] resolution service that loads [module][Module] script files from the file system.
|
||||||
///
|
///
|
||||||
@ -39,8 +40,8 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FileModuleResolver {
|
pub struct FileModuleResolver {
|
||||||
base_path: PathBuf,
|
base_path: Option<PathBuf>,
|
||||||
extension: String,
|
extension: Identifier,
|
||||||
cache_enabled: bool,
|
cache_enabled: bool,
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
@ -52,11 +53,33 @@ pub struct FileModuleResolver {
|
|||||||
impl Default for FileModuleResolver {
|
impl Default for FileModuleResolver {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new_with_path(PathBuf::default())
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileModuleResolver {
|
impl FileModuleResolver {
|
||||||
|
/// Create a new [`FileModuleResolver`] with the current directory as base path.
|
||||||
|
///
|
||||||
|
/// The default extension is `.rhai`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::FileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'FileModuleResolver' loading scripts from the current directory
|
||||||
|
/// // with file extension '.rhai' (the default).
|
||||||
|
/// let resolver = FileModuleResolver::new();
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(resolver);
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::new_with_extension(RHAI_SCRIPT_EXTENSION)
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new [`FileModuleResolver`] with a specific base path.
|
/// Create a new [`FileModuleResolver`] with a specific base path.
|
||||||
///
|
///
|
||||||
/// The default extension is `.rhai`.
|
/// The default extension is `.rhai`.
|
||||||
@ -76,7 +99,31 @@ impl FileModuleResolver {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_with_path(path: impl Into<PathBuf>) -> Self {
|
pub fn new_with_path(path: impl Into<PathBuf>) -> Self {
|
||||||
Self::new_with_path_and_extension(path, "rhai")
|
Self::new_with_path_and_extension(path, RHAI_SCRIPT_EXTENSION)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new [`FileModuleResolver`] with a file extension.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Engine;
|
||||||
|
/// use rhai::module_resolvers::FileModuleResolver;
|
||||||
|
///
|
||||||
|
/// // Create a new 'FileModuleResolver' loading scripts with file extension '.rhai' (the default).
|
||||||
|
/// let resolver = FileModuleResolver::new_with_extension("rhai");
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
/// engine.set_module_resolver(resolver);
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new_with_extension(extension: impl Into<Identifier>) -> Self {
|
||||||
|
Self {
|
||||||
|
base_path: None,
|
||||||
|
extension: extension.into(),
|
||||||
|
cache_enabled: true,
|
||||||
|
cache: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`FileModuleResolver`] with a specific base path and file extension.
|
/// Create a new [`FileModuleResolver`] with a specific base path and file extension.
|
||||||
@ -97,47 +144,25 @@ impl FileModuleResolver {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_with_path_and_extension(
|
pub fn new_with_path_and_extension(
|
||||||
path: impl Into<PathBuf>,
|
path: impl Into<PathBuf>,
|
||||||
extension: impl Into<String>,
|
extension: impl Into<Identifier>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
base_path: path.into(),
|
base_path: Some(path.into()),
|
||||||
extension: extension.into(),
|
extension: extension.into(),
|
||||||
cache_enabled: true,
|
cache_enabled: true,
|
||||||
cache: Default::default(),
|
cache: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`FileModuleResolver`] with the current directory as base path.
|
|
||||||
///
|
|
||||||
/// The default extension is `.rhai`.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use rhai::Engine;
|
|
||||||
/// use rhai::module_resolvers::FileModuleResolver;
|
|
||||||
///
|
|
||||||
/// // Create a new 'FileModuleResolver' loading scripts from the current directory
|
|
||||||
/// // with file extension '.rhai' (the default).
|
|
||||||
/// let resolver = FileModuleResolver::new();
|
|
||||||
///
|
|
||||||
/// let mut engine = Engine::new();
|
|
||||||
/// engine.set_module_resolver(resolver);
|
|
||||||
/// ```
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the base path for script files.
|
/// Get the base path for script files.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn base_path(&self) -> &Path {
|
pub fn base_path(&self) -> Option<&Path> {
|
||||||
self.base_path.as_ref()
|
self.base_path.as_ref().map(PathBuf::as_ref)
|
||||||
}
|
}
|
||||||
/// Set the base path for script files.
|
/// Set the base path for script files.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_base_path(&mut self, path: impl Into<PathBuf>) -> &mut Self {
|
pub fn set_base_path(&mut self, path: impl Into<PathBuf>) -> &mut Self {
|
||||||
self.base_path = path.into();
|
self.base_path = Some(path.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +174,7 @@ impl FileModuleResolver {
|
|||||||
|
|
||||||
/// Set the script file extension.
|
/// Set the script file extension.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_extension(&mut self, extension: impl Into<String>) -> &mut Self {
|
pub fn set_extension(&mut self, extension: impl Into<Identifier>) -> &mut Self {
|
||||||
self.extension = extension.into();
|
self.extension = extension.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -168,12 +193,12 @@ impl FileModuleResolver {
|
|||||||
|
|
||||||
/// Is a particular path cached?
|
/// Is a particular path cached?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_cached(&self, path: &str) -> bool {
|
pub fn is_cached(&self, path: &str, source_path: Option<&str>) -> bool {
|
||||||
if !self.cache_enabled {
|
if !self.cache_enabled {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let file_path = self.get_file_path(path);
|
let file_path = self.get_file_path(path, source_path);
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return self.cache.borrow_mut().contains_key(&file_path);
|
return self.cache.borrow_mut().contains_key(&file_path);
|
||||||
@ -192,8 +217,12 @@ impl FileModuleResolver {
|
|||||||
///
|
///
|
||||||
/// The next time this path is resolved, the script file will be loaded once again.
|
/// The next time this path is resolved, the script file will be loaded once again.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_cache_for_path(&mut self, path: &str) -> Option<Shared<Module>> {
|
pub fn clear_cache_for_path(
|
||||||
let file_path = self.get_file_path(path);
|
&mut self,
|
||||||
|
path: &str,
|
||||||
|
source_path: Option<&str>,
|
||||||
|
) -> Option<Shared<Module>> {
|
||||||
|
let file_path = self.get_file_path(path, source_path);
|
||||||
|
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return self
|
return self
|
||||||
@ -210,10 +239,23 @@ impl FileModuleResolver {
|
|||||||
.map(|(_, v)| v);
|
.map(|(_, v)| v);
|
||||||
}
|
}
|
||||||
/// Construct a full file path.
|
/// Construct a full file path.
|
||||||
fn get_file_path(&self, path: &str) -> PathBuf {
|
fn get_file_path(&self, path: &str, source_path: Option<&str>) -> PathBuf {
|
||||||
let mut file_path = self.base_path.clone();
|
let path = Path::new(path);
|
||||||
file_path.push(path);
|
|
||||||
file_path.set_extension(&self.extension); // Force extension
|
let mut file_path;
|
||||||
|
|
||||||
|
if path.is_relative() {
|
||||||
|
file_path = self
|
||||||
|
.base_path
|
||||||
|
.clone()
|
||||||
|
.or_else(|| source_path.map(|p| p.into()))
|
||||||
|
.unwrap_or_default();
|
||||||
|
file_path.push(path);
|
||||||
|
} else {
|
||||||
|
file_path = path.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
file_path.set_extension(self.extension.as_str()); // Force extension
|
||||||
file_path
|
file_path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,11 +264,16 @@ impl ModuleResolver for FileModuleResolver {
|
|||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
source_path: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
||||||
|
// Load relative paths from source if there is no base path specified
|
||||||
|
let source_path =
|
||||||
|
source_path.and_then(|p| Path::new(p).parent().map(|p| p.to_string_lossy()));
|
||||||
|
|
||||||
// Construct the script file path
|
// Construct the script file path
|
||||||
let file_path = self.get_file_path(path);
|
let file_path = self.get_file_path(path, source_path.as_ref().map(|p| p.as_ref()));
|
||||||
|
|
||||||
// See if it is cached
|
// See if it is cached
|
||||||
if self.is_cache_enabled() {
|
if self.is_cache_enabled() {
|
||||||
@ -276,11 +323,12 @@ impl ModuleResolver for FileModuleResolver {
|
|||||||
fn resolve_ast(
|
fn resolve_ast(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
source_path: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Option<Result<crate::AST, Box<EvalAltResult>>> {
|
) -> Option<Result<crate::AST, Box<EvalAltResult>>> {
|
||||||
// Construct the script file path
|
// Construct the script file path
|
||||||
let file_path = self.get_file_path(path);
|
let file_path = self.get_file_path(path, source_path);
|
||||||
|
|
||||||
// Load the script file and compile it
|
// Load the script file and compile it
|
||||||
match engine.compile_file(file_path).map_err(|err| match *err {
|
match engine.compile_file(file_path).map_err(|err| match *err {
|
||||||
|
@ -25,6 +25,7 @@ pub trait ModuleResolver: SendSync {
|
|||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
source_path: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Shared<Module>, Box<EvalAltResult>>;
|
) -> Result<Shared<Module>, Box<EvalAltResult>>;
|
||||||
@ -42,6 +43,7 @@ pub trait ModuleResolver: SendSync {
|
|||||||
fn resolve_ast(
|
fn resolve_ast(
|
||||||
&self,
|
&self,
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
|
source_path: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Option<Result<AST, Box<EvalAltResult>>> {
|
) -> Option<Result<AST, Box<EvalAltResult>>> {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::stdlib::{boxed::Box, collections::BTreeMap, ops::AddAssign, string::String};
|
use crate::stdlib::{boxed::Box, collections::BTreeMap, ops::AddAssign};
|
||||||
use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
use crate::{Engine, EvalAltResult, Identifier, Module, ModuleResolver, Position, Shared};
|
||||||
|
|
||||||
/// A static [module][Module] resolution service that serves [modules][Module] added into it.
|
/// A static [module][Module] resolution service that serves [modules][Module] added into it.
|
||||||
///
|
///
|
||||||
@ -19,7 +19,7 @@ use crate::{Engine, EvalAltResult, Module, ModuleResolver, Position, Shared};
|
|||||||
/// engine.set_module_resolver(resolver);
|
/// engine.set_module_resolver(resolver);
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct StaticModuleResolver(BTreeMap<String, Shared<Module>>);
|
pub struct StaticModuleResolver(BTreeMap<Identifier, Shared<Module>>);
|
||||||
|
|
||||||
impl StaticModuleResolver {
|
impl StaticModuleResolver {
|
||||||
/// Create a new [`StaticModuleResolver`].
|
/// Create a new [`StaticModuleResolver`].
|
||||||
@ -44,7 +44,7 @@ impl StaticModuleResolver {
|
|||||||
}
|
}
|
||||||
/// Add a [module][Module] keyed by its path.
|
/// Add a [module][Module] keyed by its path.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn insert(&mut self, path: impl Into<String>, mut module: Module) {
|
pub fn insert(&mut self, path: impl Into<Identifier>, mut module: Module) {
|
||||||
module.build_index();
|
module.build_index();
|
||||||
self.0.insert(path.into(), module.into());
|
self.0.insert(path.into(), module.into());
|
||||||
}
|
}
|
||||||
@ -70,13 +70,13 @@ impl StaticModuleResolver {
|
|||||||
}
|
}
|
||||||
/// Get a mutable iterator of all the modules.
|
/// Get a mutable iterator of all the modules.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (String, Shared<Module>)> {
|
pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
|
||||||
self.0.into_iter()
|
self.0.into_iter()
|
||||||
}
|
}
|
||||||
/// Get an iterator of all the [module][Module] paths.
|
/// Get an iterator of all the [module][Module] paths.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
pub fn paths(&self) -> impl Iterator<Item = &str> {
|
||||||
self.0.keys().map(String::as_str)
|
self.0.keys().map(|s| s.as_str())
|
||||||
}
|
}
|
||||||
/// Get an iterator of all the [modules][Module].
|
/// Get an iterator of all the [modules][Module].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -115,6 +115,7 @@ impl ModuleResolver for StaticModuleResolver {
|
|||||||
fn resolve(
|
fn resolve(
|
||||||
&self,
|
&self,
|
||||||
_: &Engine,
|
_: &Engine,
|
||||||
|
_: Option<&str>,
|
||||||
path: &str,
|
path: &str,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
) -> Result<Shared<Module>, Box<EvalAltResult>> {
|
||||||
|
@ -10,8 +10,8 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
pub const FUNC_TO_STRING: &'static str = "to_string";
|
||||||
const FUNC_TO_DEBUG: &'static str = "to_debug";
|
pub const FUNC_TO_DEBUG: &'static str = "to_debug";
|
||||||
|
|
||||||
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
||||||
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
combine_with_exported_module!(lib, "print_debug", print_debug_functions);
|
||||||
@ -19,9 +19,8 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
|||||||
|
|
||||||
// Register print and debug
|
// Register print and debug
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn print_with_func(
|
pub fn print_with_func(
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
ctx: &NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
value: &mut Dynamic,
|
value: &mut Dynamic,
|
||||||
@ -39,17 +38,25 @@ fn print_with_func(
|
|||||||
mod print_debug_functions {
|
mod print_debug_functions {
|
||||||
use crate::ImmutableString;
|
use crate::ImmutableString;
|
||||||
|
|
||||||
#[rhai_fn(name = "print", name = "to_string", pure)]
|
#[rhai_fn(name = "print", pure)]
|
||||||
pub fn print_generic(item: &mut Dynamic) -> ImmutableString {
|
pub fn print_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
item.to_string().into()
|
print_with_func(FUNC_TO_STRING, &ctx, item)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "debug", name = "to_debug", pure)]
|
#[rhai_fn(name = "to_string", pure)]
|
||||||
pub fn debug_generic(item: &mut Dynamic) -> ImmutableString {
|
pub fn to_string_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
format!("{:?}", item).into()
|
ctx.engine().map_type_name(&item.to_string()).into()
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "debug", pure)]
|
||||||
|
pub fn debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
|
print_with_func(FUNC_TO_DEBUG, &ctx, item)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "to_debug", pure)]
|
||||||
|
pub fn to_debug_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString {
|
||||||
|
ctx.engine().map_type_name(&format!("{:?}", item)).into()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "print", name = "debug")]
|
#[rhai_fn(name = "print", name = "debug")]
|
||||||
pub fn print_empty_string() -> ImmutableString {
|
pub fn print_empty_string() -> ImmutableString {
|
||||||
"".to_string().into()
|
Default::default()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "print", name = "to_string")]
|
#[rhai_fn(name = "print", name = "to_string")]
|
||||||
pub fn print_string(s: ImmutableString) -> ImmutableString {
|
pub fn print_string(s: ImmutableString) -> ImmutableString {
|
||||||
|
@ -6,6 +6,8 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::{def_package, Dynamic, ImmutableString, StaticVec, INT};
|
use crate::{def_package, Dynamic, ImmutableString, StaticVec, INT};
|
||||||
|
|
||||||
|
use super::string_basic::{print_with_func, FUNC_TO_STRING};
|
||||||
|
|
||||||
def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, {
|
def_package!(crate:MoreStringPackage:"Additional string utilities, including string building.", lib, {
|
||||||
combine_with_exported_module!(lib, "string", string_functions);
|
combine_with_exported_module!(lib, "string", string_functions);
|
||||||
|
|
||||||
@ -21,22 +23,30 @@ mod string_functions {
|
|||||||
use crate::ImmutableString;
|
use crate::ImmutableString;
|
||||||
|
|
||||||
#[rhai_fn(name = "+", name = "append")]
|
#[rhai_fn(name = "+", name = "append")]
|
||||||
pub fn add_append(string: &str, item: Dynamic) -> ImmutableString {
|
pub fn add_append(ctx: NativeCallContext, string: &str, mut item: Dynamic) -> ImmutableString {
|
||||||
format!("{}{}", string, item).into()
|
let s = print_with_func(FUNC_TO_STRING, &ctx, &mut item);
|
||||||
|
format!("{}{}", string, s).into()
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+", pure)]
|
#[rhai_fn(name = "+", pure)]
|
||||||
pub fn add_prepend(item: &mut Dynamic, string: &str) -> ImmutableString {
|
pub fn add_prepend(
|
||||||
format!("{}{}", item, string).into()
|
ctx: NativeCallContext,
|
||||||
|
item: &mut Dynamic,
|
||||||
|
string: &str,
|
||||||
|
) -> ImmutableString {
|
||||||
|
let s = print_with_func(FUNC_TO_STRING, &ctx, item);
|
||||||
|
format!("{}{}", s, string).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add_append_unit(string: ImmutableString, _x: ()) -> ImmutableString {
|
pub fn add_append_unit(string: ImmutableString, _item: ()) -> ImmutableString {
|
||||||
string
|
string
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "+")]
|
#[rhai_fn(name = "+")]
|
||||||
pub fn add_prepend_unit(_x: (), string: ImmutableString) -> ImmutableString {
|
pub fn add_prepend_unit(_item: (), string: ImmutableString) -> ImmutableString {
|
||||||
string
|
string
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "+=")]
|
||||||
|
pub fn add_append_assign_unit(_string: &mut ImmutableString, _item: ()) {}
|
||||||
|
|
||||||
#[rhai_fn(name = "len", get = "len")]
|
#[rhai_fn(name = "len", get = "len")]
|
||||||
pub fn len(string: &str) -> INT {
|
pub fn len(string: &str) -> INT {
|
||||||
|
@ -1621,7 +1621,7 @@ fn parse_binary_op(
|
|||||||
Token::Custom(c) => state
|
Token::Custom(c) => state
|
||||||
.engine
|
.engine
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.get(c)
|
.get(c.as_str())
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| PERR::Reserved(c.clone()).into_err(*current_pos))?,
|
.ok_or_else(|| PERR::Reserved(c.clone()).into_err(*current_pos))?,
|
||||||
Token::Reserved(c) if !is_valid_identifier(c.chars()) => {
|
Token::Reserved(c) if !is_valid_identifier(c.chars()) => {
|
||||||
@ -1646,7 +1646,7 @@ fn parse_binary_op(
|
|||||||
Token::Custom(c) => state
|
Token::Custom(c) => state
|
||||||
.engine
|
.engine
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.get(c)
|
.get(c.as_str())
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| PERR::Reserved(c.clone()).into_err(*next_pos))?,
|
.ok_or_else(|| PERR::Reserved(c.clone()).into_err(*next_pos))?,
|
||||||
Token::Reserved(c) if !is_valid_identifier(c.chars()) => {
|
Token::Reserved(c) if !is_valid_identifier(c.chars()) => {
|
||||||
@ -1753,7 +1753,7 @@ fn parse_binary_op(
|
|||||||
if state
|
if state
|
||||||
.engine
|
.engine
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.get(&s)
|
.get(s.as_str())
|
||||||
.map_or(false, Option::is_some) =>
|
.map_or(false, Option::is_some) =>
|
||||||
{
|
{
|
||||||
let hash = calc_fn_hash(empty(), &s, 2);
|
let hash = calc_fn_hash(empty(), &s, 2);
|
||||||
|
@ -212,7 +212,7 @@ impl From<&crate::Module> for ModuleMetadata {
|
|||||||
impl Engine {
|
impl Engine {
|
||||||
/// _(METADATA)_ Generate a list of all functions (including those defined in an
|
/// _(METADATA)_ Generate a list of all functions (including those defined in an
|
||||||
/// [`AST`][crate::AST]) in JSON format.
|
/// [`AST`][crate::AST]) in JSON format.
|
||||||
/// Available under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
///
|
///
|
||||||
/// Functions from the following sources are included:
|
/// Functions from the following sources are included:
|
||||||
/// 1) Functions defined in an [`AST`][crate::AST]
|
/// 1) Functions defined in an [`AST`][crate::AST]
|
||||||
@ -221,9 +221,10 @@ impl Engine {
|
|||||||
/// 4) Functions in global modules (optional)
|
/// 4) Functions in global modules (optional)
|
||||||
pub fn gen_fn_metadata_with_ast_to_json(
|
pub fn gen_fn_metadata_with_ast_to_json(
|
||||||
&self,
|
&self,
|
||||||
_ast: &AST,
|
ast: &AST,
|
||||||
include_global: bool,
|
include_global: bool,
|
||||||
) -> serde_json::Result<String> {
|
) -> serde_json::Result<String> {
|
||||||
|
let _ast = ast;
|
||||||
let mut global: ModuleMetadata = Default::default();
|
let mut global: ModuleMetadata = Default::default();
|
||||||
|
|
||||||
if include_global {
|
if include_global {
|
||||||
|
@ -1828,7 +1828,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
None => return None,
|
None => return None,
|
||||||
// Reserved keyword/symbol
|
// Reserved keyword/symbol
|
||||||
Some((Token::Reserved(s), pos)) => (match
|
Some((Token::Reserved(s), pos)) => (match
|
||||||
(s.as_str(), self.engine.custom_keywords.contains_key(&s))
|
(s.as_str(), self.engine.custom_keywords.contains_key(s.as_str()))
|
||||||
{
|
{
|
||||||
("===", false) => Token::LexError(LERR::ImproperSymbol(s,
|
("===", false) => Token::LexError(LERR::ImproperSymbol(s,
|
||||||
"'===' is not a valid operator. This is not JavaScript! Should it be '=='?".to_string(),
|
"'===' is not a valid operator. This is not JavaScript! Should it be '=='?".to_string(),
|
||||||
@ -1869,7 +1869,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
(_, false) => Token::Reserved(s),
|
(_, false) => Token::Reserved(s),
|
||||||
}, pos),
|
}, pos),
|
||||||
// Custom keyword
|
// Custom keyword
|
||||||
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&s) => {
|
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(s.as_str()) => {
|
||||||
(Token::Custom(s), pos)
|
(Token::Custom(s), pos)
|
||||||
}
|
}
|
||||||
// Custom standard keyword/symbol - must be disabled
|
// Custom standard keyword/symbol - must be disabled
|
||||||
|
@ -1,84 +1,37 @@
|
|||||||
#![cfg(not(feature = "no_function"))]
|
#![cfg(not(feature = "no_function"))]
|
||||||
use rhai::{Engine, EvalAltResult, FnNamespace, Module, NativeCallContext, ParseErrorType, INT};
|
use rhai::{Engine, EvalAltResult, FnNamespace, Module, ParseErrorType, Shared, INT};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
fn test_functions_trait_object() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
trait TestTrait {
|
||||||
|
fn greet(&self) -> INT;
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("fn add(x, n) { x + n } add(40, 2)")?, 42);
|
#[derive(Debug, Clone)]
|
||||||
|
struct ABC(INT);
|
||||||
|
|
||||||
assert_eq!(
|
impl TestTrait for ABC {
|
||||||
engine.eval::<INT>("fn add(x, n,) { x + n } add(40, 2,)")?,
|
fn greet(&self) -> INT {
|
||||||
42
|
self.0
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert_eq!(
|
type MySharedTestTrait = Shared<dyn TestTrait>;
|
||||||
engine.eval::<INT>("fn add(x, n) { x + n } let a = 40; add(a, 2); a")?,
|
|
||||||
40
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>("fn add(n) { this + n } let x = 40; x.add(2)")?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>("fn add(n) { this += n; } let x = 40; x.add(2); x")?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("fn mul2(x) { x * 2 } mul2(21)")?, 42);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>("fn mul2(x) { x *= 2 } let a = 21; mul2(a); a")?,
|
|
||||||
21
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>("fn mul2() { this * 2 } let x = 21; x.mul2()")?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>("fn mul2() { this *= 2; } let x = 21; x.mul2(); x")?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
#[test]
|
|
||||||
fn test_functions_context() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.set_max_modules(40);
|
engine
|
||||||
engine.register_fn("test", |context: NativeCallContext, x: INT| {
|
.register_type_with_name::<MySharedTestTrait>("MySharedTestTrait")
|
||||||
context.engine().max_modules() as INT + x
|
.register_fn("new_ts", || Shared::new(ABC(42)) as MySharedTestTrait)
|
||||||
});
|
.register_fn("greet", |x: MySharedTestTrait| x.greet());
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("test(2)")?, 42);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_functions_params() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
// Expect duplicated parameters error
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
engine.eval::<String>("type_of(new_ts())")?,
|
||||||
.compile("fn hello(x, x) { x }")
|
"MySharedTestTrait"
|
||||||
.expect_err("should be error")
|
|
||||||
.0,
|
|
||||||
ParseErrorType::FnDuplicatedParam("hello".to_string(), "x".to_string())
|
|
||||||
);
|
);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = new_ts(); greet(x)")?, 42);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -110,152 +63,3 @@ fn test_functions_namespaces() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
assert_eq!(engine.eval::<String>(r#"type_of(Fn("abc"))"#)?, "Fn");
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { 40 + x }
|
|
||||||
|
|
||||||
let f = Fn("foo");
|
|
||||||
call(f, 2)
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { 40 + x }
|
|
||||||
|
|
||||||
let fn_name = "f";
|
|
||||||
fn_name += "oo";
|
|
||||||
|
|
||||||
let f = Fn(fn_name);
|
|
||||||
f.call(2)
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert!(matches!(
|
|
||||||
*engine.eval::<INT>(r#"let f = Fn("abc"); f.call(0)"#).expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with("abc (")
|
|
||||||
));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { 40 + x }
|
|
||||||
|
|
||||||
let x = #{ action: Fn("foo") };
|
|
||||||
x.action.call(2)
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { this.data += x; }
|
|
||||||
|
|
||||||
let x = #{ data: 40, action: Fn("foo") };
|
|
||||||
x.action(2);
|
|
||||||
x.data
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
42
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
fn test_function_captures() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
engine.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
fn foo(y) { x += y; x }
|
|
||||||
|
|
||||||
let x = 41;
|
|
||||||
let y = 999;
|
|
||||||
|
|
||||||
foo!(1) + x
|
|
||||||
"#
|
|
||||||
)?,
|
|
||||||
83
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(engine
|
|
||||||
.eval::<INT>(
|
|
||||||
r#"
|
|
||||||
fn foo(y) { x += y; x }
|
|
||||||
|
|
||||||
let x = 41;
|
|
||||||
let y = 999;
|
|
||||||
|
|
||||||
foo(1) + x
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.is_err());
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
assert!(matches!(
|
|
||||||
*engine
|
|
||||||
.compile(
|
|
||||||
r#"
|
|
||||||
fn foo() { this += x; }
|
|
||||||
|
|
||||||
let x = 41;
|
|
||||||
let y = 999;
|
|
||||||
|
|
||||||
y.foo!();
|
|
||||||
"#
|
|
||||||
)
|
|
||||||
.expect_err("should error")
|
|
||||||
.0,
|
|
||||||
ParseErrorType::MalformedCapture(_)
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_function_is_def() -> Result<(), Box<EvalAltResult>> {
|
|
||||||
let engine = Engine::new();
|
|
||||||
|
|
||||||
assert!(engine.eval::<bool>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { x + 1 }
|
|
||||||
is_def_fn("foo", 1)
|
|
||||||
"#
|
|
||||||
)?);
|
|
||||||
assert!(!engine.eval::<bool>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { x + 1 }
|
|
||||||
is_def_fn("bar", 1)
|
|
||||||
"#
|
|
||||||
)?);
|
|
||||||
assert!(!engine.eval::<bool>(
|
|
||||||
r#"
|
|
||||||
fn foo(x) { x + 1 }
|
|
||||||
is_def_fn("foo", 0)
|
|
||||||
"#
|
|
||||||
)?);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
@ -128,3 +128,36 @@ fn test_get_set_chain() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_set_op_assignment() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct Num(INT);
|
||||||
|
|
||||||
|
impl Num {
|
||||||
|
fn get(&mut self) -> INT {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
fn set(&mut self, x: INT) {
|
||||||
|
self.0 = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine
|
||||||
|
.register_type::<Num>()
|
||||||
|
.register_fn("new_ts", || Num(40))
|
||||||
|
.register_get_set("v", Num::get, Num::set);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<Num>("let a = new_ts(); a.v = a.v + 2; a")?,
|
||||||
|
Num(42)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<Num>("let a = new_ts(); a.v += 2; a")?,
|
||||||
|
Num(42)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -18,11 +18,54 @@ fn test_internal_fn() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("fn bob() { return 4; 5 } bob()")?, 4);
|
assert_eq!(engine.eval::<INT>("fn bob() { return 4; 5 } bob()")?, 4);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("fn add(x, n) { x + n } add(40, 2)")?, 42);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn add(x, n,) { x + n } add(40, 2,)")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn add(x, n) { x + n } let a = 40; add(a, 2); a")?,
|
||||||
|
40
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn add(n) { this + n } let x = 40; x.add(2)")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn add(n) { this += n; } let x = 40; x.add(2); x")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("fn mul2(x) { x * 2 } mul2(21)")?, 42);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn mul2(x) { x *= 2 } let a = 21; mul2(a); a")?,
|
||||||
|
21
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn mul2() { this * 2 } let x = 21; x.mul2()")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("fn mul2() { this *= 2; } let x = 21; x.mul2(); x")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_big_internal_fn() -> Result<(), Box<EvalAltResult>> {
|
fn test_internal_fn_big() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -73,3 +116,168 @@ fn test_internal_fn_overloading() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_internal_fn_params() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
// Expect duplicated parameters error
|
||||||
|
assert_eq!(
|
||||||
|
*engine
|
||||||
|
.compile("fn hello(x, x) { x }")
|
||||||
|
.expect_err("should be error")
|
||||||
|
.0,
|
||||||
|
ParseErrorType::FnDuplicatedParam("hello".to_string(), "x".to_string())
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<String>(r#"type_of(Fn("abc"))"#)?, "Fn");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { 40 + x }
|
||||||
|
|
||||||
|
let f = Fn("foo");
|
||||||
|
call(f, 2)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { 40 + x }
|
||||||
|
|
||||||
|
let fn_name = "f";
|
||||||
|
fn_name += "oo";
|
||||||
|
|
||||||
|
let f = Fn(fn_name);
|
||||||
|
f.call(2)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.eval::<INT>(r#"let f = Fn("abc"); f.call(0)"#).expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with("abc (")
|
||||||
|
));
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { 40 + x }
|
||||||
|
|
||||||
|
let x = #{ action: Fn("foo") };
|
||||||
|
x.action.call(2)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { this.data += x; }
|
||||||
|
|
||||||
|
let x = #{ data: 40, action: Fn("foo") };
|
||||||
|
x.action(2);
|
||||||
|
x.data
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
fn test_internal_fn_captures() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(y) { x += y; x }
|
||||||
|
|
||||||
|
let x = 41;
|
||||||
|
let y = 999;
|
||||||
|
|
||||||
|
foo!(1) + x
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
83
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(engine
|
||||||
|
.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(y) { x += y; x }
|
||||||
|
|
||||||
|
let x = 41;
|
||||||
|
let y = 999;
|
||||||
|
|
||||||
|
foo(1) + x
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.is_err());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.compile(
|
||||||
|
r#"
|
||||||
|
fn foo() { this += x; }
|
||||||
|
|
||||||
|
let x = 41;
|
||||||
|
let y = 999;
|
||||||
|
|
||||||
|
y.foo!();
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.expect_err("should error")
|
||||||
|
.0,
|
||||||
|
ParseErrorType::MalformedCapture(_)
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_internal_fn_is_def() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { x + 1 }
|
||||||
|
is_def_fn("foo", 1)
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
assert!(!engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { x + 1 }
|
||||||
|
is_def_fn("bar", 1)
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
assert!(!engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { x + 1 }
|
||||||
|
is_def_fn("foo", 0)
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -1,8 +1,24 @@
|
|||||||
use rhai::{Dynamic, Engine, EvalAltResult, NativeCallContext, INT};
|
use rhai::{Dynamic, Engine, EvalAltResult, NativeCallContext, INT};
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_native_context() -> Result<(), Box<EvalAltResult>> {
|
fn test_native_context() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine.set_max_modules(40);
|
||||||
|
engine.register_fn("test", |context: NativeCallContext, x: INT| {
|
||||||
|
context.engine().max_modules() as INT + x
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("test(2)")?, 42);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_native_context_fn_name() -> Result<(), Box<EvalAltResult>> {
|
||||||
fn add_double(
|
fn add_double(
|
||||||
context: NativeCallContext,
|
context: NativeCallContext,
|
||||||
args: &mut [&mut Dynamic],
|
args: &mut [&mut Dynamic],
|
||||||
@ -21,14 +37,14 @@ fn test_native_context() -> Result<(), Box<EvalAltResult>> {
|
|||||||
add_double,
|
add_double,
|
||||||
)
|
)
|
||||||
.register_raw_fn(
|
.register_raw_fn(
|
||||||
"adbl",
|
"append_x2",
|
||||||
&[TypeId::of::<INT>(), TypeId::of::<INT>()],
|
&[TypeId::of::<INT>(), TypeId::of::<INT>()],
|
||||||
add_double,
|
add_double,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<String>("add_double(40, 1)")?, "add_double_42");
|
assert_eq!(engine.eval::<String>("add_double(40, 1)")?, "add_double_42");
|
||||||
|
|
||||||
assert_eq!(engine.eval::<String>("adbl(40, 1)")?, "adbl_42");
|
assert_eq!(engine.eval::<String>("append_x2(40, 1)")?, "append_x2_42");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -222,6 +222,41 @@ fn test_string_substring() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[test]
|
||||||
|
fn test_string_format() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct TestStruct {
|
||||||
|
field: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine
|
||||||
|
.register_type_with_name::<TestStruct>("TestStruct")
|
||||||
|
.register_fn("new_ts", || TestStruct { field: 42 })
|
||||||
|
.register_fn("to_string", |ts: TestStruct| format!("TS={}", ts.field))
|
||||||
|
.register_fn("to_debug", |ts: TestStruct| {
|
||||||
|
format!("!!!TS={}!!!", ts.field)
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(r#"let x = new_ts(); "foo" + x"#)?,
|
||||||
|
"fooTS=42"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(r#"let x = new_ts(); x + "foo""#)?,
|
||||||
|
"TS=42foo"
|
||||||
|
);
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(r#"let x = [new_ts()]; "foo" + x"#)?,
|
||||||
|
"foo[!!!TS=42!!!]"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string_fn() -> Result<(), Box<EvalAltResult>> {
|
fn test_string_fn() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
Loading…
Reference in New Issue
Block a user