Add fail on invalid property for maps.
This commit is contained in:
parent
6acc486e00
commit
340a047369
@ -6,7 +6,11 @@ Version 1.5.0
|
|||||||
|
|
||||||
This version adds a debugging interface, which can be used to integrate a debugger.
|
This version adds a debugging interface, which can be used to integrate a debugger.
|
||||||
|
|
||||||
The `REPL` tool uses [`rustyline`](https://crates.io/crates/rustyline) for line editing and history.
|
Based on popular demand, an option is added to throw exceptions when invalid properties are accessed
|
||||||
|
on object maps (default is to return `()`).
|
||||||
|
|
||||||
|
Also based on popular demand, the `REPL` tool now uses
|
||||||
|
[`rustyline`](https://crates.io/crates/rustyline) for line editing and history.
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
---------
|
---------
|
||||||
@ -17,6 +21,7 @@ Bug fixes
|
|||||||
* Globally-defined constants are now encapsulated correctly inside a loaded module and no longer spill across call boundaries.
|
* Globally-defined constants are now encapsulated correctly inside a loaded module and no longer spill across call boundaries.
|
||||||
* Type names display is fixed.
|
* Type names display is fixed.
|
||||||
* Exceptions thrown inside function calls now unwrap correctly when `catch`-ed.
|
* Exceptions thrown inside function calls now unwrap correctly when `catch`-ed.
|
||||||
|
* Error messages for certain invalid property accesses are fixed.
|
||||||
|
|
||||||
Script-breaking changes
|
Script-breaking changes
|
||||||
-----------------------
|
-----------------------
|
||||||
@ -29,6 +34,7 @@ New features
|
|||||||
* A debugging interface is added.
|
* A debugging interface is added.
|
||||||
* A new bin tool, `rhai-dbg` (aka _The Rhai Debugger_), is added to showcase the debugging interface.
|
* A new bin tool, `rhai-dbg` (aka _The Rhai Debugger_), is added to showcase the debugging interface.
|
||||||
* A new package, `DebuggingPackage`, is added which contains the `back_trace` function to get the current call stack anywhere in a script.
|
* A new package, `DebuggingPackage`, is added which contains the `back_trace` function to get the current call stack anywhere in a script.
|
||||||
|
* `Engine::set_fail_on_invalid_map_property` is added to control whether to raise an error (new `EvalAltResult::ErrorPropertyNotFound`) when invalid properties are accessed on object maps.
|
||||||
* `Engine::set_allow_shadowing` is added to allow/disallow variables _shadowing_, with new errors `EvalAltResult::ErrorVariableExists` and `ParseErrorType::VariableExists`.
|
* `Engine::set_allow_shadowing` is added to allow/disallow variables _shadowing_, with new errors `EvalAltResult::ErrorVariableExists` and `ParseErrorType::VariableExists`.
|
||||||
* `Engine::on_def_var` allows registering a closure which can decide whether a variable definition is allow to continue, or should fail with an error.
|
* `Engine::on_def_var` allows registering a closure which can decide whether a variable definition is allow to continue, or should fail with an error.
|
||||||
|
|
||||||
|
@ -18,10 +18,14 @@ pub struct LanguageOptions {
|
|||||||
pub allow_anonymous_fn: bool,
|
pub allow_anonymous_fn: bool,
|
||||||
/// Is looping allowed?
|
/// Is looping allowed?
|
||||||
pub allow_looping: bool,
|
pub allow_looping: bool,
|
||||||
/// Strict variables mode?
|
|
||||||
pub strict_var: bool,
|
|
||||||
/// Is variables shadowing allowed?
|
/// Is variables shadowing allowed?
|
||||||
pub allow_shadowing: bool,
|
pub allow_shadowing: bool,
|
||||||
|
/// Strict variables mode?
|
||||||
|
pub strict_var: bool,
|
||||||
|
/// Raise error if an object map property does not exist?
|
||||||
|
/// Returns `()` if `false`.
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
pub fail_on_invalid_map_property: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LanguageOptions {
|
impl LanguageOptions {
|
||||||
@ -37,6 +41,8 @@ impl LanguageOptions {
|
|||||||
allow_looping: true,
|
allow_looping: true,
|
||||||
strict_var: false,
|
strict_var: false,
|
||||||
allow_shadowing: true,
|
allow_shadowing: true,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
fail_on_invalid_map_property: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,6 +55,7 @@ impl Default for LanguageOptions {
|
|||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Is `if`-expression allowed?
|
/// Is `if`-expression allowed?
|
||||||
|
/// Default is `true`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn allow_if_expression(&self) -> bool {
|
pub fn allow_if_expression(&self) -> bool {
|
||||||
self.options.allow_if_expr
|
self.options.allow_if_expr
|
||||||
@ -59,6 +66,7 @@ impl Engine {
|
|||||||
self.options.allow_if_expr = enable;
|
self.options.allow_if_expr = enable;
|
||||||
}
|
}
|
||||||
/// Is `switch` expression allowed?
|
/// Is `switch` expression allowed?
|
||||||
|
/// Default is `true`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn allow_switch_expression(&self) -> bool {
|
pub fn allow_switch_expression(&self) -> bool {
|
||||||
self.options.allow_switch_expr
|
self.options.allow_switch_expr
|
||||||
@ -69,6 +77,7 @@ impl Engine {
|
|||||||
self.options.allow_switch_expr = enable;
|
self.options.allow_switch_expr = enable;
|
||||||
}
|
}
|
||||||
/// Is statement-expression allowed?
|
/// Is statement-expression allowed?
|
||||||
|
/// Default is `true`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn allow_statement_expression(&self) -> bool {
|
pub fn allow_statement_expression(&self) -> bool {
|
||||||
self.options.allow_stmt_expr
|
self.options.allow_stmt_expr
|
||||||
@ -79,6 +88,7 @@ impl Engine {
|
|||||||
self.options.allow_stmt_expr = enable;
|
self.options.allow_stmt_expr = enable;
|
||||||
}
|
}
|
||||||
/// Is anonymous function allowed?
|
/// Is anonymous function allowed?
|
||||||
|
/// Default is `true`.
|
||||||
///
|
///
|
||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -95,6 +105,7 @@ impl Engine {
|
|||||||
self.options.allow_anonymous_fn = enable;
|
self.options.allow_anonymous_fn = enable;
|
||||||
}
|
}
|
||||||
/// Is looping allowed?
|
/// Is looping allowed?
|
||||||
|
/// Default is `true`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn allow_looping(&self) -> bool {
|
pub fn allow_looping(&self) -> bool {
|
||||||
self.options.allow_looping
|
self.options.allow_looping
|
||||||
@ -104,17 +115,8 @@ impl Engine {
|
|||||||
pub fn set_allow_looping(&mut self, enable: bool) {
|
pub fn set_allow_looping(&mut self, enable: bool) {
|
||||||
self.options.allow_looping = enable;
|
self.options.allow_looping = enable;
|
||||||
}
|
}
|
||||||
/// Is strict variables mode enabled?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn strict_variables(&self) -> bool {
|
|
||||||
self.options.strict_var
|
|
||||||
}
|
|
||||||
/// Set whether strict variables mode is enabled.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_strict_variables(&mut self, enable: bool) {
|
|
||||||
self.options.strict_var = enable;
|
|
||||||
}
|
|
||||||
/// Is variables shadowing allowed?
|
/// Is variables shadowing allowed?
|
||||||
|
/// Default is `true`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn allow_shadowing(&self) -> bool {
|
pub fn allow_shadowing(&self) -> bool {
|
||||||
self.options.allow_shadowing
|
self.options.allow_shadowing
|
||||||
@ -124,4 +126,32 @@ impl Engine {
|
|||||||
pub fn set_allow_shadowing(&mut self, enable: bool) {
|
pub fn set_allow_shadowing(&mut self, enable: bool) {
|
||||||
self.options.allow_shadowing = enable;
|
self.options.allow_shadowing = enable;
|
||||||
}
|
}
|
||||||
|
/// Is strict variables mode enabled?
|
||||||
|
/// Default is `false`.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn strict_variables(&self) -> bool {
|
||||||
|
self.options.strict_var
|
||||||
|
}
|
||||||
|
/// Set whether strict variables mode is enabled.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_strict_variables(&mut self, enable: bool) {
|
||||||
|
self.options.strict_var = enable;
|
||||||
|
}
|
||||||
|
/// Raise error if an object map property does not exist?
|
||||||
|
/// Default is `false`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_object`.
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn fail_on_invalid_map_property(&self) -> bool {
|
||||||
|
self.options.fail_on_invalid_map_property
|
||||||
|
}
|
||||||
|
/// Set whether to raise error if an object map property does not exist.
|
||||||
|
///
|
||||||
|
/// Not available under `no_object`.
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_fail_on_invalid_map_property(&mut self, enable: bool) {
|
||||||
|
self.options.fail_on_invalid_map_property = enable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@
|
|||||||
use super::{EvalState, GlobalRuntimeState, Target};
|
use super::{EvalState, GlobalRuntimeState, Target};
|
||||||
use crate::ast::{Expr, OpAssignment};
|
use crate::ast::{Expr, OpAssignment};
|
||||||
use crate::types::dynamic::Union;
|
use crate::types::dynamic::Union;
|
||||||
use crate::{
|
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR};
|
||||||
Dynamic, Engine, Module, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR,
|
|
||||||
};
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -185,20 +183,15 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(mut new_val) = try_setter {
|
if let Some(mut new_val) = try_setter {
|
||||||
// Try to call index setter if value is changed
|
// Try to call index setter if value is changed
|
||||||
let hash_set =
|
let idx = &mut idx_val_for_setter;
|
||||||
crate::ast::FnCallHashes::from_native(global.hash_idx_set());
|
let new_val = &mut new_val;
|
||||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
self.call_indexer_set(
|
||||||
let fn_name = crate::engine::FN_IDX_SET;
|
global, state, lib, target, idx, new_val, is_ref_mut, level,
|
||||||
|
)
|
||||||
if let Err(err) = self.exec_fn_call(
|
.or_else(|idx_err| match *idx_err {
|
||||||
None, global, state, lib, fn_name, hash_set, args, is_ref_mut,
|
ERR::ErrorIndexingType(..) => Ok((Dynamic::UNIT, false)),
|
||||||
true, root_pos, level,
|
_ => Err(idx_err.fill_position(root_pos)),
|
||||||
) {
|
})?;
|
||||||
// Just ignore if there is no index setter
|
|
||||||
if !matches!(*err, ERR::ErrorFunctionNotFound(..)) {
|
|
||||||
return Err(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -234,15 +227,12 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(mut new_val) = try_setter {
|
if let Some(mut new_val) = try_setter {
|
||||||
// Try to call index setter
|
// Try to call index setter
|
||||||
let hash_set =
|
let idx = &mut idx_val_for_setter;
|
||||||
crate::ast::FnCallHashes::from_native(global.hash_idx_set());
|
let new_val = &mut new_val;
|
||||||
let args = &mut [target, &mut idx_val_for_setter, &mut new_val];
|
self.call_indexer_set(
|
||||||
let fn_name = crate::engine::FN_IDX_SET;
|
global, state, lib, target, idx, new_val, is_ref_mut, level,
|
||||||
|
)
|
||||||
self.exec_fn_call(
|
.map_err(|err| err.fill_position(root_pos))?;
|
||||||
None, global, state, lib, fn_name, hash_set, args, is_ref_mut,
|
|
||||||
true, root_pos, level,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((Dynamic::UNIT, true))
|
Ok((Dynamic::UNIT, true))
|
||||||
@ -341,12 +331,10 @@ impl Engine {
|
|||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
ERR::ErrorDotExpr(..) => {
|
ERR::ErrorDotExpr(..) => {
|
||||||
let prop = name.into();
|
let mut prop = name.into();
|
||||||
self.get_indexed_mut(
|
self.call_indexer_get(
|
||||||
global, state, lib, target, prop, *pos, false, true,
|
global, state, lib, target, &mut prop, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
.map(|v| (v.take_or_clone(), false))
|
|
||||||
.map_err(
|
.map_err(
|
||||||
|idx_err| match *idx_err {
|
|idx_err| match *idx_err {
|
||||||
ERR::ErrorIndexingType(..) => err,
|
ERR::ErrorIndexingType(..) => err,
|
||||||
@ -382,15 +370,10 @@ impl Engine {
|
|||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
ERR::ErrorDotExpr(..) => {
|
ERR::ErrorDotExpr(..) => {
|
||||||
let args = &mut [target, &mut name.into(), &mut new_val];
|
let idx = &mut name.into();
|
||||||
let fn_name = crate::engine::FN_IDX_SET;
|
let new_val = &mut new_val;
|
||||||
let hash_set =
|
self.call_indexer_set(
|
||||||
crate::ast::FnCallHashes::from_native(global.hash_idx_set());
|
global, state, lib, target, idx, new_val, is_ref_mut, level,
|
||||||
let pos = Position::NONE;
|
|
||||||
|
|
||||||
self.exec_fn_call(
|
|
||||||
None, global, state, lib, fn_name, hash_set, args, is_ref_mut,
|
|
||||||
true, pos, level,
|
|
||||||
)
|
)
|
||||||
.map_err(
|
.map_err(
|
||||||
|idx_err| match *idx_err {
|
|idx_err| match *idx_err {
|
||||||
@ -418,11 +401,10 @@ impl Engine {
|
|||||||
|err| match *err {
|
|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
ERR::ErrorDotExpr(..) => {
|
ERR::ErrorDotExpr(..) => {
|
||||||
let prop = name.into();
|
let mut prop = name.into();
|
||||||
self.get_indexed_mut(
|
self.call_indexer_get(
|
||||||
global, state, lib, target, prop, *pos, false, true, level,
|
global, state, lib, target, &mut prop, level,
|
||||||
)
|
)
|
||||||
.map(|v| (v.take_or_clone(), false))
|
|
||||||
.map_err(|idx_err| {
|
.map_err(|idx_err| {
|
||||||
match *idx_err {
|
match *idx_err {
|
||||||
ERR::ErrorIndexingType(..) => err,
|
ERR::ErrorIndexingType(..) => err,
|
||||||
@ -517,16 +499,14 @@ impl Engine {
|
|||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
ERR::ErrorDotExpr(..) => {
|
ERR::ErrorDotExpr(..) => {
|
||||||
let prop = name.into();
|
let mut prop = name.into();
|
||||||
self.get_indexed_mut(
|
self.call_indexer_get(
|
||||||
global, state, lib, target, prop, pos, false, true,
|
global, state, lib, target, &mut prop, level,
|
||||||
level,
|
|
||||||
)
|
)
|
||||||
.map(|v| (v.take_or_clone(), false))
|
|
||||||
.map_err(
|
.map_err(
|
||||||
|idx_err| match *idx_err {
|
|idx_err| match *idx_err {
|
||||||
ERR::ErrorIndexingType(..) => err,
|
ERR::ErrorIndexingType(..) => err,
|
||||||
_ => idx_err,
|
_ => idx_err.fill_position(pos),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -566,16 +546,11 @@ impl Engine {
|
|||||||
|err| match *err {
|
|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
ERR::ErrorDotExpr(..) => {
|
ERR::ErrorDotExpr(..) => {
|
||||||
let args =
|
let idx = &mut name.into();
|
||||||
&mut [target.as_mut(), &mut name.into(), val];
|
let new_val = val;
|
||||||
let fn_name = crate::engine::FN_IDX_SET;
|
self.call_indexer_set(
|
||||||
let hash_set =
|
global, state, lib, target, idx, new_val,
|
||||||
crate::ast::FnCallHashes::from_native(
|
is_ref_mut, level,
|
||||||
global.hash_idx_set(),
|
|
||||||
);
|
|
||||||
self.exec_fn_call(
|
|
||||||
None, global, state, lib, fn_name, hash_set,
|
|
||||||
args, is_ref_mut, true, pos, level,
|
|
||||||
)
|
)
|
||||||
.or_else(|idx_err| match *idx_err {
|
.or_else(|idx_err| match *idx_err {
|
||||||
ERR::ErrorIndexingType(..) => {
|
ERR::ErrorIndexingType(..) => {
|
||||||
@ -583,7 +558,7 @@ impl Engine {
|
|||||||
// the property is read-only
|
// the property is read-only
|
||||||
Ok((Dynamic::UNIT, false))
|
Ok((Dynamic::UNIT, false))
|
||||||
}
|
}
|
||||||
_ => Err(idx_err),
|
_ => Err(idx_err.fill_position(pos)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
@ -743,7 +718,7 @@ impl Engine {
|
|||||||
pos = arg_pos;
|
pos = arg_pos;
|
||||||
}
|
}
|
||||||
values.push(value.flatten());
|
values.push(value.flatten());
|
||||||
Ok::<_, RhaiError>((values, pos))
|
Ok::<_, crate::RhaiError>((values, pos))
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@ -789,7 +764,7 @@ impl Engine {
|
|||||||
pos = arg_pos
|
pos = arg_pos
|
||||||
}
|
}
|
||||||
values.push(value.flatten());
|
values.push(value.flatten());
|
||||||
Ok::<_, RhaiError>((values, pos))
|
Ok::<_, crate::RhaiError>((values, pos))
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
super::ChainArgument::from_fn_call_args(values, pos)
|
super::ChainArgument::from_fn_call_args(values, pos)
|
||||||
@ -842,6 +817,52 @@ impl Engine {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Call a get indexer.
|
||||||
|
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
||||||
|
#[inline(always)]
|
||||||
|
fn call_indexer_get(
|
||||||
|
&self,
|
||||||
|
global: &mut GlobalRuntimeState,
|
||||||
|
state: &mut EvalState,
|
||||||
|
lib: &[&Module],
|
||||||
|
target: &mut Dynamic,
|
||||||
|
idx: &mut Dynamic,
|
||||||
|
level: usize,
|
||||||
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
|
let args = &mut [target, idx];
|
||||||
|
let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get());
|
||||||
|
let fn_name = crate::engine::FN_IDX_GET;
|
||||||
|
let pos = Position::NONE;
|
||||||
|
|
||||||
|
self.exec_fn_call(
|
||||||
|
None, global, state, lib, fn_name, hash_get, args, true, true, pos, level,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a set indexer.
|
||||||
|
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
||||||
|
#[inline(always)]
|
||||||
|
fn call_indexer_set(
|
||||||
|
&self,
|
||||||
|
global: &mut GlobalRuntimeState,
|
||||||
|
state: &mut EvalState,
|
||||||
|
lib: &[&Module],
|
||||||
|
target: &mut Dynamic,
|
||||||
|
idx: &mut Dynamic,
|
||||||
|
new_val: &mut Dynamic,
|
||||||
|
is_ref_mut: bool,
|
||||||
|
level: usize,
|
||||||
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
|
let hash_set = crate::ast::FnCallHashes::from_native(global.hash_idx_set());
|
||||||
|
let args = &mut [target, idx, new_val];
|
||||||
|
let fn_name = crate::engine::FN_IDX_SET;
|
||||||
|
let pos = Position::NONE;
|
||||||
|
|
||||||
|
self.exec_fn_call(
|
||||||
|
None, global, state, lib, fn_name, hash_set, args, is_ref_mut, true, pos, level,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the value at the indexed position of a base type.
|
/// Get the value at the indexed position of a base type.
|
||||||
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
|
||||||
fn get_indexed_mut<'t>(
|
fn get_indexed_mut<'t>(
|
||||||
@ -908,10 +929,13 @@ impl Engine {
|
|||||||
map.insert(index.clone().into(), Dynamic::UNIT);
|
map.insert(index.clone().into(), Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(map
|
if let Some(value) = map.get_mut(index.as_str()) {
|
||||||
.get_mut(index.as_str())
|
Ok(Target::from(value))
|
||||||
.map(Target::from)
|
} else if self.fail_on_invalid_map_property() {
|
||||||
.unwrap_or_else(|| Target::from(Dynamic::UNIT)))
|
Err(ERR::ErrorPropertyNotFound(index.to_string(), pos).into())
|
||||||
|
} else {
|
||||||
|
Ok(Target::from(Dynamic::UNIT))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1045,17 +1069,9 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
_ if use_indexers => {
|
_ if use_indexers => self
|
||||||
let args = &mut [target, &mut idx];
|
.call_indexer_get(global, state, lib, target, &mut idx, level)
|
||||||
let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get());
|
.map(|(v, ..)| v.into()),
|
||||||
let fn_name = crate::engine::FN_IDX_GET;
|
|
||||||
let pos = Position::NONE;
|
|
||||||
|
|
||||||
self.exec_fn_call(
|
|
||||||
None, global, state, lib, fn_name, hash_get, args, true, true, pos, level,
|
|
||||||
)
|
|
||||||
.map(|(v, ..)| v.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => Err(ERR::ErrorIndexingType(
|
_ => Err(ERR::ErrorIndexingType(
|
||||||
format!(
|
format!(
|
||||||
|
@ -6,13 +6,11 @@ use crate::plugin::*;
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
use crate::{Dynamic, NativeCallContext};
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
use crate::{Array, Dynamic, NativeCallContext};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
|
@ -13,6 +13,10 @@ use std::prelude::v1::*;
|
|||||||
///
|
///
|
||||||
/// All wrapped [`Position`] values represent the location in the script where the error occurs.
|
/// All wrapped [`Position`] values represent the location in the script where the error occurs.
|
||||||
///
|
///
|
||||||
|
/// Some errors never appear when certain features are turned on.
|
||||||
|
/// They still exist so that the application can turn features on and off without going through
|
||||||
|
/// massive code changes to remove/add back enum variants in match statements.
|
||||||
|
///
|
||||||
/// # Thread Safety
|
/// # Thread Safety
|
||||||
///
|
///
|
||||||
/// Currently, [`EvalAltResult`] is neither [`Send`] nor [`Sync`].
|
/// Currently, [`EvalAltResult`] is neither [`Send`] nor [`Sync`].
|
||||||
@ -32,8 +36,10 @@ pub enum EvalAltResult {
|
|||||||
|
|
||||||
/// Shadowing of an existing variable disallowed. Wrapped value is the variable name.
|
/// Shadowing of an existing variable disallowed. Wrapped value is the variable name.
|
||||||
ErrorVariableExists(String, Position),
|
ErrorVariableExists(String, Position),
|
||||||
/// Usage of an unknown variable. Wrapped value is the variable name.
|
/// Access of an unknown variable. Wrapped value is the variable name.
|
||||||
ErrorVariableNotFound(String, Position),
|
ErrorVariableNotFound(String, Position),
|
||||||
|
/// Access of an unknown object map property. Wrapped value is the property name.
|
||||||
|
ErrorPropertyNotFound(String, Position),
|
||||||
/// Call to an unknown function. Wrapped value is the function signature.
|
/// Call to an unknown function. Wrapped value is the function signature.
|
||||||
ErrorFunctionNotFound(String, Position),
|
ErrorFunctionNotFound(String, Position),
|
||||||
/// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name.
|
/// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name.
|
||||||
@ -143,6 +149,7 @@ impl fmt::Display for EvalAltResult {
|
|||||||
|
|
||||||
Self::ErrorVariableExists(s, ..) => write!(f, "Variable is already defined: {}", s)?,
|
Self::ErrorVariableExists(s, ..) => write!(f, "Variable is already defined: {}", s)?,
|
||||||
Self::ErrorVariableNotFound(s, ..) => write!(f, "Variable not found: {}", s)?,
|
Self::ErrorVariableNotFound(s, ..) => write!(f, "Variable not found: {}", s)?,
|
||||||
|
Self::ErrorPropertyNotFound(s, ..) => write!(f, "Property not found: {}", s)?,
|
||||||
Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {}", s)?,
|
Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {}", s)?,
|
||||||
Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {}", s)?,
|
Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {}", s)?,
|
||||||
Self::ErrorDataRace(s, ..) => {
|
Self::ErrorDataRace(s, ..) => {
|
||||||
@ -279,6 +286,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorFor(..)
|
| Self::ErrorFor(..)
|
||||||
| Self::ErrorVariableExists(..)
|
| Self::ErrorVariableExists(..)
|
||||||
| Self::ErrorVariableNotFound(..)
|
| Self::ErrorVariableNotFound(..)
|
||||||
|
| Self::ErrorPropertyNotFound(..)
|
||||||
| Self::ErrorModuleNotFound(..)
|
| Self::ErrorModuleNotFound(..)
|
||||||
| Self::ErrorDataRace(..)
|
| Self::ErrorDataRace(..)
|
||||||
| Self::ErrorAssignmentToConstant(..)
|
| Self::ErrorAssignmentToConstant(..)
|
||||||
@ -370,6 +378,7 @@ impl EvalAltResult {
|
|||||||
}
|
}
|
||||||
Self::ErrorVariableExists(v, ..)
|
Self::ErrorVariableExists(v, ..)
|
||||||
| Self::ErrorVariableNotFound(v, ..)
|
| Self::ErrorVariableNotFound(v, ..)
|
||||||
|
| Self::ErrorPropertyNotFound(v, ..)
|
||||||
| Self::ErrorDataRace(v, ..)
|
| Self::ErrorDataRace(v, ..)
|
||||||
| Self::ErrorAssignmentToConstant(v, ..) => {
|
| Self::ErrorAssignmentToConstant(v, ..) => {
|
||||||
map.insert("variable".into(), v.into());
|
map.insert("variable".into(), v.into());
|
||||||
@ -432,6 +441,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorFor(pos)
|
| Self::ErrorFor(pos)
|
||||||
| Self::ErrorVariableExists(.., pos)
|
| Self::ErrorVariableExists(.., pos)
|
||||||
| Self::ErrorVariableNotFound(.., pos)
|
| Self::ErrorVariableNotFound(.., pos)
|
||||||
|
| Self::ErrorPropertyNotFound(.., pos)
|
||||||
| Self::ErrorModuleNotFound(.., pos)
|
| Self::ErrorModuleNotFound(.., pos)
|
||||||
| Self::ErrorDataRace(.., pos)
|
| Self::ErrorDataRace(.., pos)
|
||||||
| Self::ErrorAssignmentToConstant(.., pos)
|
| Self::ErrorAssignmentToConstant(.., pos)
|
||||||
@ -481,6 +491,7 @@ impl EvalAltResult {
|
|||||||
| Self::ErrorFor(pos)
|
| Self::ErrorFor(pos)
|
||||||
| Self::ErrorVariableExists(.., pos)
|
| Self::ErrorVariableExists(.., pos)
|
||||||
| Self::ErrorVariableNotFound(.., pos)
|
| Self::ErrorVariableNotFound(.., pos)
|
||||||
|
| Self::ErrorPropertyNotFound(.., pos)
|
||||||
| Self::ErrorModuleNotFound(.., pos)
|
| Self::ErrorModuleNotFound(.., pos)
|
||||||
| Self::ErrorDataRace(.., pos)
|
| Self::ErrorDataRace(.., pos)
|
||||||
| Self::ErrorAssignmentToConstant(.., pos)
|
| Self::ErrorAssignmentToConstant(.., pos)
|
||||||
|
@ -89,21 +89,12 @@ pub enum ParseErrorType {
|
|||||||
MalformedCallExpr(String),
|
MalformedCallExpr(String),
|
||||||
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error
|
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error
|
||||||
/// description (if any).
|
/// description (if any).
|
||||||
///
|
|
||||||
/// Never appears under the `no_index` feature.
|
|
||||||
MalformedIndexExpr(String),
|
MalformedIndexExpr(String),
|
||||||
/// An expression in an `in` expression has syntax error. Wrapped value is the error description
|
/// An expression in an `in` expression has syntax error. Wrapped value is the error description (if any).
|
||||||
/// (if any).
|
|
||||||
///
|
|
||||||
/// Never appears under the `no_object` and `no_index` features combination.
|
|
||||||
MalformedInExpr(String),
|
MalformedInExpr(String),
|
||||||
/// A capturing has syntax error. Wrapped value is the error description (if any).
|
/// A capturing has syntax error. Wrapped value is the error description (if any).
|
||||||
///
|
|
||||||
/// Never appears under the `no_closure` feature.
|
|
||||||
MalformedCapture(String),
|
MalformedCapture(String),
|
||||||
/// A map definition has duplicated property names. Wrapped value is the property name.
|
/// A map definition has duplicated property names. Wrapped value is the property name.
|
||||||
///
|
|
||||||
/// Never appears under the `no_object` feature.
|
|
||||||
DuplicatedProperty(String),
|
DuplicatedProperty(String),
|
||||||
/// A `switch` case is duplicated.
|
/// A `switch` case is duplicated.
|
||||||
DuplicatedSwitchCase,
|
DuplicatedSwitchCase,
|
||||||
@ -116,8 +107,6 @@ pub enum ParseErrorType {
|
|||||||
/// The case condition of a `switch` statement is not appropriate.
|
/// The case condition of a `switch` statement is not appropriate.
|
||||||
WrongSwitchCaseCondition,
|
WrongSwitchCaseCondition,
|
||||||
/// Missing a property name for custom types and maps.
|
/// Missing a property name for custom types and maps.
|
||||||
///
|
|
||||||
/// Never appears under the `no_object` feature.
|
|
||||||
PropertyExpected,
|
PropertyExpected,
|
||||||
/// Missing a variable name after the `let`, `const`, `for` or `catch` keywords.
|
/// Missing a variable name after the `let`, `const`, `for` or `catch` keywords.
|
||||||
VariableExpected,
|
VariableExpected,
|
||||||
@ -129,38 +118,22 @@ pub enum ParseErrorType {
|
|||||||
/// Missing an expression. Wrapped value is the expression type.
|
/// Missing an expression. Wrapped value is the expression type.
|
||||||
ExprExpected(String),
|
ExprExpected(String),
|
||||||
/// Defining a doc-comment in an appropriate place (e.g. not at global level).
|
/// Defining a doc-comment in an appropriate place (e.g. not at global level).
|
||||||
///
|
|
||||||
/// Never appears under the `no_function` feature.
|
|
||||||
WrongDocComment,
|
WrongDocComment,
|
||||||
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
||||||
///
|
|
||||||
/// Never appears under the `no_function` feature.
|
|
||||||
WrongFnDefinition,
|
WrongFnDefinition,
|
||||||
/// Defining a function with a name that conflicts with an existing function.
|
/// Defining a function with a name that conflicts with an existing function.
|
||||||
/// Wrapped values are the function name and number of parameters.
|
/// Wrapped values are the function name and number of parameters.
|
||||||
///
|
|
||||||
/// Never appears under the `no_object` feature.
|
|
||||||
FnDuplicatedDefinition(String, usize),
|
FnDuplicatedDefinition(String, usize),
|
||||||
/// Missing a function name after the `fn` keyword.
|
/// Missing a function name after the `fn` keyword.
|
||||||
///
|
|
||||||
/// Never appears under the `no_function` feature.
|
|
||||||
FnMissingName,
|
FnMissingName,
|
||||||
/// A function definition is missing the parameters list. Wrapped value is the function name.
|
/// A function definition is missing the parameters list. Wrapped value is the function name.
|
||||||
///
|
|
||||||
/// Never appears under the `no_function` feature.
|
|
||||||
FnMissingParams(String),
|
FnMissingParams(String),
|
||||||
/// A function definition has duplicated parameters. Wrapped values are the function name and
|
/// A function definition has duplicated parameters. Wrapped values are the function name and
|
||||||
/// parameter name.
|
/// parameter name.
|
||||||
///
|
|
||||||
/// Never appears under the `no_function` feature.
|
|
||||||
FnDuplicatedParam(String, String),
|
FnDuplicatedParam(String, String),
|
||||||
/// A function definition is missing the body. Wrapped value is the function name.
|
/// A function definition is missing the body. Wrapped value is the function name.
|
||||||
///
|
|
||||||
/// Never appears under the `no_function` feature.
|
|
||||||
FnMissingBody(String),
|
FnMissingBody(String),
|
||||||
/// Export statement not at global level.
|
/// Export statement not at global level.
|
||||||
///
|
|
||||||
/// Never appears under the `no_module` feature.
|
|
||||||
WrongExport,
|
WrongExport,
|
||||||
/// Assignment to an a constant variable. Wrapped value is the constant variable name.
|
/// Assignment to an a constant variable. Wrapped value is the constant variable name.
|
||||||
AssignmentToConstant(String),
|
AssignmentToConstant(String),
|
||||||
@ -178,16 +151,10 @@ pub enum ParseErrorType {
|
|||||||
/// An imported module is not found.
|
/// An imported module is not found.
|
||||||
///
|
///
|
||||||
/// Only appears when strict variables mode is enabled.
|
/// Only appears when strict variables mode is enabled.
|
||||||
///
|
|
||||||
/// Never appears under the `no_module` feature.
|
|
||||||
ModuleUndefined(String),
|
ModuleUndefined(String),
|
||||||
/// Expression exceeding the maximum levels of complexity.
|
/// Expression exceeding the maximum levels of complexity.
|
||||||
///
|
|
||||||
/// Never appears under the `unchecked` feature.
|
|
||||||
ExprTooDeep,
|
ExprTooDeep,
|
||||||
/// Literal exceeding the maximum size. Wrapped values are the data type name and the maximum size.
|
/// Literal exceeding the maximum size. Wrapped values are the data type name and the maximum size.
|
||||||
///
|
|
||||||
/// Never appears under the `unchecked` feature.
|
|
||||||
LiteralTooLarge(String, usize),
|
LiteralTooLarge(String, usize),
|
||||||
/// Break statement not inside a loop.
|
/// Break statement not inside a loop.
|
||||||
LoopBreak,
|
LoopBreak,
|
||||||
|
@ -107,6 +107,23 @@ b`: 1}; y["a\nb"]
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_map_prop() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<()>("let x = #{a: 42}; x.b")?, ());
|
||||||
|
|
||||||
|
engine.set_fail_on_invalid_map_property(true);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
matches!(*engine.eval::<()>("let x = #{a: 42}; x.b").expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorPropertyNotFound(prop, _) if prop == "b"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_map_assign() -> Result<(), Box<EvalAltResult>> {
|
fn test_map_assign() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
Loading…
Reference in New Issue
Block a user