commit
caa95d807f
@ -15,6 +15,9 @@ Breaking changes
|
|||||||
* `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`.
|
* `EvalAltResult::ErrorLoopBreak` is renamed to `EvalAltResult::LoopBreak`.
|
||||||
* `Engine::register_raw_fn` and `FnPtr::call_dynamic` function signatures have changed.
|
* `Engine::register_raw_fn` and `FnPtr::call_dynamic` function signatures have changed.
|
||||||
* Callback signatures to `Engine::on_var` and `Engine::register_custom_syntax` have changed.
|
* Callback signatures to `Engine::on_var` and `Engine::register_custom_syntax` have changed.
|
||||||
|
* `EvalAltResult::ErrorRuntime` now wraps a `Dynamic` instead of a string.
|
||||||
|
* Default call stack depth for `debug` builds is reduced to 8 (from 12) because it keeps overflowing the stack in GitHub CI!
|
||||||
|
* Keyword `thread` is reserved.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -27,6 +30,7 @@ Enhancements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* Calling `eval` or `Fn` in method-call style, which is an error, is now caught during parsing.
|
* Calling `eval` or `Fn` in method-call style, which is an error, is now caught during parsing.
|
||||||
|
* `func!()` call style is valid even under `no_closure` feature.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.2
|
Version 0.19.2
|
||||||
|
@ -9,27 +9,27 @@
|
|||||||
//!
|
//!
|
||||||
//! #[export_module]
|
//! #[export_module]
|
||||||
//! mod advanced_math {
|
//! mod advanced_math {
|
||||||
//! pub const MYSTIC_NUMBER: FLOAT = 42.0 as FLOAT;
|
//! pub const MYSTIC_NUMBER: FLOAT = 42.0;
|
||||||
//!
|
//!
|
||||||
//! pub fn euclidean_distance(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT {
|
//! pub fn euclidean_distance(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT {
|
||||||
//! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt()
|
//! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt()
|
||||||
//! }
|
//! }
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), Box<EvalAltResult>> {
|
//! # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
//! let mut engine = Engine::new();
|
//! let mut engine = Engine::new();
|
||||||
//! let m = rhai::exported_module!(advanced_math);
|
//! let m = exported_module!(advanced_math);
|
||||||
//! let mut r = StaticModuleResolver::new();
|
//! let mut r = StaticModuleResolver::new();
|
||||||
//! r.insert("Math::Advanced".to_string(), m);
|
//! r.insert("Math::Advanced", m);
|
||||||
//! engine.set_module_resolver(Some(r));
|
//! engine.set_module_resolver(Some(r));
|
||||||
//!
|
//!
|
||||||
//! assert_eq!(engine.eval::<FLOAT>(
|
//! assert_eq!(engine.eval::<FLOAT>(
|
||||||
//! r#"import "Math::Advanced" as math;
|
//! r#"
|
||||||
//! let m = math::MYSTIC_NUMBER;
|
//! import "Math::Advanced" as math;
|
||||||
//! let x = math::euclidean_distance(0.0, 1.0, 0.0, m);
|
//! math::euclidean_distance(0.0, 1.0, 0.0, math::MYSTIC_NUMBER)
|
||||||
//! x"#)?, 41.0);
|
//! "#)?, 41.0);
|
||||||
//! Ok(())
|
//! # Ok(())
|
||||||
//! }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Register a Rust Function with a Rhai `Module`
|
//! # Register a Rust Function with a Rhai `Module`
|
||||||
@ -44,23 +44,22 @@
|
|||||||
//! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt()
|
//! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt()
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), Box<EvalAltResult>> {
|
//! # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
//! let mut engine = Engine::new();
|
||||||
|
//! engine.register_fn("get_mystic_number", || 42.0 as FLOAT);
|
||||||
|
//! let mut m = Module::new();
|
||||||
|
//! set_exported_fn!(m, "euclidean_distance", distance_function);
|
||||||
|
//! let mut r = StaticModuleResolver::new();
|
||||||
|
//! r.insert("Math::Advanced", m);
|
||||||
|
//! engine.set_module_resolver(Some(r));
|
||||||
//!
|
//!
|
||||||
//! let mut engine = Engine::new();
|
//! assert_eq!(engine.eval::<FLOAT>(
|
||||||
//! engine.register_fn("get_mystic_number", || { 42 as FLOAT });
|
//! r#"
|
||||||
//! let mut m = Module::new();
|
//! import "Math::Advanced" as math;
|
||||||
//! rhai::set_exported_fn!(m, "euclidean_distance", distance_function);
|
//! math::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number())
|
||||||
//! let mut r = StaticModuleResolver::new();
|
//! "#)?, 41.0);
|
||||||
//! r.insert("Math::Advanced".to_string(), m);
|
//! # Ok(())
|
||||||
//! engine.set_module_resolver(Some(r));
|
//! # }
|
||||||
//!
|
|
||||||
//! assert_eq!(engine.eval::<FLOAT>(
|
|
||||||
//! r#"import "Math::Advanced" as math;
|
|
||||||
//! let m = get_mystic_number();
|
|
||||||
//! let x = math::euclidean_distance(0.0, 1.0, 0.0, m);
|
|
||||||
//! x"#)?, 41.0);
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Register a Plugin Function with an `Engine`
|
//! # Register a Plugin Function with an `Engine`
|
||||||
@ -70,23 +69,21 @@
|
|||||||
//! use rhai::plugin::*;
|
//! use rhai::plugin::*;
|
||||||
//! use rhai::module_resolvers::*;
|
//! use rhai::module_resolvers::*;
|
||||||
//!
|
//!
|
||||||
//! #[rhai::export_fn]
|
//! #[export_fn]
|
||||||
//! pub fn distance_function(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT {
|
//! pub fn distance_function(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT {
|
||||||
//! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt()
|
//! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt()
|
||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! fn main() -> Result<(), Box<EvalAltResult>> {
|
//! # fn main() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
//! let mut engine = Engine::new();
|
||||||
|
//! engine.register_fn("get_mystic_number", || { 42 as FLOAT });
|
||||||
|
//! register_exported_fn!(engine, "euclidean_distance", distance_function);
|
||||||
//!
|
//!
|
||||||
//! let mut engine = Engine::new();
|
//! assert_eq!(engine.eval::<FLOAT>(
|
||||||
//! engine.register_fn("get_mystic_number", || { 42 as FLOAT });
|
//! "euclidean_distance(0.0, 1.0, 0.0, get_mystic_number())"
|
||||||
//! rhai::register_exported_fn!(engine, "euclidean_distance", distance_function);
|
//! )?, 41.0);
|
||||||
//!
|
//! # Ok(())
|
||||||
//! assert_eq!(engine.eval::<FLOAT>(
|
//! # }
|
||||||
//! r#"let m = get_mystic_number();
|
|
||||||
//! let x = euclidean_distance(0.0, 1.0, 0.0, m);
|
|
||||||
//! x"#)?, 41.0);
|
|
||||||
//! Ok(())
|
|
||||||
//! }
|
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
@ -17,13 +17,9 @@ fn raw_fn_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.register_fn("get_mystic_number", || 42 as FLOAT);
|
engine.register_fn("get_mystic_number", || 42 as FLOAT);
|
||||||
let mut m = Module::new();
|
let mut m = Module::new();
|
||||||
rhai::set_exported_fn!(
|
rhai::set_exported_fn!(m, "euclidean_distance", raw_fn::distance_function);
|
||||||
m,
|
|
||||||
"euclidean_distance".to_string(),
|
|
||||||
raw_fn::distance_function
|
|
||||||
);
|
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -53,7 +49,7 @@ fn raw_fn_mut_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut m = Module::new();
|
let mut m = Module::new();
|
||||||
rhai::set_exported_fn!(m, "add_in_place", raw_fn_mut::add_in_place);
|
rhai::set_exported_fn!(m, "add_in_place", raw_fn_mut::add_in_place);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -85,7 +81,7 @@ fn raw_fn_str_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut m = Module::new();
|
let mut m = Module::new();
|
||||||
rhai::set_exported_fn!(m, "write_out_str", raw_fn_str::write_out_str);
|
rhai::set_exported_fn!(m, "write_out_str", raw_fn_str::write_out_str);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Host::IO".to_string(), m);
|
r.insert("Host::IO", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -143,7 +139,7 @@ fn mut_opaque_ref_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
rhai::set_exported_fn!(m, "new_os_message", mut_opaque_ref::new_os_message);
|
rhai::set_exported_fn!(m, "new_os_message", mut_opaque_ref::new_os_message);
|
||||||
rhai::set_exported_fn!(m, "write_out_message", mut_opaque_ref::write_out_message);
|
rhai::set_exported_fn!(m, "write_out_message", mut_opaque_ref::write_out_message);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Host::Msg".to_string(), m);
|
r.insert("Host::Msg", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -182,13 +178,9 @@ fn raw_returning_fn_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.register_fn("get_mystic_number", || 42 as FLOAT);
|
engine.register_fn("get_mystic_number", || 42 as FLOAT);
|
||||||
let mut m = Module::new();
|
let mut m = Module::new();
|
||||||
rhai::set_exported_fn!(
|
rhai::set_exported_fn!(m, "euclidean_distance", raw_returning_fn::distance_function);
|
||||||
m,
|
|
||||||
"euclidean_distance".to_string(),
|
|
||||||
raw_returning_fn::distance_function
|
|
||||||
);
|
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -13,7 +13,7 @@ fn empty_module_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::empty_module::EmptyModule);
|
let m = rhai::exported_module!(crate::empty_module::EmptyModule);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Module::Empty".to_string(), m);
|
r.insert("Module::Empty", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -40,7 +40,7 @@ fn one_fn_module_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::one_fn_module::advanced_math);
|
let m = rhai::exported_module!(crate::one_fn_module::advanced_math);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -74,7 +74,7 @@ fn one_fn_and_const_module_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::one_fn_and_const_module::advanced_math);
|
let m = rhai::exported_module!(crate::one_fn_and_const_module::advanced_math);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -106,7 +106,7 @@ fn raw_fn_str_module_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::raw_fn_str_module::host_io);
|
let m = rhai::exported_module!(crate::raw_fn_str_module::host_io);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Host::IO".to_string(), m);
|
r.insert("Host::IO", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -163,7 +163,7 @@ fn mut_opaque_ref_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::mut_opaque_ref_module::host_msg);
|
let m = rhai::exported_module!(crate::mut_opaque_ref_module::host_msg);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Host::Msg".to_string(), m);
|
r.insert("Host::Msg", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -205,7 +205,7 @@ fn duplicate_fn_rename_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.register_fn("get_mystic_number", || 42 as FLOAT);
|
engine.register_fn("get_mystic_number", || 42 as FLOAT);
|
||||||
let m = rhai::exported_module!(crate::duplicate_fn_rename::my_adds);
|
let m = rhai::exported_module!(crate::duplicate_fn_rename::my_adds);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
let output_array = engine.eval::<Array>(
|
let output_array = engine.eval::<Array>(
|
||||||
@ -329,7 +329,7 @@ fn export_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::export_by_prefix::my_adds);
|
let m = rhai::exported_module!(crate::export_by_prefix::my_adds);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
let output_array = engine.eval::<Array>(
|
let output_array = engine.eval::<Array>(
|
||||||
@ -412,7 +412,7 @@ fn export_all_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::export_all::my_adds);
|
let m = rhai::exported_module!(crate::export_all::my_adds);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
let output_array = engine.eval::<Array>(
|
let output_array = engine.eval::<Array>(
|
||||||
|
@ -21,7 +21,7 @@ fn one_fn_module_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::one_fn_module_nested_attr::advanced_math);
|
let m = rhai::exported_module!(crate::one_fn_module_nested_attr::advanced_math);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -57,7 +57,7 @@ fn one_fn_submodule_nested_attr_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::one_fn_submodule_nested_attr::advanced_math);
|
let m = rhai::exported_module!(crate::one_fn_submodule_nested_attr::advanced_math);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -132,7 +132,7 @@ fn export_nested_by_prefix_test() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
let m = rhai::exported_module!(crate::export_nested_by_prefix::my_adds);
|
let m = rhai::exported_module!(crate::export_nested_by_prefix::my_adds);
|
||||||
let mut r = StaticModuleResolver::new();
|
let mut r = StaticModuleResolver::new();
|
||||||
r.insert("Math::Advanced".to_string(), m);
|
r.insert("Math::Advanced", m);
|
||||||
engine.set_module_resolver(Some(r));
|
engine.set_module_resolver(Some(r));
|
||||||
|
|
||||||
let output_array = engine.eval::<Array>(
|
let output_array = engine.eval::<Array>(
|
||||||
|
@ -61,6 +61,7 @@ Reserved Keywords
|
|||||||
| `with` | scope |
|
| `with` | scope |
|
||||||
| `module` | module |
|
| `module` | module |
|
||||||
| `package` | package |
|
| `package` | package |
|
||||||
|
| `thread` | threading |
|
||||||
| `spawn` | threading |
|
| `spawn` | threading |
|
||||||
| `go` | threading |
|
| `go` | threading |
|
||||||
| `await` | async |
|
| `await` | async |
|
||||||
|
@ -16,8 +16,6 @@ it raises an evaluation error.
|
|||||||
It is possible, through a special syntax, to capture the calling scope - i.e. the scope
|
It is possible, through a special syntax, to capture the calling scope - i.e. the scope
|
||||||
that makes the function call - and access variables defined there.
|
that makes the function call - and access variables defined there.
|
||||||
|
|
||||||
Capturing can be disabled via the [`no_closure`] feature.
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined
|
fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined
|
||||||
x += y; // 'x' is modified in this function
|
x += y; // 'x' is modified in this function
|
||||||
|
@ -5,21 +5,21 @@ Keywords
|
|||||||
|
|
||||||
The following are reserved keywords in Rhai:
|
The following are reserved keywords in Rhai:
|
||||||
|
|
||||||
| Active keywords | Reserved keywords | Usage | Inactive under feature |
|
| Active keywords | Reserved keywords | Usage | Inactive under feature |
|
||||||
| ------------------------------------------------- | ------------------------------------------------ | --------------------- | :--------------------: |
|
| ------------------------------------------------- | ---------------------------------------------------------- | ---------------------- | :--------------------: |
|
||||||
| `true`, `false` | | boolean constants | |
|
| `true`, `false` | | boolean constants | |
|
||||||
| `let`, `const` | `var`, `static` | variable declarations | |
|
| `let`, `const` | `var`, `static` | variable declarations | |
|
||||||
| `is_shared` | | shared values | [`no_closure`] |
|
| `is_shared` | | shared values | [`no_closure`] |
|
||||||
| `if`, `else` | `then`, `goto`, `exit` | control flow | |
|
| `if`, `else` | `then`, `goto`, `exit` | control flow | |
|
||||||
| | `switch`, `match`, `case` | matching | |
|
| | `switch`, `match`, `case` | matching | |
|
||||||
| `while`, `loop`, `for`, `in`, `continue`, `break` | `do`, `each` | looping | |
|
| `while`, `loop`, `for`, `in`, `continue`, `break` | `do`, `each` | looping | |
|
||||||
| `fn`, `private` | `public`, `new` | functions | [`no_function`] |
|
| `fn`, `private` | `public`, `new` | functions | [`no_function`] |
|
||||||
| `return` | | return values | |
|
| `return` | | return values | |
|
||||||
| `throw` | `try`, `catch` | throw exceptions | |
|
| `throw`, `try`, `catch` | | throw/catch exceptions | |
|
||||||
| `import`, `export`, `as` | `use`, `with`, `module`, `package` | modules/packages | [`no_module`] |
|
| `import`, `export`, `as` | `use`, `with`, `module`, `package` | modules/packages | [`no_module`] |
|
||||||
| `Fn`, `call`, `curry` | | function pointers | |
|
| `Fn`, `call`, `curry` | | function pointers | |
|
||||||
| | `spawn`, `go`, `sync`, `async`, `await`, `yield` | threading/async | |
|
| | `spawn`, `thread`, `go`, `sync`, `async`, `await`, `yield` | threading/async | |
|
||||||
| `type_of`, `print`, `debug`, `eval` | | special functions | |
|
| `type_of`, `print`, `debug`, `eval` | | special functions | |
|
||||||
| | `default`, `void`, `null`, `nil` | special values | |
|
| | `default`, `void`, `null`, `nil` | special values | |
|
||||||
|
|
||||||
Keywords cannot become the name of a [function] or [variable], even when they are disabled.
|
Keywords cannot become the name of a [function] or [variable], even when they are disabled.
|
||||||
|
@ -4,11 +4,12 @@ Catch Exceptions
|
|||||||
{{#include ../links.md}}
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
|
||||||
When an [exception] is thrown via a `throw` statement, evaluation of the script halts
|
When an [exception] is thrown via a [`throw`] statement, evaluation of the script halts
|
||||||
and the [`Engine`] returns with `Err(Box<EvalAltResult::ErrorRuntime>)` containing the
|
and the [`Engine`] returns with `Err(Box<EvalAltResult::ErrorRuntime>)` containing the
|
||||||
exception value that has been thrown.
|
exception value that has been thrown.
|
||||||
|
|
||||||
It is possible, via the `try` ... `catch` statement, to _catch_ exceptions.
|
It is possible, via the `try` ... `catch` statement, to _catch_ exceptions, optionally
|
||||||
|
with an _error variable_.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Catch an exception and capturing its value
|
// Catch an exception and capturing its value
|
||||||
@ -26,7 +27,7 @@ try
|
|||||||
{
|
{
|
||||||
print(42/0); // deliberate divide-by-zero exception
|
print(42/0); // deliberate divide-by-zero exception
|
||||||
}
|
}
|
||||||
catch // no catch variable - exception value is discarded
|
catch // no error variable - exception value is discarded
|
||||||
{
|
{
|
||||||
print("Ouch!");
|
print("Ouch!");
|
||||||
}
|
}
|
||||||
@ -50,7 +51,7 @@ Re-Throw Exception
|
|||||||
------------------
|
------------------
|
||||||
|
|
||||||
Like the `try` ... `catch` syntax in most languages, it is possible to _re-throw_
|
Like the `try` ... `catch` syntax in most languages, it is possible to _re-throw_
|
||||||
an exception within the `catch` block simply by another `throw` statement without
|
an exception within the `catch` block simply by another [`throw`] statement without
|
||||||
a value.
|
a value.
|
||||||
|
|
||||||
|
|
||||||
@ -76,20 +77,23 @@ Catchable Exceptions
|
|||||||
|
|
||||||
Many script-oriented exceptions can be caught via `try` ... `catch`:
|
Many script-oriented exceptions can be caught via `try` ... `catch`:
|
||||||
|
|
||||||
* Runtime error thrown by a `throw` statement
|
| Error type | Error value |
|
||||||
* Arithmetic error
|
| --------------------------------------------- | :------------------------: |
|
||||||
* Variable not found
|
| Runtime error thrown by a [`throw`] statement | value in `throw` statement |
|
||||||
* [Function] not found
|
| Other runtime error | error message [string] |
|
||||||
* [Module] not found
|
| Arithmetic error | error message [string] |
|
||||||
* Unbound [`this`]
|
| Variable not found | error message [string] |
|
||||||
* Data type mismatch
|
| [Function] not found | error message [string] |
|
||||||
* [Array]/[string] indexing out-of-bounds
|
| [Module] not found | error message [string] |
|
||||||
* Indexing with an inappropriate type
|
| Unbound [`this`] | error message [string] |
|
||||||
* `for` statement without an iterator
|
| Data type mismatch | error message [string] |
|
||||||
* Error in an `in` expression
|
| Assignment to a calculated/constant value | error message [string] |
|
||||||
* Data race detected
|
| [Array]/[string] indexing out-of-bounds | error message [string] |
|
||||||
* Assignment to a calculated value/constant value
|
| Indexing with an inappropriate data type | error message [string] |
|
||||||
* Dot expression error
|
| Error in a dot expression | error message [string] |
|
||||||
|
| `for` statement without an iterator | error message [string] |
|
||||||
|
| Error in an `in` expression | error message [string] |
|
||||||
|
| Data race detected | error message [string] |
|
||||||
|
|
||||||
|
|
||||||
Non-Catchable Exceptions
|
Non-Catchable Exceptions
|
||||||
@ -99,6 +103,6 @@ Some exceptions _cannot_ be caught:
|
|||||||
|
|
||||||
* Syntax error during parsing
|
* Syntax error during parsing
|
||||||
* System error - e.g. script file not found
|
* System error - e.g. script file not found
|
||||||
* Script evaluation over [limits]({{rootUrl}}/safety/index.md)
|
* Script evaluation metrics over [safety limits]({{rootUrl}}/safety/index.md)
|
||||||
* [Stack overflow][maximum call stack depth]
|
* Function calls nesting exceeding [maximum call stack depth]
|
||||||
* Script evaluation manually terminated
|
* Script evaluation manually terminated
|
||||||
|
@ -89,6 +89,7 @@
|
|||||||
[function overloading]: {{rootUrl}}/rust/functions.md#function-overloading
|
[function overloading]: {{rootUrl}}/rust/functions.md#function-overloading
|
||||||
[fallible function]: {{rootUrl}}/rust/fallible.md
|
[fallible function]: {{rootUrl}}/rust/fallible.md
|
||||||
[fallible functions]: {{rootUrl}}/rust/fallible.md
|
[fallible functions]: {{rootUrl}}/rust/fallible.md
|
||||||
|
[`throw`]: {{rootUrl}}/language/throw.md
|
||||||
[exception]: {{rootUrl}}/language/throw.md
|
[exception]: {{rootUrl}}/language/throw.md
|
||||||
[exceptions]: {{rootUrl}}/language/throw.md
|
[exceptions]: {{rootUrl}}/language/throw.md
|
||||||
[function pointer]: {{rootUrl}}/language/fn-ptr.md
|
[function pointer]: {{rootUrl}}/language/fn-ptr.md
|
||||||
|
@ -33,3 +33,13 @@ The most important resources to watch out for are:
|
|||||||
|
|
||||||
* **Data**: A malicious script may attempt to read from and/or write to data that it does not own. If this happens,
|
* **Data**: A malicious script may attempt to read from and/or write to data that it does not own. If this happens,
|
||||||
it is a severe security breach and may put the entire system at risk.
|
it is a severe security breach and may put the entire system at risk.
|
||||||
|
|
||||||
|
|
||||||
|
`unchecked`
|
||||||
|
-----------
|
||||||
|
|
||||||
|
All these safe-guards can be turned off via the [`unchecked`] feature, which disables all
|
||||||
|
safety checks (even fatal errors such as arithmetic overflows and division-by-zero).
|
||||||
|
|
||||||
|
This will increase script evaluation performance, at the expense of having an erroneous
|
||||||
|
script able to panic the entire system.
|
||||||
|
@ -1695,11 +1695,11 @@ impl Engine {
|
|||||||
|
|
||||||
// Normal function call
|
// Normal function call
|
||||||
Expr::FnCall(x) if x.1.is_none() => {
|
Expr::FnCall(x) if x.1.is_none() => {
|
||||||
let ((name, native, capture, pos), _, hash, args_expr, def_val) = x.as_ref();
|
let ((name, native, cap_scope, pos), _, hash, args_expr, def_val) = x.as_ref();
|
||||||
let def_val = def_val.map(Into::<Dynamic>::into);
|
let def_val = def_val.map(Into::<Dynamic>::into);
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, mods, state, lib, this_ptr, name, args_expr, &def_val, *hash, *native,
|
scope, mods, state, lib, this_ptr, name, args_expr, &def_val, *hash, *native,
|
||||||
false, *capture, level,
|
false, *cap_scope, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
}
|
}
|
||||||
@ -1918,8 +1918,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.rewind(scope.len() - 1);
|
|
||||||
state.scope_level -= 1;
|
state.scope_level -= 1;
|
||||||
|
scope.rewind(scope.len() - 1);
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
} else {
|
} else {
|
||||||
EvalAltResult::ErrorFor(x.1.position()).into()
|
EvalAltResult::ErrorFor(x.1.position()).into()
|
||||||
@ -1940,52 +1940,47 @@ impl Engine {
|
|||||||
.eval_stmt(scope, mods, state, lib, this_ptr, body, level)
|
.eval_stmt(scope, mods, state, lib, this_ptr, body, level)
|
||||||
.map(|_| ().into());
|
.map(|_| ().into());
|
||||||
|
|
||||||
if let Err(err) = result {
|
match result {
|
||||||
match *err {
|
Ok(_) => result,
|
||||||
|
Err(err) => match *err {
|
||||||
mut err @ EvalAltResult::ErrorRuntime(_, _) | mut err
|
mut err @ EvalAltResult::ErrorRuntime(_, _) | mut err
|
||||||
if err.catchable() =>
|
if err.catchable() =>
|
||||||
{
|
{
|
||||||
let value = match err {
|
let value = if let EvalAltResult::ErrorRuntime(ref x, _) = err {
|
||||||
EvalAltResult::ErrorRuntime(ref x, _) => x.clone(),
|
x.clone()
|
||||||
_ => {
|
} else {
|
||||||
err.set_position(Position::none());
|
err.set_position(Position::none());
|
||||||
err.to_string().into()
|
err.to_string().into()
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let has_var = if let Some((var_name, _)) = var_def {
|
|
||||||
|
let orig_scope_len = scope.len();
|
||||||
|
state.scope_level += 1;
|
||||||
|
|
||||||
|
if let Some((var_name, _)) = var_def {
|
||||||
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
let var_name = unsafe_cast_var_name_to_lifetime(var_name, &state);
|
||||||
scope.push(var_name, value);
|
scope.push(var_name, value);
|
||||||
state.scope_level += 1;
|
}
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut result = self
|
let mut result = self
|
||||||
.eval_stmt(scope, mods, state, lib, this_ptr, catch_body, level)
|
.eval_stmt(scope, mods, state, lib, this_ptr, catch_body, level)
|
||||||
.map(|_| ().into());
|
.map(|_| ().into());
|
||||||
|
|
||||||
if let Some(result_err) = result.as_ref().err() {
|
if let Some(result_err) = result.as_ref().err() {
|
||||||
match result_err.as_ref() {
|
if let EvalAltResult::ErrorRuntime(Dynamic(Union::Unit(_)), pos) =
|
||||||
EvalAltResult::ErrorRuntime(x, pos) if x.is::<()>() => {
|
result_err.as_ref()
|
||||||
err.set_position(*pos);
|
{
|
||||||
result = Err(Box::new(err));
|
err.set_position(*pos);
|
||||||
}
|
result = Err(Box::new(err));
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_var {
|
state.scope_level -= 1;
|
||||||
scope.rewind(scope.len() - 1);
|
scope.rewind(orig_scope_len);
|
||||||
state.scope_level -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
}
|
},
|
||||||
} else {
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,7 +490,7 @@ impl Engine {
|
|||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
_is_method: bool,
|
_is_method: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
_capture: Option<Scope>,
|
_capture_scope: Option<Scope>,
|
||||||
def_val: &Option<Dynamic>,
|
def_val: &Option<Dynamic>,
|
||||||
_level: usize,
|
_level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
@ -562,7 +562,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Move captured variables into scope
|
// Move captured variables into scope
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if let Some(captured) = _capture {
|
if let Some(captured) = _capture_scope {
|
||||||
captured
|
captured
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|ScopeEntry { name, .. }| {
|
.filter(|ScopeEntry { name, .. }| {
|
||||||
@ -868,7 +868,7 @@ impl Engine {
|
|||||||
mut hash_script: u64,
|
mut hash_script: u64,
|
||||||
native: bool,
|
native: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
capture: bool,
|
capture_scope: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let args_expr = args_expr.as_ref();
|
let args_expr = args_expr.as_ref();
|
||||||
@ -1043,7 +1043,7 @@ impl Engine {
|
|||||||
let mut arg_values: StaticVec<_>;
|
let mut arg_values: StaticVec<_>;
|
||||||
let mut args: StaticVec<_>;
|
let mut args: StaticVec<_>;
|
||||||
let mut is_ref = false;
|
let mut is_ref = false;
|
||||||
let capture = if cfg!(not(feature = "no_closure")) && capture && !scope.is_empty() {
|
let capture = if capture_scope && !scope.is_empty() {
|
||||||
Some(scope.clone_visible())
|
Some(scope.clone_visible())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -16,7 +16,7 @@ use crate::stdlib::{boxed::Box, collections::HashMap, ops::AddAssign, string::St
|
|||||||
/// let mut resolver = StaticModuleResolver::new();
|
/// let mut resolver = StaticModuleResolver::new();
|
||||||
///
|
///
|
||||||
/// let module = Module::new();
|
/// let module = Module::new();
|
||||||
/// resolver.insert("hello".to_string(), module);
|
/// resolver.insert("hello", module);
|
||||||
///
|
///
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
|
@ -1998,7 +1998,6 @@ fn parse_primary(
|
|||||||
|
|
||||||
root_expr = match (root_expr, token) {
|
root_expr = match (root_expr, token) {
|
||||||
// Qualified function call with !
|
// Qualified function call with !
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
(Expr::Variable(x), Token::Bang) if x.1.is_some() => {
|
(Expr::Variable(x), Token::Bang) if x.1.is_some() => {
|
||||||
return Err(if !match_token(input, Token::LeftParen).0 {
|
return Err(if !match_token(input, Token::LeftParen).0 {
|
||||||
LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos)
|
LexError::UnexpectedInput(Token::Bang.syntax().to_string()).into_err(token_pos)
|
||||||
@ -2008,7 +2007,6 @@ fn parse_primary(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Function call with !
|
// Function call with !
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
(Expr::Variable(x), Token::Bang) => {
|
(Expr::Variable(x), Token::Bang) => {
|
||||||
let (matched, pos) = match_token(input, Token::LeftParen);
|
let (matched, pos) = match_token(input, Token::LeftParen);
|
||||||
if !matched {
|
if !matched {
|
||||||
|
@ -525,8 +525,8 @@ impl Token {
|
|||||||
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" | "public"
|
"===" | "!==" | "->" | "<-" | "=>" | ":=" | "::<" | "(*" | "*)" | "#" | "public"
|
||||||
| "new" | "use" | "module" | "package" | "var" | "static" | "shared" | "with"
|
| "new" | "use" | "module" | "package" | "var" | "static" | "shared" | "with"
|
||||||
| "do" | "each" | "then" | "goto" | "exit" | "switch" | "match" | "case"
|
| "do" | "each" | "then" | "goto" | "exit" | "switch" | "match" | "case"
|
||||||
| "default" | "void" | "null" | "nil" | "spawn" | "go" | "sync" | "async" | "await"
|
| "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
|
||||||
| "yield" => Reserved(syntax.into()),
|
| "async" | "await" | "yield" => Reserved(syntax.into()),
|
||||||
|
|
||||||
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
|
||||||
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR
|
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR
|
||||||
|
@ -158,13 +158,13 @@ fn test_function_captures() -> Result<(), Box<EvalAltResult>> {
|
|||||||
*engine
|
*engine
|
||||||
.compile(
|
.compile(
|
||||||
r#"
|
r#"
|
||||||
fn foo() { this += x; }
|
fn foo() { this += x; }
|
||||||
|
|
||||||
let x = 41;
|
let x = 41;
|
||||||
let y = 999;
|
let y = 999;
|
||||||
|
|
||||||
y.foo!();
|
y.foo!();
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
.0,
|
.0,
|
||||||
|
Loading…
Reference in New Issue
Block a user