Guard against setters mutating constants, and allow pure setters.
This commit is contained in:
parent
565134c4da
commit
941e09d29d
@ -4,11 +4,17 @@ Rhai Release Notes
|
|||||||
Version 0.20.2
|
Version 0.20.2
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Constant propagation during optimization for constants held in a custom scope now works properly instead of always replacing by `()`.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
|
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
|
||||||
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). There is no need to specify the number of variables added/removed.
|
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). There is no need to specify the number of variables added/removed.
|
||||||
|
* Assigning to a property of a constant is now allowed and no longer raise an `EvalAltResult::ErrorAssignmentToConstant` error. This is to facilitate the Singleton pattern. Registered setter functions are automatically guarded against setters calling on constants and will continue to raise errors unless the `pure` attribute is present (for plugins).
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -22,7 +28,7 @@ Enhancements
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). This allows more flexibility for cases where the number of new variables declared depends on internal logic.
|
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). This allows more flexibility for cases where the number of new variables declared depends on internal logic.
|
||||||
* Putting a `pure` attribute on a plugin property setter now raises a syntax error.
|
* Putting a `pure` attribute on a plugin property/index setter now enables it to be used on constants.
|
||||||
|
|
||||||
|
|
||||||
Version 0.20.1
|
Version 0.20.1
|
||||||
|
@ -550,13 +550,6 @@ impl ExportedFn {
|
|||||||
"property setter cannot return any value",
|
"property setter cannot return any value",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// 3c. Property setters cannot be pure.
|
|
||||||
FnSpecialAccess::Property(Property::Set(_)) if params.pure.is_some() => {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
params.pure.unwrap(),
|
|
||||||
"property setter cannot be pure",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
// 4a. Index getters must take the subject and the accessed "index" as arguments.
|
// 4a. Index getters must take the subject and the accessed "index" as arguments.
|
||||||
FnSpecialAccess::Index(Index::Get) if self.arg_count() != 2 => {
|
FnSpecialAccess::Index(Index::Get) if self.arg_count() != 2 => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
@ -587,13 +580,6 @@ impl ExportedFn {
|
|||||||
"index setter cannot return any value",
|
"index setter cannot return any value",
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
// 5b. Index setters cannot be pure.
|
|
||||||
FnSpecialAccess::Index(Index::Set) if params.pure.is_some() => {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
params.pure.unwrap(),
|
|
||||||
"index setter cannot be pure",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -711,9 +697,7 @@ impl ExportedFn {
|
|||||||
unpack_statements.push(
|
unpack_statements.push(
|
||||||
syn::parse2::<syn::Stmt>(quote! {
|
syn::parse2::<syn::Stmt>(quote! {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant(#arg_lit_str.to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant(#arg_lit_str.to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -492,9 +492,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
let arg1 = mem::take(args[1usize]).cast::<usize>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<usize>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<usize>().unwrap();
|
||||||
|
@ -1107,9 +1107,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
Ok(Dynamic::from(increment(arg0)))
|
Ok(Dynamic::from(increment(arg0)))
|
||||||
@ -1169,9 +1167,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
Ok(Dynamic::from(increment(arg0)))
|
Ok(Dynamic::from(increment(arg0)))
|
||||||
@ -1252,9 +1248,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
|
||||||
Ok(Dynamic::from(increment(arg0)))
|
Ok(Dynamic::from(increment(arg0)))
|
||||||
@ -1336,9 +1330,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
Ok(Dynamic::from(int_foo(arg0)))
|
Ok(Dynamic::from(int_foo(arg0)))
|
||||||
@ -1399,9 +1391,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
Ok(Dynamic::from(int_foo(arg0)))
|
Ok(Dynamic::from(int_foo(arg0)))
|
||||||
@ -1459,9 +1449,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
@ -1523,9 +1511,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
|
||||||
@ -1584,9 +1570,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
@ -1648,9 +1632,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
|
||||||
@ -1709,9 +1691,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||||
@ -1774,9 +1754,7 @@ mod generate_tests {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||||
if args[0usize].is_read_only() {
|
if args[0usize].is_read_only() {
|
||||||
return Err(Box::new(
|
return EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into();
|
||||||
EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE)
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
let arg1 = mem::take(args[1usize]).cast::<u64>();
|
||||||
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
|
||||||
|
10
src/ast.rs
10
src/ast.rs
@ -1523,7 +1523,7 @@ pub struct FnCallExpr {
|
|||||||
/// List of function call argument expressions.
|
/// List of function call argument expressions.
|
||||||
pub args: StaticVec<Expr>,
|
pub args: StaticVec<Expr>,
|
||||||
/// List of function call arguments that are constants.
|
/// List of function call arguments that are constants.
|
||||||
pub constant_args: smallvec::SmallVec<[(Dynamic, Position); 2]>,
|
pub literal_args: smallvec::SmallVec<[(Dynamic, Position); 2]>,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
@ -1539,12 +1539,12 @@ impl FnCallExpr {
|
|||||||
/// Are there no arguments to this function call?
|
/// Are there no arguments to this function call?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_args_empty(&self) -> bool {
|
pub fn is_args_empty(&self) -> bool {
|
||||||
self.args.is_empty() && self.constant_args.is_empty()
|
self.args.is_empty() && self.literal_args.is_empty()
|
||||||
}
|
}
|
||||||
/// Get the number of arguments to this function call.
|
/// Get the number of arguments to this function call.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn args_count(&self) -> usize {
|
pub fn args_count(&self) -> usize {
|
||||||
self.args.len() + self.constant_args.len()
|
self.args.len() + self.literal_args.len()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1793,8 +1793,8 @@ impl fmt::Debug for Expr {
|
|||||||
ff.field("name", &x.name)
|
ff.field("name", &x.name)
|
||||||
.field("hash", &x.hashes)
|
.field("hash", &x.hashes)
|
||||||
.field("args", &x.args);
|
.field("args", &x.args);
|
||||||
if !x.constant_args.is_empty() {
|
if !x.literal_args.is_empty() {
|
||||||
ff.field("constant_args", &x.constant_args);
|
ff.field("literal_args", &x.literal_args);
|
||||||
}
|
}
|
||||||
if x.capture {
|
if x.capture {
|
||||||
ff.field("capture", &x.capture);
|
ff.field("capture", &x.capture);
|
||||||
|
@ -870,9 +870,15 @@ impl Dynamic {
|
|||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
Union::Decimal(_, _, access) => *access = typ,
|
Union::Decimal(_, _, access) => *access = typ,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(_, _, access) => *access = typ,
|
Union::Array(a, _, access) => {
|
||||||
|
*access = typ;
|
||||||
|
a.iter_mut().for_each(|v| v.set_access_mode(typ));
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(_, _, access) => *access = typ,
|
Union::Map(m, _, access) => {
|
||||||
|
*access = typ;
|
||||||
|
m.values_mut().for_each(|v| v.set_access_mode(typ));
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_, _, access) => *access = typ,
|
Union::TimeStamp(_, _, access) => *access = typ,
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
@ -1131,6 +1131,7 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
|
root: (&str, Position),
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
idx_values: &mut StaticVec<ChainArgument>,
|
idx_values: &mut StaticVec<ChainArgument>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
@ -1166,8 +1167,8 @@ impl Engine {
|
|||||||
let rhs_chain = rhs_chain.unwrap();
|
let rhs_chain = rhs_chain.unwrap();
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, rhs_chain,
|
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values,
|
||||||
level, new_val,
|
rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
}
|
}
|
||||||
@ -1186,7 +1187,8 @@ impl Engine {
|
|||||||
Ok(obj_ptr) => {
|
Ok(obj_ptr) => {
|
||||||
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods, state, lib, op_info, op_pos, obj_ptr, new_val, new_pos,
|
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val,
|
||||||
|
new_pos,
|
||||||
)?;
|
)?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -1269,7 +1271,7 @@ impl Engine {
|
|||||||
)?;
|
)?;
|
||||||
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods, state, lib, op_info, op_pos, val, new_val, new_pos,
|
mods, state, lib, op_info, op_pos, val, root, new_val, new_pos,
|
||||||
)?;
|
)?;
|
||||||
Ok((Dynamic::UNIT, true))
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
@ -1298,7 +1300,7 @@ impl Engine {
|
|||||||
)?;
|
)?;
|
||||||
let obj_ptr = (&mut orig_val).into();
|
let obj_ptr = (&mut orig_val).into();
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods, state, lib, op_info, op_pos, obj_ptr, new_val, new_pos,
|
mods, state, lib, op_info, op_pos, obj_ptr, root, new_val, new_pos,
|
||||||
)?;
|
)?;
|
||||||
new_val = orig_val;
|
new_val = orig_val;
|
||||||
}
|
}
|
||||||
@ -1352,8 +1354,8 @@ impl Engine {
|
|||||||
let rhs_chain = rhs_chain.unwrap();
|
let rhs_chain = rhs_chain.unwrap();
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, &mut val, &x.rhs, idx_values, rhs_chain,
|
mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values,
|
||||||
level, new_val,
|
rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
}
|
}
|
||||||
@ -1383,6 +1385,7 @@ impl Engine {
|
|||||||
lib,
|
lib,
|
||||||
this_ptr,
|
this_ptr,
|
||||||
&mut val.into(),
|
&mut val.into(),
|
||||||
|
root,
|
||||||
&x.rhs,
|
&x.rhs,
|
||||||
idx_values,
|
idx_values,
|
||||||
rhs_chain,
|
rhs_chain,
|
||||||
@ -1425,7 +1428,7 @@ impl Engine {
|
|||||||
let target = &mut val.into();
|
let target = &mut val.into();
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, target, &x.rhs, idx_values,
|
mods, state, lib, this_ptr, target, root, &x.rhs, idx_values,
|
||||||
rhs_chain, level, new_val,
|
rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
@ -1474,21 +1477,16 @@ impl Engine {
|
|||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(_, _var_pos, x) => {
|
Expr::Variable(_, var_pos, x) => {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, *_var_pos)?;
|
self.inc_operations(state, *var_pos)?;
|
||||||
|
|
||||||
let (target, pos) =
|
let (target, _) = self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
|
||||||
|
|
||||||
// Constants cannot be modified
|
|
||||||
if target.is_read_only() && new_val.is_some() {
|
|
||||||
return EvalAltResult::ErrorAssignmentToConstant(x.2.to_string(), pos).into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let obj_ptr = &mut target.into();
|
let obj_ptr = &mut target.into();
|
||||||
|
let root = (x.2.as_str(), *var_pos);
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, &mut None, obj_ptr, rhs, idx_values, chain_type, level,
|
mods, state, lib, &mut None, obj_ptr, root, rhs, idx_values, chain_type, level,
|
||||||
new_val,
|
new_val,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
@ -1500,8 +1498,9 @@ impl Engine {
|
|||||||
expr => {
|
expr => {
|
||||||
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
let value = self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?;
|
||||||
let obj_ptr = &mut value.into();
|
let obj_ptr = &mut value.into();
|
||||||
|
let root = ("", expr.position());
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, obj_ptr, rhs, idx_values, chain_type, level,
|
mods, state, lib, this_ptr, obj_ptr, root, rhs, idx_values, chain_type, level,
|
||||||
new_val,
|
new_val,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
@ -1545,7 +1544,7 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
.collect::<Result<StaticVec<_>, _>>()?;
|
.collect::<Result<StaticVec<_>, _>>()?;
|
||||||
|
|
||||||
x.constant_args
|
x.literal_args
|
||||||
.iter()
|
.iter()
|
||||||
.inspect(|(_, pos)| arg_positions.push(*pos))
|
.inspect(|(_, pos)| arg_positions.push(*pos))
|
||||||
.for_each(|(v, _)| arg_values.push(v.clone()));
|
.for_each(|(v, _)| arg_values.push(v.clone()));
|
||||||
@ -1590,7 +1589,7 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
.collect::<Result<StaticVec<_>, _>>()?;
|
.collect::<Result<StaticVec<_>, _>>()?;
|
||||||
|
|
||||||
x.constant_args
|
x.literal_args
|
||||||
.iter()
|
.iter()
|
||||||
.inspect(|(_, pos)| arg_positions.push(*pos))
|
.inspect(|(_, pos)| arg_positions.push(*pos))
|
||||||
.for_each(|(v, _)| arg_values.push(v.clone()));
|
.for_each(|(v, _)| arg_values.push(v.clone()));
|
||||||
@ -1848,6 +1847,7 @@ impl Engine {
|
|||||||
Some(OpAssignment::new(TOKEN_OP_CONCAT)),
|
Some(OpAssignment::new(TOKEN_OP_CONCAT)),
|
||||||
pos,
|
pos,
|
||||||
(&mut result).into(),
|
(&mut result).into(),
|
||||||
|
("", Position::NONE),
|
||||||
item,
|
item,
|
||||||
expr.position(),
|
expr.position(),
|
||||||
)?;
|
)?;
|
||||||
@ -1892,7 +1892,7 @@ impl Engine {
|
|||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
constant_args: c_args,
|
literal_args: c_args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace.as_ref();
|
let namespace = namespace.as_ref();
|
||||||
@ -1910,7 +1910,7 @@ impl Engine {
|
|||||||
capture,
|
capture,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
constant_args: c_args,
|
literal_args: c_args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
@ -2064,11 +2064,13 @@ impl Engine {
|
|||||||
op_info: Option<OpAssignment>,
|
op_info: Option<OpAssignment>,
|
||||||
op_pos: Position,
|
op_pos: Position,
|
||||||
mut target: Target,
|
mut target: Target,
|
||||||
|
root: (&str, Position),
|
||||||
mut new_value: Dynamic,
|
mut new_value: Dynamic,
|
||||||
new_value_pos: Position,
|
new_value_pos: Position,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
if target.is_read_only() {
|
if target.is_read_only() {
|
||||||
unreachable!("LHS should not be read-only");
|
// Assignment to constant variable
|
||||||
|
return EvalAltResult::ErrorAssignmentToConstant(root.0.to_string(), root.1).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(OpAssignment {
|
if let Some(OpAssignment {
|
||||||
@ -2167,14 +2169,6 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(state, pos)?;
|
||||||
|
|
||||||
if lhs_ptr.is_read_only() {
|
|
||||||
// Assignment to constant variable
|
|
||||||
EvalAltResult::ErrorAssignmentToConstant(
|
|
||||||
lhs_expr.get_variable_name(false).unwrap().to_string(),
|
|
||||||
pos,
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
} else {
|
|
||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
mods,
|
mods,
|
||||||
state,
|
state,
|
||||||
@ -2182,12 +2176,12 @@ impl Engine {
|
|||||||
op_info.clone(),
|
op_info.clone(),
|
||||||
*op_pos,
|
*op_pos,
|
||||||
lhs_ptr,
|
lhs_ptr,
|
||||||
|
(lhs_expr.get_variable_name(false).unwrap(), pos),
|
||||||
rhs_val,
|
rhs_val,
|
||||||
rhs_expr.position(),
|
rhs_expr.position(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// lhs op= rhs
|
// lhs op= rhs
|
||||||
Stmt::Assignment(x, op_pos) => {
|
Stmt::Assignment(x, op_pos) => {
|
||||||
@ -2454,7 +2448,7 @@ impl Engine {
|
|||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
constant_args: c_args,
|
literal_args: c_args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let namespace = namespace.as_ref();
|
let namespace = namespace.as_ref();
|
||||||
@ -2472,7 +2466,7 @@ impl Engine {
|
|||||||
capture,
|
capture,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
constant_args: c_args,
|
literal_args: c_args,
|
||||||
..
|
..
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
|
@ -1065,7 +1065,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
constant_args: &[(Dynamic, Position)],
|
literal_args: &[(Dynamic, Position)],
|
||||||
mut hashes: FnCallHashes,
|
mut hashes: FnCallHashes,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
@ -1074,8 +1074,8 @@ impl Engine {
|
|||||||
// Handle call() - Redirect function call
|
// Handle call() - Redirect function call
|
||||||
let redirected;
|
let redirected;
|
||||||
let mut args_expr = args_expr;
|
let mut args_expr = args_expr;
|
||||||
let mut constant_args = constant_args;
|
let mut literal_args = literal_args;
|
||||||
let mut total_args = args_expr.len() + constant_args.len();
|
let mut total_args = args_expr.len() + literal_args.len();
|
||||||
let mut curry = StaticVec::new();
|
let mut curry = StaticVec::new();
|
||||||
let mut name = fn_name;
|
let mut name = fn_name;
|
||||||
|
|
||||||
@ -1083,7 +1083,7 @@ impl Engine {
|
|||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
||||||
|| Ok(constant_args[0].clone()),
|
|| Ok(literal_args[0].clone()),
|
||||||
|arg| {
|
|arg| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
.map(|v| (v, arg.position()))
|
.map(|v| (v, arg.position()))
|
||||||
@ -1108,7 +1108,7 @@ impl Engine {
|
|||||||
if !args_expr.is_empty() {
|
if !args_expr.is_empty() {
|
||||||
args_expr = &args_expr[1..];
|
args_expr = &args_expr[1..];
|
||||||
} else {
|
} else {
|
||||||
constant_args = &constant_args[1..];
|
literal_args = &literal_args[1..];
|
||||||
}
|
}
|
||||||
total_args -= 1;
|
total_args -= 1;
|
||||||
|
|
||||||
@ -1123,7 +1123,7 @@ impl Engine {
|
|||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
KEYWORD_FN_PTR if total_args == 1 => {
|
KEYWORD_FN_PTR if total_args == 1 => {
|
||||||
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
||||||
|| Ok(constant_args[0].clone()),
|
|| Ok(literal_args[0].clone()),
|
||||||
|arg| {
|
|arg| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
.map(|v| (v, arg.position()))
|
.map(|v| (v, arg.position()))
|
||||||
@ -1142,7 +1142,7 @@ impl Engine {
|
|||||||
// Handle curry()
|
// Handle curry()
|
||||||
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
||||||
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
||||||
|| Ok(constant_args[0].clone()),
|
|| Ok(literal_args[0].clone()),
|
||||||
|arg| {
|
|arg| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
.map(|v| (v, arg.position()))
|
.map(|v| (v, arg.position()))
|
||||||
@ -1164,9 +1164,9 @@ impl Engine {
|
|||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(|value| fn_curry.push(value))
|
.map(|value| fn_curry.push(value))
|
||||||
})?;
|
})?;
|
||||||
fn_curry.extend(constant_args.iter().map(|(v, _)| v.clone()));
|
fn_curry.extend(literal_args.iter().map(|(v, _)| v.clone()));
|
||||||
} else {
|
} else {
|
||||||
fn_curry.extend(constant_args.iter().skip(1).map(|(v, _)| v.clone()));
|
fn_curry.extend(literal_args.iter().skip(1).map(|(v, _)| v.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
return Ok(FnPtr::new_unchecked(name, fn_curry).into());
|
||||||
@ -1176,7 +1176,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
||||||
let arg = args_expr.get(0).map_or_else(
|
let arg = args_expr.get(0).map_or_else(
|
||||||
|| Ok(constant_args[0].0.clone()),
|
|| Ok(literal_args[0].0.clone()),
|
||||||
|arg| self.eval_expr(scope, mods, state, lib, this_ptr, arg, level),
|
|arg| self.eval_expr(scope, mods, state, lib, this_ptr, arg, level),
|
||||||
)?;
|
)?;
|
||||||
return Ok(arg.is_shared().into());
|
return Ok(arg.is_shared().into());
|
||||||
@ -1191,7 +1191,7 @@ impl Engine {
|
|||||||
args_expr[0].position(),
|
args_expr[0].position(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
constant_args[0].clone()
|
literal_args[0].clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let fn_name = arg
|
let fn_name = arg
|
||||||
@ -1204,7 +1204,7 @@ impl Engine {
|
|||||||
args_expr[1].position(),
|
args_expr[1].position(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
constant_args[if args_expr.is_empty() { 1 } else { 0 }].clone()
|
literal_args[if args_expr.is_empty() { 1 } else { 0 }].clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let num_params = arg
|
let num_params = arg
|
||||||
@ -1223,7 +1223,7 @@ impl Engine {
|
|||||||
// Handle is_def_var()
|
// Handle is_def_var()
|
||||||
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
||||||
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
let (arg, arg_pos) = args_expr.get(0).map_or_else(
|
||||||
|| Ok(constant_args[0].clone()),
|
|| Ok(literal_args[0].clone()),
|
||||||
|arg| {
|
|arg| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, arg, level)
|
||||||
.map(|v| (v, arg.position()))
|
.map(|v| (v, arg.position()))
|
||||||
@ -1240,7 +1240,7 @@ impl Engine {
|
|||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let prev_len = scope.len();
|
let prev_len = scope.len();
|
||||||
let (script, script_pos) = args_expr.get(0).map_or_else(
|
let (script, script_pos) = args_expr.get(0).map_or_else(
|
||||||
|| Ok(constant_args[0].clone()),
|
|| Ok(literal_args[0].clone()),
|
||||||
|script_expr| {
|
|script_expr| {
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, script_expr, level)
|
||||||
.map(|v| (v, script_expr.position()))
|
.map(|v| (v, script_expr.position()))
|
||||||
@ -1292,7 +1292,7 @@ impl Engine {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
if args_expr.is_empty() && constant_args.is_empty() && curry.is_empty() {
|
if args_expr.is_empty() && literal_args.is_empty() && curry.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
args = Default::default();
|
args = Default::default();
|
||||||
} else {
|
} else {
|
||||||
@ -1308,7 +1308,7 @@ impl Engine {
|
|||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
})
|
})
|
||||||
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
.chain(literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let (mut target, _pos) =
|
let (mut target, _pos) =
|
||||||
@ -1344,7 +1344,7 @@ impl Engine {
|
|||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
})
|
})
|
||||||
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
.chain(literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
args = curry.iter_mut().chain(arg_values.iter_mut()).collect();
|
||||||
@ -1368,7 +1368,7 @@ impl Engine {
|
|||||||
namespace: Option<&NamespaceRef>,
|
namespace: Option<&NamespaceRef>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
constant_args: &[(Dynamic, Position)],
|
literal_args: &[(Dynamic, Position)],
|
||||||
hash: u64,
|
hash: u64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -1378,7 +1378,7 @@ impl Engine {
|
|||||||
let mut first_arg_value = None;
|
let mut first_arg_value = None;
|
||||||
let mut args: StaticVec<_>;
|
let mut args: StaticVec<_>;
|
||||||
|
|
||||||
if args_expr.is_empty() && constant_args.is_empty() {
|
if args_expr.is_empty() && literal_args.is_empty() {
|
||||||
// No arguments
|
// No arguments
|
||||||
args = Default::default();
|
args = Default::default();
|
||||||
} else {
|
} else {
|
||||||
@ -1399,7 +1399,7 @@ impl Engine {
|
|||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
.chain(literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
@ -1432,7 +1432,7 @@ impl Engine {
|
|||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(Dynamic::flatten)
|
.map(Dynamic::flatten)
|
||||||
})
|
})
|
||||||
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
.chain(literal_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
args = arg_values.iter_mut().collect();
|
args = arg_values.iter_mut().collect();
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::dynamic::{DynamicWriteLock, Variant};
|
use crate::dynamic::{DynamicWriteLock, Variant};
|
||||||
|
use crate::engine::{FN_IDX_SET, FN_SET};
|
||||||
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
|
||||||
use crate::r#unsafe::unsafe_try_cast;
|
use crate::r#unsafe::unsafe_try_cast;
|
||||||
|
use crate::token::Position;
|
||||||
use crate::{Dynamic, EvalAltResult, NativeCallContext};
|
use crate::{Dynamic, EvalAltResult, NativeCallContext};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -97,7 +99,11 @@ macro_rules! def_register {
|
|||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<RET>() }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RET>() }
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
|
if args.len() == 2 && args[0].is_read_only() && (ctx.fn_name().starts_with(FN_SET) || ctx.fn_name().starts_with(FN_IDX_SET)) {
|
||||||
|
return EvalAltResult::ErrorAssignmentToConstant(Default::default(), Position::NONE).into();
|
||||||
|
}
|
||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||||
@ -122,6 +128,10 @@ macro_rules! def_register {
|
|||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RET>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<RET>() }
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
|
if args.len() == 2 && args[0].is_read_only() && (ctx.fn_name().starts_with(FN_SET) || ctx.fn_name().starts_with(FN_IDX_SET)) {
|
||||||
|
return EvalAltResult::ErrorAssignmentToConstant(Default::default(), Position::NONE).into();
|
||||||
|
}
|
||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||||
@ -145,7 +155,11 @@ macro_rules! def_register {
|
|||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type() -> TypeId { TypeId::of::<Result<RET, Box<EvalAltResult>>>() }
|
||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<Result<RET, Box<EvalAltResult>>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<Result<RET, Box<EvalAltResult>>>() }
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Box::new(move |_: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
|
if args.len() == 2 && args[0].is_read_only() && (ctx.fn_name().starts_with(FN_SET) || ctx.fn_name().starts_with(FN_IDX_SET)) {
|
||||||
|
return EvalAltResult::ErrorAssignmentToConstant(Default::default(), Position::NONE).into();
|
||||||
|
}
|
||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||||
@ -167,6 +181,10 @@ macro_rules! def_register {
|
|||||||
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<Result<RET, Box<EvalAltResult>>>() }
|
#[cfg(feature = "metadata")] #[inline(always)] fn return_type_name() -> &'static str { std::any::type_name::<Result<RET, Box<EvalAltResult>>>() }
|
||||||
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
#[inline(always)] fn into_callable_function(self) -> CallableFunction {
|
||||||
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
CallableFunction::$abi(Box::new(move |ctx: NativeCallContext, args: &mut FnCallArgs| {
|
||||||
|
if args.len() == 2 && args[0].is_read_only() && (ctx.fn_name().starts_with(FN_SET) || ctx.fn_name().starts_with(FN_IDX_SET)) {
|
||||||
|
return EvalAltResult::ErrorAssignmentToConstant(Default::default(), Position::NONE).into();
|
||||||
|
}
|
||||||
|
|
||||||
// The arguments are assumed to be of the correct number and types!
|
// The arguments are assumed to be of the correct number and types!
|
||||||
let mut _drain = args.iter_mut();
|
let mut _drain = args.iter_mut();
|
||||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||||
|
@ -26,7 +26,7 @@ where
|
|||||||
if r == from {
|
if r == from {
|
||||||
return EvalAltResult::ErrorInFunctionCall(
|
return EvalAltResult::ErrorInFunctionCall(
|
||||||
"range".to_string(),
|
"range".to_string(),
|
||||||
"".to_string(),
|
Default::default(),
|
||||||
Box::new(EvalAltResult::ErrorArithmetic(
|
Box::new(EvalAltResult::ErrorArithmetic(
|
||||||
"step value cannot be zero".to_string(),
|
"step value cannot be zero".to_string(),
|
||||||
crate::Position::NONE,
|
crate::Position::NONE,
|
||||||
|
@ -1468,24 +1468,10 @@ fn make_assignment_stmt<'a>(
|
|||||||
Expr::Index(x, _) | Expr::Dot(x, _) => {
|
Expr::Index(x, _) | Expr::Dot(x, _) => {
|
||||||
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
|
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
|
||||||
None => match &x.lhs {
|
None => match &x.lhs {
|
||||||
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
|
// var[???] = rhs, var.??? = rhs
|
||||||
Expr::Variable(None, _, x) if x.0.is_none() => {
|
Expr::Variable(_, _, _) => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||||
}
|
}
|
||||||
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
|
||||||
Expr::Variable(i, var_pos, x) => {
|
|
||||||
let (index, _, name) = x.as_ref();
|
|
||||||
let index = i.map_or_else(|| index.unwrap().get(), |n| n.get() as usize);
|
|
||||||
match state.stack[state.stack.len() - index].1 {
|
|
||||||
AccessMode::ReadWrite => {
|
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
|
||||||
}
|
|
||||||
// Constant values cannot be assigned to
|
|
||||||
AccessMode::ReadOnly => {
|
|
||||||
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*var_pos))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// expr[???] = rhs, expr.??? = rhs
|
// expr[???] = rhs, expr.??? = rhs
|
||||||
expr => {
|
expr => {
|
||||||
Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position()))
|
Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position()))
|
||||||
|
@ -115,7 +115,7 @@ pub fn from_dynamic<'de, T: Deserialize<'de>>(
|
|||||||
|
|
||||||
impl Error for Box<EvalAltResult> {
|
impl Error for Box<EvalAltResult> {
|
||||||
fn custom<T: fmt::Display>(err: T) -> Self {
|
fn custom<T: fmt::Display>(err: T) -> Self {
|
||||||
LexError::ImproperSymbol("".to_string(), err.to_string())
|
LexError::ImproperSymbol(Default::default(), err.to_string())
|
||||||
.into_err(Position::NONE)
|
.into_err(Position::NONE)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.consume("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
*engine.consume("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
|
||||||
EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x"
|
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "x"
|
||||||
));
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -45,17 +45,66 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
fn set_value(obj: &mut TestStruct, value: INT) {
|
||||||
|
obj.0 = value;
|
||||||
|
}
|
||||||
|
|
||||||
engine
|
engine
|
||||||
.register_type_with_name::<TestStruct>("TestStruct")
|
.register_type_with_name::<TestStruct>("TestStruct")
|
||||||
|
.register_fn("new_ts", || TestStruct(123))
|
||||||
.register_get("value", |obj: &mut TestStruct| obj.0)
|
.register_get("value", |obj: &mut TestStruct| obj.0)
|
||||||
.register_fn("update_value", |obj: &mut TestStruct, value: INT| {
|
.register_set("value", set_value)
|
||||||
obj.0 = value
|
.register_fn("update_value", set_value);
|
||||||
});
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
const MY_NUMBER = new_ts();
|
||||||
|
MY_NUMBER.update_value(42);
|
||||||
|
MY_NUMBER.value
|
||||||
|
",
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
const MY_NUMBER = new_ts();
|
||||||
|
update_value(MY_NUMBER, 42);
|
||||||
|
MY_NUMBER.value
|
||||||
|
",
|
||||||
|
)?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.consume(
|
||||||
|
"
|
||||||
|
const MY_NUMBER = new_ts();
|
||||||
|
MY_NUMBER.value = 42;
|
||||||
|
"
|
||||||
|
)
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorAssignmentToConstant(_, _)
|
||||||
|
));
|
||||||
|
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
|
||||||
scope.push_constant("MY_NUMBER", TestStruct(123));
|
scope.push_constant("MY_NUMBER", TestStruct(123));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
"
|
||||||
|
update_value(MY_NUMBER, 42);
|
||||||
|
MY_NUMBER.value
|
||||||
|
",
|
||||||
|
)?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<INT>(
|
engine.eval_with_scope::<INT>(
|
||||||
&mut scope,
|
&mut scope,
|
||||||
@ -67,5 +116,12 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
|
|||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine
|
||||||
|
.consume_with_scope(&mut scope, "MY_NUMBER.value = 42;")
|
||||||
|
.expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorAssignmentToConstant(_, _)
|
||||||
|
));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,10 @@ mod test {
|
|||||||
pub fn funky_add(x: INT, y: INT) -> INT {
|
pub fn funky_add(x: INT, y: INT) -> INT {
|
||||||
x / 2 + y * 2
|
x / 2 + y * 2
|
||||||
}
|
}
|
||||||
#[rhai_fn(pure)]
|
#[rhai_fn(name = "no_effect", set = "no_effect", pure)]
|
||||||
pub fn no_effect(_array: &mut Array, _value: INT) {
|
pub fn no_effect(array: &mut Array, value: INT) {
|
||||||
// do nothing to array
|
// array is not modified
|
||||||
|
println!("Array = {:?}, Value = {}", array, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,6 +88,7 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
|
|||||||
{
|
{
|
||||||
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; a.foo")?, 1);
|
assert_eq!(engine.eval::<INT>("let a = [1, 2, 3]; a.foo")?, 1);
|
||||||
engine.consume("const A = [1, 2, 3]; A.no_effect(42);")?;
|
engine.consume("const A = [1, 2, 3]; A.no_effect(42);")?;
|
||||||
|
engine.consume("const A = [1, 2, 3]; A.no_effect = 42;")?;
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
matches!(*engine.consume("const A = [1, 2, 3]; A.test(42);").expect_err("should error"),
|
matches!(*engine.consume("const A = [1, 2, 3]; A.test(42);").expect_err("should error"),
|
||||||
|
@ -128,7 +128,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
|||||||
s => Err(ParseError(
|
s => Err(ParseError(
|
||||||
Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol(
|
Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol(
|
||||||
s.to_string(),
|
s.to_string(),
|
||||||
"".to_string(),
|
Default::default(),
|
||||||
))),
|
))),
|
||||||
Position::NONE,
|
Position::NONE,
|
||||||
)),
|
)),
|
||||||
|
Loading…
Reference in New Issue
Block a user