From 694ac5b5bd36ca246375ad50b4598acc3274bafc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 4 Jul 2021 16:40:15 +0800 Subject: [PATCH] General code fixups. --- codegen/src/module.rs | 6 ++- src/ast.rs | 9 ---- src/custom_syntax.rs | 19 +++++---- src/dynamic.rs | 3 +- src/engine.rs | 98 ++++++++++++++++++++++++++++--------------- src/engine_api.rs | 8 ++-- src/fn_call.rs | 34 +++++++-------- src/fn_native.rs | 8 ++-- src/lib.rs | 3 +- src/module/mod.rs | 6 +++ src/optimize.rs | 18 ++++---- src/parse.rs | 75 +++++++++++++-------------------- tests/native.rs | 1 + tests/switch.rs | 1 + 14 files changed, 155 insertions(+), 134 deletions(-) diff --git a/codegen/src/module.rs b/codegen/src/module.rs index df065797..f78dad1f 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -205,7 +205,6 @@ impl Parse for Module { } } -#[allow(dead_code)] impl Module { pub fn attrs(&self) -> &Vec { &self.mod_all.attrs @@ -300,22 +299,27 @@ impl Module { } } + #[allow(dead_code)] pub fn name(&self) -> &syn::Ident { &self.mod_all.ident } + #[allow(dead_code)] pub fn consts(&self) -> &[ExportedConst] { &self.consts } + #[allow(dead_code)] pub fn fns(&self) -> &[ExportedFn] { &self.fns } + #[allow(dead_code)] pub fn sub_modules(&self) -> &[Module] { &self.sub_modules } + #[allow(dead_code)] pub fn content(&self) -> Option<&Vec> { match self.mod_all { syn::ItemMod { diff --git a/src/ast.rs b/src/ast.rs index 66a29558..3bd1d9b9 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1399,15 +1399,6 @@ pub struct CustomExpr { pub tokens: StaticVec, } -impl CustomExpr { - /// Convert this into a [`Expr::Custom`]. - #[inline(always)] - #[must_use] - pub fn into_custom_syntax_expr(self, pos: Position) -> Expr { - Expr::Custom(self.into(), pos) - } -} - /// _(INTERNALS)_ A binary expression. /// Exported under the `internals` feature only. /// diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 3df71978..7e1aa614 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -163,21 +163,22 @@ impl Engine { /// Register a custom syntax with the [`Engine`]. /// /// * `keywords` holds a slice of strings that define the custom syntax. - /// * `scope_may_be_changed` specifies variables have been added/removed by this custom syntax. + /// * `scope_may_be_changed` specifies variables _may_ be added/removed by this custom syntax. /// * `func` is the implementation function. /// - /// # Caveat - Do not change beyond block scope + /// ## Note on `scope_may_be_changed` /// - /// If `scope_may_be_changed` is `true`, then the current [`Scope`][crate::Scope] is assumed to be - /// modified by this custom syntax. + /// If `scope_may_be_changed` is `true`, then _size_ of the current [`Scope`][crate::Scope] + /// _may_ be modified by this custom syntax. /// - /// Adding new variables and/or removing variables are allowed. Simply modifying the values of - /// variables does NOT count, so `false` should be passed in this case. + /// Adding new variables and/or removing variables count. /// - /// However, only variables declared within the current _block scope_ should be touched, - /// since they all go away at the end of the block. + /// Simply modifying the values of existing variables does NOT count, as the _size_ of the + /// current [`Scope`][crate::Scope] is unchanged, so `false` should be passed. /// - /// Variables in parent blocks should be left untouched as they persist beyond the current block. + /// Replacing one variable with another (i.e. adding a new variable and removing one variable at + /// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also + /// does NOT count, so `false` should be passed. #[must_use] pub fn register_custom_syntax + Into>( &mut self, diff --git a/src/dynamic.rs b/src/dynamic.rs index 81dde6f6..22c66a8a 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -564,7 +564,8 @@ impl Hash for Dynamic { } } - _ => unimplemented!("{} cannot be hashed", self.type_name()), + #[cfg(not(feature = "no_std"))] + Union::TimeStamp(_, _, _) => unimplemented!("{} cannot be hashed", self.type_name()), } } } diff --git a/src/engine.rs b/src/engine.rs index cfe0e843..66f825e2 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -378,7 +378,7 @@ impl<'a> Target<'a> { #[allow(dead_code)] #[inline(always)] #[must_use] - pub fn is_ref(&self) -> bool { + pub const fn is_ref(&self) -> bool { match self { Self::RefMut(_) => true, #[cfg(not(feature = "no_closure"))] @@ -393,7 +393,7 @@ impl<'a> Target<'a> { /// Is the `Target` a temp value? #[inline(always)] #[must_use] - pub fn is_temp_value(&self) -> bool { + pub const fn is_temp_value(&self) -> bool { match self { Self::RefMut(_) => false, #[cfg(not(feature = "no_closure"))] @@ -608,7 +608,12 @@ impl> From for Target<'_> { } } -/// An entry in a function resolution cache. +/// _(INTERNALS)_ An entry in a function resolution cache. +/// Exported under the `internals` feature only. +/// +/// # Volatile Data Structure +/// +/// This type is volatile and may change. #[derive(Debug, Clone)] pub struct FnResolutionCacheEntry { /// Function. @@ -617,7 +622,12 @@ pub struct FnResolutionCacheEntry { pub source: Option, } -/// A function resolution cache. +/// _(INTERNALS)_ A function resolution cache. +/// Exported under the `internals` feature only. +/// +/// # Volatile Data Structure +/// +/// This type is volatile and may change. pub type FnResolutionCache = BTreeMap>>; /// _(INTERNALS)_ A type that holds all the current states of the [`Engine`]. @@ -627,15 +637,15 @@ pub type FnResolutionCache = BTreeMap>>; /// /// This type is volatile and may change. #[derive(Debug, Clone, Default)] -pub struct State { +pub struct EvalState { /// Source of the current context. pub source: Option, - /// Normally, access to variables are parsed with a relative offset into the scope to avoid a lookup. - /// In some situation, e.g. after running an `eval` statement, subsequent offsets become mis-aligned. - /// When that happens, this flag is turned on to force a scope lookup by name. - pub always_search: bool, - /// Level of the current scope. The global (root) level is zero, a new block - /// (or function call) is one level higher, and so on. + /// Normally, access to variables are parsed with a relative offset into the [`Scope`] to avoid a lookup. + /// In some situation, e.g. after running an `eval` statement, subsequent offsets may become mis-aligned. + /// When that happens, this flag is turned on to force a [`Scope`] search by name. + pub always_search_scope: bool, + /// Level of the current scope. The global (root) level is zero, a new block (or function call) + /// is one level higher, and so on. pub scope_level: usize, /// Number of operations performed. pub operations: u64, @@ -644,15 +654,29 @@ pub struct State { /// Embedded module resolver. #[cfg(not(feature = "no_module"))] pub resolver: Option>, - /// Function resolution cache and free list. - fn_resolution_caches: StaticVec, + /// Stack of function resolution caches. + fn_resolution_caches: Vec, } -impl State { +impl EvalState { + /// Create a new [`EvalState`]. + #[inline(always)] + #[must_use] + pub const fn new() -> Self { + Self { + source: None, + always_search_scope: false, + scope_level: 0, + operations: 0, + modules: 0, + resolver: None, + fn_resolution_caches: Vec::new(), + } + } /// Is the state currently at global (root) level? #[inline(always)] #[must_use] - pub fn is_global(&self) -> bool { + pub const fn is_global(&self) -> bool { self.scope_level == 0 } /// Get a mutable reference to the current function resolution cache. @@ -759,13 +783,13 @@ pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> { pub(crate) engine: &'a Engine, pub(crate) scope: &'x mut Scope<'px>, pub(crate) mods: &'m mut Imports, - pub(crate) state: &'s mut State, + pub(crate) state: &'s mut EvalState, pub(crate) lib: &'b [&'b Module], pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>, pub(crate) level: usize, } -impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, '_> { +impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> { /// The current [`Engine`]. #[inline(always)] #[must_use] @@ -826,6 +850,12 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, '_> { pub fn this_ptr(&self) -> Option<&Dynamic> { self.this_ptr.as_ref().map(|v| &**v) } + /// Mutable reference to the current bound `this` pointer, if any. + #[inline(always)] + #[must_use] + pub fn this_ptr_mut(&mut self) -> Option<&mut &'pt mut Dynamic> { + self.this_ptr.as_mut() + } /// The current nesting level of function calls. #[inline(always)] #[must_use] @@ -1032,13 +1062,13 @@ impl Engine { pub(crate) fn search_imports( &self, mods: &Imports, - state: &mut State, + state: &mut EvalState, namespace: &NamespaceRef, ) -> Option> { let root = &namespace[0].name; // Qualified - check if the root module is directly indexed - let index = if state.always_search { + let index = if state.always_search_scope { None } else { namespace.index() @@ -1067,7 +1097,7 @@ impl Engine { &self, scope: &'s mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, @@ -1117,7 +1147,7 @@ impl Engine { &self, scope: &'s mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &'s mut Option<&mut Dynamic>, expr: &Expr, @@ -1133,7 +1163,7 @@ impl Engine { EvalAltResult::ErrorUnboundThis(*pos).into() } } - _ if state.always_search => (0, expr.position()), + _ if state.always_search_scope => (0, expr.position()), Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos), Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), _ => unreachable!("Expr::Variable expected, but gets {:?}", expr), @@ -1190,7 +1220,7 @@ impl Engine { fn eval_dot_index_chain_helper( &self, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, target: &mut Target, @@ -1622,7 +1652,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, @@ -1686,7 +1716,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, @@ -1815,7 +1845,7 @@ impl Engine { fn get_indexed_mut<'t>( &self, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], target: &'t mut Dynamic, mut idx: Dynamic, @@ -1873,7 +1903,7 @@ impl Engine { #[cfg(not(feature = "no_object"))] Dynamic(Union::Map(map, _, _)) => { // val_map[idx] - let index = &*idx.read_lock::().ok_or_else(|| { + let index = idx.read_lock::().ok_or_else(|| { self.make_type_mismatch_err::(idx.type_name(), idx_pos) })?; @@ -1987,7 +2017,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, @@ -2188,7 +2218,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, statements: &[Stmt], @@ -2200,7 +2230,7 @@ impl Engine { } let mut _extra_fn_resolution_cache = false; - let prev_always_search = state.always_search; + let prev_always_search_scope = state.always_search_scope; let prev_scope_len = scope.len(); let prev_mods_len = mods.len(); @@ -2253,7 +2283,7 @@ impl Engine { // The impact of new local variables goes away at the end of a block // because any new variables introduced will go out of scope - state.always_search = prev_always_search; + state.always_search_scope = prev_always_search_scope; } result @@ -2265,7 +2295,7 @@ impl Engine { pub(crate) fn eval_op_assignment( &self, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], op_info: Option, op_pos: Position, @@ -2340,7 +2370,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, stmt: &Stmt, @@ -3107,7 +3137,7 @@ impl Engine { #[must_use] pub(crate) fn inc_operations( &self, - state: &mut State, + state: &mut EvalState, pos: Position, ) -> Result<(), Box> { state.operations += 1; diff --git a/src/engine_api.rs b/src/engine_api.rs index 6a1b350b..ab896a2a 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1,7 +1,7 @@ //! Module that defines the extern API of [`Engine`]. use crate::dynamic::Variant; -use crate::engine::{EvalContext, Imports, State}; +use crate::engine::{EvalContext, EvalState, Imports}; use crate::fn_call::FnCallArgs; use crate::fn_native::SendSync; use crate::fn_register::RegisterNativeFunction; @@ -1749,7 +1749,7 @@ impl Engine { ast: &'a AST, level: usize, ) -> RhaiResult { - let mut state: State = Default::default(); + let mut state = EvalState::new(); state.source = ast.source_raw().cloned(); #[cfg(not(feature = "no_module"))] { @@ -1831,7 +1831,7 @@ impl Engine { ast: &AST, ) -> Result<(), Box> { let mods = &mut Default::default(); - let mut state: State = Default::default(); + let mut state = EvalState::new(); state.source = ast.source_raw().cloned(); #[cfg(not(feature = "no_module"))] { @@ -2004,7 +2004,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, args: &mut FnCallArgs, ) -> RhaiResult { - let state = &mut Default::default(); + let state = &mut EvalState::new(); let mods = &mut Default::default(); let lib = &[ast.lib()]; let statements = ast.statements(); diff --git a/src/fn_call.rs b/src/fn_call.rs index 36594362..45cc65e6 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -2,7 +2,7 @@ use crate::ast::FnCallHashes; use crate::engine::{ - FnResolutionCacheEntry, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, + EvalState, FnResolutionCacheEntry, Imports, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, MAX_DYNAMIC_PARAMETERS, }; @@ -162,7 +162,7 @@ impl Engine { fn resolve_fn<'s>( &self, mods: &Imports, - state: &'s mut State, + state: &'s mut EvalState, lib: &[&Module], fn_name: &str, hash_script: u64, @@ -266,7 +266,7 @@ impl Engine { pub(crate) fn call_native_fn( &self, mods: &Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], name: &str, hash: u64, @@ -439,7 +439,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, fn_def: &crate::ast::ScriptFnDef, @@ -451,7 +451,7 @@ impl Engine { fn make_error( name: String, fn_def: &crate::ast::ScriptFnDef, - state: &State, + state: &EvalState, err: Box, pos: Position, ) -> RhaiResult { @@ -569,7 +569,7 @@ impl Engine { pub(crate) fn has_script_fn( &self, mods: Option<&Imports>, - state: &mut State, + state: &mut EvalState, lib: &[&Module], hash_script: u64, ) -> bool { @@ -608,7 +608,7 @@ impl Engine { pub(crate) fn exec_fn_call( &self, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], fn_name: &str, hashes: FnCallHashes, @@ -644,7 +644,7 @@ impl Engine { crate::engine::KEYWORD_IS_DEF_FN if args.len() == 2 && args[0].is::() && args[1].is::() => { - let fn_name = &*args[0] + let fn_name = args[0] .read_lock::() .expect("never fails because `args[0]` is `FnPtr`"); let num_params = args[1] @@ -655,7 +655,7 @@ impl Engine { if num_params < 0 { Dynamic::FALSE } else { - let hash_script = calc_fn_hash(fn_name, num_params as usize); + let hash_script = calc_fn_hash(fn_name.as_str(), num_params as usize); self.has_script_fn(Some(mods), state, lib, hash_script) .into() }, @@ -788,7 +788,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, statements: &[Stmt], lib: &[&Module], level: usize, @@ -809,7 +809,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], script: &str, _pos: Position, @@ -842,7 +842,7 @@ impl Engine { } // Evaluate the AST - let mut new_state: State = Default::default(); + let mut new_state = EvalState::new(); new_state.source = state.source.clone(); new_state.operations = state.operations; @@ -860,7 +860,7 @@ impl Engine { pub(crate) fn make_method_call( &self, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], fn_name: &str, mut hash: FnCallHashes, @@ -1024,7 +1024,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, level: usize, @@ -1046,7 +1046,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, fn_name: &str, @@ -1202,7 +1202,7 @@ impl Engine { // IMPORTANT! If the eval defines new variables in the current scope, // all variable offsets from this point on will be mis-aligned. if scope.len() != prev_len { - state.always_search = true; + state.always_search_scope = true; } return result.map_err(|err| { @@ -1298,7 +1298,7 @@ impl Engine { &self, scope: &mut Scope, mods: &mut Imports, - state: &mut State, + state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, namespace: &NamespaceRef, diff --git a/src/fn_native.rs b/src/fn_native.rs index 4f91cec5..82670f89 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -399,7 +399,7 @@ impl CallableFunction { /// Is this an iterator function? #[inline(always)] #[must_use] - pub fn is_iter(&self) -> bool { + pub const fn is_iter(&self) -> bool { match self { Self::Iterator(_) => true, Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => false, @@ -411,7 +411,7 @@ impl CallableFunction { /// Is this a Rhai-scripted function? #[inline(always)] #[must_use] - pub fn is_script(&self) -> bool { + pub const fn is_script(&self) -> bool { match self { #[cfg(not(feature = "no_function"))] Self::Script(_) => true, @@ -422,7 +422,7 @@ impl CallableFunction { /// Is this a plugin function? #[inline(always)] #[must_use] - pub fn is_plugin_fn(&self) -> bool { + pub const fn is_plugin_fn(&self) -> bool { match self { Self::Plugin(_) => true, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, @@ -434,7 +434,7 @@ impl CallableFunction { /// Is this a native Rust function? #[inline(always)] #[must_use] - pub fn is_native(&self) -> bool { + pub const fn is_native(&self) -> bool { match self { Self::Pure(_) | Self::Method(_) => true, Self::Plugin(_) => true, diff --git a/src/lib.rs b/src/lib.rs index 6d1a9aae..6b13fac0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,10 +233,11 @@ pub use ast::{ #[cfg(feature = "internals")] #[deprecated = "this type is volatile and may change"] -pub use engine::{Imports, State as EvalState}; +pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, Imports}; #[cfg(feature = "internals")] #[cfg(not(feature = "unchecked"))] +#[deprecated = "this type is volatile and may change"] pub use engine::Limits; #[cfg(feature = "internals")] diff --git a/src/module/mod.rs b/src/module/mod.rs index 19d674e5..1ff6e4a8 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1731,6 +1731,12 @@ impl From> for NamespaceRef { } impl NamespaceRef { + /// Create a new [`NamespaceRef`]. + #[inline(always)] + #[must_use] + pub fn new(&self) -> Self { + Default::default() + } /// Get the [`Scope`][crate::Scope] index offset. #[inline(always)] #[must_use] diff --git a/src/optimize.rs b/src/optimize.rs index 44ce7478..17468495 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -43,7 +43,7 @@ impl Default for OptimizationLevel { /// Mutable state throughout an optimization pass. #[derive(Debug, Clone)] -struct State<'a> { +struct OptimizerState<'a> { /// Has the [`AST`] been changed during this pass? changed: bool, /// Collection of constants to use for eager function evaluations. @@ -58,7 +58,7 @@ struct State<'a> { optimization_level: OptimizationLevel, } -impl<'a> State<'a> { +impl<'a> OptimizerState<'a> { /// Create a new State. #[inline(always)] pub const fn new( @@ -121,7 +121,7 @@ impl<'a> State<'a> { } // Has a system function a Rust-native override? -fn has_native_fn(state: &State, hash_script: u64, arg_types: &[TypeId]) -> bool { +fn has_native_fn(state: &OptimizerState, hash_script: u64, arg_types: &[TypeId]) -> bool { let hash_params = calc_fn_params_hash(arg_types.iter().cloned()); let hash = combine_hashes(hash_script, hash_params); @@ -135,7 +135,7 @@ fn has_native_fn(state: &State, hash_script: u64, arg_types: &[TypeId]) -> bool /// Call a registered function fn call_fn_with_constant_arguments( - state: &State, + state: &OptimizerState, fn_name: &str, arg_values: &mut [Dynamic], ) -> Option { @@ -159,7 +159,7 @@ fn call_fn_with_constant_arguments( /// Optimize a block of [statements][Stmt]. fn optimize_stmt_block( mut statements: Vec, - state: &mut State, + state: &mut OptimizerState, preserve_result: bool, is_internal: bool, reduce_return: bool, @@ -368,7 +368,7 @@ fn optimize_stmt_block( } /// Optimize a [statement][Stmt]. -fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { +fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: bool) { match stmt { // var = var op expr => var op= expr Stmt::Assignment(x, _) @@ -682,7 +682,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { } /// Optimize an [expression][Expr]. -fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { +fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { // These keywords are handled specially const DONT_EVAL_KEYWORDS: &[&str] = &[ KEYWORD_PRINT, // side effects @@ -1077,7 +1077,7 @@ fn optimize_top_level( } // Set up the state - let mut state = State::new(engine, lib, optimization_level); + let mut state = OptimizerState::new(engine, lib, optimization_level); // Add constants and variables from the scope scope.iter().for_each(|(name, constant, value)| { @@ -1142,7 +1142,7 @@ pub fn optimize_into_ast( let mut fn_def = crate::fn_native::shared_take_or_clone(fn_def); // Optimize the function body - let state = &mut State::new(engine, lib2, level); + let state = &mut OptimizerState::new(engine, lib2, level); let body = mem::take(fn_def.body.statements_mut()).into_vec(); diff --git a/src/parse.rs b/src/parse.rs index a03c1dbc..9d00409f 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -137,7 +137,7 @@ impl<'e> ParseState<'e> { /// /// If the variable is not present in the scope adds it to the list of external variables /// - /// The return value is the offset to be deducted from `ParseState::stack::len`, + /// The return value is the offset to be deducted from `ParseState::stack::len()`, /// i.e. the top element of [`ParseState`]'s variables stack is offset 1. /// /// Return `None` when the variable name is not found in the `stack`. @@ -156,7 +156,7 @@ impl<'e> ParseState<'e> { barrier = true; false } else { - *n == name + n == name } }) .and_then(|(i, _)| NonZeroUsize::new(i + 1)); @@ -324,7 +324,30 @@ impl Expr { } } +/// Make sure that the next expression is not a statement expression (i.e. wrapped in `{}`). +#[inline(always)] +fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result<(), ParseError> { + match input.peek().expect(NEVER_ENDS) { + (Token::LeftBrace, pos) => Err(PERR::ExprExpected(type_name.to_string()).into_err(*pos)), + _ => Ok(()), + } +} + +/// Make sure that the next expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`). +#[inline(always)] +fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> { + match input.peek().expect(NEVER_ENDS) { + (Token::Equals, pos) => Err(LexError::ImproperSymbol( + "=".to_string(), + "Possibly a typo of '=='?".to_string(), + ) + .into_err(*pos)), + _ => Ok(()), + } +} + /// Consume a particular [token][Token], checking that it is the expected one. +#[inline] fn eat_token(input: &mut TokenStream, token: Token) -> Position { let (t, pos) = input.next().expect(NEVER_ENDS); @@ -340,6 +363,7 @@ fn eat_token(input: &mut TokenStream, token: Token) -> Position { } /// Match a particular [token][Token], consuming it if matched. +#[inline] fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) { let (t, pos) = input.peek().expect(NEVER_ENDS); if *t == token { @@ -2113,46 +2137,6 @@ fn parse_expr( ) } -/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`). -fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result<(), ParseError> { - match input.peek().expect(NEVER_ENDS) { - // Disallow statement expressions - (Token::LeftBrace, pos) | (Token::EOF, pos) => { - Err(PERR::ExprExpected(type_name.to_string()).into_err(*pos)) - } - // No need to check for others at this time - leave it for the expr parser - _ => Ok(()), - } -} - -/// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`). -fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> { - match input.peek().expect(NEVER_ENDS) { - (Token::Equals, pos) => Err(LexError::ImproperSymbol( - "=".to_string(), - "Possibly a typo of '=='?".to_string(), - ) - .into_err(*pos)), - (token @ Token::PlusAssign, pos) - | (token @ Token::MinusAssign, pos) - | (token @ Token::MultiplyAssign, pos) - | (token @ Token::DivideAssign, pos) - | (token @ Token::LeftShiftAssign, pos) - | (token @ Token::RightShiftAssign, pos) - | (token @ Token::ModuloAssign, pos) - | (token @ Token::PowerOfAssign, pos) - | (token @ Token::AndAssign, pos) - | (token @ Token::OrAssign, pos) - | (token @ Token::XOrAssign, pos) => Err(LexError::ImproperSymbol( - token.syntax().to_string(), - "Expecting a boolean expression, not an assignment".to_string(), - ) - .into_err(*pos)), - - _ => Ok(()), - } -} - /// Parse an if statement. fn parse_if( input: &mut TokenStream, @@ -2207,15 +2191,15 @@ fn parse_while_loop( (Token::While, pos) => { ensure_not_statement_expr(input, "a boolean")?; let expr = parse_expr(input, state, lib, settings.level_up())?.ensure_bool_expr()?; + ensure_not_assignment(input)?; (expr, pos) } (Token::Loop, pos) => (Expr::Unit(Position::NONE), pos), _ => unreachable!(), }; settings.pos = token_pos; - - ensure_not_assignment(input)?; settings.is_breakable = true; + let body = parse_block(input, state, lib, settings.level_up())?; Ok(Stmt::While(guard, Box::new(body.into()), settings.pos)) @@ -2249,8 +2233,9 @@ fn parse_do( } }; - ensure_not_statement_expr(input, "a boolean")?; settings.is_breakable = false; + + ensure_not_statement_expr(input, "a boolean")?; let guard = parse_expr(input, state, lib, settings.level_up())?.ensure_bool_expr()?; ensure_not_assignment(input)?; diff --git a/tests/native.rs b/tests/native.rs index 2112f6f7..349cebf8 100644 --- a/tests/native.rs +++ b/tests/native.rs @@ -30,6 +30,7 @@ fn test_native_context_fn_name() -> Result<(), Box> { let mut engine = Engine::new(); + #[allow(deprecated)] engine .register_raw_fn( "add_double", diff --git a/tests/switch.rs b/tests/switch.rs index 614d0876..b3bbc803 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -160,6 +160,7 @@ mod test_switch_enum { use super::*; use rhai::Array; #[derive(Debug, Clone)] + #[allow(dead_code)] enum MyEnum { Foo, Bar(INT),