From 047e65223d01f7c66e42cea78ea79f36be77c3bb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 7 Jan 2022 12:19:01 +0800 Subject: [PATCH] Fix builds. --- src/README.md | 5 + src/engine.rs | 32 +- src/eval/chaining.rs | 956 ++++++++++++++++++++++++++++++++++++++- src/eval/data_check.rs | 34 +- src/eval/expr.rs | 954 +------------------------------------- src/eval/global_state.rs | 24 +- src/eval/stmt.rs | 8 +- src/eval/target.rs | 32 +- src/module/mod.rs | 5 + src/packages/logic.rs | 6 +- src/tokenizer.rs | 7 + 11 files changed, 1052 insertions(+), 1011 deletions(-) diff --git a/src/README.md b/src/README.md index 1a6deda1..5feefa30 100644 --- a/src/README.md +++ b/src/README.md @@ -1,6 +1,8 @@ Source Structure ================ +Root Sources +------------ | Source file | Description | | -------------- | ------------------------------------------------------------------------------- | @@ -13,6 +15,9 @@ Source Structure | `tests.rs` | Unit tests (not integration tests, which are in the `rhai/tests` sub-directory) | +Sub-Directories +--------------- + | Sub-directory | Description | | ------------- | ----------------------------------------------------- | | `types` | Common data types (e.g. `Dynamic`, errors) | diff --git a/src/engine.rs b/src/engine.rs index 9f9b865e..698068d4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -4,8 +4,11 @@ use crate::api::custom_syntax::CustomSyntax; use crate::func::native::{OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback}; use crate::packages::{Package, StandardPackage}; use crate::tokenizer::Token; -use crate::types::dynamic::map_std_type_name; -use crate::{Identifier, ImmutableString, Module, Position, RhaiError, Shared, StaticVec, ERR}; +use crate::types::dynamic::{map_std_type_name, Union}; +use crate::{ + Dynamic, Identifier, ImmutableString, Module, Position, RhaiError, RhaiResult, Shared, + StaticVec, ERR, +}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -287,6 +290,31 @@ impl Engine { self.empty_string.clone() } + /// Check a result to ensure that it is valid. + pub(crate) fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult { + let _pos = pos; + + match result { + Ok(ref mut r) => { + // Concentrate all empty strings into one instance to save memory + if let Dynamic(Union::Str(s, _, _)) = r { + if s.is_empty() { + if !s.ptr_eq(&self.empty_string) { + *s = self.const_empty_string(); + } + return result; + } + } + + #[cfg(not(feature = "unchecked"))] + self.check_data_size(&r, _pos)?; + } + _ => (), + } + + result + } + /// Pretty-print a type name. /// /// If a type is registered via [`register_type_with_name`][Engine::register_type_with_name], diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index db40c3d7..85aae79c 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -1,8 +1,10 @@ //! Types to support chaining operations (i.e. indexing and dotting). #![cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::ast::Expr; -use crate::{Dynamic, Position}; +use super::{EvalState, GlobalRuntimeState, Target}; +use crate::ast::{Expr, OpAssignment}; +use crate::types::dynamic::Union; +use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR}; use std::hash::Hash; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -111,3 +113,953 @@ impl ChainArgument { Self::IndexValue(value, pos) } } + +impl Engine { + /// Chain-evaluate a dot/index chain. + /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards. + fn eval_dot_index_chain_helper( + &self, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + target: &mut Target, + root: (&str, Position), + rhs: &Expr, + terminate_chaining: bool, + idx_values: &mut StaticVec, + chain_type: ChainType, + level: usize, + new_val: Option<((Dynamic, Position), (Option, Position))>, + ) -> RhaiResultOf<(Dynamic, bool)> { + let is_ref_mut = target.is_ref(); + let _terminate_chaining = terminate_chaining; + + // Pop the last index value + let idx_val = idx_values.pop().unwrap(); + + match chain_type { + #[cfg(not(feature = "no_index"))] + ChainType::Indexing => { + let pos = rhs.position(); + let root_pos = idx_val.position(); + let idx_val = idx_val.into_index_value().expect("`ChainType::Index`"); + + match rhs { + // xxx[idx].expr... | xxx[idx][expr]... + Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos) + if !_terminate_chaining => + { + let mut idx_val_for_setter = idx_val.clone(); + let idx_pos = x.lhs.position(); + let rhs_chain = rhs.into(); + + let (try_setter, result) = { + let mut obj = self.get_indexed_mut( + global, state, lib, target, idx_val, idx_pos, false, true, level, + )?; + let is_obj_temp_val = obj.is_temp_value(); + let obj_ptr = &mut obj; + + match self.eval_dot_index_chain_helper( + global, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, + idx_values, rhs_chain, level, new_val, + ) { + Ok((result, true)) if is_obj_temp_val => { + (Some(obj.take_or_clone()), (result, true)) + } + Ok(result) => (None, result), + Err(err) => return Err(err.fill_position(*x_pos)), + } + }; + + if let Some(mut new_val) = try_setter { + // Try to call index setter if value is changed + let hash_set = + crate::ast::FnCallHashes::from_native(global.hash_idx_set()); + let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; + let fn_name = crate::engine::FN_IDX_SET; + + if let Err(err) = self.exec_fn_call( + global, state, lib, fn_name, hash_set, args, is_ref_mut, true, + root_pos, None, level, + ) { + // Just ignore if there is no index setter + if !matches!(*err, ERR::ErrorFunctionNotFound(_, _)) { + return Err(err); + } + } + } + + Ok(result) + } + // xxx[rhs] op= new_val + _ if new_val.is_some() => { + let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); + let mut idx_val_for_setter = idx_val.clone(); + + let try_setter = match self.get_indexed_mut( + global, state, lib, target, idx_val, pos, true, false, level, + ) { + // Indexed value is a reference - update directly + Ok(ref mut obj_ptr) => { + self.eval_op_assignment( + global, state, lib, op_info, op_pos, obj_ptr, root, new_val, + ) + .map_err(|err| err.fill_position(new_pos))?; + #[cfg(not(feature = "unchecked"))] + self.check_data_size(obj_ptr, new_pos)?; + None + } + // Can't index - try to call an index setter + #[cfg(not(feature = "no_index"))] + Err(err) if matches!(*err, ERR::ErrorIndexingType(_, _)) => { + Some(new_val) + } + // Any other error + Err(err) => return Err(err), + }; + + if let Some(mut new_val) = try_setter { + // Try to call index setter + let hash_set = + crate::ast::FnCallHashes::from_native(global.hash_idx_set()); + let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; + let fn_name = crate::engine::FN_IDX_SET; + + self.exec_fn_call( + global, state, lib, fn_name, hash_set, args, is_ref_mut, true, + root_pos, None, level, + )?; + } + + Ok((Dynamic::UNIT, true)) + } + // xxx[rhs] + _ => self + .get_indexed_mut( + global, state, lib, target, idx_val, pos, false, true, level, + ) + .map(|v| (v.take_or_clone(), false)), + } + } + + #[cfg(not(feature = "no_object"))] + ChainType::Dotting => { + match rhs { + // xxx.fn_name(arg_expr_list) + Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => { + let crate::ast::FnCallExpr { name, hashes, .. } = x.as_ref(); + let call_args = &mut idx_val.into_fn_call_args(); + self.make_method_call( + global, state, lib, name, *hashes, target, call_args, *pos, level, + ) + } + // xxx.fn_name(...) = ??? + Expr::FnCall(_, _) if new_val.is_some() => { + unreachable!("method call cannot be assigned to") + } + // xxx.module::fn_name(...) - syntax error + Expr::FnCall(_, _) => { + unreachable!("function call in dot chain should not be namespace-qualified") + } + // {xxx:map}.id op= ??? + Expr::Property(x) if target.is::() && new_val.is_some() => { + let (name, pos) = &x.2; + let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); + let index = name.into(); + { + let val_target = &mut self.get_indexed_mut( + global, state, lib, target, index, *pos, true, false, level, + )?; + self.eval_op_assignment( + global, state, lib, op_info, op_pos, val_target, root, new_val, + ) + .map_err(|err| err.fill_position(new_pos))?; + } + #[cfg(not(feature = "unchecked"))] + self.check_data_size(target.source(), new_pos)?; + Ok((Dynamic::UNIT, true)) + } + // {xxx:map}.id + Expr::Property(x) if target.is::() => { + let (name, pos) = &x.2; + let index = name.into(); + let val = self.get_indexed_mut( + global, state, lib, target, index, *pos, false, false, level, + )?; + Ok((val.take_or_clone(), false)) + } + // xxx.id op= ??? + Expr::Property(x) if new_val.is_some() => { + let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref(); + let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); + + if op_info.is_some() { + let hash = crate::ast::FnCallHashes::from_native(*hash_get); + let args = &mut [target.as_mut()]; + let (mut orig_val, _) = self + .exec_fn_call( + global, state, lib, getter, hash, args, is_ref_mut, true, *pos, + None, level, + ) + .or_else(|err| match *err { + // Try an indexer if property does not exist + ERR::ErrorDotExpr(_, _) => { + let prop = name.into(); + self.get_indexed_mut( + global, state, lib, target, prop, *pos, false, true, + level, + ) + .map(|v| (v.take_or_clone(), false)) + .map_err( + |idx_err| match *idx_err { + ERR::ErrorIndexingType(_, _) => err, + _ => idx_err, + }, + ) + } + _ => Err(err), + })?; + + self.eval_op_assignment( + global, + state, + lib, + op_info, + op_pos, + &mut (&mut orig_val).into(), + root, + new_val, + ) + .map_err(|err| err.fill_position(new_pos))?; + + new_val = orig_val; + } + + let hash = crate::ast::FnCallHashes::from_native(*hash_set); + let args = &mut [target.as_mut(), &mut new_val]; + self.exec_fn_call( + global, state, lib, setter, hash, args, is_ref_mut, true, *pos, None, + level, + ) + .or_else(|err| match *err { + // Try an indexer if property does not exist + ERR::ErrorDotExpr(_, _) => { + let args = &mut [target, &mut name.into(), &mut new_val]; + let fn_name = crate::engine::FN_IDX_SET; + let hash_set = + crate::ast::FnCallHashes::from_native(global.hash_idx_set()); + let pos = Position::NONE; + + self.exec_fn_call( + global, state, lib, fn_name, hash_set, args, is_ref_mut, true, + pos, None, level, + ) + .map_err( + |idx_err| match *idx_err { + ERR::ErrorIndexingType(_, _) => err, + _ => idx_err, + }, + ) + } + _ => Err(err), + }) + } + // xxx.id + Expr::Property(x) => { + let ((getter, hash_get), _, (name, pos)) = x.as_ref(); + let hash = crate::ast::FnCallHashes::from_native(*hash_get); + let args = &mut [target.as_mut()]; + self.exec_fn_call( + global, state, lib, getter, hash, args, is_ref_mut, true, *pos, None, + level, + ) + .map_or_else( + |err| match *err { + // Try an indexer if property does not exist + ERR::ErrorDotExpr(_, _) => { + let prop = name.into(); + self.get_indexed_mut( + global, state, lib, target, prop, *pos, false, true, level, + ) + .map(|v| (v.take_or_clone(), false)) + .map_err(|idx_err| { + match *idx_err { + ERR::ErrorIndexingType(_, _) => err, + _ => idx_err, + } + }) + } + _ => Err(err), + }, + // Assume getters are always pure + |(v, _)| Ok((v, false)), + ) + } + // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr + Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) + if target.is::() => + { + let val_target = &mut match x.lhs { + Expr::Property(ref p) => { + let (name, pos) = &p.2; + let index = name.into(); + self.get_indexed_mut( + global, state, lib, target, index, *pos, false, true, level, + )? + } + // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr + Expr::FnCall(ref x, pos) if !x.is_qualified() => { + let crate::ast::FnCallExpr { name, hashes, .. } = x.as_ref(); + let call_args = &mut idx_val.into_fn_call_args(); + let (val, _) = self.make_method_call( + global, state, lib, name, *hashes, target, call_args, pos, + level, + )?; + val.into() + } + // {xxx:map}.module::fn_name(...) - syntax error + Expr::FnCall(_, _) => unreachable!( + "function call in dot chain should not be namespace-qualified" + ), + // Others - syntax error + ref expr => unreachable!("invalid dot expression: {:?}", expr), + }; + let rhs_chain = rhs.into(); + + self.eval_dot_index_chain_helper( + global, state, lib, this_ptr, val_target, root, &x.rhs, *term, + idx_values, rhs_chain, level, new_val, + ) + .map_err(|err| err.fill_position(*x_pos)) + } + // xxx.sub_lhs[expr] | xxx.sub_lhs.expr + Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) => { + match x.lhs { + // xxx.prop[expr] | xxx.prop.expr + Expr::Property(ref p) => { + let ((getter, hash_get), (setter, hash_set), (name, pos)) = + p.as_ref(); + let rhs_chain = rhs.into(); + let hash_get = crate::ast::FnCallHashes::from_native(*hash_get); + let hash_set = crate::ast::FnCallHashes::from_native(*hash_set); + let mut arg_values = [target.as_mut(), &mut Dynamic::UNIT.clone()]; + let args = &mut arg_values[..1]; + + // Assume getters are always pure + let (mut val, _) = self + .exec_fn_call( + global, state, lib, getter, hash_get, args, is_ref_mut, + true, *pos, None, level, + ) + .or_else(|err| match *err { + // Try an indexer if property does not exist + ERR::ErrorDotExpr(_, _) => { + let prop = name.into(); + self.get_indexed_mut( + global, state, lib, target, prop, *pos, false, + true, level, + ) + .map(|v| (v.take_or_clone(), false)) + .map_err( + |idx_err| match *idx_err { + ERR::ErrorIndexingType(_, _) => err, + _ => idx_err, + }, + ) + } + _ => Err(err), + })?; + + let val = &mut val; + + let (result, may_be_changed) = self + .eval_dot_index_chain_helper( + global, + state, + lib, + this_ptr, + &mut val.into(), + root, + &x.rhs, + *term, + idx_values, + rhs_chain, + level, + new_val, + ) + .map_err(|err| err.fill_position(*x_pos))?; + + // Feed the value back via a setter just in case it has been updated + if may_be_changed { + // Re-use args because the first &mut parameter will not be consumed + let mut arg_values = [target.as_mut(), val]; + let args = &mut arg_values; + self.exec_fn_call( + global, state, lib, setter, hash_set, args, is_ref_mut, + true, *pos, None, level, + ) + .or_else( + |err| match *err { + // Try an indexer if property does not exist + ERR::ErrorDotExpr(_, _) => { + let args = + &mut [target.as_mut(), &mut name.into(), val]; + let fn_name = crate::engine::FN_IDX_SET; + let hash_set = + crate::ast::FnCallHashes::from_native( + global.hash_idx_set(), + ); + self.exec_fn_call( + global, state, lib, fn_name, hash_set, args, + is_ref_mut, true, *pos, None, level, + ) + .or_else(|idx_err| match *idx_err { + ERR::ErrorIndexingType(_, _) => { + // If there is no setter, no need to feed it back because + // the property is read-only + Ok((Dynamic::UNIT, false)) + } + _ => Err(idx_err), + }) + } + _ => Err(err), + }, + )?; + } + + Ok((result, may_be_changed)) + } + // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr + Expr::FnCall(ref f, pos) if !f.is_qualified() => { + let crate::ast::FnCallExpr { name, hashes, .. } = f.as_ref(); + let rhs_chain = rhs.into(); + let args = &mut idx_val.into_fn_call_args(); + let (mut val, _) = self.make_method_call( + global, state, lib, name, *hashes, target, args, pos, level, + )?; + let val = &mut val; + let target = &mut val.into(); + + self.eval_dot_index_chain_helper( + global, state, lib, this_ptr, target, root, &x.rhs, *term, + idx_values, rhs_chain, level, new_val, + ) + .map_err(|err| err.fill_position(pos)) + } + // xxx.module::fn_name(...) - syntax error + Expr::FnCall(_, _) => unreachable!( + "function call in dot chain should not be namespace-qualified" + ), + // Others - syntax error + ref expr => unreachable!("invalid dot expression: {:?}", expr), + } + } + // Syntax error + _ => Err(ERR::ErrorDotExpr("".into(), rhs.position()).into()), + } + } + } + } + + /// Evaluate a dot/index chain. + pub(crate) fn eval_dot_index_chain( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &Expr, + level: usize, + new_val: Option<((Dynamic, Position), (Option, Position))>, + ) -> RhaiResult { + let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, term, op_pos) = match expr { + #[cfg(not(feature = "no_index"))] + Expr::Index(x, term, pos) => (x.as_ref(), ChainType::Indexing, *term, *pos), + #[cfg(not(feature = "no_object"))] + Expr::Dot(x, term, pos) => (x.as_ref(), ChainType::Dotting, *term, *pos), + expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr), + }; + + let idx_values = &mut StaticVec::new_const(); + + self.eval_dot_index_chain_arguments( + scope, global, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level, + )?; + + let is_assignment = new_val.is_some(); + + match lhs { + // id.??? or id[???] + Expr::Variable(_, var_pos, x) => { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, *var_pos)?; + + let (mut target, _) = + self.search_namespace(scope, global, state, lib, this_ptr, lhs)?; + + let obj_ptr = &mut target; + let root = (x.2.as_str(), *var_pos); + + self.eval_dot_index_chain_helper( + global, state, lib, &mut None, obj_ptr, root, rhs, term, idx_values, + chain_type, level, new_val, + ) + .map(|(v, _)| v) + .map_err(|err| err.fill_position(op_pos)) + } + // {expr}.??? = ??? or {expr}[???] = ??? + _ if is_assignment => unreachable!("cannot assign to an expression"), + // {expr}.??? or {expr}[???] + expr => { + let value = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?; + let obj_ptr = &mut value.into(); + let root = ("", expr.position()); + self.eval_dot_index_chain_helper( + global, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type, + level, new_val, + ) + .map(|(v, _)| if is_assignment { Dynamic::UNIT } else { v }) + .map_err(|err| err.fill_position(op_pos)) + } + } + } + + /// Evaluate a chain of indexes and store the results in a [`StaticVec`]. + /// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of + /// just a few levels of indexing. + fn eval_dot_index_chain_arguments( + &self, + scope: &mut Scope, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + this_ptr: &mut Option<&mut Dynamic>, + expr: &Expr, + terminate_chaining: bool, + parent_chain_type: ChainType, + idx_values: &mut StaticVec, + size: usize, + level: usize, + ) -> RhaiResultOf<()> { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, expr.position())?; + + let _parent_chain_type = parent_chain_type; + + match expr { + #[cfg(not(feature = "no_object"))] + Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => { + let crate::ast::FnCallExpr { + args, constants, .. + } = x.as_ref(); + + let (values, pos) = args.iter().try_fold( + (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), + |(mut values, mut pos), expr| -> RhaiResultOf<_> { + let (value, arg_pos) = self.get_arg_value( + scope, global, state, lib, this_ptr, level, expr, constants, + )?; + if values.is_empty() { + pos = arg_pos; + } + values.push(value.flatten()); + Ok((values, pos)) + }, + )?; + + idx_values.push(super::ChainArgument::from_fn_call_args(values, pos)); + } + #[cfg(not(feature = "no_object"))] + Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { + unreachable!("function call in dot chain should not be namespace-qualified") + } + + #[cfg(not(feature = "no_object"))] + Expr::Property(x) if _parent_chain_type == ChainType::Dotting => { + idx_values.push(super::ChainArgument::Property((x.2).1)) + } + Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), + + Expr::Index(x, term, _) | Expr::Dot(x, term, _) if !terminate_chaining => { + let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref(); + + // Evaluate in left-to-right order + let lhs_arg_val = match lhs { + #[cfg(not(feature = "no_object"))] + Expr::Property(x) if _parent_chain_type == ChainType::Dotting => { + super::ChainArgument::Property((x.2).1) + } + Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), + + #[cfg(not(feature = "no_object"))] + Expr::FnCall(x, _) + if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => + { + let crate::ast::FnCallExpr { + args, constants, .. + } = x.as_ref(); + + let (values, pos) = args.iter().try_fold( + (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), + |(mut values, mut pos), expr| -> RhaiResultOf<_> { + let (value, arg_pos) = self.get_arg_value( + scope, global, state, lib, this_ptr, level, expr, constants, + )?; + if values.is_empty() { + pos = arg_pos + } + values.push(value.flatten()); + Ok((values, pos)) + }, + )?; + super::ChainArgument::from_fn_call_args(values, pos) + } + #[cfg(not(feature = "no_object"))] + Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { + unreachable!("function call in dot chain should not be namespace-qualified") + } + #[cfg(not(feature = "no_object"))] + expr if _parent_chain_type == ChainType::Dotting => { + unreachable!("invalid dot expression: {:?}", expr); + } + #[cfg(not(feature = "no_index"))] + _ if _parent_chain_type == ChainType::Indexing => self + .eval_expr(scope, global, state, lib, this_ptr, lhs, level) + .map(|v| { + super::ChainArgument::from_index_value(v.flatten(), lhs.position()) + })?, + expr => unreachable!("unknown chained expression: {:?}", expr), + }; + + // Push in reverse order + let chain_type = expr.into(); + + self.eval_dot_index_chain_arguments( + scope, global, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size, + level, + )?; + + idx_values.push(lhs_arg_val); + } + + #[cfg(not(feature = "no_object"))] + _ if _parent_chain_type == ChainType::Dotting => { + unreachable!("invalid dot expression: {:?}", expr); + } + #[cfg(not(feature = "no_index"))] + _ if _parent_chain_type == ChainType::Indexing => idx_values.push( + self.eval_expr(scope, global, state, lib, this_ptr, expr, level) + .map(|v| { + super::ChainArgument::from_index_value(v.flatten(), expr.position()) + })?, + ), + _ => unreachable!("unknown chained expression: {:?}", expr), + } + + Ok(()) + } + + /// Get the value at the indexed position of a base type. + /// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards. + fn get_indexed_mut<'t>( + &self, + global: &mut GlobalRuntimeState, + state: &mut EvalState, + lib: &[&Module], + target: &'t mut Dynamic, + idx: Dynamic, + idx_pos: Position, + add_if_not_found: bool, + use_indexers: bool, + level: usize, + ) -> RhaiResultOf> { + #[cfg(not(feature = "unchecked"))] + self.inc_operations(&mut global.num_operations, Position::NONE)?; + + let mut idx = idx; + let _add_if_not_found = add_if_not_found; + + match target { + #[cfg(not(feature = "no_index"))] + Dynamic(Union::Array(arr, _, _)) => { + // val_array[idx] + let index = idx + .as_int() + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; + + let arr_len = arr.len(); + + #[cfg(not(feature = "unchecked"))] + let arr_idx = if index < 0 { + // Count from end if negative + arr_len + - index + .checked_abs() + .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos)) + .and_then(|n| { + if n as usize > arr_len { + Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) + } else { + Ok(n as usize) + } + })? + } else { + index as usize + }; + #[cfg(feature = "unchecked")] + let arr_idx = if index < 0 { + // Count from end if negative + arr_len - index.abs() as usize + } else { + index as usize + }; + + arr.get_mut(arr_idx) + .map(Target::from) + .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) + } + + #[cfg(not(feature = "no_index"))] + Dynamic(Union::Blob(arr, _, _)) => { + // val_blob[idx] + let index = idx + .as_int() + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; + + let arr_len = arr.len(); + + #[cfg(not(feature = "unchecked"))] + let arr_idx = if index < 0 { + // Count from end if negative + arr_len + - index + .checked_abs() + .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos)) + .and_then(|n| { + if n as usize > arr_len { + Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) + } else { + Ok(n as usize) + } + })? + } else { + index as usize + }; + #[cfg(feature = "unchecked")] + let arr_idx = if index < 0 { + // Count from end if negative + arr_len - index.abs() as usize + } else { + index as usize + }; + + let value = arr + .get(arr_idx) + .map(|&v| (v as crate::INT).into()) + .ok_or_else(|| Box::new(ERR::ErrorArrayBounds(arr_len, index, idx_pos)))?; + Ok(Target::BlobByte { + source: target, + value, + index: arr_idx, + }) + } + + #[cfg(not(feature = "no_object"))] + Dynamic(Union::Map(map, _, _)) => { + // val_map[idx] + let index = idx.read_lock::().ok_or_else(|| { + self.make_type_mismatch_err::(idx.type_name(), idx_pos) + })?; + + if _add_if_not_found && !map.contains_key(index.as_str()) { + map.insert(index.clone().into(), Dynamic::UNIT); + } + + Ok(map + .get_mut(index.as_str()) + .map(Target::from) + .unwrap_or_else(|| Target::from(Dynamic::UNIT))) + } + + #[cfg(not(feature = "no_index"))] + Dynamic(Union::Int(value, _, _)) + if idx.is::() || idx.is::() => + { + #[cfg(not(feature = "only_i32"))] + type BASE = u64; + #[cfg(feature = "only_i32")] + type BASE = u32; + + // val_int[range] + const BITS: usize = std::mem::size_of::() * 8; + + let (shift, mask) = if let Some(range) = idx.read_lock::() { + let start = range.start; + let end = range.end; + + if start < 0 || start as usize >= BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into()); + } else if end < 0 || end as usize >= BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into()); + } else if end <= start { + (0, 0) + } else if end as usize == BITS && start == 0 { + // -1 = all bits set + (0, -1) + } else { + ( + start as u8, + // 2^bits - 1 + (((2 as BASE).pow((end - start) as u32) - 1) as crate::INT) << start, + ) + } + } else if let Some(range) = idx.read_lock::() { + let start = *range.start(); + let end = *range.end(); + + if start < 0 || start as usize >= BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into()); + } else if end < 0 || end as usize >= BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into()); + } else if end < start { + (0, 0) + } else if end as usize == BITS - 1 && start == 0 { + // -1 = all bits set + (0, -1) + } else { + ( + start as u8, + // 2^bits - 1 + (((2 as BASE).pow((end - start + 1) as u32) - 1) as crate::INT) + << start, + ) + } + } else { + unreachable!("Range or RangeInclusive expected but gets {:?}", idx); + }; + + let field_value = (*value & mask) >> shift; + + Ok(Target::BitField { + source: target, + value: field_value.into(), + mask, + shift, + }) + } + + #[cfg(not(feature = "no_index"))] + Dynamic(Union::Int(value, _, _)) => { + // val_int[idx] + let index = idx + .as_int() + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; + + const BITS: usize = std::mem::size_of::() * 8; + + let (bit_value, offset) = if index >= 0 { + let offset = index as usize; + ( + if offset >= BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); + } else { + (*value & (1 << offset)) != 0 + }, + offset as u8, + ) + } else if let Some(abs_index) = index.checked_abs() { + let offset = abs_index as usize; + ( + // Count from end if negative + if offset > BITS { + return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); + } else { + (*value & (1 << (BITS - offset))) != 0 + }, + offset as u8, + ) + } else { + return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); + }; + + Ok(Target::Bit { + source: target, + value: bit_value.into(), + bit: offset, + }) + } + + #[cfg(not(feature = "no_index"))] + Dynamic(Union::Str(s, _, _)) => { + // val_string[idx] + let index = idx + .as_int() + .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; + + let (ch, offset) = if index >= 0 { + let offset = index as usize; + ( + s.chars().nth(offset).ok_or_else(|| { + let chars_len = s.chars().count(); + ERR::ErrorStringBounds(chars_len, index, idx_pos) + })?, + offset, + ) + } else if let Some(abs_index) = index.checked_abs() { + let offset = abs_index as usize; + ( + // Count from end if negative + s.chars().rev().nth(offset - 1).ok_or_else(|| { + let chars_len = s.chars().count(); + ERR::ErrorStringBounds(chars_len, index, idx_pos) + })?, + offset, + ) + } else { + let chars_len = s.chars().count(); + return Err(ERR::ErrorStringBounds(chars_len, index, idx_pos).into()); + }; + + Ok(Target::StringChar { + source: target, + value: ch.into(), + index: offset, + }) + } + + _ if use_indexers => { + let args = &mut [target, &mut idx]; + let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get()); + let idx_pos = Position::NONE; + + self.exec_fn_call( + global, + state, + lib, + crate::engine::FN_IDX_GET, + hash_get, + args, + true, + true, + idx_pos, + None, + level, + ) + .map(|(v, _)| v.into()) + } + + _ => Err(ERR::ErrorIndexingType( + format!( + "{} [{}]", + self.map_type_name(target.type_name()), + self.map_type_name(idx.type_name()) + ), + Position::NONE, + ) + .into()), + } + } +} diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index 3458ab34..4d5e1f78 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -1,37 +1,13 @@ -//! Data checks during evaluation. +//! Data size checks during evaluation. +#![cfg(not(feature = "unchecked"))] use crate::types::dynamic::Union; -use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, ERR}; +use crate::{Dynamic, Engine, Position, RhaiResultOf, ERR}; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; impl Engine { - /// Check a result to ensure that the data size is within allowable limit. - pub(crate) fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult { - let _pos = pos; - - match result { - Ok(ref mut r) => { - // Concentrate all empty strings into one instance to save memory - if let Dynamic(Union::Str(s, _, _)) = r { - if s.is_empty() { - if !s.ptr_eq(&self.empty_string) { - *s = self.const_empty_string(); - } - return result; - } - } - - #[cfg(not(feature = "unchecked"))] - self.check_data_size(&r, _pos)?; - } - _ => (), - } - - result - } - /// Recursively calculate the sizes of a value. /// /// Sizes returned are `(`[`Array`][crate::Array], [`Map`][crate::Map] and `String)`. @@ -41,6 +17,8 @@ impl Engine { /// Panics if any interior data is shared (should never happen). #[cfg(not(feature = "unchecked"))] pub(crate) fn calc_data_sizes(value: &Dynamic, top: bool) -> (usize, usize, usize) { + let _top = top; + match value.0 { #[cfg(not(feature = "no_index"))] Union::Array(ref arr, _, _) => { @@ -83,7 +61,7 @@ impl Engine { } Union::Str(ref s, _, _) => (0, 0, s.len()), #[cfg(not(feature = "no_closure"))] - Union::Shared(_, _, _) if top => { + Union::Shared(_, _, _) if _top => { Self::calc_data_sizes(&*value.read_lock::().unwrap(), true) } #[cfg(not(feature = "no_closure"))] diff --git a/src/eval/expr.rs b/src/eval/expr.rs index f05dc5ad..5a12be07 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -1,14 +1,12 @@ //! Module defining functions for evaluating an expression. -use super::{ChainArgument, ChainType, EvalContext, EvalState, GlobalRuntimeState, Target}; -use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment}; -use crate::engine::{FN_IDX_GET, FN_IDX_SET, KEYWORD_GLOBAL, KEYWORD_THIS, OP_CONCAT}; +use super::{EvalContext, EvalState, GlobalRuntimeState, Target}; +use crate::ast::{Expr, FnCallExpr, OpAssignment}; +use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::module::Namespace; -use crate::tokenizer::Token; -use crate::types::dynamic::{AccessMode, Union}; +use crate::types::dynamic::AccessMode; use crate::{ - Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, - StaticVec, ERR, INT, + Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, ERR, }; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] @@ -80,7 +78,7 @@ impl Engine { format!( "{}{}{}", namespace, - Token::DoubleColon.literal_syntax(), + crate::tokenizer::Token::DoubleColon.literal_syntax(), var_name ), namespace[0].pos, @@ -92,7 +90,7 @@ impl Engine { } #[cfg(not(feature = "no_function"))] - if namespace.len() == 1 && namespace[0].name == KEYWORD_GLOBAL { + if namespace.len() == 1 && namespace[0].name == crate::engine::KEYWORD_GLOBAL { // global::VARIABLE let global_constants = global.constants_mut(); @@ -109,7 +107,7 @@ impl Engine { format!( "{}{}{}", namespace, - Token::DoubleColon.literal_syntax(), + crate::tokenizer::Token::DoubleColon.literal_syntax(), var_name ), namespace[0].pos, @@ -199,940 +197,6 @@ impl Engine { Ok((val.into(), var_pos)) } - /// Chain-evaluate a dot/index chain. - /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn eval_dot_index_chain_helper( - &self, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - target: &mut Target, - root: (&str, Position), - rhs: &Expr, - terminate_chaining: bool, - idx_values: &mut StaticVec, - chain_type: ChainType, - level: usize, - new_val: Option<((Dynamic, Position), (Option, Position))>, - ) -> RhaiResultOf<(Dynamic, bool)> { - let is_ref_mut = target.is_ref(); - let _terminate_chaining = terminate_chaining; - - // Pop the last index value - let idx_val = idx_values.pop().unwrap(); - - match chain_type { - #[cfg(not(feature = "no_index"))] - ChainType::Indexing => { - let pos = rhs.position(); - let root_pos = idx_val.position(); - let idx_val = idx_val.into_index_value().expect("`ChainType::Index`"); - - match rhs { - // xxx[idx].expr... | xxx[idx][expr]... - Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos) - if !_terminate_chaining => - { - let mut idx_val_for_setter = idx_val.clone(); - let idx_pos = x.lhs.position(); - let rhs_chain = rhs.into(); - - let (try_setter, result) = { - let mut obj = self.get_indexed_mut( - global, state, lib, target, idx_val, idx_pos, false, true, level, - )?; - let is_obj_temp_val = obj.is_temp_value(); - let obj_ptr = &mut obj; - - match self.eval_dot_index_chain_helper( - global, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, - idx_values, rhs_chain, level, new_val, - ) { - Ok((result, true)) if is_obj_temp_val => { - (Some(obj.take_or_clone()), (result, true)) - } - Ok(result) => (None, result), - Err(err) => return Err(err.fill_position(*x_pos)), - } - }; - - if let Some(mut new_val) = try_setter { - // Try to call index setter if value is changed - let hash_set = - crate::ast::FnCallHashes::from_native(global.hash_idx_set()); - let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; - - if let Err(err) = self.exec_fn_call( - global, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, - root_pos, None, level, - ) { - // Just ignore if there is no index setter - if !matches!(*err, ERR::ErrorFunctionNotFound(_, _)) { - return Err(err); - } - } - } - - Ok(result) - } - // xxx[rhs] op= new_val - _ if new_val.is_some() => { - let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); - let mut idx_val_for_setter = idx_val.clone(); - - let try_setter = match self.get_indexed_mut( - global, state, lib, target, idx_val, pos, true, false, level, - ) { - // Indexed value is a reference - update directly - Ok(ref mut obj_ptr) => { - self.eval_op_assignment( - global, state, lib, op_info, op_pos, obj_ptr, root, new_val, - ) - .map_err(|err| err.fill_position(new_pos))?; - #[cfg(not(feature = "unchecked"))] - self.check_data_size(obj_ptr, new_pos)?; - None - } - // Can't index - try to call an index setter - #[cfg(not(feature = "no_index"))] - Err(err) if matches!(*err, ERR::ErrorIndexingType(_, _)) => { - Some(new_val) - } - // Any other error - Err(err) => return Err(err), - }; - - if let Some(mut new_val) = try_setter { - // Try to call index setter - let hash_set = - crate::ast::FnCallHashes::from_native(global.hash_idx_set()); - let args = &mut [target, &mut idx_val_for_setter, &mut new_val]; - - self.exec_fn_call( - global, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, true, - root_pos, None, level, - )?; - } - - Ok((Dynamic::UNIT, true)) - } - // xxx[rhs] - _ => self - .get_indexed_mut( - global, state, lib, target, idx_val, pos, false, true, level, - ) - .map(|v| (v.take_or_clone(), false)), - } - } - - #[cfg(not(feature = "no_object"))] - ChainType::Dotting => { - match rhs { - // xxx.fn_name(arg_expr_list) - Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => { - let FnCallExpr { name, hashes, .. } = x.as_ref(); - let call_args = &mut idx_val.into_fn_call_args(); - self.make_method_call( - global, state, lib, name, *hashes, target, call_args, *pos, level, - ) - } - // xxx.fn_name(...) = ??? - Expr::FnCall(_, _) if new_val.is_some() => { - unreachable!("method call cannot be assigned to") - } - // xxx.module::fn_name(...) - syntax error - Expr::FnCall(_, _) => { - unreachable!("function call in dot chain should not be namespace-qualified") - } - // {xxx:map}.id op= ??? - Expr::Property(x) if target.is::() && new_val.is_some() => { - let (name, pos) = &x.2; - let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); - let index = name.into(); - { - let val_target = &mut self.get_indexed_mut( - global, state, lib, target, index, *pos, true, false, level, - )?; - self.eval_op_assignment( - global, state, lib, op_info, op_pos, val_target, root, new_val, - ) - .map_err(|err| err.fill_position(new_pos))?; - } - #[cfg(not(feature = "unchecked"))] - self.check_data_size(target.source(), new_pos)?; - Ok((Dynamic::UNIT, true)) - } - // {xxx:map}.id - Expr::Property(x) if target.is::() => { - let (name, pos) = &x.2; - let index = name.into(); - let val = self.get_indexed_mut( - global, state, lib, target, index, *pos, false, false, level, - )?; - Ok((val.take_or_clone(), false)) - } - // xxx.id op= ??? - Expr::Property(x) if new_val.is_some() => { - let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref(); - let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`"); - - if op_info.is_some() { - let hash = crate::ast::FnCallHashes::from_native(*hash_get); - let args = &mut [target.as_mut()]; - let (mut orig_val, _) = self - .exec_fn_call( - global, state, lib, getter, hash, args, is_ref_mut, true, *pos, - None, level, - ) - .or_else(|err| match *err { - // Try an indexer if property does not exist - ERR::ErrorDotExpr(_, _) => { - let prop = name.into(); - self.get_indexed_mut( - global, state, lib, target, prop, *pos, false, true, - level, - ) - .map(|v| (v.take_or_clone(), false)) - .map_err( - |idx_err| match *idx_err { - ERR::ErrorIndexingType(_, _) => err, - _ => idx_err, - }, - ) - } - _ => Err(err), - })?; - - self.eval_op_assignment( - global, - state, - lib, - op_info, - op_pos, - &mut (&mut orig_val).into(), - root, - new_val, - ) - .map_err(|err| err.fill_position(new_pos))?; - - new_val = orig_val; - } - - let hash = crate::ast::FnCallHashes::from_native(*hash_set); - let args = &mut [target.as_mut(), &mut new_val]; - self.exec_fn_call( - global, state, lib, setter, hash, args, is_ref_mut, true, *pos, None, - level, - ) - .or_else(|err| match *err { - // Try an indexer if property does not exist - ERR::ErrorDotExpr(_, _) => { - let args = &mut [target, &mut name.into(), &mut new_val]; - let hash_set = - crate::ast::FnCallHashes::from_native(global.hash_idx_set()); - let pos = Position::NONE; - - self.exec_fn_call( - global, state, lib, FN_IDX_SET, hash_set, args, is_ref_mut, - true, pos, None, level, - ) - .map_err( - |idx_err| match *idx_err { - ERR::ErrorIndexingType(_, _) => err, - _ => idx_err, - }, - ) - } - _ => Err(err), - }) - } - // xxx.id - Expr::Property(x) => { - let ((getter, hash_get), _, (name, pos)) = x.as_ref(); - let hash = crate::ast::FnCallHashes::from_native(*hash_get); - let args = &mut [target.as_mut()]; - self.exec_fn_call( - global, state, lib, getter, hash, args, is_ref_mut, true, *pos, None, - level, - ) - .map_or_else( - |err| match *err { - // Try an indexer if property does not exist - ERR::ErrorDotExpr(_, _) => { - let prop = name.into(); - self.get_indexed_mut( - global, state, lib, target, prop, *pos, false, true, level, - ) - .map(|v| (v.take_or_clone(), false)) - .map_err(|idx_err| { - match *idx_err { - ERR::ErrorIndexingType(_, _) => err, - _ => idx_err, - } - }) - } - _ => Err(err), - }, - // Assume getters are always pure - |(v, _)| Ok((v, false)), - ) - } - // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr - Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) - if target.is::() => - { - let val_target = &mut match x.lhs { - Expr::Property(ref p) => { - let (name, pos) = &p.2; - let index = name.into(); - self.get_indexed_mut( - global, state, lib, target, index, *pos, false, true, level, - )? - } - // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr - Expr::FnCall(ref x, pos) if !x.is_qualified() => { - let FnCallExpr { name, hashes, .. } = x.as_ref(); - let call_args = &mut idx_val.into_fn_call_args(); - let (val, _) = self.make_method_call( - global, state, lib, name, *hashes, target, call_args, pos, - level, - )?; - val.into() - } - // {xxx:map}.module::fn_name(...) - syntax error - Expr::FnCall(_, _) => unreachable!( - "function call in dot chain should not be namespace-qualified" - ), - // Others - syntax error - ref expr => unreachable!("invalid dot expression: {:?}", expr), - }; - let rhs_chain = rhs.into(); - - self.eval_dot_index_chain_helper( - global, state, lib, this_ptr, val_target, root, &x.rhs, *term, - idx_values, rhs_chain, level, new_val, - ) - .map_err(|err| err.fill_position(*x_pos)) - } - // xxx.sub_lhs[expr] | xxx.sub_lhs.expr - Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) => { - match x.lhs { - // xxx.prop[expr] | xxx.prop.expr - Expr::Property(ref p) => { - let ((getter, hash_get), (setter, hash_set), (name, pos)) = - p.as_ref(); - let rhs_chain = rhs.into(); - let hash_get = crate::ast::FnCallHashes::from_native(*hash_get); - let hash_set = crate::ast::FnCallHashes::from_native(*hash_set); - let mut arg_values = [target.as_mut(), &mut Dynamic::UNIT.clone()]; - let args = &mut arg_values[..1]; - - // Assume getters are always pure - let (mut val, _) = self - .exec_fn_call( - global, state, lib, getter, hash_get, args, is_ref_mut, - true, *pos, None, level, - ) - .or_else(|err| match *err { - // Try an indexer if property does not exist - ERR::ErrorDotExpr(_, _) => { - let prop = name.into(); - self.get_indexed_mut( - global, state, lib, target, prop, *pos, false, - true, level, - ) - .map(|v| (v.take_or_clone(), false)) - .map_err( - |idx_err| match *idx_err { - ERR::ErrorIndexingType(_, _) => err, - _ => idx_err, - }, - ) - } - _ => Err(err), - })?; - - let val = &mut val; - - let (result, may_be_changed) = self - .eval_dot_index_chain_helper( - global, - state, - lib, - this_ptr, - &mut val.into(), - root, - &x.rhs, - *term, - idx_values, - rhs_chain, - level, - new_val, - ) - .map_err(|err| err.fill_position(*x_pos))?; - - // Feed the value back via a setter just in case it has been updated - if may_be_changed { - // Re-use args because the first &mut parameter will not be consumed - let mut arg_values = [target.as_mut(), val]; - let args = &mut arg_values; - self.exec_fn_call( - global, state, lib, setter, hash_set, args, is_ref_mut, - true, *pos, None, level, - ) - .or_else( - |err| match *err { - // Try an indexer if property does not exist - ERR::ErrorDotExpr(_, _) => { - let args = - &mut [target.as_mut(), &mut name.into(), val]; - let hash_set = - crate::ast::FnCallHashes::from_native( - global.hash_idx_set(), - ); - self.exec_fn_call( - global, state, lib, FN_IDX_SET, hash_set, args, - is_ref_mut, true, *pos, None, level, - ) - .or_else(|idx_err| match *idx_err { - ERR::ErrorIndexingType(_, _) => { - // If there is no setter, no need to feed it back because - // the property is read-only - Ok((Dynamic::UNIT, false)) - } - _ => Err(idx_err), - }) - } - _ => Err(err), - }, - )?; - } - - Ok((result, may_be_changed)) - } - // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr - Expr::FnCall(ref f, pos) if !f.is_qualified() => { - let FnCallExpr { name, hashes, .. } = f.as_ref(); - let rhs_chain = rhs.into(); - let args = &mut idx_val.into_fn_call_args(); - let (mut val, _) = self.make_method_call( - global, state, lib, name, *hashes, target, args, pos, level, - )?; - let val = &mut val; - let target = &mut val.into(); - - self.eval_dot_index_chain_helper( - global, state, lib, this_ptr, target, root, &x.rhs, *term, - idx_values, rhs_chain, level, new_val, - ) - .map_err(|err| err.fill_position(pos)) - } - // xxx.module::fn_name(...) - syntax error - Expr::FnCall(_, _) => unreachable!( - "function call in dot chain should not be namespace-qualified" - ), - // Others - syntax error - ref expr => unreachable!("invalid dot expression: {:?}", expr), - } - } - // Syntax error - _ => Err(ERR::ErrorDotExpr("".into(), rhs.position()).into()), - } - } - } - } - - /// Evaluate a dot/index chain. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - pub(crate) fn eval_dot_index_chain( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - expr: &Expr, - level: usize, - new_val: Option<((Dynamic, Position), (Option, Position))>, - ) -> RhaiResult { - let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, term, op_pos) = match expr { - #[cfg(not(feature = "no_index"))] - Expr::Index(x, term, pos) => (x.as_ref(), ChainType::Indexing, *term, *pos), - #[cfg(not(feature = "no_object"))] - Expr::Dot(x, term, pos) => (x.as_ref(), ChainType::Dotting, *term, *pos), - expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr), - }; - - let idx_values = &mut StaticVec::new_const(); - - self.eval_dot_index_chain_arguments( - scope, global, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level, - )?; - - let is_assignment = new_val.is_some(); - - match lhs { - // id.??? or id[???] - Expr::Variable(_, var_pos, x) => { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, *var_pos)?; - - let (mut target, _) = - self.search_namespace(scope, global, state, lib, this_ptr, lhs)?; - - let obj_ptr = &mut target; - let root = (x.2.as_str(), *var_pos); - - self.eval_dot_index_chain_helper( - global, state, lib, &mut None, obj_ptr, root, rhs, term, idx_values, - chain_type, level, new_val, - ) - .map(|(v, _)| v) - .map_err(|err| err.fill_position(op_pos)) - } - // {expr}.??? = ??? or {expr}[???] = ??? - _ if is_assignment => unreachable!("cannot assign to an expression"), - // {expr}.??? or {expr}[???] - expr => { - let value = self.eval_expr(scope, global, state, lib, this_ptr, expr, level)?; - let obj_ptr = &mut value.into(); - let root = ("", expr.position()); - self.eval_dot_index_chain_helper( - global, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type, - level, new_val, - ) - .map(|(v, _)| if is_assignment { Dynamic::UNIT } else { v }) - .map_err(|err| err.fill_position(op_pos)) - } - } - } - - /// Evaluate a chain of indexes and store the results in a [`StaticVec`]. - /// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of - /// just a few levels of indexing. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn eval_dot_index_chain_arguments( - &self, - scope: &mut Scope, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - this_ptr: &mut Option<&mut Dynamic>, - expr: &Expr, - terminate_chaining: bool, - parent_chain_type: ChainType, - idx_values: &mut StaticVec, - size: usize, - level: usize, - ) -> RhaiResultOf<()> { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, expr.position())?; - - let _parent_chain_type = parent_chain_type; - - match expr { - #[cfg(not(feature = "no_object"))] - Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => { - let crate::ast::FnCallExpr { - args, constants, .. - } = x.as_ref(); - - let (values, pos) = args.iter().try_fold( - (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), - |(mut values, mut pos), expr| -> RhaiResultOf<_> { - let (value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, level, expr, constants, - )?; - if values.is_empty() { - pos = arg_pos; - } - values.push(value.flatten()); - Ok((values, pos)) - }, - )?; - - idx_values.push(ChainArgument::from_fn_call_args(values, pos)); - } - #[cfg(not(feature = "no_object"))] - Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { - unreachable!("function call in dot chain should not be namespace-qualified") - } - - #[cfg(not(feature = "no_object"))] - Expr::Property(x) if _parent_chain_type == ChainType::Dotting => { - idx_values.push(ChainArgument::Property((x.2).1)) - } - Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), - - Expr::Index(x, term, _) | Expr::Dot(x, term, _) if !terminate_chaining => { - let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref(); - - // Evaluate in left-to-right order - let lhs_arg_val = match lhs { - #[cfg(not(feature = "no_object"))] - Expr::Property(x) if _parent_chain_type == ChainType::Dotting => { - ChainArgument::Property((x.2).1) - } - Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), - - #[cfg(not(feature = "no_object"))] - Expr::FnCall(x, _) - if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => - { - let crate::ast::FnCallExpr { - args, constants, .. - } = x.as_ref(); - - let (values, pos) = args.iter().try_fold( - (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), - |(mut values, mut pos), expr| -> RhaiResultOf<_> { - let (value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, level, expr, constants, - )?; - if values.is_empty() { - pos = arg_pos - } - values.push(value.flatten()); - Ok((values, pos)) - }, - )?; - ChainArgument::from_fn_call_args(values, pos) - } - #[cfg(not(feature = "no_object"))] - Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { - unreachable!("function call in dot chain should not be namespace-qualified") - } - #[cfg(not(feature = "no_object"))] - expr if _parent_chain_type == ChainType::Dotting => { - unreachable!("invalid dot expression: {:?}", expr); - } - #[cfg(not(feature = "no_index"))] - _ if _parent_chain_type == ChainType::Indexing => self - .eval_expr(scope, global, state, lib, this_ptr, lhs, level) - .map(|v| ChainArgument::from_index_value(v.flatten(), lhs.position()))?, - expr => unreachable!("unknown chained expression: {:?}", expr), - }; - - // Push in reverse order - let chain_type = expr.into(); - - self.eval_dot_index_chain_arguments( - scope, global, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size, - level, - )?; - - idx_values.push(lhs_arg_val); - } - - #[cfg(not(feature = "no_object"))] - _ if _parent_chain_type == ChainType::Dotting => { - unreachable!("invalid dot expression: {:?}", expr); - } - #[cfg(not(feature = "no_index"))] - _ if _parent_chain_type == ChainType::Indexing => idx_values.push( - self.eval_expr(scope, global, state, lib, this_ptr, expr, level) - .map(|v| ChainArgument::from_index_value(v.flatten(), expr.position()))?, - ), - _ => unreachable!("unknown chained expression: {:?}", expr), - } - - Ok(()) - } - - /// Get the value at the indexed position of a base type. - /// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn get_indexed_mut<'t>( - &self, - global: &mut GlobalRuntimeState, - state: &mut EvalState, - lib: &[&Module], - target: &'t mut Dynamic, - idx: Dynamic, - idx_pos: Position, - add_if_not_found: bool, - use_indexers: bool, - level: usize, - ) -> RhaiResultOf> { - #[cfg(not(feature = "unchecked"))] - self.inc_operations(&mut global.num_operations, Position::NONE)?; - - let mut idx = idx; - let _add_if_not_found = add_if_not_found; - - match target { - #[cfg(not(feature = "no_index"))] - Dynamic(Union::Array(arr, _, _)) => { - // val_array[idx] - let index = idx - .as_int() - .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; - - let arr_len = arr.len(); - - #[cfg(not(feature = "unchecked"))] - let arr_idx = if index < 0 { - // Count from end if negative - arr_len - - index - .checked_abs() - .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos)) - .and_then(|n| { - if n as usize > arr_len { - Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) - } else { - Ok(n as usize) - } - })? - } else { - index as usize - }; - #[cfg(feature = "unchecked")] - let arr_idx = if index < 0 { - // Count from end if negative - arr_len - index.abs() as usize - } else { - index as usize - }; - - arr.get_mut(arr_idx) - .map(Target::from) - .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) - } - - #[cfg(not(feature = "no_index"))] - Dynamic(Union::Blob(arr, _, _)) => { - // val_blob[idx] - let index = idx - .as_int() - .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; - - let arr_len = arr.len(); - - #[cfg(not(feature = "unchecked"))] - let arr_idx = if index < 0 { - // Count from end if negative - arr_len - - index - .checked_abs() - .ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos)) - .and_then(|n| { - if n as usize > arr_len { - Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into()) - } else { - Ok(n as usize) - } - })? - } else { - index as usize - }; - #[cfg(feature = "unchecked")] - let arr_idx = if index < 0 { - // Count from end if negative - arr_len - index.abs() as usize - } else { - index as usize - }; - - let value = arr - .get(arr_idx) - .map(|&v| (v as INT).into()) - .ok_or_else(|| Box::new(ERR::ErrorArrayBounds(arr_len, index, idx_pos)))?; - Ok(Target::BlobByte { - source: target, - value, - index: arr_idx, - }) - } - - #[cfg(not(feature = "no_object"))] - Dynamic(Union::Map(map, _, _)) => { - // val_map[idx] - let index = idx.read_lock::().ok_or_else(|| { - self.make_type_mismatch_err::(idx.type_name(), idx_pos) - })?; - - if _add_if_not_found && !map.contains_key(index.as_str()) { - map.insert(index.clone().into(), Dynamic::UNIT); - } - - Ok(map - .get_mut(index.as_str()) - .map(Target::from) - .unwrap_or_else(|| Target::from(Dynamic::UNIT))) - } - - #[cfg(not(feature = "no_index"))] - Dynamic(Union::Int(value, _, _)) - if idx.is::() || idx.is::() => - { - #[cfg(not(feature = "only_i32"))] - type BASE = u64; - #[cfg(feature = "only_i32")] - type BASE = u32; - - // val_int[range] - const BITS: usize = std::mem::size_of::() * 8; - - let (shift, mask) = if let Some(range) = idx.read_lock::() { - let start = range.start; - let end = range.end; - - if start < 0 || start as usize >= BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into()); - } else if end < 0 || end as usize >= BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into()); - } else if end <= start { - (0, 0) - } else if end as usize == BITS && start == 0 { - // -1 = all bits set - (0, -1) - } else { - ( - start as u8, - // 2^bits - 1 - (((2 as BASE).pow((end - start) as u32) - 1) as INT) << start, - ) - } - } else if let Some(range) = idx.read_lock::() { - let start = *range.start(); - let end = *range.end(); - - if start < 0 || start as usize >= BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into()); - } else if end < 0 || end as usize >= BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into()); - } else if end < start { - (0, 0) - } else if end as usize == BITS - 1 && start == 0 { - // -1 = all bits set - (0, -1) - } else { - ( - start as u8, - // 2^bits - 1 - (((2 as BASE).pow((end - start + 1) as u32) - 1) as INT) << start, - ) - } - } else { - unreachable!("Range or RangeInclusive expected but gets {:?}", idx); - }; - - let field_value = (*value & mask) >> shift; - - Ok(Target::BitField { - source: target, - value: field_value.into(), - mask, - shift, - }) - } - - #[cfg(not(feature = "no_index"))] - Dynamic(Union::Int(value, _, _)) => { - // val_int[idx] - let index = idx - .as_int() - .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; - - const BITS: usize = std::mem::size_of::() * 8; - - let (bit_value, offset) = if index >= 0 { - let offset = index as usize; - ( - if offset >= BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); - } else { - (*value & (1 << offset)) != 0 - }, - offset as u8, - ) - } else if let Some(abs_index) = index.checked_abs() { - let offset = abs_index as usize; - ( - // Count from end if negative - if offset > BITS { - return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); - } else { - (*value & (1 << (BITS - offset))) != 0 - }, - offset as u8, - ) - } else { - return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()); - }; - - Ok(Target::Bit { - source: target, - value: bit_value.into(), - bit: offset, - }) - } - - #[cfg(not(feature = "no_index"))] - Dynamic(Union::Str(s, _, _)) => { - // val_string[idx] - let index = idx - .as_int() - .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; - - let (ch, offset) = if index >= 0 { - let offset = index as usize; - ( - s.chars().nth(offset).ok_or_else(|| { - let chars_len = s.chars().count(); - ERR::ErrorStringBounds(chars_len, index, idx_pos) - })?, - offset, - ) - } else if let Some(abs_index) = index.checked_abs() { - let offset = abs_index as usize; - ( - // Count from end if negative - s.chars().rev().nth(offset - 1).ok_or_else(|| { - let chars_len = s.chars().count(); - ERR::ErrorStringBounds(chars_len, index, idx_pos) - })?, - offset, - ) - } else { - let chars_len = s.chars().count(); - return Err(ERR::ErrorStringBounds(chars_len, index, idx_pos).into()); - }; - - Ok(Target::StringChar { - source: target, - value: ch.into(), - index: offset, - }) - } - - _ if use_indexers => { - let args = &mut [target, &mut idx]; - let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get()); - let idx_pos = Position::NONE; - - self.exec_fn_call( - global, state, lib, FN_IDX_GET, hash_get, args, true, true, idx_pos, None, - level, - ) - .map(|(v, _)| v.into()) - } - - _ => Err(ERR::ErrorIndexingType( - format!( - "{} [{}]", - self.map_type_name(target.type_name()), - self.map_type_name(idx.type_name()) - ), - Position::NONE, - ) - .into()), - } - } - /// Evaluate a function call expression. pub(crate) fn eval_fn_call_expr( &self, @@ -1293,7 +357,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] let mut sizes = (0, 0, 0); - for (Ident { name, .. }, value_expr) in x.0.iter() { + for (crate::ast::Ident { name, .. }, value_expr) in x.0.iter() { let key = name.as_str(); let value = self .eval_expr(scope, global, state, lib, this_ptr, value_expr, level)? diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index fcb86feb..603aa202 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -1,16 +1,13 @@ //! Global runtime state. -use crate::engine::{FN_IDX_GET, FN_IDX_SET}; use crate::func::{CallableFunction, IteratorFn}; -use crate::{Dynamic, Identifier, Module, Shared, StaticVec}; +use crate::{Identifier, Module, Shared, StaticVec}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ any::TypeId, - collections::BTreeMap, fmt, iter::{FromIterator, Rev, Zip}, - ops::DerefMut, }; /// _(internals)_ A stack of imported [modules][Module] plus mutable global runtime states. @@ -46,7 +43,8 @@ pub struct GlobalRuntimeState { /// Cache of globally-defined constants. #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] - constants: Option>>>, + constants: + Option>>>, } impl Default for GlobalRuntimeState { @@ -183,7 +181,9 @@ impl GlobalRuntimeState { #[must_use] pub(crate) fn constants_mut<'a>( &'a mut self, - ) -> Option> + 'a> { + ) -> Option< + impl std::ops::DerefMut> + 'a, + > { if let Some(ref global_constants) = self.constants { Some(crate::func::native::shared_write_lock(global_constants)) } else { @@ -193,9 +193,9 @@ impl GlobalRuntimeState { /// Set a constant into the cache of globally-defined constants. #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] - pub(crate) fn set_constant(&mut self, name: impl Into, value: Dynamic) { + pub(crate) fn set_constant(&mut self, name: impl Into, value: crate::Dynamic) { if self.constants.is_none() { - let dict: crate::Locked<_> = BTreeMap::new().into(); + let dict: crate::Locked<_> = std::collections::BTreeMap::new().into(); self.constants = Some(dict.into()); } @@ -209,8 +209,8 @@ impl GlobalRuntimeState { if self.fn_hash_indexing != (0, 0) { self.fn_hash_indexing.0 } else { - let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); - let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); + let n1 = crate::calc_fn_hash(crate::engine::FN_IDX_GET, 2); + let n2 = crate::calc_fn_hash(crate::engine::FN_IDX_SET, 3); self.fn_hash_indexing = (n1, n2); n1 } @@ -222,8 +222,8 @@ impl GlobalRuntimeState { if self.fn_hash_indexing != (0, 0) { self.fn_hash_indexing.1 } else { - let n1 = crate::calc_fn_hash(FN_IDX_GET, 2); - let n2 = crate::calc_fn_hash(FN_IDX_SET, 3); + let n1 = crate::calc_fn_hash(crate::engine::FN_IDX_GET, 2); + let n2 = crate::calc_fn_hash(crate::engine::FN_IDX_SET, 3); self.fn_hash_indexing = (n1, n2); n2 } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 6c33ee18..0502c996 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -5,9 +5,7 @@ use crate::ast::{Expr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*}; use crate::func::get_hasher; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::types::dynamic::{AccessMode, Union}; -use crate::{ - Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT, -}; +use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -735,7 +733,7 @@ impl Engine { if let Some(path) = self .eval_expr(scope, global, state, lib, this_ptr, &expr, level)? - .try_cast::() + .try_cast::() { use crate::ModuleResolver; @@ -776,7 +774,7 @@ impl Engine { Ok(Dynamic::UNIT) } else { - Err(self.make_type_mismatch_err::("", expr.position())) + Err(self.make_type_mismatch_err::("", expr.position())) } } diff --git a/src/eval/target.rs b/src/eval/target.rs index fff16027..4c644fdd 100644 --- a/src/eval/target.rs +++ b/src/eval/target.rs @@ -1,13 +1,10 @@ //! Type to hold a mutable reference to the target of an evaluation. use crate::types::dynamic::Variant; -use crate::{Dynamic, ImmutableString, Position, RhaiResultOf, ERR, INT}; +use crate::{Dynamic, RhaiResultOf}; +use std::ops::{Deref, DerefMut}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{ - any::TypeId, - ops::{Deref, DerefMut}, -}; /// A type that encapsulates a mutation target for an expression with side effects. #[derive(Debug)] @@ -44,7 +41,7 @@ pub enum Target<'a> { /// Copy of the integer value of the bits, as a [`Dynamic`]. value: Dynamic, /// Bitmask to apply to the source value (i.e. shifted) - mask: INT, + mask: crate::INT, /// Number of bits to right-shift the source value. shift: u8, }, @@ -128,6 +125,9 @@ impl<'a> Target<'a> { #[inline] #[must_use] pub fn is(&self) -> bool { + #[allow(unused_imports)] + use std::any::TypeId; + match self { Self::RefMut(r) => r.is::(), #[cfg(not(feature = "no_closure"))] @@ -136,7 +136,7 @@ impl<'a> Target<'a> { #[cfg(not(feature = "no_index"))] Self::Bit { .. } => TypeId::of::() == TypeId::of::(), #[cfg(not(feature = "no_index"))] - Self::BitField { .. } => TypeId::of::() == TypeId::of::(), + Self::BitField { .. } => TypeId::of::() == TypeId::of::(), #[cfg(not(feature = "no_index"))] Self::BlobByte { .. } => TypeId::of::() == TypeId::of::(), #[cfg(not(feature = "no_index"))] @@ -214,10 +214,10 @@ impl<'a> Target<'a> { Self::Bit { source, value, bit } => { // Replace the bit at the specified index position let new_bit = value.as_bool().map_err(|err| { - Box::new(ERR::ErrorMismatchDataType( + Box::new(crate::ERR::ErrorMismatchDataType( "bool".to_string(), err.to_string(), - Position::NONE, + crate::Position::NONE, )) })?; @@ -244,10 +244,10 @@ impl<'a> Target<'a> { // Replace the bit at the specified index position let new_value = value.as_int().map_err(|err| { - Box::new(ERR::ErrorMismatchDataType( + Box::new(crate::ERR::ErrorMismatchDataType( "integer".to_string(), err.to_string(), - Position::NONE, + crate::Position::NONE, )) })?; @@ -265,10 +265,10 @@ impl<'a> Target<'a> { } => { // Replace the byte at the specified index position let new_byte = value.as_int().map_err(|err| { - Box::new(ERR::ErrorMismatchDataType( + Box::new(crate::ERR::ErrorMismatchDataType( "INT".to_string(), err.to_string(), - Position::NONE, + crate::Position::NONE, )) })?; @@ -290,15 +290,15 @@ impl<'a> Target<'a> { } => { // Replace the character at the specified index position let new_ch = value.as_char().map_err(|err| { - Box::new(ERR::ErrorMismatchDataType( + Box::new(crate::ERR::ErrorMismatchDataType( "char".to_string(), err.to_string(), - Position::NONE, + crate::Position::NONE, )) })?; let s = &mut *source - .write_lock::() + .write_lock::() .expect("`ImmutableString`"); let index = *index; diff --git a/src/module/mod.rs b/src/module/mod.rs index 9542a1a3..b70bf3da 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1585,16 +1585,21 @@ impl Module { }); // Extra modules left in the scope become sub-modules + #[cfg(not(feature = "no_function"))] let mut func_global = None; global.into_iter().skip(orig_mods_len).for_each(|kv| { + #[cfg(not(feature = "no_function"))] if func_global.is_none() { func_global = Some(StaticVec::new()); } + #[cfg(not(feature = "no_function"))] func_global.as_mut().expect("`Some`").push(kv.clone()); + module.set_sub_module(kv.0, kv.1); }); + #[cfg(not(feature = "no_function"))] let func_global = func_global.map(|v| v.into_boxed_slice()); // Non-private functions defined become module functions diff --git a/src/packages/logic.rs b/src/packages/logic.rs index 9d51db0e..0e435c1e 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -1,7 +1,7 @@ #![allow(non_snake_case)] +use crate::def_package; use crate::plugin::*; -use crate::{def_package, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -86,6 +86,8 @@ gen_cmp_functions!(float => f64); #[cfg(not(feature = "no_float"))] #[export_module] mod f32_functions { + use crate::INT; + #[rhai_fn(name = "==")] pub fn eq_if(x: INT, y: f32) -> bool { (x as f32) == (y as f32) @@ -139,6 +141,8 @@ mod f32_functions { #[cfg(not(feature = "no_float"))] #[export_module] mod f64_functions { + use crate::INT; + #[rhai_fn(name = "==")] pub fn eq_if(x: INT, y: f64) -> bool { (x as f64) == (y as f64) diff --git a/src/tokenizer.rs b/src/tokenizer.rs index d4827dfe..df7508da 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1862,6 +1862,10 @@ fn get_next_token_inner( eat_next(stream, pos); return Some((Token::Reserved(":=".into()), start_pos)); } + (':', ';') => { + eat_next(stream, pos); + return Some((Token::Reserved(":;".into()), start_pos)); + } (':', _) => return Some((Token::Colon, start_pos)), ('<', '=') => { @@ -2203,6 +2207,9 @@ impl<'a> Iterator for TokenIterator<'a> { (":=", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), "':=' is not a valid assignment operator. This is not Go or Pascal! Should it be simply '='?".to_string(), )), + (":;", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), + "':;' is not a valid symbol. Should it be '::'?".to_string(), + )), ("::<", false) => Token::LexError(LERR::ImproperSymbol(s.to_string(), "'::<>' is not a valid symbol. This is not Rust! Should it be '::'?".to_string(), )),