commit
160f72b4cd
@ -11,6 +11,7 @@ Buf fixes
|
|||||||
|
|
||||||
* `is_shared` is a reserved keyword and is now handled properly (e.g. it cannot be the target of a function pointer).
|
* `is_shared` is a reserved keyword and is now handled properly (e.g. it cannot be the target of a function pointer).
|
||||||
* Re-optimizing an AST via `optimize_ast` with constants now works correctly for closures. Previously the hidden `Share` nodes are not removed and causes variable-not-found errors during runtime if the constants are not available in the scope.
|
* Re-optimizing an AST via `optimize_ast` with constants now works correctly for closures. Previously the hidden `Share` nodes are not removed and causes variable-not-found errors during runtime if the constants are not available in the scope.
|
||||||
|
* Expressions such as `(v[0].func()).prop` now parse correctly.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -18,6 +19,11 @@ New features
|
|||||||
* It is now possible to require a specific _type_ to the `this` pointer for a particular script-defined function so that it is called only when the `this` pointer contains the specified type.
|
* It is now possible to require a specific _type_ to the `this` pointer for a particular script-defined function so that it is called only when the `this` pointer contains the specified type.
|
||||||
* `is_def_fn` is extended to support checking for typed methods, with syntax `is_def_fn(this_type, fn_name, arity)`
|
* `is_def_fn` is extended to support checking for typed methods, with syntax `is_def_fn(this_type, fn_name, arity)`
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* `Engine::is_symbol_disabled` is added to test whether a particular keyword/symbol is disabled.
|
||||||
|
|
||||||
|
|
||||||
Version 1.13.0
|
Version 1.13.0
|
||||||
==============
|
==============
|
||||||
|
@ -11,21 +11,23 @@ Root Sources
|
|||||||
| `tokenizer.rs` | Script tokenizer/lexer |
|
| `tokenizer.rs` | Script tokenizer/lexer |
|
||||||
| `parser.rs` | Script parser |
|
| `parser.rs` | Script parser |
|
||||||
| `optimizer.rs` | Script optimizer |
|
| `optimizer.rs` | Script optimizer |
|
||||||
|
| `defer.rs` | Utilities for deferred clean-up of resources |
|
||||||
| `reify.rs` | Utilities for making generic types concrete |
|
| `reify.rs` | Utilities for making generic types concrete |
|
||||||
| `tests.rs` | Unit tests (not integration tests, which are in the `rhai/tests` sub-directory) |
|
| `tests.rs` | Unit tests (not integration tests, which are in the main `tests` sub-directory) |
|
||||||
|
|
||||||
|
|
||||||
Sub-Directories
|
Sub-Directories
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
| Sub-directory | Description |
|
| Sub-directory | Description |
|
||||||
| ------------- | ----------------------------------------------------- |
|
| ------------- | ------------------------------------------------------------------ |
|
||||||
| `types` | Common data types (e.g. `Dynamic`, errors) |
|
| `config` | Configuration |
|
||||||
| `api` | Public API for the scripting engine |
|
| `types` | Common data types (e.g. `Dynamic`, errors) |
|
||||||
| `ast` | AST definition |
|
| `api` | Public API for the scripting engine |
|
||||||
| `module` | Support for modules |
|
| `ast` | AST definition |
|
||||||
| `packages` | Pre-defined packages |
|
| `module` | Support for modules |
|
||||||
| `func` | Support for function calls |
|
| `packages` | Pre-defined packages |
|
||||||
| `eval` | Evaluation engine |
|
| `func` | Registering and calling functions (native Rust and script-defined) |
|
||||||
| `serde` | Support for [`serde`](https://crates.io/crates/serde) |
|
| `eval` | AST evaluation |
|
||||||
| `bin` | Pre-built CLI binaries (e.g. `rhai-run`, `rhai-repl`) |
|
| `serde` | Support for [`serde`](https://crates.io/crates/serde) and metadata |
|
||||||
|
| `bin` | Pre-built CLI binaries |
|
||||||
|
@ -259,15 +259,9 @@ impl Engine {
|
|||||||
// Keyword/symbol not in first position
|
// Keyword/symbol not in first position
|
||||||
_ if !segments.is_empty() && token.is_some() => {
|
_ if !segments.is_empty() && token.is_some() => {
|
||||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||||
if (self
|
if (self.is_symbol_disabled(s)
|
||||||
.disabled_symbols
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |m| m.contains(s))
|
|
||||||
|| token.as_ref().map_or(false, Token::is_reserved))
|
|| token.as_ref().map_or(false, Token::is_reserved))
|
||||||
&& !self
|
&& !self.is_custom_keyword(s)
|
||||||
.custom_keywords
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |m| m.contains_key(s))
|
|
||||||
{
|
{
|
||||||
self.custom_keywords
|
self.custom_keywords
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
@ -279,10 +273,7 @@ impl Engine {
|
|||||||
// Standard keyword in first position but not disabled
|
// Standard keyword in first position but not disabled
|
||||||
_ if segments.is_empty()
|
_ if segments.is_empty()
|
||||||
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
||||||
&& !self
|
&& !self.is_symbol_disabled(s) =>
|
||||||
.disabled_symbols
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |m| m.contains(s)) =>
|
|
||||||
{
|
{
|
||||||
return Err(LexError::ImproperSymbol(
|
return Err(LexError::ImproperSymbol(
|
||||||
s.to_string(),
|
s.to_string(),
|
||||||
@ -299,15 +290,9 @@ impl Engine {
|
|||||||
&& (is_valid_identifier(s) || is_reserved_keyword_or_symbol(s).0) =>
|
&& (is_valid_identifier(s) || is_reserved_keyword_or_symbol(s).0) =>
|
||||||
{
|
{
|
||||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||||
if self
|
if self.is_symbol_disabled(s)
|
||||||
.disabled_symbols
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |m| m.contains(s))
|
|
||||||
|| (token.as_ref().map_or(false, Token::is_reserved)
|
|| (token.as_ref().map_or(false, Token::is_reserved)
|
||||||
&& !self
|
&& !self.is_custom_keyword(s))
|
||||||
.custom_keywords
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |m| m.contains_key(s)))
|
|
||||||
{
|
{
|
||||||
self.custom_keywords
|
self.custom_keywords
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
|
@ -457,12 +457,7 @@ impl Module {
|
|||||||
!f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name);
|
!f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
let operator = operator
|
let operator = operator || def.engine.is_custom_keyword(f.metadata.name.as_str());
|
||||||
|| def
|
|
||||||
.engine
|
|
||||||
.custom_keywords
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
|
|
||||||
|
|
||||||
f.write_definition(writer, def, operator)?;
|
f.write_definition(writer, def, operator)?;
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline]
|
||||||
pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
|
pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
|
||||||
self.disabled_symbols
|
self.disabled_symbols
|
||||||
.get_or_insert_with(Default::default)
|
.get_or_insert_with(Default::default)
|
||||||
@ -117,6 +117,26 @@ impl Engine {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is a particular keyword or operator disabled?
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rhai::Engine;
|
||||||
|
///
|
||||||
|
/// let mut engine = Engine::new();
|
||||||
|
///
|
||||||
|
/// engine.disable_symbol("if"); // disable the 'if' keyword
|
||||||
|
///
|
||||||
|
/// assert!(engine.is_symbol_disabled("if"));
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn is_symbol_disabled(&self, symbol: &str) -> bool {
|
||||||
|
self.disabled_symbols
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |m| m.contains(symbol))
|
||||||
|
}
|
||||||
|
|
||||||
/// Register a custom operator with a precedence into the language.
|
/// Register a custom operator with a precedence into the language.
|
||||||
///
|
///
|
||||||
/// Not available under `no_custom_syntax`.
|
/// Not available under `no_custom_syntax`.
|
||||||
@ -168,32 +188,21 @@ impl Engine {
|
|||||||
Some(Token::Custom(..)) => (),
|
Some(Token::Custom(..)) => (),
|
||||||
// Active standard keywords cannot be made custom
|
// Active standard keywords cannot be made custom
|
||||||
// Disabled keywords are OK
|
// Disabled keywords are OK
|
||||||
Some(token) if token.is_standard_keyword() => {
|
Some(token)
|
||||||
if !self
|
if token.is_standard_keyword()
|
||||||
.disabled_symbols
|
&& !self.is_symbol_disabled(token.literal_syntax()) =>
|
||||||
.as_ref()
|
{
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
return Err(format!("'{keyword}' is a reserved keyword"))
|
||||||
{
|
|
||||||
return Err(format!("'{keyword}' is a reserved keyword"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Active standard symbols cannot be made custom
|
|
||||||
Some(token) if token.is_standard_symbol() => {
|
|
||||||
if !self
|
|
||||||
.disabled_symbols
|
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
|
||||||
{
|
|
||||||
return Err(format!("'{keyword}' is a reserved operator"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Active standard symbols cannot be made custom
|
// Active standard symbols cannot be made custom
|
||||||
Some(token)
|
Some(token)
|
||||||
if !self
|
if token.is_standard_symbol()
|
||||||
.disabled_symbols
|
&& !self.is_symbol_disabled(token.literal_syntax()) =>
|
||||||
.as_ref()
|
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax())) =>
|
|
||||||
{
|
{
|
||||||
|
return Err(format!("'{keyword}' is a reserved operator"))
|
||||||
|
}
|
||||||
|
// Active standard symbols cannot be made custom
|
||||||
|
Some(token) if !self.is_symbol_disabled(token.literal_syntax()) => {
|
||||||
return Err(format!("'{keyword}' is a reserved symbol"))
|
return Err(format!("'{keyword}' is a reserved symbol"))
|
||||||
}
|
}
|
||||||
// Disabled symbols are OK
|
// Disabled symbols are OK
|
||||||
@ -207,6 +216,16 @@ impl Engine {
|
|||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
/// Is a keyword registered as a custom keyword?
|
||||||
|
///
|
||||||
|
/// Not available under `no_custom_syntax`.
|
||||||
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn is_custom_keyword(&self, keyword: &str) -> bool {
|
||||||
|
self.custom_keywords
|
||||||
|
.as_ref()
|
||||||
|
.map_or(false, |m| m.contains_key(keyword))
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the default value of the custom state for each evaluation run.
|
/// Get the default value of the custom state for each evaluation run.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -90,15 +90,15 @@ impl Engine {
|
|||||||
let param_type_names: Option<&[&str]> = None;
|
let param_type_names: Option<&[&str]> = None;
|
||||||
|
|
||||||
let fn_name = name.as_ref();
|
let fn_name = name.as_ref();
|
||||||
let no_const = false;
|
let is_pure = true;
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
let no_const = no_const || (F::num_params() == 3 && fn_name == crate::engine::FN_IDX_SET);
|
let is_pure = is_pure && (F::num_params() != 3 || fn_name != crate::engine::FN_IDX_SET);
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
let no_const =
|
let is_pure =
|
||||||
no_const || (F::num_params() == 2 && fn_name.starts_with(crate::engine::FN_SET));
|
is_pure && (F::num_params() != 2 || !fn_name.starts_with(crate::engine::FN_SET));
|
||||||
|
|
||||||
let func = func.into_callable_function(fn_name.into(), no_const);
|
let func = func.into_callable_function(fn_name.into(), is_pure);
|
||||||
|
|
||||||
self.global_namespace_mut().set_fn(
|
self.global_namespace_mut().set_fn(
|
||||||
name,
|
name,
|
||||||
|
@ -721,7 +721,7 @@ pub enum Stmt {
|
|||||||
/// This variant does not map to any language structure. It is currently only used only to
|
/// This variant does not map to any language structure. It is currently only used only to
|
||||||
/// convert normal variables into shared variables when they are _captured_ by a closure.
|
/// convert normal variables into shared variables when they are _captured_ by a closure.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Share(Box<crate::FnArgsVec<(crate::ImmutableString, Option<NonZeroUsize>, Position)>>),
|
Share(Box<crate::FnArgsVec<(crate::ast::Ident, Option<NonZeroUsize>)>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Stmt {
|
impl Default for Stmt {
|
||||||
@ -816,7 +816,7 @@ impl Stmt {
|
|||||||
Self::Export(.., pos) => *pos,
|
Self::Export(.., pos) => *pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(x) => x[0].2,
|
Self::Share(x) => x[0].0.pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Override the [position][Position] of this statement.
|
/// Override the [position][Position] of this statement.
|
||||||
@ -848,7 +848,7 @@ impl Stmt {
|
|||||||
Self::Export(.., pos) => *pos = new_pos,
|
Self::Export(.., pos) => *pos = new_pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(x) => x.iter_mut().for_each(|(_, _, pos)| *pos = new_pos),
|
Self::Share(x) => x.iter_mut().for_each(|(x, _)| x.pos = new_pos),
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
|
@ -927,19 +927,19 @@ impl Engine {
|
|||||||
// Share statement
|
// Share statement
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Stmt::Share(x) => {
|
Stmt::Share(x) => {
|
||||||
for (name, index, pos) in &**x {
|
for (var, index) in &**x {
|
||||||
if let Some(index) = index
|
let index = index
|
||||||
.map(|n| scope.len() - n.get())
|
.map(|n| scope.len() - n.get())
|
||||||
.or_else(|| scope.search(name))
|
.or_else(|| scope.search(&var.name))
|
||||||
{
|
.ok_or_else(|| {
|
||||||
let val = scope.get_mut_by_index(index);
|
Box::new(ERR::ErrorVariableNotFound(var.name.to_string(), var.pos))
|
||||||
|
})?;
|
||||||
|
|
||||||
if !val.is_shared() {
|
let val = scope.get_mut_by_index(index);
|
||||||
// Replace the variable with a shared value.
|
|
||||||
*val = std::mem::take(val).into_shared();
|
if !val.is_shared() {
|
||||||
}
|
// Replace the variable with a shared value.
|
||||||
} else {
|
*val = std::mem::take(val).into_shared();
|
||||||
return Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,6 +276,7 @@ impl Engine {
|
|||||||
func: CallableFunction::Method {
|
func: CallableFunction::Method {
|
||||||
func: Shared::new(f),
|
func: Shared::new(f),
|
||||||
has_context,
|
has_context,
|
||||||
|
is_pure: false,
|
||||||
},
|
},
|
||||||
source: None,
|
source: None,
|
||||||
})
|
})
|
||||||
@ -285,6 +286,7 @@ impl Engine {
|
|||||||
func: CallableFunction::Method {
|
func: CallableFunction::Method {
|
||||||
func: Shared::new(f),
|
func: Shared::new(f),
|
||||||
has_context,
|
has_context,
|
||||||
|
is_pure: true,
|
||||||
},
|
},
|
||||||
source: None,
|
source: None,
|
||||||
}),
|
}),
|
||||||
@ -372,8 +374,8 @@ impl Engine {
|
|||||||
|
|
||||||
let backup = &mut ArgBackup::new();
|
let backup = &mut ArgBackup::new();
|
||||||
|
|
||||||
// Calling pure function but the first argument is a reference?
|
// Calling non-method function but the first argument is a reference?
|
||||||
let swap = is_ref_mut && func.is_pure() && !args.is_empty();
|
let swap = is_ref_mut && !func.is_method() && !args.is_empty();
|
||||||
|
|
||||||
if swap {
|
if swap {
|
||||||
// Clone the first argument
|
// Clone the first argument
|
||||||
@ -400,12 +402,11 @@ impl Engine {
|
|||||||
.has_context()
|
.has_context()
|
||||||
.then(|| (self, name, src, &*global, pos).into());
|
.then(|| (self, name, src, &*global, pos).into());
|
||||||
|
|
||||||
let mut _result = if let Some(f) = func.get_plugin_fn() {
|
let mut _result = if !func.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
||||||
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
// If function is not pure, there must be at least one argument
|
||||||
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
|
Err(ERR::ErrorNonPureMethodCallOnConstant(name.to_string(), pos).into())
|
||||||
} else {
|
} else if let Some(f) = func.get_plugin_fn() {
|
||||||
f.call(context, args)
|
f.call(context, args)
|
||||||
}
|
|
||||||
} else if let Some(f) = func.get_native_fn() {
|
} else if let Some(f) = func.get_native_fn() {
|
||||||
f(context, args)
|
f(context, args)
|
||||||
} else {
|
} else {
|
||||||
@ -603,8 +604,11 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if error {
|
if error {
|
||||||
let sig = self.gen_fn_call_signature(fn_name, args);
|
return Err(ERR::ErrorFunctionNotFound(
|
||||||
return Err(ERR::ErrorFunctionNotFound(sig, pos).into());
|
self.gen_fn_call_signature(fn_name, args),
|
||||||
|
pos,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -814,12 +818,16 @@ impl Engine {
|
|||||||
// Handle obj.call(fn_ptr, ...)
|
// Handle obj.call(fn_ptr, ...)
|
||||||
KEYWORD_FN_PTR_CALL => {
|
KEYWORD_FN_PTR_CALL => {
|
||||||
if call_args.is_empty() {
|
if call_args.is_empty() {
|
||||||
let typ = self.map_type_name(target.type_name());
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(typ, fn_call_pos));
|
self.map_type_name(target.type_name()),
|
||||||
|
fn_call_pos,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
if !call_args[0].is_fnptr() {
|
if !call_args[0].is_fnptr() {
|
||||||
let typ = self.map_type_name(call_args[0].type_name());
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(typ, first_arg_pos));
|
self.map_type_name(call_args[0].type_name()),
|
||||||
|
first_arg_pos,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// FnPtr call on object
|
// FnPtr call on object
|
||||||
@ -903,8 +911,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
KEYWORD_FN_PTR_CURRY => {
|
KEYWORD_FN_PTR_CURRY => {
|
||||||
if !target.is_fnptr() {
|
if !target.is_fnptr() {
|
||||||
let typ = self.map_type_name(target.type_name());
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(typ, fn_call_pos));
|
self.map_type_name(target.type_name()),
|
||||||
|
fn_call_pos,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`").clone();
|
let mut fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`").clone();
|
||||||
@ -1035,8 +1045,10 @@ impl Engine {
|
|||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
|
||||||
|
|
||||||
if !arg_value.is_fnptr() {
|
if !arg_value.is_fnptr() {
|
||||||
let typ = self.map_type_name(arg_value.type_name());
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos));
|
self.map_type_name(arg_value.type_name()),
|
||||||
|
arg_pos,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fn_ptr = arg_value.cast::<FnPtr>();
|
let fn_ptr = arg_value.cast::<FnPtr>();
|
||||||
@ -1121,8 +1133,10 @@ impl Engine {
|
|||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
||||||
|
|
||||||
if !arg_value.is_fnptr() {
|
if !arg_value.is_fnptr() {
|
||||||
let typ = self.map_type_name(arg_value.type_name());
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos));
|
self.map_type_name(arg_value.type_name()),
|
||||||
|
arg_pos,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fn_ptr = arg_value.cast::<FnPtr>();
|
let mut fn_ptr = arg_value.cast::<FnPtr>();
|
||||||
@ -1493,17 +1507,18 @@ impl Engine {
|
|||||||
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(f) if !f.is_pure() && args[0].is_read_only() => {
|
||||||
|
// If function is not pure, there must be at least one argument
|
||||||
|
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
||||||
|
}
|
||||||
|
|
||||||
Some(f) if f.is_plugin_fn() => {
|
Some(f) if f.is_plugin_fn() => {
|
||||||
let f = f.get_plugin_fn().expect("plugin function");
|
let f = f.get_plugin_fn().expect("plugin function");
|
||||||
let context = f
|
let context = f
|
||||||
.has_context()
|
.has_context()
|
||||||
.then(|| (self, fn_name, module.id(), &*global, pos).into());
|
.then(|| (self, fn_name, module.id(), &*global, pos).into());
|
||||||
if !f.is_pure() && !args.is_empty() && args[0].is_read_only() {
|
f.call(context, args)
|
||||||
Err(ERR::ErrorNonPureMethodCallOnConstant(fn_name.to_string(), pos).into())
|
.and_then(|r| self.check_data_size(r, pos))
|
||||||
} else {
|
|
||||||
f.call(context, args)
|
|
||||||
.and_then(|r| self.check_data_size(r, pos))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(f) if f.is_native() => {
|
Some(f) if f.is_native() => {
|
||||||
|
@ -39,6 +39,8 @@ pub enum CallableFunction {
|
|||||||
func: Shared<FnAny>,
|
func: Shared<FnAny>,
|
||||||
/// Does the function take a [`NativeCallContext`][crate::NativeCallContext] parameter?
|
/// Does the function take a [`NativeCallContext`][crate::NativeCallContext] parameter?
|
||||||
has_context: bool,
|
has_context: bool,
|
||||||
|
/// This is a dummy field and is not used.
|
||||||
|
is_pure: bool,
|
||||||
},
|
},
|
||||||
/// A native Rust object method with the first argument passed by reference,
|
/// A native Rust object method with the first argument passed by reference,
|
||||||
/// and the rest passed by value.
|
/// and the rest passed by value.
|
||||||
@ -47,6 +49,8 @@ pub enum CallableFunction {
|
|||||||
func: Shared<FnAny>,
|
func: Shared<FnAny>,
|
||||||
/// Does the function take a [`NativeCallContext`][crate::NativeCallContext] parameter?
|
/// Does the function take a [`NativeCallContext`][crate::NativeCallContext] parameter?
|
||||||
has_context: bool,
|
has_context: bool,
|
||||||
|
/// Allow operating on constants?
|
||||||
|
is_pure: bool,
|
||||||
},
|
},
|
||||||
/// An iterator function.
|
/// An iterator function.
|
||||||
Iterator {
|
Iterator {
|
||||||
@ -105,9 +109,10 @@ impl CallableFunction {
|
|||||||
pub fn is_pure(&self) -> bool {
|
pub fn is_pure(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Pure { .. } => true,
|
Self::Pure { .. } => true,
|
||||||
Self::Method { .. } | Self::Iterator { .. } => false,
|
Self::Method { is_pure, .. } => *is_pure,
|
||||||
|
Self::Iterator { .. } => true,
|
||||||
|
|
||||||
Self::Plugin { func, .. } => !func.is_method_call(),
|
Self::Plugin { func, .. } => func.is_pure(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Self::Script { .. } => false,
|
Self::Script { .. } => false,
|
||||||
|
@ -84,7 +84,7 @@ pub trait RegisterNativeFunction<
|
|||||||
{
|
{
|
||||||
/// Convert this function into a [`CallableFunction`].
|
/// Convert this function into a [`CallableFunction`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn into_callable_function(self, name: Identifier, no_const: bool) -> CallableFunction;
|
fn into_callable_function(self, name: Identifier, is_pure: bool) -> CallableFunction;
|
||||||
/// Get the type ID's of this function's parameters.
|
/// Get the type ID's of this function's parameters.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn param_types() -> [TypeId; N];
|
fn param_types() -> [TypeId; N];
|
||||||
@ -127,19 +127,6 @@ pub trait RegisterNativeFunction<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! check_constant {
|
|
||||||
($abi:ident, $n:expr, $fn_name:ident, $no_const:ident, $args:ident) => {
|
|
||||||
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
|
|
||||||
if stringify!($abi) == "Method" && $no_const && $args[0].is_read_only() {
|
|
||||||
return Err(crate::ERR::ErrorNonPureMethodCallOnConstant(
|
|
||||||
$fn_name.to_string(),
|
|
||||||
crate::Position::NONE,
|
|
||||||
)
|
|
||||||
.into());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! def_register {
|
macro_rules! def_register {
|
||||||
() => {
|
() => {
|
||||||
def_register!(imp Pure : 0;);
|
def_register!(imp Pure : 0;);
|
||||||
@ -160,11 +147,9 @@ macro_rules! def_register {
|
|||||||
> RegisterNativeFunction<($($mark,)*), $n, false, RET, false> for FN {
|
> RegisterNativeFunction<($($mark,)*), $n, false, RET, false> for FN {
|
||||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, is_pure: bool) -> CallableFunction {
|
||||||
CallableFunction::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| {
|
CallableFunction::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| {
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
check_constant!($abi, $n, fn_name, no_const, args);
|
|
||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
$(let mut $par = $clone(drain.next().unwrap()); )*
|
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||||
|
|
||||||
@ -173,7 +158,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// Map the result
|
// Map the result
|
||||||
Ok(Dynamic::from(r))
|
Ok(Dynamic::from(r))
|
||||||
}), has_context: false }
|
}), has_context: false, is_pure }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,13 +169,11 @@ macro_rules! def_register {
|
|||||||
> RegisterNativeFunction<($($mark,)*), $n, true, RET, false> for FN {
|
> RegisterNativeFunction<($($mark,)*), $n, true, RET, false> for FN {
|
||||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, is_pure: bool) -> CallableFunction {
|
||||||
CallableFunction::$abi { func: Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
CallableFunction::$abi { func: Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||||
let ctx = ctx.unwrap();
|
let ctx = ctx.unwrap();
|
||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
check_constant!($abi, $n, fn_name, no_const, args);
|
|
||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
$(let mut $par = $clone(drain.next().unwrap()); )*
|
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||||
|
|
||||||
@ -199,7 +182,7 @@ macro_rules! def_register {
|
|||||||
|
|
||||||
// Map the result
|
// Map the result
|
||||||
Ok(Dynamic::from(r))
|
Ok(Dynamic::from(r))
|
||||||
}), has_context: true }
|
}), has_context: true, is_pure }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,17 +194,15 @@ macro_rules! def_register {
|
|||||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
|
||||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, is_pure: bool) -> CallableFunction {
|
||||||
CallableFunction::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| {
|
CallableFunction::$abi { func: Shared::new(move |_, args: &mut FnCallArgs| {
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
check_constant!($abi, $n, fn_name, no_const, args);
|
|
||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
$(let mut $par = $clone(drain.next().unwrap()); )*
|
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
self($($arg),*).map(Dynamic::from)
|
self($($arg),*).map(Dynamic::from)
|
||||||
}), has_context: false }
|
}), has_context: false, is_pure }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,19 +214,17 @@ macro_rules! def_register {
|
|||||||
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
#[inline(always)] fn param_types() -> [TypeId;$n] { [$(TypeId::of::<$par>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
#[cfg(feature = "metadata")] #[inline(always)] fn param_names() -> [&'static str;$n] { [$(type_name::<$param>()),*] }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { type_name::<RhaiResultOf<RET>>() }
|
||||||
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, no_const: bool) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self, fn_name: Identifier, is_pure: bool) -> CallableFunction {
|
||||||
CallableFunction::$abi { func: Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
CallableFunction::$abi { func: Shared::new(move |ctx: Option<NativeCallContext>, args: &mut FnCallArgs| {
|
||||||
let ctx = ctx.unwrap();
|
let ctx = ctx.unwrap();
|
||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
check_constant!($abi, $n, fn_name, no_const, args);
|
|
||||||
|
|
||||||
let mut drain = args.iter_mut();
|
let mut drain = args.iter_mut();
|
||||||
$(let mut $par = $clone(drain.next().unwrap()); )*
|
$(let mut $par = $clone(drain.next().unwrap()); )*
|
||||||
|
|
||||||
// Call the function with each argument value
|
// Call the function with each argument value
|
||||||
self(ctx, $($arg),*).map(Dynamic::from)
|
self(ctx, $($arg),*).map(Dynamic::from)
|
||||||
}), has_context: true }
|
}), has_context: true, is_pure }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1266,6 +1266,7 @@ impl Module {
|
|||||||
CallableFunction::Method {
|
CallableFunction::Method {
|
||||||
func: Shared::new(f),
|
func: Shared::new(f),
|
||||||
has_context: true,
|
has_context: true,
|
||||||
|
is_pure: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -1303,15 +1304,15 @@ impl Module {
|
|||||||
F: RegisterNativeFunction<A, N, C, T, true>,
|
F: RegisterNativeFunction<A, N, C, T, true>,
|
||||||
{
|
{
|
||||||
let fn_name = name.into();
|
let fn_name = name.into();
|
||||||
let no_const = false;
|
let is_pure = true;
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
let no_const = no_const || (F::num_params() == 3 && fn_name == crate::engine::FN_IDX_SET);
|
let is_pure = is_pure && (F::num_params() != 3 || fn_name != crate::engine::FN_IDX_SET);
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
let no_const =
|
let is_pure =
|
||||||
no_const || (F::num_params() == 2 && fn_name.starts_with(crate::engine::FN_SET));
|
is_pure && (F::num_params() != 2 || !fn_name.starts_with(crate::engine::FN_SET));
|
||||||
|
|
||||||
let func = func.into_callable_function(fn_name.clone(), no_const);
|
let func = func.into_callable_function(fn_name.clone(), is_pure);
|
||||||
|
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
fn_name,
|
fn_name,
|
||||||
@ -1350,7 +1351,7 @@ impl Module {
|
|||||||
F: RegisterNativeFunction<(Mut<A>,), 1, C, T, true> + SendSync + 'static,
|
F: RegisterNativeFunction<(Mut<A>,), 1, C, T, true> + SendSync + 'static,
|
||||||
{
|
{
|
||||||
let fn_name = crate::engine::make_getter(name.as_ref());
|
let fn_name = crate::engine::make_getter(name.as_ref());
|
||||||
let func = func.into_callable_function(fn_name.clone(), false);
|
let func = func.into_callable_function(fn_name.clone(), true);
|
||||||
|
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
fn_name,
|
fn_name,
|
||||||
@ -1394,7 +1395,7 @@ impl Module {
|
|||||||
F: RegisterNativeFunction<(Mut<A>, T), 2, C, (), true> + SendSync + 'static,
|
F: RegisterNativeFunction<(Mut<A>, T), 2, C, (), true> + SendSync + 'static,
|
||||||
{
|
{
|
||||||
let fn_name = crate::engine::make_setter(name.as_ref());
|
let fn_name = crate::engine::make_setter(name.as_ref());
|
||||||
let func = func.into_callable_function(fn_name.clone(), true);
|
let func = func.into_callable_function(fn_name.clone(), false);
|
||||||
|
|
||||||
self.set_fn(
|
self.set_fn(
|
||||||
fn_name,
|
fn_name,
|
||||||
@ -1510,7 +1511,7 @@ impl Module {
|
|||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
F::param_types(),
|
F::param_types(),
|
||||||
func.into_callable_function(crate::engine::FN_IDX_GET.into(), false),
|
func.into_callable_function(crate::engine::FN_IDX_GET.into(), true),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1571,7 +1572,7 @@ impl Module {
|
|||||||
FnAccess::Public,
|
FnAccess::Public,
|
||||||
None,
|
None,
|
||||||
F::param_types(),
|
F::param_types(),
|
||||||
func.into_callable_function(crate::engine::FN_IDX_SET.into(), true),
|
func.into_callable_function(crate::engine::FN_IDX_SET.into(), false),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1986,23 +1987,35 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the sub-modules in the [`Module`].
|
/// Get an iterator to the sub-modules in the [`Module`].
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
||||||
|
self.iter_sub_modules_raw().map(|(k, m)| (k.as_str(), m))
|
||||||
|
}
|
||||||
|
/// Get an iterator to the sub-modules in the [`Module`].
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn iter_sub_modules_raw(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (&Identifier, &SharedModule)> {
|
||||||
self.modules
|
self.modules
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|(k, m)| (k.as_str(), m))
|
.map(|(k, m)| (k, m))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the variables in the [`Module`].
|
/// Get an iterator to the variables in the [`Module`].
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
||||||
|
self.iter_var_raw().map(|(k, v)| (k.as_str(), v))
|
||||||
|
}
|
||||||
|
/// Get an iterator to the variables in the [`Module`].
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn iter_var_raw(&self) -> impl Iterator<Item = (&Identifier, &Dynamic)> {
|
||||||
self.variables
|
self.variables
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|(k, v)| (k.as_str(), v))
|
.map(|(k, v)| (k, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an iterator to the functions in the [`Module`].
|
/// Get an iterator to the functions in the [`Module`].
|
||||||
|
@ -14,10 +14,9 @@ use crate::func::builtin::get_builtin_binary_op_fn;
|
|||||||
use crate::func::hashing::get_hasher;
|
use crate::func::hashing::get_hasher;
|
||||||
use crate::module::ModuleFlags;
|
use crate::module::ModuleFlags;
|
||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
use crate::types::dynamic::AccessMode;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, Identifier, ImmutableString, Position,
|
calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnPtr, ImmutableString, Position, Scope,
|
||||||
Scope, StaticVec, AST,
|
StaticVec, AST,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -54,8 +53,8 @@ impl Default for OptimizationLevel {
|
|||||||
struct OptimizerState<'a> {
|
struct OptimizerState<'a> {
|
||||||
/// Has the [`AST`] been changed during this pass?
|
/// Has the [`AST`] been changed during this pass?
|
||||||
changed: bool,
|
changed: bool,
|
||||||
/// Collection of constants to use for eager function evaluations.
|
/// Collection of variables/constants to use for eager function evaluations.
|
||||||
variables: StaticVec<(Identifier, AccessMode, Option<Dynamic>)>,
|
variables: StaticVec<(ImmutableString, Option<Dynamic>)>,
|
||||||
/// Activate constants propagation?
|
/// Activate constants propagation?
|
||||||
propagate_constants: bool,
|
propagate_constants: bool,
|
||||||
/// An [`Engine`] instance for eager function evaluation.
|
/// An [`Engine`] instance for eager function evaluation.
|
||||||
@ -115,14 +114,11 @@ impl<'a> OptimizerState<'a> {
|
|||||||
self.variables.truncate(len);
|
self.variables.truncate(len);
|
||||||
}
|
}
|
||||||
/// Add a new variable to the list.
|
/// Add a new variable to the list.
|
||||||
|
///
|
||||||
|
/// `Some(value)` if constant, `None` or otherwise.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_var(
|
pub fn push_var(&mut self, name: ImmutableString, value: Option<Dynamic>) {
|
||||||
&mut self,
|
self.variables.push((name, value));
|
||||||
name: impl Into<Identifier>,
|
|
||||||
access: AccessMode,
|
|
||||||
value: Option<Dynamic>,
|
|
||||||
) {
|
|
||||||
self.variables.push((name.into(), access, value));
|
|
||||||
}
|
}
|
||||||
/// Look up a constant from the list.
|
/// Look up a constant from the list.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -131,12 +127,9 @@ impl<'a> OptimizerState<'a> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (n, access, value) in self.variables.iter().rev() {
|
for (n, value) in self.variables.iter().rev() {
|
||||||
if n == name {
|
if n.as_str() == name {
|
||||||
return match access {
|
return value.as_ref();
|
||||||
AccessMode::ReadWrite => None,
|
|
||||||
AccessMode::ReadOnly => value.as_ref(),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,16 +230,13 @@ fn optimize_stmt_block(
|
|||||||
optimize_expr(&mut x.1, state, false);
|
optimize_expr(&mut x.1, state, false);
|
||||||
|
|
||||||
if x.1.is_constant() {
|
if x.1.is_constant() {
|
||||||
state.push_var(
|
state
|
||||||
x.0.as_str(),
|
.push_var(x.0.name.clone(), Some(x.1.get_literal_value().unwrap()));
|
||||||
AccessMode::ReadOnly,
|
|
||||||
x.1.get_literal_value(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Add variables into the state
|
// Add variables into the state
|
||||||
optimize_expr(&mut x.1, state, false);
|
optimize_expr(&mut x.1, state, false);
|
||||||
state.push_var(x.0.as_str(), AccessMode::ReadWrite, None);
|
state.push_var(x.0.name.clone(), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
@ -858,7 +848,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Stmt::Share(x) => {
|
Stmt::Share(x) => {
|
||||||
let len = x.len();
|
let len = x.len();
|
||||||
x.retain(|(v, _, _)| !state.find_constant(v).is_some());
|
x.retain(|(v, _)| !state.find_constant(v).is_some());
|
||||||
if x.len() != len {
|
if x.len() != len {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
@ -1335,20 +1325,14 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.flat_map(|m| m.iter_var())
|
.flat_map(|m| m.iter_var())
|
||||||
.for_each(|(name, value)| {
|
.for_each(|(name, value)| state.push_var(name.into(), Some(value.clone())));
|
||||||
state.push_var(name, AccessMode::ReadOnly, Some(value.clone()))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add constants and variables from the scope
|
// Add constants and variables from the scope
|
||||||
scope
|
scope
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(Scope::iter)
|
.flat_map(Scope::iter)
|
||||||
.for_each(|(name, constant, value)| {
|
.for_each(|(name, constant, value)| {
|
||||||
if constant {
|
state.push_var(name.into(), if constant { Some(value) } else { None });
|
||||||
state.push_var(name, AccessMode::ReadOnly, Some(value));
|
|
||||||
} else {
|
|
||||||
state.push_var(name, AccessMode::ReadWrite, None);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
optimize_stmt_block(statements, &mut state, true, false, true)
|
optimize_stmt_block(statements, &mut state, true, false, true)
|
||||||
|
@ -1728,6 +1728,9 @@ impl Engine {
|
|||||||
) -> ParseResult<Expr> {
|
) -> ParseResult<Expr> {
|
||||||
let mut settings = settings;
|
let mut settings = settings;
|
||||||
|
|
||||||
|
// Break just in case `lhs` is `Expr::Dot` or `Expr::Index`
|
||||||
|
let mut parent_options = ASTFlags::BREAK;
|
||||||
|
|
||||||
// Tail processing all possible postfix operators
|
// Tail processing all possible postfix operators
|
||||||
loop {
|
loop {
|
||||||
let (tail_token, ..) = input.peek().expect(NEVER_ENDS);
|
let (tail_token, ..) = input.peek().expect(NEVER_ENDS);
|
||||||
@ -1842,13 +1845,16 @@ impl Engine {
|
|||||||
let rhs =
|
let rhs =
|
||||||
self.parse_primary(input, state, lib, settings.level_up()?, options)?;
|
self.parse_primary(input, state, lib, settings.level_up()?, options)?;
|
||||||
|
|
||||||
Self::make_dot_expr(state, expr, rhs, ASTFlags::empty(), op_flags, tail_pos)?
|
Self::make_dot_expr(state, expr, rhs, parent_options, op_flags, tail_pos)?
|
||||||
}
|
}
|
||||||
// Unknown postfix operator
|
// Unknown postfix operator
|
||||||
(expr, token) => {
|
(expr, token) => {
|
||||||
unreachable!("unknown postfix operator '{}' for {:?}", token, expr)
|
unreachable!("unknown postfix operator '{}' for {:?}", token, expr)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// The chain is now extended
|
||||||
|
parent_options = ASTFlags::empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the hash key for namespace-qualified variables
|
// Cache the hash key for namespace-qualified variables
|
||||||
@ -2431,13 +2437,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
Token::Custom(s)
|
Token::Custom(s) if self.is_custom_keyword(s.as_str()) => {
|
||||||
if self
|
|
||||||
.custom_keywords
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|m| m.get(s.as_str()))
|
|
||||||
.map_or(false, Option::is_some) =>
|
|
||||||
{
|
|
||||||
op_base.hashes = if native_only {
|
op_base.hashes = if native_only {
|
||||||
FnCallHashes::from_native_only(calc_fn_hash(None, &s, 2))
|
FnCallHashes::from_native_only(calc_fn_hash(None, &s, 2))
|
||||||
} else {
|
} else {
|
||||||
@ -3781,9 +3781,9 @@ impl Engine {
|
|||||||
statements.push(Stmt::Share(
|
statements.push(Stmt::Share(
|
||||||
externals
|
externals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|Ident { name, pos }| {
|
.map(|var| {
|
||||||
let (index, _) = parent.access_var(&name, lib, pos);
|
let (index, _) = parent.access_var(&var.name, lib, var.pos);
|
||||||
(name, index, pos)
|
(var, index)
|
||||||
})
|
})
|
||||||
.collect::<crate::FnArgsVec<_>>()
|
.collect::<crate::FnArgsVec<_>>()
|
||||||
.into(),
|
.into(),
|
||||||
|
@ -2492,7 +2492,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
Some((Token::Reserved(s), pos)) => (match
|
Some((Token::Reserved(s), pos)) => (match
|
||||||
(s.as_str(),
|
(s.as_str(),
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
self.engine.custom_keywords.as_ref().map_or(false, |m| m.contains_key(&*s)),
|
self.engine.is_custom_keyword(&*s),
|
||||||
#[cfg(feature = "no_custom_syntax")]
|
#[cfg(feature = "no_custom_syntax")]
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
@ -2529,7 +2529,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
#[cfg(feature = "no_custom_syntax")]
|
#[cfg(feature = "no_custom_syntax")]
|
||||||
(.., true) => unreachable!("no custom operators"),
|
(.., true) => unreachable!("no custom operators"),
|
||||||
// Reserved keyword that is not custom and disabled.
|
// Reserved keyword that is not custom and disabled.
|
||||||
(token, false) if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token)) => {
|
(token, false) if self.engine.is_symbol_disabled(token) => {
|
||||||
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"});
|
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"});
|
||||||
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
|
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
|
||||||
},
|
},
|
||||||
@ -2538,13 +2538,13 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
}, pos),
|
}, pos),
|
||||||
// Custom keyword
|
// Custom keyword
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(&*s)) => {
|
Some((Token::Identifier(s), pos)) if self.engine.is_custom_keyword(&*s) => {
|
||||||
(Token::Custom(s), pos)
|
(Token::Custom(s), pos)
|
||||||
}
|
}
|
||||||
// Custom keyword/symbol - must be disabled
|
// Custom keyword/symbol - must be disabled
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
Some((token, pos)) if token.is_literal() && self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(token.literal_syntax())) => {
|
Some((token, pos)) if token.is_literal() && self.engine.is_custom_keyword(token.literal_syntax()) => {
|
||||||
if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) {
|
if self.engine.is_symbol_disabled(token.literal_syntax()) {
|
||||||
// Disabled standard keyword/symbol
|
// Disabled standard keyword/symbol
|
||||||
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
|
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
|
||||||
} else {
|
} else {
|
||||||
@ -2553,7 +2553,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Disabled symbol
|
// Disabled symbol
|
||||||
Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) => {
|
Some((token, pos)) if token.is_literal() && self.engine.is_symbol_disabled(token.literal_syntax()) => {
|
||||||
(Token::Reserved(Box::new(token.literal_syntax().into())), pos)
|
(Token::Reserved(Box::new(token.literal_syntax().into())), pos)
|
||||||
}
|
}
|
||||||
// Normal symbol
|
// Normal symbol
|
||||||
|
@ -1017,18 +1017,23 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
/// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is.
|
/// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is.
|
||||||
///
|
///
|
||||||
/// # Notes
|
/// # Arrays
|
||||||
///
|
///
|
||||||
/// Beware that you need to pass in an [`Array`][crate::Array] type for it to be recognized as
|
/// Beware that you need to pass in an [`Array`][crate::Array] type for it to be recognized as
|
||||||
/// an [`Array`][crate::Array]. A [`Vec<T>`][Vec] does not get automatically converted to an
|
/// an [`Array`][crate::Array]. A [`Vec<T>`][Vec] does not get automatically converted to an
|
||||||
/// [`Array`][crate::Array], but will be a custom type instead (stored as a trait object). Use
|
/// [`Array`][crate::Array], but will be a custom type instead (stored as a trait object).
|
||||||
/// [`Dynamic::from_array`] to convert a [`Vec<T>`][Vec] into a [`Dynamic`] as an
|
///
|
||||||
/// [`Array`][crate::Array] value.
|
/// Use `array.into()` or `array.into_iter()` to convert a [`Vec<T>`][Vec] into a [`Dynamic`] as
|
||||||
|
/// an [`Array`][crate::Array] value. See the examples for details.
|
||||||
|
///
|
||||||
|
/// # Hash Maps
|
||||||
///
|
///
|
||||||
/// Similarly, passing in a [`HashMap<String, T>`][std::collections::HashMap] or
|
/// Similarly, passing in a [`HashMap<String, T>`][std::collections::HashMap] or
|
||||||
/// [`BTreeMap<String, T>`][std::collections::BTreeMap] will not get a [`Map`][crate::Map] but a
|
/// [`BTreeMap<String, T>`][std::collections::BTreeMap] will not get a [`Map`][crate::Map] but a
|
||||||
/// custom type. Again, use [`Dynamic::from_map`] to get a [`Dynamic`] with a [`Map`][crate::Map]
|
/// custom type.
|
||||||
/// value.
|
///
|
||||||
|
/// Again, use `map.into()` to get a [`Dynamic`] with a [`Map`][crate::Map] value.
|
||||||
|
/// See the examples for details.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@ -1046,6 +1051,33 @@ impl Dynamic {
|
|||||||
/// let new_result = Dynamic::from(result);
|
/// let new_result = Dynamic::from(result);
|
||||||
/// assert_eq!(new_result.type_name(), "string");
|
/// assert_eq!(new_result.type_name(), "string");
|
||||||
/// assert_eq!(new_result.to_string(), "hello");
|
/// assert_eq!(new_result.to_string(), "hello");
|
||||||
|
///
|
||||||
|
/// # #[cfg(not(feature = "no_index"))]
|
||||||
|
/// # {
|
||||||
|
/// // Arrays - this is a custom object!
|
||||||
|
/// let result = Dynamic::from(vec![1_i64, 2, 3]);
|
||||||
|
/// assert_eq!(result.type_name(), "alloc::vec::Vec<i64>");
|
||||||
|
///
|
||||||
|
/// // Use '.into()' to convert a Vec<T> into an Array
|
||||||
|
/// let result: Dynamic = vec![1_i64, 2, 3].into();
|
||||||
|
/// assert_eq!(result.type_name(), "array");
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
/// # #[cfg(not(feature = "no_object"))]
|
||||||
|
/// # {
|
||||||
|
/// # use std::collections::HashMap;
|
||||||
|
/// // Hash map
|
||||||
|
/// let mut map = HashMap::new();
|
||||||
|
/// map.insert("a".to_string(), 1_i64);
|
||||||
|
///
|
||||||
|
/// // This is a custom object!
|
||||||
|
/// let result = Dynamic::from(map.clone());
|
||||||
|
/// assert_eq!(result.type_name(), "std::collections::hash::map::HashMap<alloc::string::String, i64>");
|
||||||
|
///
|
||||||
|
/// // Use '.into()' to convert a HashMap<String, T> into an object map
|
||||||
|
/// let result: Dynamic = map.into();
|
||||||
|
/// assert_eq!(result.type_name(), "map");
|
||||||
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
pub fn from<T: Variant + Clone>(value: T) -> Self {
|
||||||
|
@ -203,6 +203,22 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[test]
|
||||||
|
fn test_array_chaining() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(
|
||||||
|
"
|
||||||
|
let v = [ PI() ];
|
||||||
|
( v[0].cos() ).sin() == v[0].cos().sin()
|
||||||
|
"
|
||||||
|
)?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_array_index_types() -> Result<(), Box<EvalAltResult>> {
|
fn test_array_index_types() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
@ -210,34 +226,25 @@ fn test_array_index_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.compile("[1, 2, 3][0]['x']")?;
|
engine.compile("[1, 2, 3][0]['x']")?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine.compile("[1, 2, 3]['x']").unwrap_err().err_type(),
|
||||||
.compile("[1, 2, 3]['x']")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine.compile("[1, 2, 3][123.456]").unwrap_err().err_type(),
|
||||||
.compile("[1, 2, 3][123.456]")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine.compile("[1, 2, 3][()]").unwrap_err().err_type(),
|
||||||
.compile("[1, 2, 3][()]")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile(r#"[1, 2, 3]["hello"]"#)
|
.compile(r#"[1, 2, 3]["hello"]"#)
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
@ -245,7 +252,7 @@ fn test_array_index_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("[1, 2, 3][true && false]")
|
.compile("[1, 2, 3][true && false]")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
@ -517,9 +524,9 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.eval::<()>(
|
engine.eval::<()>(
|
||||||
"
|
"
|
||||||
let x = [1, 2, 3, 2, 1];
|
let x = [1, 2, 3, 2, 1];
|
||||||
x.find(|v| v > 4)
|
x.find(|v| v > 4)
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -534,9 +541,9 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine.eval::<()>(
|
engine.eval::<()>(
|
||||||
"
|
"
|
||||||
let x = [#{alice: 1}, #{bob: 2}, #{clara: 3}];
|
let x = [#{alice: 1}, #{bob: 2}, #{clara: 3}];
|
||||||
x.find_map(|v| v.dave)
|
x.find_map(|v| v.dave)
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -21,71 +21,44 @@ fn test_assignments_bad_lhs() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("(x+y) = 42;").unwrap_err().err_type(),
|
||||||
.compile("(x+y) = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("foo(x) = 42;").unwrap_err().err_type(),
|
||||||
.compile("foo(x) = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("true = 42;").unwrap_err().err_type(),
|
||||||
.compile("true = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToConstant(String::new())
|
ParseErrorType::AssignmentToConstant(String::new())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("123 = 42;").unwrap_err().err_type(),
|
||||||
.compile("123 = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToConstant(String::new())
|
ParseErrorType::AssignmentToConstant(String::new())
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
{
|
{
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("x.foo() = 42;").unwrap_err().err_type(),
|
||||||
.compile("x.foo() = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("x.foo().x.y = 42;").unwrap_err().err_type(),
|
||||||
.compile("x.foo().x.y = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("x.y.z.foo() = 42;").unwrap_err().err_type(),
|
||||||
.compile("x.y.z.foo() = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
||||||
);
|
);
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("x.foo()[0] = 42;").unwrap_err().err_type(),
|
||||||
.compile("x.foo()[0] = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
||||||
);
|
);
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("x[y].z.foo() = 42;").unwrap_err().err_type(),
|
||||||
.compile("x[y].z.foo() = 42;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
ParseErrorType::AssignmentToInvalidLHS(String::new())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,7 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
|
|||||||
scope.push("x", 42 as INT);
|
scope.push("x", 42 as INT);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine.compile_expression("|x| {}").unwrap_err().err_type(),
|
||||||
.compile_expression("|x| {}")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::BadInput(..)
|
ParseErrorType::BadInput(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -292,7 +289,7 @@ fn test_closures_data_race() -> Result<(), Box<EvalAltResult>> {
|
|||||||
a
|
a
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataRace(..)
|
EvalAltResult::ErrorDataRace(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
|
|||||||
MY_NUMBER.value = 42;
|
MY_NUMBER.value = 42;
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorNonPureMethodCallOnConstant(..)
|
EvalAltResult::ErrorNonPureMethodCallOnConstant(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run_with_scope(&mut scope, "MY_NUMBER.value = 42;")
|
.run_with_scope(&mut scope, "MY_NUMBER.value = 42;")
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorNonPureMethodCallOnConstant(..)
|
EvalAltResult::ErrorNonPureMethodCallOnConstant(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -13,11 +13,11 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
// Disable 'while' and make sure it still works with custom syntax
|
// Disable 'while' and make sure it still works with custom syntax
|
||||||
engine.disable_symbol("while");
|
engine.disable_symbol("while");
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile("while false {}").expect_err("should error").err_type(),
|
engine.compile("while false {}").unwrap_err().err_type(),
|
||||||
ParseErrorType::Reserved(err) if err == "while"
|
ParseErrorType::Reserved(err) if err == "while"
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile("let while = 0").expect_err("should error").err_type(),
|
engine.compile("let while = 0").unwrap_err().err_type(),
|
||||||
ParseErrorType::Reserved(err) if err == "while"
|
ParseErrorType::Reserved(err) if err == "while"
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run("let foo = (exec [x<<15] -> { x += 2 } while x < 42) * 10;")
|
.run("let foo = (exec [x<<15] -> { x += 2 } while x < 42) * 10;")
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorRuntime(..)
|
EvalAltResult::ErrorRuntime(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -195,7 +195,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.register_custom_syntax(["!"], false, |_, _| Ok(Dynamic::UNIT))
|
.register_custom_syntax(["!"], false, |_, _| Ok(Dynamic::UNIT))
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::BadInput(LexError::ImproperSymbol(
|
ParseErrorType::BadInput(LexError::ImproperSymbol(
|
||||||
"!".to_string(),
|
"!".to_string(),
|
||||||
@ -364,10 +364,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
assert_eq!(engine.eval::<INT>("(hello kitty) + foo")?, 1041);
|
assert_eq!(engine.eval::<INT>("(hello kitty) + foo")?, 1041);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("hello hey").unwrap_err().err_type(),
|
||||||
.compile("hello hey")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string(), String::new()))
|
ParseErrorType::BadInput(LexError::ImproperSymbol("hey".to_string(), String::new()))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile(r#"let x = "hello, world!";"#)
|
.compile(r#"let x = "hello, world!";"#)
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::LiteralTooLarge("Length of string".to_string(), 10)
|
ParseErrorType::LiteralTooLarge("Length of string".to_string(), 10)
|
||||||
);
|
);
|
||||||
@ -23,7 +23,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile(r#"let x = "朝に紅顔、暮に白骨";"#)
|
.compile(r#"let x = "朝に紅顔、暮に白骨";"#)
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::LiteralTooLarge("Length of string".to_string(), 10)
|
ParseErrorType::LiteralTooLarge("Length of string".to_string(), 10)
|
||||||
);
|
);
|
||||||
@ -37,7 +37,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x + y
|
x + y
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile("let x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];")
|
.compile("let x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::LiteralTooLarge("Size of array literal".to_string(), 10)
|
ParseErrorType::LiteralTooLarge("Size of array literal".to_string(), 10)
|
||||||
);
|
);
|
||||||
@ -97,7 +97,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x + y
|
x + y
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
loop { x[0] = x; }
|
loop { x[0] = x; }
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -168,7 +168,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x
|
x
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
[x, x, x, x]
|
[x, x, x, x]
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
[x, x, x, x]
|
[x, x, x, x]
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
[z, z, z]
|
[z, z, z]
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
.compile(
|
.compile(
|
||||||
"let x = #{a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10,k:11,l:12,m:13,n:14,o:15};"
|
"let x = #{a:1,b:2,c:3,d:4,e:5,f:6,g:7,h:8,i:9,j:10,k:11,l:12,m:13,n:14,o:15};"
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::LiteralTooLarge(
|
ParseErrorType::LiteralTooLarge(
|
||||||
"Number of properties in object map literal".to_string(),
|
"Number of properties in object map literal".to_string(),
|
||||||
@ -281,7 +281,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
loop { x.a = x; }
|
loop { x.a = x; }
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -294,7 +294,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x + y
|
x + y
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -306,7 +306,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#{u:x, v:x, w:x, z:x}
|
#{u:x, v:x, w:x, z:x}
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#{u:x, v:x, w:x, z:x}
|
#{u:x, v:x, w:x, z:x}
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorDataTooLarge(..)
|
EvalAltResult::ErrorDataTooLarge(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ fn test_eval_disabled() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile(r#"eval("40 + 2")"#)
|
.compile(r#"eval("40 + 2")"#)
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::BadInput(LexError::ImproperSymbol(err, ..)) if err == "eval"
|
ParseErrorType::BadInput(LexError::ImproperSymbol(err, ..)) if err == "eval"
|
||||||
));
|
));
|
||||||
|
@ -72,7 +72,7 @@ fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x
|
x
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorInFunctionCall(fn_name, _, err, ..)
|
EvalAltResult::ErrorInFunctionCall(fn_name, _, err, ..)
|
||||||
if fn_name == "foo" && matches!(*err, EvalAltResult::ErrorUnboundThis(..))
|
if fn_name == "foo" && matches!(*err, EvalAltResult::ErrorUnboundThis(..))
|
||||||
));
|
));
|
||||||
|
@ -93,7 +93,7 @@ fn test_functions_global_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
const ANSWER = 42;
|
const ANSWER = 42;
|
||||||
foo()
|
foo()
|
||||||
}
|
}
|
||||||
").expect_err("should error"),
|
").unwrap_err(),
|
||||||
EvalAltResult::ErrorInFunctionCall(.., err, _)
|
EvalAltResult::ErrorInFunctionCall(.., err, _)
|
||||||
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, ..) if v == "global::ANSWER")
|
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, ..) if v == "global::ANSWER")
|
||||||
));
|
));
|
||||||
@ -112,7 +112,7 @@ fn test_functions_global_module() -> Result<(), Box<EvalAltResult>> {
|
|||||||
const LOCAL_VALUE = 42;
|
const LOCAL_VALUE = 42;
|
||||||
global::LOCAL_VALUE
|
global::LOCAL_VALUE
|
||||||
});
|
});
|
||||||
").expect_err("should error"),
|
").unwrap_err(),
|
||||||
EvalAltResult::ErrorInFunctionCall(.., err, _)
|
EvalAltResult::ErrorInFunctionCall(.., err, _)
|
||||||
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, ..) if v == "global::LOCAL_VALUE")
|
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, ..) if v == "global::LOCAL_VALUE")
|
||||||
));
|
));
|
||||||
|
@ -109,7 +109,7 @@ fn test_internal_fn_overloading() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn abc(x) { x - 42 }
|
fn abc(x) { x - 42 }
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::FnDuplicatedDefinition("abc".to_string(), 1)
|
ParseErrorType::FnDuplicatedDefinition("abc".to_string(), 1)
|
||||||
);
|
);
|
||||||
@ -125,7 +125,7 @@ fn test_internal_fn_params() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile("fn hello(x, x) { x }")
|
.compile("fn hello(x, x) { x }")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::FnDuplicatedParam("hello".to_string(), "x".to_string())
|
ParseErrorType::FnDuplicatedParam("hello".to_string(), "x".to_string())
|
||||||
);
|
);
|
||||||
@ -169,7 +169,7 @@ fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<INT>(r#"let f = Fn("abc"); f.call(0)"#).expect_err("should error"),
|
*engine.eval::<INT>(r#"let f = Fn("abc"); f.call(0)"#).unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("abc (")
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("abc (")
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -247,7 +247,7 @@ fn test_internal_fn_bang() -> Result<(), Box<EvalAltResult>> {
|
|||||||
y.foo!();
|
y.foo!();
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::MalformedCapture(..)
|
ParseErrorType::MalformedCapture(..)
|
||||||
));
|
));
|
||||||
|
@ -27,10 +27,7 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine.compile("let x = 0; break;").unwrap_err().err_type(),
|
||||||
.compile("let x = 0; break;")
|
|
||||||
.expect_err("should error")
|
|
||||||
.err_type(),
|
|
||||||
ParseErrorType::LoopBreak
|
ParseErrorType::LoopBreak
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -38,7 +35,7 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile("loop { let f = || { break; } }")
|
.compile("loop { let f = || { break; } }")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::LoopBreak
|
ParseErrorType::LoopBreak
|
||||||
);
|
);
|
||||||
@ -46,7 +43,7 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile("let x = 0; if x > 0 { continue; }")
|
.compile("let x = 0; if x > 0 { continue; }")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::LoopBreak
|
ParseErrorType::LoopBreak
|
||||||
);
|
);
|
||||||
|
@ -52,7 +52,7 @@ b`: 1}; y["a\nb"]
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<INT>("let y = #{`a${1}`: 1}; y.a1")
|
.eval::<INT>("let y = #{`a${1}`: 1}; y.a1")
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorParsing(ParseErrorType::PropertyExpected, ..)
|
EvalAltResult::ErrorParsing(ParseErrorType::PropertyExpected, ..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -116,7 +116,7 @@ fn test_map_prop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.set_fail_on_invalid_map_property(true);
|
engine.set_fail_on_invalid_map_property(true);
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(*engine.eval::<()>("let x = #{a: 42}; x.b").expect_err("should error"),
|
matches!(*engine.eval::<()>("let x = #{a: 42}; x.b").unwrap_err(),
|
||||||
EvalAltResult::ErrorPropertyNotFound(prop, _) if prop == "b"
|
EvalAltResult::ErrorPropertyNotFound(prop, _) if prop == "b"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -134,7 +134,7 @@ fn test_map_index_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("#{a:1, b:2, c:3}['x']")
|
.compile("#{a:1, b:2, c:3}['x']")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
@ -142,7 +142,7 @@ fn test_map_index_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("#{a:1, b:2, c:3}[1]")
|
.compile("#{a:1, b:2, c:3}[1]")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
@ -151,7 +151,7 @@ fn test_map_index_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("#{a:1, b:2, c:3}[123.456]")
|
.compile("#{a:1, b:2, c:3}[123.456]")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
@ -159,7 +159,7 @@ fn test_map_index_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("#{a:1, b:2, c:3}[()]")
|
.compile("#{a:1, b:2, c:3}[()]")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
@ -167,7 +167,7 @@ fn test_map_index_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("#{a:1, b:2, c:3}[true && false]")
|
.compile("#{a:1, b:2, c:3}[true && false]")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::MalformedIndexExpr(..)
|
ParseErrorType::MalformedIndexExpr(..)
|
||||||
));
|
));
|
||||||
@ -272,38 +272,32 @@ fn test_map_json() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.parse_json(json, true)?;
|
engine.parse_json(json, true)?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.parse_json("123", true).expect_err("should error"),
|
*engine.parse_json("123", true).unwrap_err(),
|
||||||
EvalAltResult::ErrorMismatchOutputType(..)
|
EvalAltResult::ErrorMismatchOutputType(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.parse_json("{a:42}", true).expect_err("should error"),
|
*engine.parse_json("{a:42}", true).unwrap_err(),
|
||||||
EvalAltResult::ErrorParsing(..)
|
EvalAltResult::ErrorParsing(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine.parse_json("#{a:123}", true).unwrap_err(),
|
||||||
.parse_json("#{a:123}", true)
|
|
||||||
.expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorParsing(..)
|
EvalAltResult::ErrorParsing(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.parse_json("{a:()}", true).expect_err("should error"),
|
*engine.parse_json("{a:()}", true).unwrap_err(),
|
||||||
EvalAltResult::ErrorParsing(..)
|
EvalAltResult::ErrorParsing(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine.parse_json("#{a:123+456}", true).unwrap_err(),
|
||||||
.parse_json("#{a:123+456}", true)
|
|
||||||
.expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorParsing(..)
|
EvalAltResult::ErrorParsing(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine.parse_json("{a:`hello${world}`}", true).unwrap_err(),
|
||||||
.parse_json("{a:`hello${world}`}", true)
|
|
||||||
.expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorParsing(..)
|
EvalAltResult::ErrorParsing(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ fn test_method_call_typed() -> Result<(), Box<EvalAltResult>> {
|
|||||||
foo(1000);
|
foo(1000);
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("foo")
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("foo")
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ fn test_method_call_typed() -> Result<(), Box<EvalAltResult>> {
|
|||||||
x.foo(1000);
|
x.foo(1000);
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("foo")
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("foo")
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -36,21 +36,21 @@ fn test_mismatched_op_custom_type() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let x = new_ts();
|
let x = new_ts();
|
||||||
let y = new_ts();
|
let y = new_ts();
|
||||||
x == y
|
x == y
|
||||||
").expect_err("should error"),
|
").unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f == "== (TestStruct, TestStruct)"));
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f == "== (TestStruct, TestStruct)"));
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(*engine.eval::<bool>("new_ts() == 42").expect_err("should error"),
|
matches!(*engine.eval::<bool>("new_ts() == 42").unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (TestStruct, "))
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (TestStruct, "))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<INT>("60 + new_ts()").expect_err("should error"),
|
*engine.eval::<INT>("60 + new_ts()").unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f == format!("+ ({}, TestStruct)", std::any::type_name::<INT>())
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f == format!("+ ({}, TestStruct)", std::any::type_name::<INT>())
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval::<TestStruct>("42").expect_err("should error"),
|
*engine.eval::<TestStruct>("42").unwrap_err(),
|
||||||
EvalAltResult::ErrorMismatchOutputType(need, actual, ..)
|
EvalAltResult::ErrorMismatchOutputType(need, actual, ..)
|
||||||
if need == "TestStruct" && actual == std::any::type_name::<INT>()
|
if need == "TestStruct" && actual == std::any::type_name::<INT>()
|
||||||
));
|
));
|
||||||
|
@ -227,7 +227,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
sum
|
sum
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorTooManyModules(..)
|
EvalAltResult::ErrorTooManyModules(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -250,7 +250,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
sum
|
sum
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorInFunctionCall(fn_name, ..) if fn_name == "foo"
|
EvalAltResult::ErrorInFunctionCall(fn_name, ..) if fn_name == "foo"
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -403,7 +403,7 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run(r#"import "testing" as ttt; ttt::hidden()"#)
|
.run(r#"import "testing" as ttt; ttt::hidden()"#)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_name, ..) if fn_name == "ttt::hidden ()"
|
EvalAltResult::ErrorFunctionNotFound(fn_name, ..) if fn_name == "ttt::hidden ()"
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -415,13 +415,13 @@ fn test_module_export() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile("let x = 10; { export x; }").expect_err("should error"),
|
engine.compile("let x = 10; { export x; }").unwrap_err(),
|
||||||
ParseError(x, ..) if *x == ParseErrorType::WrongExport
|
ParseError(x, ..) if *x == ParseErrorType::WrongExport
|
||||||
));
|
));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile("fn abc(x) { export x; }").expect_err("should error"),
|
engine.compile("fn abc(x) { export x; }").unwrap_err(),
|
||||||
ParseError(x, ..) if *x == ParseErrorType::WrongExport
|
ParseError(x, ..) if *x == ParseErrorType::WrongExport
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.run("let x = 0; while x < 20 { x += 1; }")?;
|
engine.run("let x = 0; while x < 20 { x += 1; }")?;
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.run("for x in 0..500 {}").expect_err("should error"),
|
*engine.run("for x in 0..500 {}").unwrap_err(),
|
||||||
EvalAltResult::ErrorTooManyOperations(..)
|
EvalAltResult::ErrorTooManyOperations(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -41,9 +41,7 @@ fn test_max_operations_literal() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine.run("[1, 2, 3, 4, 5, 6, 7, 8, 9]").unwrap_err(),
|
||||||
.run("[1, 2, 3, 4, 5, 6, 7, 8, 9]")
|
|
||||||
.expect_err("should error"),
|
|
||||||
EvalAltResult::ErrorTooManyOperations(..)
|
EvalAltResult::ErrorTooManyOperations(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -54,7 +52,7 @@ fn test_max_operations_literal() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run("#{a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9}")
|
.run("#{a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9}")
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorTooManyOperations(..)
|
EvalAltResult::ErrorTooManyOperations(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -110,7 +108,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorTooManyOperations(..)
|
EvalAltResult::ErrorTooManyOperations(..)
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -137,7 +135,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
eval(script);
|
eval(script);
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorInFunctionCall(.., err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(..))
|
EvalAltResult::ErrorInFunctionCall(.., err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(..))
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -162,7 +160,7 @@ fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run("for x in 0..500 {}")
|
.run("for x in 0..500 {}")
|
||||||
.expect_err("should error"),
|
.unwrap_err(),
|
||||||
EvalAltResult::ErrorTerminated(x, ..) if x.as_int()? == 42
|
EvalAltResult::ErrorTerminated(x, ..) if x.as_int()? == 42
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -26,17 +26,17 @@ fn test_ops_other_number_types() -> Result<(), Box<EvalAltResult>> {
|
|||||||
scope.push("x", 42_u16);
|
scope.push("x", 42_u16);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval_with_scope::<bool>(&mut scope, "x == 42").expect_err("should error"),
|
*engine.eval_with_scope::<bool>(&mut scope, "x == 42").unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (u16,")
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (u16,")
|
||||||
));
|
));
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.eval_with_scope::<bool>(&mut scope, "x == 42.0").expect_err("should error"),
|
*engine.eval_with_scope::<bool>(&mut scope, "x == 42.0").unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (u16,")
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (u16,")
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(*engine.eval_with_scope::<bool>(&mut scope, r#"x == "hello""#).expect_err("should error"),
|
matches!(*engine.eval_with_scope::<bool>(&mut scope, r#"x == "hello""#).unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (u16,")
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (u16,")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -182,9 +182,44 @@ fn test_optimizer_reoptimize() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let scope: Scope = ast.iter_literal_variables(true, false).collect();
|
let scope: Scope = ast.iter_literal_variables(true, false).collect();
|
||||||
let ast = engine.optimize_ast(&scope, ast, OptimizationLevel::Simple);
|
let ast = engine.optimize_ast(&scope, ast, OptimizationLevel::Simple);
|
||||||
|
|
||||||
println!("{ast:#?}");
|
|
||||||
|
|
||||||
assert_eq!(engine.eval_ast::<INT>(&ast)?, 84);
|
assert_eq!(engine.eval_ast::<INT>(&ast)?, 84);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_optimizer_full() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct TestStruct(INT);
|
||||||
|
|
||||||
|
const SCRIPT: &str = "
|
||||||
|
const FOO = ts(40) + ts(2);
|
||||||
|
value(FOO)
|
||||||
|
";
|
||||||
|
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
|
engine.set_optimization_level(OptimizationLevel::Full);
|
||||||
|
|
||||||
|
engine
|
||||||
|
.register_type_with_name::<TestStruct>("TestStruct")
|
||||||
|
.register_fn("ts", |n: INT| TestStruct(n))
|
||||||
|
.register_fn("value", |ts: &mut TestStruct| ts.0)
|
||||||
|
.register_fn("+", |ts1: &mut TestStruct, ts2: TestStruct| {
|
||||||
|
TestStruct(ts1.0 + ts2.0)
|
||||||
|
});
|
||||||
|
|
||||||
|
let ast = engine.compile(SCRIPT)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
assert_eq!(ast.statements().len(), 2);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval_ast_with_scope::<INT>(&mut scope, &ast)?, 42);
|
||||||
|
|
||||||
|
assert_eq!(scope.len(), 1);
|
||||||
|
|
||||||
|
assert_eq!(scope.get_value::<TestStruct>("FOO").unwrap().0, 42);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -118,7 +118,7 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.run("const A = [1, 2, 3]; A.no_effect = 42;")?;
|
engine.run("const A = [1, 2, 3]; A.no_effect = 42;")?;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(*engine.run("const A = [1, 2, 3]; A.test(42);").expect_err("should error"),
|
matches!(*engine.run("const A = [1, 2, 3]; A.test(42);").unwrap_err(),
|
||||||
EvalAltResult::ErrorNonPureMethodCallOnConstant(x, ..) if x == "test")
|
EvalAltResult::ErrorNonPureMethodCallOnConstant(x, ..) if x == "test")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -116,7 +116,7 @@ fn test_string_mut() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>(r#"foo("hello")"#)?, 5);
|
assert_eq!(engine.eval::<INT>(r#"foo("hello")"#)?, 5);
|
||||||
assert_eq!(engine.eval::<INT>(r#"bar("hello")"#)?, 5);
|
assert_eq!(engine.eval::<INT>(r#"bar("hello")"#)?, 5);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(*engine.eval::<INT>(r#"baz("hello")"#).expect_err("should error"),
|
matches!(*engine.eval::<INT>(r#"baz("hello")"#).unwrap_err(),
|
||||||
EvalAltResult::ErrorFunctionNotFound(f, ..) if f == "baz (&str | ImmutableString | String)"
|
EvalAltResult::ErrorFunctionNotFound(f, ..) if f == "baz (&str | ImmutableString | String)"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -110,7 +110,7 @@ fn test_switch_errors() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("switch x { _ => 123, 1 => 42 }")
|
.compile("switch x { _ => 123, 1 => 42 }")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::WrongSwitchDefaultCase
|
ParseErrorType::WrongSwitchDefaultCase
|
||||||
));
|
));
|
||||||
@ -174,7 +174,7 @@ fn test_switch_condition() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("switch x { 1 => 123, _ if true => 42 }")
|
.compile("switch x { 1 => 123, _ if true => 42 }")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::WrongSwitchCaseCondition
|
ParseErrorType::WrongSwitchCaseCondition
|
||||||
));
|
));
|
||||||
@ -269,14 +269,14 @@ fn test_switch_ranges() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile(
|
engine.compile(
|
||||||
"switch x { 10..20 => (), 20..=42 => 'a', 25..45 => 'z', 42 => 'x', 30..100 => true }"
|
"switch x { 10..20 => (), 20..=42 => 'a', 25..45 => 'z', 42 => 'x', 30..100 => true }"
|
||||||
).expect_err("should error").err_type(),
|
).unwrap_err().err_type(),
|
||||||
ParseErrorType::WrongSwitchIntegerCase
|
ParseErrorType::WrongSwitchIntegerCase
|
||||||
));
|
));
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile(
|
engine.compile(
|
||||||
"switch x { 10..20 => (), 20..=42 => 'a', 25..45 => 'z', 42.0 => 'x', 30..100 => true }"
|
"switch x { 10..20 => (), 20..=42 => 'a', 25..45 => 'z', 42.0 => 'x', 30..100 => true }"
|
||||||
).expect_err("should error").err_type(),
|
).unwrap_err().err_type(),
|
||||||
ParseErrorType::WrongSwitchIntegerCase
|
ParseErrorType::WrongSwitchIntegerCase
|
||||||
));
|
));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -9,7 +9,7 @@ fn test_tokens_disabled() {
|
|||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine
|
engine
|
||||||
.compile("let x = if true { 42 } else { 0 };")
|
.compile("let x = if true { 42 } else { 0 };")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::Reserved(err) if err == "if"
|
ParseErrorType::Reserved(err) if err == "if"
|
||||||
));
|
));
|
||||||
@ -19,13 +19,13 @@ fn test_tokens_disabled() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.compile("let x = 40 + 2; x += 1;")
|
.compile("let x = 40 + 2; x += 1;")
|
||||||
.expect_err("should error")
|
.unwrap_err()
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::UnknownOperator("+=".to_string())
|
ParseErrorType::UnknownOperator("+=".to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile("let x = += 0;").expect_err("should error").err_type(),
|
engine.compile("let x = += 0;").unwrap_err().err_type(),
|
||||||
ParseErrorType::Reserved(err) if err == "+="
|
ParseErrorType::Reserved(err) if err == "+="
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,6 @@ fn test_unit_eq() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_unit_with_spaces() -> Result<(), Box<EvalAltResult>> {
|
fn test_unit_with_spaces() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
let _ = engine.run("let x = ( ); x").expect_err("should error");
|
let _ = engine.run("let x = ( ); x").unwrap_err();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -209,7 +209,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "chameleon")?, 1);
|
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "chameleon")?, 1);
|
||||||
assert!(
|
assert!(
|
||||||
matches!(*engine.eval_with_scope::<INT>(&mut scope, "DO_NOT_USE").expect_err("should error"),
|
matches!(*engine.eval_with_scope::<INT>(&mut scope, "DO_NOT_USE").unwrap_err(),
|
||||||
EvalAltResult::ErrorVariableNotFound(n, ..) if n == "DO_NOT_USE")
|
EvalAltResult::ErrorVariableNotFound(n, ..) if n == "DO_NOT_USE")
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ fn test_var_def_filter() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
engine.compile("let x = 42;").expect_err("should error").err_type(),
|
engine.compile("let x = 42;").unwrap_err().err_type(),
|
||||||
ParseErrorType::ForbiddenVariable(s) if s == "x"
|
ParseErrorType::ForbiddenVariable(s) if s == "x"
|
||||||
));
|
));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
Build Tools
|
Build Tools
|
||||||
===========
|
===========
|
||||||
|
|
||||||
| File | Description |
|
This directory contains input files for various build tools required for building Rhai.
|
||||||
| -------------- | ------------------------------------------- |
|
|
||||||
| `keywords.txt` | Input file for GNU gperf for the tokenizer. |
|
| File | Build tool | Description |
|
||||||
| `reserved.txt` | Input file for GNU gperf for the tokenizer. |
|
| -------------- | :---------: | ------------------------------------------------------------------ |
|
||||||
|
| `keywords.txt` | GNU `gperf` | Input file for the tokenizer – keywords recognition. |
|
||||||
|
| `reserved.txt` | GNU `gperf` | Input file for the tokenizer – reserved symbols recognition. |
|
||||||
|
Loading…
Reference in New Issue
Block a user