From 02e886e33bfe73ffd121e85d1ff0f9d7f7ea6764 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 22 Jun 2021 22:43:44 +0800 Subject: [PATCH 01/25] Bump rhai_codegen version to require 0.20.3 of rhai. --- codegen/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 53b6ab47..34008654 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai_codegen" -version = "0.4.0" +version = "0.4.1" edition = "2018" authors = ["jhwgh1968", "Stephen Chung"] description = "Procedural macros support package for Rhai, a scripting language and engine for Rust" From 178fe6b978b986bd45d0995275e403c2445e0473 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 24 Jun 2021 09:48:44 +0800 Subject: [PATCH 02/25] Remove deprecated API. --- src/dynamic.rs | 33 --------------------------------- src/engine_api.rs | 21 --------------------- src/lib.rs | 16 ---------------- src/packages/mod.rs | 9 --------- 4 files changed, 79 deletions(-) diff --git a/src/dynamic.rs b/src/dynamic.rs index 7c876431..49d5de9d 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1825,23 +1825,6 @@ impl Dynamic { /// Convert the [`Dynamic`] into a [`String`] and return it. /// If there are other references to the same string, a cloned copy is returned. /// Returns the name of the actual type if the cast fails. - /// - /// # Deprecated - /// - /// This method is deprecated and will be removed in the future. - /// Use [`as_string`][Dynamic::as_string] instead. - #[inline(always)] - #[must_use] - #[deprecated( - since = "0.20.3", - note = "this method is deprecated and will be removed in the future" - )] - pub fn take_string(self) -> Result { - self.as_string() - } - /// Convert the [`Dynamic`] into a [`String`] and return it. - /// If there are other references to the same string, a cloned copy is returned. - /// Returns the name of the actual type if the cast fails. #[inline(always)] #[must_use] pub fn as_string(self) -> Result { @@ -1849,22 +1832,6 @@ impl Dynamic { } /// Convert the [`Dynamic`] into an [`ImmutableString`] and return it. /// Returns the name of the actual type if the cast fails. - /// - /// # Deprecated - /// - /// This method is deprecated and will be removed in the future. - /// Use [`as_immutable_string`][Dynamic::as_immutable_string] instead. - #[inline(always)] - #[must_use] - #[deprecated( - since = "0.20.3", - note = "this method is deprecated and will be removed in the future" - )] - pub fn take_immutable_string(self) -> Result { - self.as_immutable_string() - } - /// Convert the [`Dynamic`] into an [`ImmutableString`] and return it. - /// Returns the name of the actual type if the cast fails. #[inline(always)] #[must_use] pub fn as_immutable_string(self) -> Result { diff --git a/src/engine_api.rs b/src/engine_api.rs index c0052d75..3b8149ca 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -903,14 +903,6 @@ impl Engine { self.global_modules.insert(0, module); self } - /// Register a shared [`Module`] into the global namespace of [`Engine`]. - /// This function is deprecated and will be removed in the future. - /// Use [`register_global_module`][Engine::register_global_module] instead. - #[inline(always)] - #[deprecated(since = "0.19.9", note = "use `register_global_module` instead")] - pub fn load_package(&mut self, module: impl Into>) -> &mut Self { - self.register_global_module(module.into()) - } /// Register a shared [`Module`] as a static module namespace with the [`Engine`]. /// /// Functions marked [`FnNamespace::Global`] and type iterators are exposed to scripts without @@ -999,19 +991,6 @@ impl Engine { self } - /// Register a shared [`Module`] as a static module namespace with the [`Engine`]. - /// This function is deprecated and will be removed in the future. - /// Use [`register_static_module`][Engine::register_static_module] instead. - #[cfg(not(feature = "no_module"))] - #[inline(always)] - #[deprecated(since = "0.19.9", note = "use `register_static_module` instead")] - pub fn register_module( - &mut self, - name: impl AsRef + Into, - module: impl Into>, - ) -> &mut Self { - self.register_static_module(name, module.into()) - } /// Compile a string into an [`AST`], which can be used later for evaluation. /// /// # Example diff --git a/src/lib.rs b/src/lib.rs index 669ff9ac..905e5655 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -141,22 +141,6 @@ pub use module::{FnNamespace, Module}; pub use scope::Scope; pub use token::Position; -/// A trait to enable registering Rust functions. -/// This trait is no longer needed and will be removed in the future. -#[deprecated( - since = "0.19.15", - note = "this trait is no longer needed and will be removed in the future" -)] -pub trait RegisterFn {} - -/// A trait to enable registering Rust functions. -/// This trait is no longer needed and will be removed in the future. -#[deprecated( - since = "0.19.15", - note = "this trait is no longer needed and will be removed in the future" -)] -pub trait RegisterResultFn {} - /// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most /// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline. #[cfg(not(feature = "internals"))] diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 5e9b335d..f3705088 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -40,15 +40,6 @@ pub trait Package { /// Retrieve the generic package library from this package. #[must_use] fn as_shared_module(&self) -> Shared; - - /// Retrieve the generic package library from this package. - /// This method is deprecated and will be removed in the future. - /// Use [`as_shared_module`][Package::as_shared_module] instead. - #[must_use] - #[deprecated(since = "0.19.9", note = "use `as_shared_module` instead")] - fn get(&self) -> Shared { - self.as_shared_module() - } } /// Macro that makes it easy to define a _package_ (which is basically a shared [module][Module]) From 83297ffa0078cd3aa1f7778f887321fa29c62e3e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 24 Jun 2021 11:50:03 +0800 Subject: [PATCH 03/25] Bump version to 1.0. --- CHANGELOG.md | 8 ++++++++ Cargo.toml | 2 +- src/engine_api.rs | 3 ++- src/module/mod.rs | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58434e5d..0be17a00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ Rhai Release Notes ================== +Version 1.0.0 +============= + +The official version `1.0`. + +Almost the same version as `0.20.3` but with deprecated API removed. + + Version 0.20.3 ============== diff --git a/Cargo.toml b/Cargo.toml index eb75503c..834c4e29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [".", "codegen"] [package] name = "rhai" -version = "0.20.3" +version = "1.0.0" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" diff --git a/src/engine_api.rs b/src/engine_api.rs index 3b8149ca..0f8f70fe 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1314,7 +1314,8 @@ impl Engine { Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?)) } /// Parse a JSON string into an [object map][`Map`]. - /// This is a light-weight alternative to using, say, [`serde_json`] to deserialize the JSON. + /// This is a light-weight alternative to using, say, + /// [`serde_json`](https://crates.io/crates/serde_json) to deserialize the JSON. /// /// Not available under `no_object`. /// diff --git a/src/module/mod.rs b/src/module/mod.rs index 9479e6c1..ebd3f2db 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -290,7 +290,7 @@ impl Module { /// ``` #[inline(always)] #[must_use] - pub fn id_raw(&self) -> Option<&Identifier> { + pub(crate) fn id_raw(&self) -> Option<&Identifier> { self.id.as_ref() } From 1247b893521314658f37004343948a54774bda02 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 24 Jun 2021 14:55:53 +0800 Subject: [PATCH 04/25] Deprecate description for errors. --- src/error.rs | 95 +++++++++++++------------------------------ src/error_parsing.rs | 97 +++++++++++++------------------------------- 2 files changed, 58 insertions(+), 134 deletions(-) diff --git a/src/error.rs b/src/error.rs index ea3ad170..61edc035 100644 --- a/src/error.rs +++ b/src/error.rs @@ -93,55 +93,13 @@ pub enum EvalAltResult { Return(Dynamic, Position), } -impl EvalAltResult { - #[must_use] - pub(crate) fn desc(&self) -> &str { - match self { - #[allow(deprecated)] - Self::ErrorSystem(_, s) => s.description(), - Self::ErrorParsing(p, _) => p.desc(), - Self::ErrorInFunctionCall(_, _, _, _) => "Error in called function", - Self::ErrorInModule(_, _, _) => "Error in module", - Self::ErrorFunctionNotFound(_, _) => "Function not found", - Self::ErrorUnboundThis(_) => "'this' is not bound", - Self::ErrorMismatchDataType(_, _, _) => "Data type is incorrect", - Self::ErrorIndexingType(_, _) => "No indexer of the appropriate types defined", - Self::ErrorArrayBounds(0, _, _) => "Empty array has nothing to access", - Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds", - Self::ErrorStringBounds(0, _, _) => "Empty string has nothing to index", - Self::ErrorStringBounds(_, _, _) => "String index out of bounds", - Self::ErrorBitFieldBounds(_, _, _) => "Bit-field index out of bounds", - Self::ErrorFor(_) => "For loop expects a type with an iterator defined", - Self::ErrorVariableNotFound(_, _) => "Variable not found", - Self::ErrorModuleNotFound(_, _) => "Module not found", - Self::ErrorDataRace(_, _) => "Data race detected when accessing variable", - Self::ErrorAssignmentToConstant(_, _) => "Cannot modify a constant", - Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect", - Self::ErrorDotExpr(_, _) => "Malformed dot expression", - Self::ErrorArithmetic(_, _) => "Arithmetic error", - Self::ErrorTooManyOperations(_) => "Too many operations", - Self::ErrorTooManyModules(_) => "Too many modules imported", - Self::ErrorStackOverflow(_) => "Stack overflow", - Self::ErrorDataTooLarge(_, _) => "Data size exceeds maximum limit", - Self::ErrorTerminated(_, _) => "Script terminated.", - Self::ErrorRuntime(_, _) => "Runtime error", - Self::LoopBreak(true, _) => "Break statement not inside a loop", - Self::LoopBreak(false, _) => "Continue statement not inside a loop", - Self::Return(_, _) => "[Not Error] Function returns value", - } - } -} - impl Error for EvalAltResult {} impl fmt::Display for EvalAltResult { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let desc = self.desc(); - let pos = self.position(); - match self { - Self::ErrorSystem(s, _) if s.is_empty() => f.write_str(desc)?, - Self::ErrorSystem(s, _) => write!(f, "{}: {}", s, desc)?, + Self::ErrorSystem(s, err) if s.is_empty() => write!(f, "{}", err)?, + Self::ErrorSystem(s, err) => write!(f, "{}: {}", s, err)?, Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?, @@ -164,32 +122,35 @@ impl fmt::Display for EvalAltResult { } Self::ErrorInModule(s, err, _) => write!(f, "Error in module '{}': {}", s, err)?, - Self::ErrorFunctionNotFound(s, _) - | Self::ErrorVariableNotFound(s, _) - | Self::ErrorDataRace(s, _) => write!(f, "{}: {}", desc, s)?, - - Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?, - + Self::ErrorFunctionNotFound(s, _) => write!(f, "Function not found: {}", s)?, + Self::ErrorVariableNotFound(s, _) => write!(f, "Variable not found: {}", s)?, + Self::ErrorModuleNotFound(s, _) => write!(f, "Module not found: '{}'", s)?, + Self::ErrorDataRace(s, _) => { + write!(f, "Data race detected when accessing variable: {}", s)? + } Self::ErrorDotExpr(s, _) if !s.is_empty() => f.write_str(s)?, - Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for '{}'", s)?, - - Self::ErrorUnboundThis(_) - | Self::ErrorFor(_) - | Self::ErrorDotExpr(_, _) - | Self::ErrorTooManyOperations(_) - | Self::ErrorTooManyModules(_) - | Self::ErrorStackOverflow(_) - | Self::ErrorTerminated(_, _) => f.write_str(desc)?, + Self::ErrorUnboundThis(_) => f.write_str("'this' is not bound")?, + Self::ErrorFor(_) => f.write_str("For loop expects a type with an iterator defined")?, + Self::ErrorDotExpr(_, _) => f.write_str("Malformed dot expression")?, + Self::ErrorTooManyOperations(_) => f.write_str("Too many operations")?, + Self::ErrorTooManyModules(_) => f.write_str("Too many modules imported")?, + Self::ErrorStackOverflow(_) => f.write_str("Stack overflow")?, + Self::ErrorTerminated(_, _) => f.write_str("Script terminated")?, Self::ErrorRuntime(d, _) if d.is::() => { let s = &*d .read_lock::() .expect("never fails because the type was checked"); - write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })? + + if s.is_empty() { + f.write_str("Runtime error")? + } else { + write!(f, "Runtime error: {}", s)? + } } - Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?, - Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?, + Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str("Runtime error")?, + Self::ErrorRuntime(d, _) => write!(f, "Runtime error: {}", d)?, Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot modify constant {}", s)?, Self::ErrorMismatchOutputType(s, r, _) => { @@ -206,8 +167,10 @@ impl fmt::Display for EvalAltResult { } Self::ErrorArithmetic(s, _) => f.write_str(s)?, - Self::LoopBreak(_, _) => f.write_str(desc)?, - Self::Return(_, _) => f.write_str(desc)?, + Self::LoopBreak(true, _) => f.write_str("Break statement not inside a loop")?, + Self::LoopBreak(false, _) => f.write_str("Continue statement not inside a loop")?, + + Self::Return(_, _) => f.write_str("NOT AN ERROR - Function returns value")?, Self::ErrorArrayBounds(0, index, _) => { write!(f, "Array index {} out of bounds: array is empty", index)? @@ -244,8 +207,8 @@ impl fmt::Display for EvalAltResult { } // Do not write any position if None - if !pos.is_none() { - write!(f, " ({})", pos)?; + if !self.position().is_none() { + write!(f, " ({})", self.position())?; } Ok(()) diff --git a/src/error_parsing.rs b/src/error_parsing.rs index 795b85f3..d228190e 100644 --- a/src/error_parsing.rs +++ b/src/error_parsing.rs @@ -194,43 +194,6 @@ impl ParseErrorType { pub(crate) fn into_err(self, pos: Position) -> ParseError { ParseError(Box::new(self), pos) } - - #[must_use] - pub(crate) fn desc(&self) -> &str { - match self { - Self::UnexpectedEOF => "Script is incomplete", - Self::BadInput(err) => err.desc(), - Self::UnknownOperator(_) => "Unknown operator", - Self::MissingToken(_, _) => "Expecting a certain token that is missing", - Self::MissingSymbol(_) => "Expecting a certain symbol that is missing", - Self::MalformedCallExpr(_) => "Invalid expression in function call arguments", - Self::MalformedIndexExpr(_) => "Invalid index in indexing expression", - Self::MalformedInExpr(_) => "Invalid 'in' expression", - Self::MalformedCapture(_) => "Invalid capturing", - Self::DuplicatedProperty(_) => "Duplicated property in object map literal", - Self::DuplicatedSwitchCase => "Duplicated switch case", - Self::DuplicatedVariable(_) => "Duplicated variable name", - Self::WrongSwitchDefaultCase => "Default switch case is not the last", - Self::WrongSwitchCaseCondition => "Default switch case cannot have condition", - Self::PropertyExpected => "Expecting name of a property", - Self::VariableExpected => "Expecting name of a variable", - Self::Reserved(_) => "Invalid use of reserved keyword", - Self::ExprExpected(_) => "Expecting an expression", - Self::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function", - Self::FnDuplicatedDefinition(_, _) => "Function already exists", - Self::FnMissingName => "Expecting function name in function declaration", - Self::FnMissingParams(_) => "Expecting parameters in function declaration", - Self::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration", - Self::FnMissingBody(_) => "Expecting body statement block for function declaration", - Self::WrongDocComment => "Doc-comment must be followed immediately by a function definition", - Self::WrongExport => "Export statement can only appear at global level", - Self::AssignmentToConstant(_) => "Cannot assign to a constant value", - Self::AssignmentToInvalidLHS(_) => "Expression cannot be assigned to", - Self::ExprTooDeep => "Expression exceeds maximum complexity", - Self::LiteralTooLarge(_, _) => "Literal exceeds maximum limit", - Self::LoopBreak => "Break statement should only be used inside a loop" - } - } } impl fmt::Display for ParseErrorType { @@ -238,12 +201,14 @@ impl fmt::Display for ParseErrorType { match self { Self::BadInput(err) => write!(f, "{}", err), - Self::MalformedCallExpr(s) => f.write_str(if s.is_empty() { self.desc() } else { s }), - Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s), + Self::UnknownOperator(s) => write!(f, "Unknown operator: '{}'", s), - Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => { - f.write_str(if s.is_empty() { self.desc() } else { s }) - } + Self::MalformedCallExpr(s) if s.is_empty() => f.write_str("Invalid expression in function call arguments"), + Self::MalformedIndexExpr(s) if s.is_empty() => f.write_str("Invalid index in indexing expression"), + Self::MalformedInExpr(s) if s.is_empty() => f.write_str("Invalid 'in' expression"), + Self::MalformedCapture(s) if s.is_empty() => f.write_str("Invalid capturing"), + + Self::MalformedCallExpr(s) | Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => f.write_str(s), Self::FnDuplicatedDefinition(s, n) => { write!(f, "Function '{}' with ", s)?; @@ -253,43 +218,39 @@ impl fmt::Display for ParseErrorType { _ => write!(f, "{} parameters already exists", n), } } - Self::DuplicatedProperty(s) => { - write!(f, "Duplicated property '{}' for object map literal", s) - } - Self::DuplicatedSwitchCase => f.write_str(self.desc()), + + Self::FnMissingBody(s) if s.is_empty() => f.write_str("Expecting body statement block for anonymous function"), + Self::FnMissingBody(s) => write!(f, "Expecting body statement block for function '{}'", s), + + Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s), + Self::FnDuplicatedParam(s, arg) => write!(f, "Duplicated parameter '{}' for function '{}'", arg, s), + Self::DuplicatedProperty(s) => write!(f, "Duplicated property '{}' for object map literal", s), + Self::DuplicatedSwitchCase => f.write_str("Duplicated switch case"), Self::DuplicatedVariable(s) => write!(f, "Duplicated variable name '{}'", s), Self::ExprExpected(s) => write!(f, "Expecting {} expression", s), - - Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s), - - Self::FnMissingBody(s) if s.is_empty() => { - f.write_str("Expecting body statement block for anonymous function") - } - Self::FnMissingBody(s) => { - write!(f, "Expecting body statement block for function '{}'", s) - } - - Self::FnDuplicatedParam(s, arg) => { - write!(f, "Duplicated parameter '{}' for function '{}'", arg, s) - } - Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s), Self::MissingSymbol(s) => f.write_str(s), - Self::AssignmentToConstant(s) if s.is_empty() => f.write_str(self.desc()), + Self::AssignmentToConstant(s) if s.is_empty() => f.write_str("Cannot assign to a constant value"), Self::AssignmentToConstant(s) => write!(f, "Cannot assign to constant '{}'", s), - Self::AssignmentToInvalidLHS(s) if s.is_empty() => f.write_str(self.desc()), + Self::AssignmentToInvalidLHS(s) if s.is_empty() => f.write_str("Expression cannot be assigned to"), Self::AssignmentToInvalidLHS(s) => f.write_str(s), - Self::LiteralTooLarge(typ, max) => { - write!(f, "{} exceeds the maximum limit ({})", typ, max) - } - + Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max), Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s), - - _ => f.write_str(self.desc()), + Self::UnexpectedEOF => f.write_str("Script is incomplete"), + Self::WrongSwitchDefaultCase => f.write_str("Default switch case is not the last"), + Self::WrongSwitchCaseCondition => f.write_str("Default switch case cannot have condition"), + Self::PropertyExpected => f.write_str("Expecting name of a property"), + Self::VariableExpected => f.write_str("Expecting name of a variable"), + Self::WrongFnDefinition => f.write_str("Function definitions must be at global level and cannot be inside a block or another function"), + Self::FnMissingName => f.write_str("Expecting function name in function declaration"), + Self::WrongDocComment => f.write_str("Doc-comment must be followed immediately by a function definition"), + Self::WrongExport => f.write_str("Export statement can only appear at global level"), + Self::ExprTooDeep => f.write_str("Expression exceeds maximum complexity"), + Self::LoopBreak => f.write_str("Break statement should only be used inside a loop"), } } } From 7196b017f501a68e4eba3353d789cc33139b6009 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 27 Jun 2021 22:10:52 +0800 Subject: [PATCH 05/25] Add is_odd, is_even and is_zero. --- CHANGELOG.md | 5 +++++ src/packages/arithmetic.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0be17a00..2c1f9ffe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ The official version `1.0`. Almost the same version as `0.20.3` but with deprecated API removed. +Enhancements +------------ + +* New methods `is_odd`, `is_even` for integers, and `is_zero` for all numbers. + Version 0.20.3 ============== diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index 27d639c1..074b19cf 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -121,6 +121,15 @@ macro_rules! gen_arithmetic_functions { pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type { x ^ y } + pub fn is_zero(x: $arg_type) -> bool { + x == 0 + } + pub fn is_odd(x: $arg_type) -> bool { + x % 2 != 0 + } + pub fn is_even(x: $arg_type) -> bool { + x % 2 == 0 + } } })* } } @@ -174,6 +183,7 @@ macro_rules! reg_functions { } def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { + combine_with_exported_module!(lib, "int", int_functions); reg_functions!(lib += signed_basic; INT); #[cfg(not(feature = "only_i32"))] @@ -201,6 +211,19 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { combine_with_exported_module!(lib, "decimal", decimal_functions); }); +#[export_module] +mod int_functions { + pub fn is_zero(x: INT) -> bool { + x == 0 + } + pub fn is_odd(x: INT) -> bool { + x % 2 != 0 + } + pub fn is_even(x: INT) -> bool { + x % 2 == 0 + } +} + gen_arithmetic_functions!(arith_basic => INT); #[cfg(not(feature = "only_i32"))] @@ -315,6 +338,9 @@ mod f32_functions { 1 } } + pub fn is_zero(x: f32) -> bool { + x == 0.0 + } #[rhai_fn(name = "**", return_raw)] pub fn pow_f_i(x: f32, y: INT) -> Result> { if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) { @@ -420,6 +446,9 @@ mod f64_functions { 1 } } + pub fn is_zero(x: f64) -> bool { + x == 0.0 + } } #[cfg(feature = "decimal")] @@ -511,4 +540,7 @@ pub mod decimal_functions { 1 } } + pub fn is_zero(x: Decimal) -> bool { + x.is_zero() + } } From 0888738b75d94e832685728945a63fd6e8c84a2a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Jun 2021 13:53:30 +0800 Subject: [PATCH 06/25] Change AST::clone_source to AST::source_raw. --- CHANGELOG.md | 6 ++++ src/ast.rs | 10 +++--- src/engine_api.rs | 4 +-- src/module/mod.rs | 87 +++++++++++++++++++++++------------------------ 4 files changed, 55 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c1f9ffe..15cfc23c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ The official version `1.0`. Almost the same version as `0.20.3` but with deprecated API removed. +Breaking changes +---------------- + +* All deprecated API's (e.g. the `RegisterFn` and `RegisterResultFn` traits) are removed. +* `Module::set_id` is split into `Module::set_id` and `Module::clear_id` pair. + Enhancements ------------ diff --git a/src/ast.rs b/src/ast.rs index 6df1f3c9..3748d061 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -251,20 +251,20 @@ impl AST { pub fn source(&self) -> Option<&str> { self.source.as_ref().map(|s| s.as_str()) } - /// Clone the source, if any. + /// Get a reference to the source. #[inline(always)] #[must_use] - pub(crate) fn clone_source(&self) -> Option { - self.source.clone() + pub(crate) fn source_raw(&self) -> Option<&Identifier> { + self.source.as_ref() } /// Set the source. #[inline(always)] pub fn set_source(&mut self, source: impl Into) -> &mut Self { - let source = Some(source.into()); + let source = source.into(); Shared::get_mut(&mut self.functions) .as_mut() .map(|m| m.set_id(source.clone())); - self.source = source; + self.source = Some(source); self } /// Clear the source. diff --git a/src/engine_api.rs b/src/engine_api.rs index 0f8f70fe..fff7aca6 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1750,7 +1750,7 @@ impl Engine { level: usize, ) -> RhaiResult { let mut state: State = Default::default(); - state.source = ast.clone_source(); + state.source = ast.source_raw().cloned(); #[cfg(not(feature = "no_module"))] { state.resolver = ast.resolver(); @@ -1832,7 +1832,7 @@ impl Engine { ) -> Result<(), Box> { let mods = &mut Default::default(); let mut state: State = Default::default(); - state.source = ast.clone_source(); + state.source = ast.source_raw().cloned(); #[cfg(not(feature = "no_module"))] { state.resolver = ast.resolver(); diff --git a/src/module/mod.rs b/src/module/mod.rs index ebd3f2db..07a2d070 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -235,8 +235,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); @@ -265,10 +264,9 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); - /// module.set_id(Some("hello")); + /// module.set_id("hello"); /// assert_eq!(module.id(), Some("hello")); /// ``` #[inline(always)] @@ -278,16 +276,6 @@ impl Module { } /// Get the ID of the [`Module`] as an [`Identifier`], if any. - /// - /// # Example - /// - /// ``` - /// use rhai::Module; - /// - /// let mut module = Module::new(); - /// module.set_id(Some("hello")); - /// assert_eq!(module.id_raw().map(|s| s.as_str()), Some("hello")); - /// ``` #[inline(always)] #[must_use] pub(crate) fn id_raw(&self) -> Option<&Identifier> { @@ -299,15 +287,31 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); - /// module.set_id(Some("hello")); + /// module.set_id("hello"); /// assert_eq!(module.id(), Some("hello")); /// ``` #[inline(always)] - pub fn set_id>(&mut self, id: Option) -> &mut Self { - self.id = id.map(|s| s.into()); + pub fn set_id(&mut self, id: impl Into) -> &mut Self { + self.id = Some(id.into()); + self + } + /// Clear the ID of the [`Module`]. + /// + /// # Example + /// + /// ``` + /// # use rhai::Module; + /// let mut module = Module::new(); + /// module.set_id("hello"); + /// assert_eq!(module.id(), Some("hello")); + /// module.clear_id(); + /// assert_eq!(module.id(), None); + /// ``` + #[inline(always)] + pub fn clear_id(&mut self) -> &mut Self { + self.id = None; self } @@ -316,8 +320,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let module = Module::new(); /// assert!(module.is_empty()); /// ``` @@ -340,8 +343,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// assert!(module.is_indexed()); /// @@ -380,8 +382,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert!(module.contains_var("answer")); @@ -397,8 +398,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); @@ -414,8 +414,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var("answer").unwrap().cast::(), 42); @@ -433,8 +432,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// module.set_var("answer", 42_i64); /// assert_eq!(module.get_var_value::("answer").unwrap(), 42); @@ -539,8 +537,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// let sub_module = Module::new(); /// module.set_sub_module("question", sub_module); @@ -557,8 +554,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// let sub_module = Module::new(); /// module.set_sub_module("question", sub_module); @@ -577,8 +573,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// let sub_module = Module::new(); /// module.set_sub_module("question", sub_module); @@ -603,8 +598,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// let hash = module.set_native_fn("calc", || Ok(42_i64)); /// assert!(module.contains_fn(hash)); @@ -845,8 +839,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// let hash = module.set_native_fn("calc", || Ok(42_i64)); /// assert!(module.contains_fn(hash)); @@ -880,8 +873,7 @@ impl Module { /// # Example /// /// ``` - /// use rhai::Module; - /// + /// # use rhai::Module; /// let mut module = Module::new(); /// let hash = module.set_getter_fn("value", |x: &mut i64| { Ok(*x) }); /// assert!(module.contains_fn(hash)); @@ -1480,7 +1472,12 @@ impl Module { module.set_script_fn(func); }); - module.set_id(ast.clone_source()); + if let Some(s) = ast.source_raw() { + module.set_id(s.clone()); + } else { + module.clear_id(); + } + module.build_index(); Ok(module) From 0c99165007cc295e056beda00b829fb5eed2da46 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Jun 2021 17:24:05 +0800 Subject: [PATCH 07/25] Remove description. --- src/error_parsing.rs | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/error_parsing.rs b/src/error_parsing.rs index d228190e..84e9392d 100644 --- a/src/error_parsing.rs +++ b/src/error_parsing.rs @@ -43,12 +43,16 @@ impl fmt::Display for LexError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s), - Self::MalformedEscapeSequence(s) => write!(f, "{}: '{}'", self.desc(), s), - Self::MalformedNumber(s) => write!(f, "{}: '{}'", self.desc(), s), - Self::MalformedChar(s) => write!(f, "{}: '{}'", self.desc(), s), - Self::MalformedIdentifier(s) => write!(f, "{}: '{}'", self.desc(), s), - Self::UnterminatedString => f.write_str(self.desc()), - Self::StringTooLong(max) => write!(f, "{} ({})", self.desc(), max), + Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s), + Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s), + Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s), + Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s), + Self::UnterminatedString => f.write_str("Open string is not terminated"), + Self::StringTooLong(max) => write!( + f, + "Length of string literal exceeds the maximum limit ({})", + max + ), Self::ImproperSymbol(s, d) if d.is_empty() => { write!(f, "Invalid symbol encountered: '{}'", s) } @@ -58,19 +62,6 @@ impl fmt::Display for LexError { } impl LexError { - #[must_use] - pub(crate) fn desc(&self) -> &str { - match self { - Self::UnexpectedInput(_) => "Unexpected character encountered", - Self::UnterminatedString => "Open string is not terminated", - Self::StringTooLong(_) => "Length of string literal exceeds the maximum limit", - Self::MalformedEscapeSequence(_) => "Invalid escape sequence", - Self::MalformedNumber(_) => "Invalid number", - Self::MalformedChar(_) => "Invalid character", - Self::MalformedIdentifier(_) => "Variable name is not proper", - Self::ImproperSymbol(_, _) => "Invalid symbol encountered", - } - } /// Convert a [`LexError`] into a [`ParseError`]. #[inline(always)] #[must_use] From e40e81ac1ab395b4cb98e1dc15993749ed81f93c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Jun 2021 18:06:05 +0800 Subject: [PATCH 08/25] Mark simple functions const. --- src/ast.rs | 34 ++++++++++--------- src/custom_syntax.rs | 33 ++++++++++++------ src/dynamic.rs | 77 +++++++++++++++++++++--------------------- src/engine_api.rs | 7 +++- src/engine_settings.rs | 52 ++++++++++++++++++++-------- src/error.rs | 22 +++--------- src/fn_builtin.rs | 1 + src/fn_call.rs | 20 ++++++++--- src/fn_hash.rs | 2 +- src/fn_native.rs | 67 ++++++++++++------------------------ src/fn_ptr.rs | 2 +- src/module/mod.rs | 19 +++++++---- src/optimize.rs | 4 +-- src/parse.rs | 51 ++++++++++++++++------------ src/scope.rs | 6 ++-- src/token.rs | 56 +++++++++++++++--------------- 16 files changed, 243 insertions(+), 210 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 3748d061..7d6309f8 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -911,7 +911,7 @@ impl StmtBlock { /// Get the position of this statements block. #[inline(always)] #[must_use] - pub fn position(&self) -> Position { + pub const fn position(&self) -> Position { self.1 } /// Get the statements of this statements block. @@ -1044,7 +1044,7 @@ impl Stmt { /// Is this statement [`Noop`][Stmt::Noop]? #[inline(always)] #[must_use] - pub fn is_noop(&self) -> bool { + pub const fn is_noop(&self) -> bool { match self { Self::Noop(_) => true, _ => false, @@ -1117,7 +1117,7 @@ impl Stmt { } /// Does this statement return a value? #[must_use] - pub fn returns_value(&self) -> bool { + pub const fn returns_value(&self) -> bool { match self { Self::If(_, _, _) | Self::Switch(_, _, _) @@ -1142,12 +1142,12 @@ impl Stmt { Self::Import(_, _, _) | Self::Export(_, _) => false, #[cfg(not(feature = "no_closure"))] - Self::Share(_) => unreachable!("Stmt::Share should not be parsed"), + Self::Share(_) => false, } } /// Is this statement self-terminated (i.e. no need for a semicolon terminator)? #[must_use] - pub fn is_self_terminated(&self) -> bool { + pub const fn is_self_terminated(&self) -> bool { match self { Self::If(_, _, _) | Self::Switch(_, _, _) @@ -1173,7 +1173,7 @@ impl Stmt { Self::Import(_, _, _) | Self::Export(_, _) => false, #[cfg(not(feature = "no_closure"))] - Self::Share(_) => unreachable!("Stmt::Share should not be parsed"), + Self::Share(_) => false, } } /// Is this statement _pure_? @@ -1247,7 +1247,7 @@ impl Stmt { /// All statements following this statement will essentially be dead code. #[inline(always)] #[must_use] - pub fn is_control_flow_break(&self) -> bool { + pub const fn is_control_flow_break(&self) -> bool { match self { Self::Return(_, _, _) | Self::Break(_) | Self::Continue(_) => true, _ => false, @@ -1512,7 +1512,7 @@ impl FnCallHashes { /// Create a [`FnCallHashes`] with only the native Rust hash. #[inline(always)] #[must_use] - pub fn from_native(hash: u64) -> Self { + pub const fn from_native(hash: u64) -> Self { Self { script: None, native: hash, @@ -1521,7 +1521,7 @@ impl FnCallHashes { /// Create a [`FnCallHashes`] with both native Rust and script function hashes set to the same value. #[inline(always)] #[must_use] - pub fn from_script(hash: u64) -> Self { + pub const fn from_script(hash: u64) -> Self { Self { script: Some(hash), native: hash, @@ -1530,7 +1530,7 @@ impl FnCallHashes { /// Create a [`FnCallHashes`] with both native Rust and script function hashes. #[inline(always)] #[must_use] - pub fn from_script_and_native(script: u64, native: u64) -> Self { + pub const fn from_script_and_native(script: u64, native: u64) -> Self { Self { script: Some(script), native, @@ -1539,7 +1539,7 @@ impl FnCallHashes { /// Is this [`FnCallHashes`] native Rust only? #[inline(always)] #[must_use] - pub fn is_native_only(&self) -> bool { + pub const fn is_native_only(&self) -> bool { self.script.is_none() } } @@ -1570,7 +1570,7 @@ impl FnCallExpr { /// Does this function call contain a qualified namespace? #[inline(always)] #[must_use] - pub fn is_qualified(&self) -> bool { + pub const fn is_qualified(&self) -> bool { self.namespace.is_some() } /// Convert this into a [`FnCall`][Expr::FnCall]. @@ -1683,6 +1683,7 @@ impl FloatWrapper { /// Minimum floating-point number for natural display before switching to scientific notation. pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001; + /// Create a new [`FloatWrapper`]. #[inline(always)] #[must_use] pub fn new(value: F) -> Self { @@ -1692,6 +1693,7 @@ impl FloatWrapper { #[cfg(not(feature = "no_float"))] impl FloatWrapper { + /// Create a new [`FloatWrapper`]. #[inline(always)] #[must_use] pub(crate) const fn const_new(value: FLOAT) -> Self { @@ -1928,7 +1930,7 @@ impl Expr { /// Is the expression a simple variable access? #[inline(always)] #[must_use] - pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool { + pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool { match self { Self::Variable(_, _, x) => !non_qualified || x.1.is_none(), _ => false, @@ -2038,7 +2040,7 @@ impl Expr { /// Is the expression the unit `()` literal? #[inline(always)] #[must_use] - pub fn is_unit(&self) -> bool { + pub const fn is_unit(&self) -> bool { match self { Self::Unit(_) => true, _ => false, @@ -2070,7 +2072,7 @@ impl Expr { /// Is a particular [token][Token] allowed as a postfix operator to this expression? #[inline] #[must_use] - pub fn is_valid_postfix(&self, token: &Token) -> bool { + pub const fn is_valid_postfix(&self, token: &Token) -> bool { match token { #[cfg(not(feature = "no_object"))] Token::Period => return true, @@ -2120,7 +2122,7 @@ impl Expr { Self::Custom(_, _) => false, - Self::Stack(_, _) => unreachable!("Expr::Stack should not occur naturally"), + Self::Stack(_, _) => false, } } /// Recursively walk this expression. diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index edc1b9b3..3b3b18a0 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -14,14 +14,21 @@ use std::any::TypeId; #[cfg(feature = "no_std")] use std::prelude::v1::*; -pub const MARKER_EXPR: &str = "$expr$"; -pub const MARKER_BLOCK: &str = "$block$"; -pub const MARKER_IDENT: &str = "$ident$"; -pub const MARKER_STRING: &str = "$string$"; -pub const MARKER_INT: &str = "$int$"; +/// Special marker for matching an expression. +pub const CUSTOM_SYNTAX_MARKER_EXPR: &str = "$expr$"; +/// Special marker for matching a statements block. +pub const CUSTOM_SYNTAX_MARKER_BLOCK: &str = "$block$"; +/// Special marker for matching an identifier. +pub const CUSTOM_SYNTAX_MARKER_IDENT: &str = "$ident$"; +/// Special marker for matching a string literal. +pub const CUSTOM_SYNTAX_MARKER_STRING: &str = "$string$"; +/// Special marker for matching an integer number. +pub const CUSTOM_SYNTAX_MARKER_INT: &str = "$int$"; +/// Special marker for matching a floating-point number. #[cfg(not(feature = "no_float"))] -pub const MARKER_FLOAT: &str = "$float$"; -pub const MARKER_BOOL: &str = "$bool$"; +pub const CUSTOM_SYNTAX_MARKER_FLOAT: &str = "$float$"; +/// Special marker for matching a boolean value. +pub const CUSTOM_SYNTAX_MARKER_BOOL: &str = "$bool$"; /// A general expression evaluation trait object. #[cfg(not(feature = "sync"))] @@ -185,7 +192,7 @@ impl Engine { for s in keywords { let s = s.as_ref().trim(); - // skip empty keywords + // Skip empty keywords if s.is_empty() { continue; } @@ -194,15 +201,19 @@ impl Engine { let seg = match s { // Markers not in first position - MARKER_IDENT | MARKER_EXPR | MARKER_BLOCK | MARKER_BOOL | MARKER_INT - | MARKER_STRING + CUSTOM_SYNTAX_MARKER_IDENT + | CUSTOM_SYNTAX_MARKER_EXPR + | CUSTOM_SYNTAX_MARKER_BLOCK + | CUSTOM_SYNTAX_MARKER_BOOL + | CUSTOM_SYNTAX_MARKER_INT + | CUSTOM_SYNTAX_MARKER_STRING if !segments.is_empty() => { s.into() } // Markers not in first position #[cfg(not(feature = "no_float"))] - MARKER_FLOAT if !segments.is_empty() => s.into(), + CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(), // Standard or reserved keyword/symbol not in first position s if !segments.is_empty() && token.is_some() => { // Make it a custom keyword/symbol if it is disabled or reserved diff --git a/src/dynamic.rs b/src/dynamic.rs index 49d5de9d..584e33ac 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -33,6 +33,7 @@ use fmt::Debug; #[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))] use instant::Instant; +/// The message: never fails because the type was checked const CHECKED: &str = "never fails because the type was checked"; mod private { @@ -164,7 +165,7 @@ pub type Tag = i32; pub type Tag = i16; /// Default tag value for [`Dynamic`]. -const DEFAULT_TAG: Tag = 0; +const DEFAULT_TAG_VALUE: Tag = 0; /// Dynamic type containing any value. pub struct Dynamic(pub(crate) Union); @@ -711,8 +712,6 @@ impl fmt::Debug for Dynamic { let _value_any = (***value).as_any(); let _type_id = _value_any.type_id(); - const CHECKED: &str = "never fails because the type was checked"; - #[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i64"))] if _type_id == TypeId::of::() { @@ -819,28 +818,28 @@ impl Default for Dynamic { impl Dynamic { /// A [`Dynamic`] containing a `()`. - pub const UNIT: Dynamic = Self(Union::Unit((), DEFAULT_TAG, ReadWrite)); + pub const UNIT: Dynamic = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing a `true`. - pub const TRUE: Dynamic = Self(Union::Bool(true, DEFAULT_TAG, ReadWrite)); + pub const TRUE: Dynamic = Self(Union::Bool(true, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing a [`false`]. - pub const FALSE: Dynamic = Self(Union::Bool(false, DEFAULT_TAG, ReadWrite)); + pub const FALSE: Dynamic = Self(Union::Bool(false, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing the integer zero. - pub const ZERO: Dynamic = Self(Union::Int(0, DEFAULT_TAG, ReadWrite)); + pub const ZERO: Dynamic = Self(Union::Int(0, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing the integer one. - pub const ONE: Dynamic = Self(Union::Int(1, DEFAULT_TAG, ReadWrite)); + pub const ONE: Dynamic = Self(Union::Int(1, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing the integer two. - pub const TWO: Dynamic = Self(Union::Int(2, DEFAULT_TAG, ReadWrite)); + pub const TWO: Dynamic = Self(Union::Int(2, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing the integer ten. - pub const TEN: Dynamic = Self(Union::Int(10, DEFAULT_TAG, ReadWrite)); + pub const TEN: Dynamic = Self(Union::Int(10, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing the integer negative one. - pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, DEFAULT_TAG, ReadWrite)); + pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, DEFAULT_TAG_VALUE, ReadWrite)); /// A [`Dynamic`] containing `0.0`. /// /// Not available under `no_float`. #[cfg(not(feature = "no_float"))] pub const FLOAT_ZERO: Dynamic = Self(Union::Float( FloatWrapper::const_new(0.0), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )); /// A [`Dynamic`] containing `1.0`. @@ -849,7 +848,7 @@ impl Dynamic { #[cfg(not(feature = "no_float"))] pub const FLOAT_ONE: Dynamic = Self(Union::Float( FloatWrapper::const_new(1.0), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )); /// A [`Dynamic`] containing `2.0`. @@ -858,7 +857,7 @@ impl Dynamic { #[cfg(not(feature = "no_float"))] pub const FLOAT_TWO: Dynamic = Self(Union::Float( FloatWrapper::const_new(2.0), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )); /// A [`Dynamic`] containing `10.0`. @@ -867,7 +866,7 @@ impl Dynamic { #[cfg(not(feature = "no_float"))] pub const FLOAT_TEN: Dynamic = Self(Union::Float( FloatWrapper::const_new(10.0), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )); /// A [`Dynamic`] containing the `-1.0`. @@ -876,7 +875,7 @@ impl Dynamic { #[cfg(not(feature = "no_float"))] pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float( FloatWrapper::const_new(-1.0), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )); @@ -1043,8 +1042,6 @@ impl Dynamic { pub fn from(mut value: T) -> Self { // Coded this way in order to maximally leverage potentials for dead-code removal. - const CHECKED: &str = "never fails because the type was checked"; - if TypeId::of::() == TypeId::of::() { return unsafe_try_cast::<_, Dynamic>(value).ok().expect(CHECKED); } @@ -1125,7 +1122,7 @@ impl Dynamic { Self(Union::Variant( Box::new(Box::new(value)), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )) } @@ -1153,7 +1150,7 @@ impl Dynamic { Union::Shared(_, _, _) => self, _ => Self(Union::Shared( crate::Locked::new(self).into(), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, _access, )), } @@ -1857,33 +1854,33 @@ impl Dynamic { impl From<()> for Dynamic { #[inline(always)] fn from(value: ()) -> Self { - Self(Union::Unit(value, DEFAULT_TAG, ReadWrite)) + Self(Union::Unit(value, DEFAULT_TAG_VALUE, ReadWrite)) } } impl From for Dynamic { #[inline(always)] fn from(value: bool) -> Self { - Self(Union::Bool(value, DEFAULT_TAG, ReadWrite)) + Self(Union::Bool(value, DEFAULT_TAG_VALUE, ReadWrite)) } } impl From for Dynamic { #[inline(always)] fn from(value: INT) -> Self { - Self(Union::Int(value, DEFAULT_TAG, ReadWrite)) + Self(Union::Int(value, DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_float"))] impl From for Dynamic { #[inline(always)] fn from(value: FLOAT) -> Self { - Self(Union::Float(value.into(), DEFAULT_TAG, ReadWrite)) + Self(Union::Float(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_float"))] impl From> for Dynamic { #[inline(always)] fn from(value: FloatWrapper) -> Self { - Self(Union::Float(value, DEFAULT_TAG, ReadWrite)) + Self(Union::Float(value, DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(feature = "decimal")] @@ -1900,13 +1897,13 @@ impl From for Dynamic { impl From for Dynamic { #[inline(always)] fn from(value: char) -> Self { - Self(Union::Char(value, DEFAULT_TAG, ReadWrite)) + Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite)) } } impl> From for Dynamic { #[inline(always)] fn from(value: S) -> Self { - Self(Union::Str(value.into(), DEFAULT_TAG, ReadWrite)) + Self(Union::Str(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } impl From<&ImmutableString> for Dynamic { @@ -1927,7 +1924,7 @@ impl Dynamic { /// Create a [`Dynamic`] from an [`Array`]. #[inline(always)] pub(crate) fn from_array(array: Array) -> Self { - Self(Union::Array(Box::new(array), DEFAULT_TAG, ReadWrite)) + Self(Union::Array(Box::new(array), DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_index"))] @@ -1936,7 +1933,7 @@ impl From> for Dynamic { fn from(value: Vec) -> Self { Self(Union::Array( Box::new(value.into_iter().map(Dynamic::from).collect()), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )) } @@ -1947,7 +1944,7 @@ impl From<&[T]> for Dynamic { fn from(value: &[T]) -> Self { Self(Union::Array( Box::new(value.iter().cloned().map(Dynamic::from).collect()), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )) } @@ -1958,7 +1955,7 @@ impl std::iter::FromIterator for Dynamic { fn from_iter>(iter: X) -> Self { Self(Union::Array( Box::new(iter.into_iter().map(Dynamic::from).collect()), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )) } @@ -1968,7 +1965,7 @@ impl Dynamic { /// Create a [`Dynamic`] from a [`Map`]. #[inline(always)] pub(crate) fn from_map(map: Map) -> Self { - Self(Union::Map(Box::new(map), DEFAULT_TAG, ReadWrite)) + Self(Union::Map(Box::new(map), DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_object"))] @@ -1985,7 +1982,7 @@ impl, T: Variant + Clone> From, T: Variant + Clone> From, T: Variant + Clone> From for Dynamic { #[inline(always)] fn from(value: FnPtr) -> Self { - Self(Union::FnPtr(Box::new(value), DEFAULT_TAG, ReadWrite)) + Self(Union::FnPtr(Box::new(value), DEFAULT_TAG_VALUE, ReadWrite)) } } impl From> for Dynamic { #[inline(always)] fn from(value: Box) -> Self { - Self(Union::FnPtr(value, DEFAULT_TAG, ReadWrite)) + Self(Union::FnPtr(value, DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_std"))] impl From for Dynamic { #[inline(always)] fn from(value: Instant) -> Self { - Self(Union::TimeStamp(Box::new(value), DEFAULT_TAG, ReadWrite)) + Self(Union::TimeStamp( + Box::new(value), + DEFAULT_TAG_VALUE, + ReadWrite, + )) } } #[cfg(not(feature = "no_closure"))] impl From>> for Dynamic { #[inline(always)] fn from(value: crate::Shared>) -> Self { - Self(Union::Shared(value.into(), DEFAULT_TAG, ReadWrite)) + Self(Union::Shared(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } diff --git a/src/engine_api.rs b/src/engine_api.rs index fff7aca6..c012e3b7 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -2064,7 +2064,12 @@ impl Engine { .lib() .iter_fn() .filter(|f| f.func.is_script()) - .map(|f| f.func.get_fn_def().clone()) + .map(|f| { + f.func + .get_script_fn_def() + .expect("never fails because the function is scripted") + .clone() + }) .collect(); #[cfg(feature = "no_function")] diff --git a/src/engine_settings.rs b/src/engine_settings.rs index fb16e9fd..dbadafd9 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -50,7 +50,7 @@ impl Engine { #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub fn max_call_levels(&self) -> usize { + pub const fn max_call_levels(&self) -> usize { self.limits.max_call_stack_depth } /// Set the maximum number of operations allowed for a script to run to avoid @@ -69,8 +69,12 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] #[must_use] - pub fn max_operations(&self) -> u64 { - self.limits.max_operations.map_or(0, NonZeroU64::get) + pub const fn max_operations(&self) -> u64 { + if let Some(n) = self.limits.max_operations { + n.get() + } else { + 0 + } } /// Set the maximum number of imported [modules][crate::Module] allowed for a script. /// @@ -89,7 +93,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn max_modules(&self) -> usize { + pub const fn max_modules(&self) -> usize { self.limits.max_modules } /// Set the depth limits for expressions (0 for unlimited). @@ -115,8 +119,12 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] #[must_use] - pub fn max_expr_depth(&self) -> usize { - self.limits.max_expr_depth.map_or(0, NonZeroUsize::get) + pub const fn max_expr_depth(&self) -> usize { + if let Some(n) = self.limits.max_expr_depth { + n.get() + } else { + 0 + } } /// The depth limit for expressions in functions (0 for unlimited). /// @@ -125,10 +133,12 @@ impl Engine { #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub fn max_function_expr_depth(&self) -> usize { - self.limits - .max_function_expr_depth - .map_or(0, NonZeroUsize::get) + pub const fn max_function_expr_depth(&self) -> usize { + if let Some(n) = self.limits.max_function_expr_depth { + n.get() + } else { + 0 + } } /// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited). /// @@ -145,8 +155,12 @@ impl Engine { #[cfg(not(feature = "unchecked"))] #[inline(always)] #[must_use] - pub fn max_string_size(&self) -> usize { - self.limits.max_string_size.map_or(0, NonZeroUsize::get) + pub const fn max_string_size(&self) -> usize { + if let Some(n) = self.limits.max_string_size { + n.get() + } else { + 0 + } } /// Set the maximum length of [arrays][crate::Array] (0 for unlimited). /// @@ -166,7 +180,11 @@ impl Engine { #[inline(always)] #[must_use] pub fn max_array_size(&self) -> usize { - self.limits.max_array_size.map_or(0, NonZeroUsize::get) + if let Some(n) = self.limits.max_array_size { + n.get() + } else { + 0 + } } /// Set the maximum size of [object maps][crate::Map] (0 for unlimited). /// @@ -185,8 +203,12 @@ impl Engine { #[cfg(not(feature = "no_object"))] #[inline(always)] #[must_use] - pub fn max_map_size(&self) -> usize { - self.limits.max_map_size.map_or(0, NonZeroUsize::get) + pub const fn max_map_size(&self) -> usize { + if let Some(n) = self.limits.max_map_size { + n.get() + } else { + 0 + } } /// Set the module resolution service used by the [`Engine`]. /// diff --git a/src/error.rs b/src/error.rs index 61edc035..6dd09b70 100644 --- a/src/error.rs +++ b/src/error.rs @@ -237,19 +237,15 @@ impl EvalAltResult { /// /// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors. #[must_use] - pub fn is_pseudo_error(&self) -> bool { + pub const fn is_pseudo_error(&self) -> bool { match self { Self::LoopBreak(_, _) | Self::Return(_, _) => true, _ => false, } } /// Can this error be caught? - /// - /// # Panics - /// - /// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return]. #[must_use] - pub fn is_catchable(&self) -> bool { + pub const fn is_catchable(&self) -> bool { match self { Self::ErrorSystem(_, _) => false, Self::ErrorParsing(_, _) => false, @@ -279,17 +275,12 @@ impl EvalAltResult { | Self::ErrorDataTooLarge(_, _) | Self::ErrorTerminated(_, _) => false, - Self::LoopBreak(_, _) => panic!("EvalAltResult::LoopBreak should not occur naturally"), - Self::Return(_, _) => panic!("EvalAltResult::Return should not occur naturally"), + Self::LoopBreak(_, _) | Self::Return(_, _) => false, } } /// Is this error a system exception? - /// - /// # Panics - /// - /// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return]. #[must_use] - pub fn is_system_exception(&self) -> bool { + pub const fn is_system_exception(&self) -> bool { match self { Self::ErrorSystem(_, _) => true, Self::ErrorParsing(_, _) => true, @@ -301,9 +292,6 @@ impl EvalAltResult { Self::ErrorTerminated(_, _) => true, - Self::LoopBreak(_, _) => panic!("EvalAltResult::LoopBreak should not occur naturally"), - Self::Return(_, _) => panic!("EvalAltResult::Return should not occur naturally"), - _ => false, } } @@ -377,7 +365,7 @@ impl EvalAltResult { } /// Get the [position][Position] of this error. #[must_use] - pub fn position(&self) -> Position { + pub const fn position(&self) -> Position { match self { Self::ErrorSystem(_, _) => Position::NONE, diff --git a/src/fn_builtin.rs b/src/fn_builtin.rs index d01e8381..3d4346bf 100644 --- a/src/fn_builtin.rs +++ b/src/fn_builtin.rs @@ -14,6 +14,7 @@ use crate::FLOAT; #[cfg(feature = "decimal")] use rust_decimal::Decimal; +/// The message: never fails because this is built-in code and the type is already checked const BUILTIN: &str = "never fails because this is built-in code and the type is already checked"; /// Is the type a numeric type? diff --git a/src/fn_call.rs b/src/fn_call.rs index a0310c0c..b0693fad 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -302,9 +302,13 @@ impl Engine { let result = if func.is_plugin_fn() { func.get_plugin_fn() + .expect("never fails because the function is a plugin") .call((self, name, source, mods, lib).into(), args) } else { - func.get_native_fn()((self, name, source, mods, lib).into(), args) + let func = func + .get_native_fn() + .expect("never fails because the function is native"); + func((self, name, source, mods, lib).into(), args) }; // Restore the original reference @@ -681,7 +685,9 @@ impl Engine { // Script function call assert!(func.is_script()); - let func = func.get_fn_def(); + let func = func + .get_script_fn_def() + .expect("never fails because the function is scripted"); if func.body.is_empty() { return Ok((Dynamic::UNIT, false)); @@ -1384,7 +1390,9 @@ impl Engine { match func { #[cfg(not(feature = "no_function"))] Some(f) if f.is_script() => { - let fn_def = f.get_fn_def(); + let fn_def = f + .get_script_fn_def() + .expect("never fails because the function is scripted"); if fn_def.body.is_empty() { Ok(Dynamic::UNIT) @@ -1408,12 +1416,16 @@ impl Engine { Some(f) if f.is_plugin_fn() => f .get_plugin_fn() + .expect("never fails because the function is a plugin") .clone() .call((self, fn_name, module.id(), &*mods, lib).into(), &mut args) .map_err(|err| err.fill_position(pos)), Some(f) if f.is_native() => { - f.get_native_fn()((self, fn_name, module.id(), &*mods, lib).into(), &mut args) + let func = f + .get_native_fn() + .expect("never fails because the function is native"); + func((self, fn_name, module.id(), &*mods, lib).into(), &mut args) .map_err(|err| err.fill_position(pos)) } diff --git a/src/fn_hash.rs b/src/fn_hash.rs index e74d0a9b..c503daf5 100644 --- a/src/fn_hash.rs +++ b/src/fn_hash.rs @@ -108,6 +108,6 @@ pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { /// Combine two [`u64`] hashes by taking the XOR of them. #[inline(always)] #[must_use] -pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 { +pub(crate) const fn combine_hashes(a: u64, b: u64) -> u64 { a ^ b } diff --git a/src/fn_native.rs b/src/fn_native.rs index 95fa9f94..6ca637d5 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -89,7 +89,7 @@ impl<'a> NativeCallContext<'a> { /// Create a new [`NativeCallContext`]. #[inline(always)] #[must_use] - pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { + pub const fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { Self { engine, fn_name, @@ -106,7 +106,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn new_with_all_fields( + pub const fn new_with_all_fields( engine: &'a Engine, fn_name: &'a str, source: &'a Option<&str>, @@ -124,19 +124,19 @@ impl<'a> NativeCallContext<'a> { /// The current [`Engine`]. #[inline(always)] #[must_use] - pub fn engine(&self) -> &Engine { + pub const fn engine(&self) -> &Engine { self.engine } /// Name of the function called. #[inline(always)] #[must_use] - pub fn fn_name(&self) -> &str { + pub const fn fn_name(&self) -> &str { self.fn_name } /// The current source. #[inline(always)] #[must_use] - pub fn source(&self) -> Option<&str> { + pub const fn source(&self) -> Option<&str> { self.source } /// Get an iterator over the current set of modules imported via `import` statements. @@ -166,7 +166,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn imports(&self) -> Option<&Imports> { + pub const fn imports(&self) -> Option<&Imports> { self.mods } /// Get an iterator over the namespaces containing definitions of all script-defined functions. @@ -180,7 +180,7 @@ impl<'a> NativeCallContext<'a> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub fn namespaces(&self) -> &[&Module] { + pub const fn namespaces(&self) -> &[&Module] { self.lib } /// Call a function inside the call context. @@ -457,74 +457,51 @@ impl CallableFunction { } } /// Get a shared reference to a native Rust function. - /// - /// # Panics - /// - /// Panics if the [`CallableFunction`] is not [`Pure`][CallableFunction::Pure] or - /// [`Method`][CallableFunction::Method]. #[inline(always)] #[must_use] - pub fn get_native_fn(&self) -> &Shared { + pub fn get_native_fn(&self) -> Option<&Shared> { match self { - Self::Pure(f) | Self::Method(f) => f, - Self::Iterator(_) | Self::Plugin(_) => panic!("function should be native"), + Self::Pure(f) | Self::Method(f) => Some(f), + Self::Iterator(_) | Self::Plugin(_) => None, #[cfg(not(feature = "no_function"))] - Self::Script(_) => panic!("function should be native"), + Self::Script(_) => None, } } /// Get a shared reference to a script-defined function definition. /// /// Not available under `no_function`. - /// - /// # Panics - /// - /// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script]. #[cfg(not(feature = "no_function"))] #[inline(always)] #[must_use] - pub fn get_fn_def(&self) -> &Shared { + pub const fn get_script_fn_def(&self) -> Option<&Shared> { match self { - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => { - panic!("function should be scripted") - } - Self::Script(f) => f, + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => None, + Self::Script(f) => Some(f), } } /// Get a reference to an iterator function. - /// - /// # Panics - /// - /// Panics if the [`CallableFunction`] is not [`Iterator`][CallableFunction::Iterator]. #[inline(always)] #[must_use] - pub fn get_iter_fn(&self) -> IteratorFn { + pub fn get_iter_fn(&self) -> Option { match self { - Self::Iterator(f) => *f, - Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => { - panic!("function should an iterator") - } + Self::Iterator(f) => Some(*f), + Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => None, #[cfg(not(feature = "no_function"))] - Self::Script(_) => panic!("function should be an iterator"), + Self::Script(_) => None, } } /// Get a shared reference to a plugin function. - /// - /// # Panics - /// - /// Panics if the [`CallableFunction`] is not [`Plugin`][CallableFunction::Plugin]. #[inline(always)] #[must_use] - pub fn get_plugin_fn<'s>(&'s self) -> &Shared { + pub fn get_plugin_fn<'s>(&'s self) -> Option<&Shared> { match self { - Self::Plugin(f) => f, - Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => { - panic!("function should a plugin") - } + Self::Plugin(f) => Some(f), + Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None, #[cfg(not(feature = "no_function"))] - Self::Script(_) => panic!("function should a plugin"), + Self::Script(_) => None, } } /// Create a new [`CallableFunction::Pure`]. diff --git a/src/fn_ptr.rs b/src/fn_ptr.rs index fefe90ba..77544d38 100644 --- a/src/fn_ptr.rs +++ b/src/fn_ptr.rs @@ -38,7 +38,7 @@ impl FnPtr { /// Get the name of the function. #[inline(always)] #[must_use] - pub(crate) fn get_fn_name(&self) -> &Identifier { + pub(crate) const fn get_fn_name(&self) -> &Identifier { &self.0 } /// Get the underlying data of the function pointer. diff --git a/src/module/mod.rs b/src/module/mod.rs index 07a2d070..f23b883f 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -278,7 +278,7 @@ impl Module { /// Get the ID of the [`Module`] as an [`Identifier`], if any. #[inline(always)] #[must_use] - pub(crate) fn id_raw(&self) -> Option<&Identifier> { + pub(crate) const fn id_raw(&self) -> Option<&Identifier> { self.id.as_ref() } @@ -358,7 +358,7 @@ impl Module { /// ``` #[inline(always)] #[must_use] - pub fn is_indexed(&self) -> bool { + pub const fn is_indexed(&self) -> bool { self.indexed } @@ -508,7 +508,7 @@ impl Module { self.functions .values() .find(|f| f.params == num_params && f.name == name) - .map(|f| f.func.get_fn_def()) + .and_then(|f| f.func.get_script_fn_def()) } /// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules. @@ -1341,7 +1341,9 @@ impl Module { f.access, f.name.as_str(), f.params, - f.func.get_fn_def(), + f.func + .get_script_fn_def() + .expect("never fails because the function is scripted"), ) }) } @@ -1466,7 +1468,12 @@ impl Module { .filter(|f| f.func.is_script()) .for_each(|f| { // Encapsulate AST environment - let mut func = f.func.get_fn_def().as_ref().clone(); + let mut func = f + .func + .get_script_fn_def() + .expect("never fails because the function is scripted") + .as_ref() + .clone(); func.lib = Some(ast.shared_lib()); func.mods = func_mods.clone(); module.set_script_fn(func); @@ -1725,7 +1732,7 @@ impl NamespaceRef { /// Get the [`Scope`][crate::Scope] index offset. #[inline(always)] #[must_use] - pub(crate) fn index(&self) -> Option { + pub(crate) const fn index(&self) -> Option { self.index } /// Set the [`Scope`][crate::Scope] index offset. diff --git a/src/optimize.rs b/src/optimize.rs index 8d3054bf..1282f92a 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -61,7 +61,7 @@ struct State<'a> { impl<'a> State<'a> { /// Create a new State. #[inline(always)] - pub fn new( + pub const fn new( engine: &'a Engine, lib: &'a [&'a Module], optimization_level: OptimizationLevel, @@ -87,7 +87,7 @@ impl<'a> State<'a> { } /// Is the [`AST`] dirty (i.e. changed)? #[inline(always)] - pub fn is_dirty(&self) -> bool { + pub const fn is_dirty(&self) -> bool { self.changed } /// Prune the list of constants back to a specified size. diff --git a/src/parse.rs b/src/parse.rs index ece916a4..13a48ce7 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -5,7 +5,8 @@ use crate::ast::{ ScriptFnDef, Stmt, StmtBlock, }; use crate::custom_syntax::{ - CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING, + CustomSyntax, CUSTOM_SYNTAX_MARKER_BLOCK, CUSTOM_SYNTAX_MARKER_BOOL, CUSTOM_SYNTAX_MARKER_EXPR, + CUSTOM_SYNTAX_MARKER_IDENT, CUSTOM_SYNTAX_MARKER_INT, CUSTOM_SYNTAX_MARKER_STRING, }; use crate::dynamic::{AccessMode, Union}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; @@ -29,7 +30,7 @@ use std::{ }; #[cfg(not(feature = "no_float"))] -use crate::{custom_syntax::MARKER_FLOAT, FLOAT}; +use crate::{custom_syntax::CUSTOM_SYNTAX_MARKER_FLOAT, FLOAT}; #[cfg(not(feature = "no_function"))] use crate::FnAccess; @@ -38,6 +39,10 @@ type PERR = ParseErrorType; type FunctionsLib = BTreeMap>; +/// Invalid variable name that acts as a search barrier in a [`Scope`]. +const SCOPE_SEARCH_BARRIER_MARKER: &str = "$BARRIER$"; + +/// The message: never fails because `TokenStream` never ends const NEVER_ENDS: &str = "never fails because `TokenStream` never ends"; /// A factory of identifiers from text strings. @@ -147,8 +152,8 @@ impl<'e> ParseState<'e> { .rev() .enumerate() .find(|(_, (n, _))| { - if n.is_empty() { - // Do not go beyond empty variable names + if n == SCOPE_SEARCH_BARRIER_MARKER { + // Do not go beyond the barrier barrier = true; false } else { @@ -230,7 +235,7 @@ impl ParseSettings { /// Create a new `ParseSettings` with one higher expression level. #[inline(always)] #[must_use] - pub fn level_up(&self) -> Self { + pub const fn level_up(&self) -> Self { Self { level: self.level + 1, ..*self @@ -244,8 +249,10 @@ impl ParseSettings { &self, limit: Option, ) -> Result<(), ParseError> { - if limit.map(|limit| self.level > limit.get()).unwrap_or(false) { - return Err(PERR::ExprTooDeep.into_err(self.pos)); + if let Some(limit) = limit { + if self.level > limit.get() { + return Err(PERR::ExprTooDeep.into_err(self.pos)); + } } Ok(()) } @@ -1894,7 +1901,7 @@ fn parse_custom_syntax( // Add an empty variable name to the stack. // Empty variable names act as a barrier so earlier variables will not be matched. // Variable searches stop at the first empty variable name. - let empty = state.get_identifier(""); + let empty = state.get_identifier(SCOPE_SEARCH_BARRIER_MARKER); state.stack.push((empty, AccessMode::ReadWrite)); } @@ -1915,32 +1922,32 @@ fn parse_custom_syntax( }; match required_token.as_str() { - MARKER_IDENT => { + CUSTOM_SYNTAX_MARKER_IDENT => { let (name, pos) = parse_var_name(input)?; let name = state.get_identifier(name); segments.push(name.clone().into()); - tokens.push(state.get_identifier(MARKER_IDENT)); + tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_IDENT)); keywords.push(Expr::Variable(None, pos, Box::new((None, None, name)))); } - MARKER_EXPR => { + CUSTOM_SYNTAX_MARKER_EXPR => { keywords.push(parse_expr(input, state, lib, settings)?); - let keyword = state.get_identifier(MARKER_EXPR); + let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_EXPR); segments.push(keyword.clone().into()); tokens.push(keyword); } - MARKER_BLOCK => match parse_block(input, state, lib, settings)? { + CUSTOM_SYNTAX_MARKER_BLOCK => match parse_block(input, state, lib, settings)? { block @ Stmt::Block(_, _) => { keywords.push(Expr::Stmt(Box::new(block.into()))); - let keyword = state.get_identifier(MARKER_BLOCK); + let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_BLOCK); segments.push(keyword.clone().into()); tokens.push(keyword); } stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt), }, - MARKER_BOOL => match input.next().expect(NEVER_ENDS) { + CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) { (b @ Token::True, pos) | (b @ Token::False, pos) => { keywords.push(Expr::BoolConstant(b == Token::True, pos)); - let keyword = state.get_identifier(MARKER_BOOL); + let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_BOOL); segments.push(keyword.clone().into()); tokens.push(keyword); } @@ -1951,10 +1958,10 @@ fn parse_custom_syntax( ) } }, - MARKER_INT => match input.next().expect(NEVER_ENDS) { + CUSTOM_SYNTAX_MARKER_INT => match input.next().expect(NEVER_ENDS) { (Token::IntegerConstant(i), pos) => { keywords.push(Expr::IntegerConstant(i, pos)); - let keyword = state.get_identifier(MARKER_INT); + let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_INT); segments.push(keyword.clone().into()); tokens.push(keyword); } @@ -1966,10 +1973,10 @@ fn parse_custom_syntax( } }, #[cfg(not(feature = "no_float"))] - MARKER_FLOAT => match input.next().expect(NEVER_ENDS) { + CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) { (Token::FloatConstant(f), pos) => { keywords.push(Expr::FloatConstant(f, pos)); - let keyword = state.get_identifier(MARKER_FLOAT); + let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_FLOAT); segments.push(keyword.clone().into()); tokens.push(keyword); } @@ -1980,10 +1987,10 @@ fn parse_custom_syntax( .into_err(pos)) } }, - MARKER_STRING => match input.next().expect(NEVER_ENDS) { + CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { keywords.push(Expr::StringConstant(state.get_identifier(s).into(), pos)); - let keyword = state.get_identifier(MARKER_STRING); + let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_STRING); segments.push(keyword.clone().into()); tokens.push(keyword); } diff --git a/src/scope.rs b/src/scope.rs index 513a9e89..2fe9bcfb 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -7,7 +7,7 @@ use std::prelude::v1::*; use std::{borrow::Cow, iter::Extend}; /// Keep a number of entries inline (since [`Dynamic`] is usually small enough). -const SCOPE_SIZE: usize = 8; +const SCOPE_ENTRIES_INLINED: usize = 8; /// Type containing information about the current scope. /// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs. @@ -54,7 +54,7 @@ const SCOPE_SIZE: usize = 8; #[derive(Debug, Clone, Hash)] pub struct Scope<'a> { /// Current value of the entry. - values: smallvec::SmallVec<[Dynamic; SCOPE_SIZE]>, + values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, /// (Name, aliases) of the entry. names: Vec<(Cow<'a, str>, Option>>)>, } @@ -161,7 +161,7 @@ impl<'a> Scope<'a> { #[inline(always)] #[must_use] pub fn is_empty(&self) -> bool { - self.values.len() == 0 + self.values.is_empty() } /// Add (push) a new entry to the [`Scope`]. /// diff --git a/src/token.rs b/src/token.rs index aaac31c7..0e8878fb 100644 --- a/src/token.rs +++ b/src/token.rs @@ -41,7 +41,7 @@ pub type TokenizerControl = Rc>; type LERR = LexError; /// Separator character for numbers. -const NUM_SEP: char = '_'; +const NUMBER_SEPARATOR: char = '_'; /// A stream of tokens. pub type TokenStream<'a> = Peekable>; @@ -110,7 +110,7 @@ impl Position { #[cfg(not(feature = "no_position"))] return Some(self.line as usize); #[cfg(feature = "no_position")] - unreachable!(); + unreachable!("there is no Position"); } } /// Get the character position (1-based), or [`None`] if at beginning of a line. @@ -128,7 +128,7 @@ impl Position { }; #[cfg(feature = "no_position")] - unreachable!(); + unreachable!("there is no Position"); } } /// Advance by one character position. @@ -175,7 +175,7 @@ impl Position { /// Is this [`Position`] at the beginning of a line? #[inline(always)] #[must_use] - pub fn is_beginning_of_line(self) -> bool { + pub const fn is_beginning_of_line(self) -> bool { #[cfg(not(feature = "no_position"))] return self.pos == 0 && !self.is_none(); #[cfg(feature = "no_position")] @@ -184,9 +184,9 @@ impl Position { /// Is there no [`Position`]? #[inline(always)] #[must_use] - pub fn is_none(self) -> bool { + pub const fn is_none(self) -> bool { #[cfg(not(feature = "no_position"))] - return self == Self::NONE; + return self.line == 0 && self.pos == 0; #[cfg(feature = "no_position")] return true; } @@ -467,12 +467,8 @@ pub enum Token { impl Token { /// Get the syntax of the token if it is a keyword. - /// - /// # Panics - /// - /// Panics if the token is not a keyword. #[must_use] - pub fn keyword_syntax(&self) -> &'static str { + pub const fn keyword_syntax(&self) -> &'static str { use Token::*; match self { @@ -556,7 +552,7 @@ impl Token { #[cfg(not(feature = "no_module"))] As => "as", - t => unreachable!("{:?} is not a keyword", t), + _ => "ERROR: NOT A KEYWORD", } } @@ -589,7 +585,7 @@ impl Token { /// Is this token an op-assignment operator? #[inline] #[must_use] - pub fn is_op_assignment(&self) -> bool { + pub const fn is_op_assignment(&self) -> bool { match self { Self::PlusAssign | Self::MinusAssign @@ -608,7 +604,7 @@ impl Token { /// Get the corresponding operator of the token if it is an op-assignment operator. #[must_use] - pub fn map_op_assignment(&self) -> Option { + pub const fn map_op_assignment(&self) -> Option { Some(match self { Self::PlusAssign => Self::Plus, Self::MinusAssign => Self::Minus, @@ -628,7 +624,7 @@ impl Token { /// Has this token a corresponding op-assignment operator? #[inline] #[must_use] - pub fn has_op_assignment(&self) -> bool { + pub const fn has_op_assignment(&self) -> bool { match self { Self::Plus | Self::Minus @@ -647,7 +643,7 @@ impl Token { /// Get the corresponding op-assignment operator of the token. #[must_use] - pub fn make_op_assignment(&self) -> Option { + pub const fn make_op_assignment(&self) -> Option { Some(match self { Self::Plus => Self::PlusAssign, Self::Minus => Self::MinusAssign, @@ -777,7 +773,7 @@ impl Token { // Is this token [`EOF`][Token::EOF]? #[inline(always)] #[must_use] - pub fn is_eof(&self) -> bool { + pub const fn is_eof(&self) -> bool { use Token::*; match self { @@ -789,7 +785,7 @@ impl Token { // If another operator is after these, it's probably an unary operator // (not sure about `fn` name). #[must_use] - pub fn is_next_unary(&self) -> bool { + pub const fn is_next_unary(&self) -> bool { use Token::*; match self { @@ -850,7 +846,7 @@ impl Token { /// Get the precedence number of the token. #[must_use] - pub fn precedence(&self) -> Option { + pub const fn precedence(&self) -> Option { use Token::*; Precedence::new(match self { @@ -885,7 +881,7 @@ impl Token { /// Does an expression bind to the right (instead of left)? #[must_use] - pub fn is_bind_right(&self) -> bool { + pub const fn is_bind_right(&self) -> bool { use Token::*; match self { @@ -906,7 +902,7 @@ impl Token { /// Is this token a standard symbol used in the language? #[must_use] - pub fn is_symbol(&self) -> bool { + pub const fn is_symbol(&self) -> bool { use Token::*; match self { @@ -924,7 +920,7 @@ impl Token { /// Is this token an active standard keyword? #[must_use] - pub fn is_keyword(&self) -> bool { + pub const fn is_keyword(&self) -> bool { use Token::*; match self { @@ -944,7 +940,7 @@ impl Token { /// Is this token a reserved symbol? #[inline(always)] #[must_use] - pub fn is_reserved(&self) -> bool { + pub const fn is_reserved(&self) -> bool { match self { Self::Reserved(_) => true, _ => false, @@ -964,7 +960,7 @@ impl Token { /// Is this token a custom keyword? #[inline(always)] #[must_use] - pub fn is_custom(&self) -> bool { + pub const fn is_custom(&self) -> bool { match self { Self::Custom(_) => true, _ => false, @@ -1435,7 +1431,7 @@ fn get_next_token_inner( while let Some(next_char) = stream.peek_next() { match next_char { - ch if valid(ch) || ch == NUM_SEP => { + ch if valid(ch) || ch == NUMBER_SEPARATOR => { result.push(next_char); eat_next(stream, pos); } @@ -1536,8 +1532,11 @@ fn get_next_token_inner( // Parse number return Some(( if let Some(radix) = radix_base { - let out: String = - result.iter().skip(2).filter(|&&c| c != NUM_SEP).collect(); + let out: String = result + .iter() + .skip(2) + .filter(|&&c| c != NUMBER_SEPARATOR) + .collect(); INT::from_str_radix(&out, radix) .map(Token::IntegerConstant) @@ -1545,7 +1544,8 @@ fn get_next_token_inner( Token::LexError(LERR::MalformedNumber(result.into_iter().collect())) }) } else { - let out: String = result.iter().filter(|&&c| c != NUM_SEP).collect(); + let out: String = + result.iter().filter(|&&c| c != NUMBER_SEPARATOR).collect(); let num = INT::from_str(&out).map(Token::IntegerConstant); // If integer parsing is unnecessary, try float instead From a13a72436108bf9f8ac64644403103004f0edcd6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Jun 2021 18:29:53 +0800 Subject: [PATCH 09/25] Fix builds. --- src/dynamic.rs | 2 +- src/fn_native.rs | 4 ++-- src/serde/metadata.rs | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/dynamic.rs b/src/dynamic.rs index 584e33ac..5e16cedf 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1889,7 +1889,7 @@ impl From for Dynamic { fn from(value: Decimal) -> Self { Self(Union::Decimal( Box::new(value.into()), - DEFAULT_TAG, + DEFAULT_TAG_VALUE, ReadWrite, )) } diff --git a/src/fn_native.rs b/src/fn_native.rs index 6ca637d5..4f91cec5 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -109,14 +109,14 @@ impl<'a> NativeCallContext<'a> { pub const fn new_with_all_fields( engine: &'a Engine, fn_name: &'a str, - source: &'a Option<&str>, + source: Option<&'a str>, imports: &'a Imports, lib: &'a [&Module], ) -> Self { Self { engine, fn_name, - source: source.clone(), + source, mods: Some(imports), lib, } diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 985e2dda..31e04aee 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -146,7 +146,11 @@ impl From<&crate::module::FuncInfo> for FnMetadata { } #[cfg(not(feature = "no_function"))] { - info.func.get_fn_def().comments.to_vec() + info.func + .get_script_fn_def() + .expect("never fails because the function is scripted") + .comments + .to_vec() } } else { Default::default() From 8f4a582f88d1d495a09db279a8f0543cf1b62e77 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 28 Jun 2021 22:06:46 +0800 Subject: [PATCH 10/25] Remove free list of empty function resolution caches. --- src/engine.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index a795a1ee..6b79dc70 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -631,7 +631,7 @@ pub struct State { #[cfg(not(feature = "no_module"))] pub resolver: Option>, /// Function resolution cache and free list. - fn_resolution_caches: (StaticVec, Vec), + fn_resolution_caches: StaticVec, } impl State { @@ -645,11 +645,11 @@ impl State { #[inline(always)] #[must_use] pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { - if self.fn_resolution_caches.0.is_empty() { + if self.fn_resolution_caches.is_empty() { // Push a new function resolution cache if the stack is empty - self.fn_resolution_caches.0.push(BTreeMap::new()); + self.fn_resolution_caches.push(Default::default()); } - self.fn_resolution_caches.0.last_mut().expect( + self.fn_resolution_caches.last_mut().expect( "never fails because there is at least one function resolution cache by this point", ) } @@ -657,9 +657,7 @@ impl State { #[allow(dead_code)] #[inline(always)] pub fn push_fn_resolution_cache(&mut self) { - self.fn_resolution_caches - .0 - .push(self.fn_resolution_caches.1.pop().unwrap_or_default()); + self.fn_resolution_caches.push(Default::default()); } /// Remove the current function resolution cache from the stack and make the last one current. /// @@ -668,13 +666,9 @@ impl State { /// Panics if there are no more function resolution cache in the stack. #[inline(always)] pub fn pop_fn_resolution_cache(&mut self) { - let mut cache = self - .fn_resolution_caches - .0 + self.fn_resolution_caches .pop() .expect("there should be at least one function resolution cache"); - cache.clear(); - self.fn_resolution_caches.1.push(cache); } } From 27c126d2f02a1f310ce9cb1394ee2fb97bb69853 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 17:42:03 +0800 Subject: [PATCH 11/25] Make on_print/on_debug Option. --- src/engine.rs | 19 +++++++++---------- src/engine_api.rs | 4 ++-- src/fn_call.rs | 42 +++++++++++++++++++++++++----------------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 6b79dc70..b9bb7825 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -850,9 +850,9 @@ pub struct Engine { pub(crate) resolve_var: Option, /// Callback closure for implementing the `print` command. - pub(crate) print: OnPrintCallback, + pub(crate) print: Option, /// Callback closure for implementing the `debug` command. - pub(crate) debug: OnDebugCallback, + pub(crate) debug: Option, /// Callback closure for progress reporting. #[cfg(not(feature = "unchecked"))] pub(crate) progress: Option, @@ -954,8 +954,8 @@ impl Engine { resolve_var: None, // default print/debug implementations - print: Box::new(default_print), - debug: Box::new(default_debug), + print: Some(Box::new(default_print)), + debug: Some(Box::new(default_debug)), // progress callback #[cfg(not(feature = "unchecked"))] @@ -1010,8 +1010,8 @@ impl Engine { resolve_var: None, - print: Box::new(|_| {}), - debug: Box::new(|_, _, _| {}), + print: None, + debug: None, #[cfg(not(feature = "unchecked"))] progress: None, @@ -2755,10 +2755,9 @@ impl Engine { err_map.insert("message".into(), err.to_string().into()); - state - .source - .as_ref() - .map(|source| err_map.insert("source".into(), source.into())); + if let Some(ref source) = state.source { + err_map.insert("source".into(), source.as_str().into()); + } if err_pos.is_none() { // No position info diff --git a/src/engine_api.rs b/src/engine_api.rs index c012e3b7..6a1b350b 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -2219,7 +2219,7 @@ impl Engine { /// ``` #[inline(always)] pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self { - self.print = Box::new(callback); + self.print = Some(Box::new(callback)); self } /// Override default action of `debug` (print to stdout using [`println!`]) @@ -2258,7 +2258,7 @@ impl Engine { &mut self, callback: impl Fn(&str, Option<&str>, Position) + SendSync + 'static, ) -> &mut Self { - self.debug = Box::new(callback); + self.debug = Some(Box::new(callback)); self } } diff --git a/src/fn_call.rs b/src/fn_call.rs index b0693fad..d4251efb 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -319,25 +319,33 @@ impl Engine { // See if the function match print/debug (which requires special processing) return Ok(match name { KEYWORD_PRINT => { - let text = result.as_immutable_string().map_err(|typ| { - EvalAltResult::ErrorMismatchOutputType( - self.map_type_name(type_name::()).into(), - typ.into(), - pos, - ) - })?; - ((self.print)(&text).into(), false) + if let Some(ref print) = self.print { + let text = result.as_immutable_string().map_err(|typ| { + EvalAltResult::ErrorMismatchOutputType( + self.map_type_name(type_name::()).into(), + typ.into(), + pos, + ) + })?; + (print(&text).into(), false) + } else { + (Dynamic::UNIT, false) + } } KEYWORD_DEBUG => { - let text = result.as_immutable_string().map_err(|typ| { - EvalAltResult::ErrorMismatchOutputType( - self.map_type_name(type_name::()).into(), - typ.into(), - pos, - ) - })?; - let source = state.source.as_ref().map(|s| s.as_str()); - ((self.debug)(&text, source, pos).into(), false) + if let Some(ref debug) = self.debug { + let text = result.as_immutable_string().map_err(|typ| { + EvalAltResult::ErrorMismatchOutputType( + self.map_type_name(type_name::()).into(), + typ.into(), + pos, + ) + })?; + let source = state.source.as_ref().map(|s| s.as_str()); + (debug(&text, source, pos).into(), false) + } else { + (Dynamic::UNIT, false) + } } _ => (result, func.is_method()), }); From 4143ef1e3f9e781e573b20941f0a0e79b2b0fbb8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 17:47:31 +0800 Subject: [PATCH 12/25] Simplify Engine creation. --- src/engine.rs | 120 +++++++++++++++++++------------------------------- 1 file changed, 45 insertions(+), 75 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index b9bb7825..99d9ff8a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -718,6 +718,26 @@ pub struct Limits { pub max_map_size: Option, } +impl Default for Limits { + fn default() -> Self { + Self { + #[cfg(not(feature = "no_function"))] + max_call_stack_depth: MAX_CALL_STACK_DEPTH, + max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH), + #[cfg(not(feature = "no_function"))] + max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH), + max_operations: None, + #[cfg(not(feature = "no_module"))] + max_modules: usize::MAX, + max_string_size: None, + #[cfg(not(feature = "no_index"))] + max_array_size: None, + #[cfg(not(feature = "no_object"))] + max_map_size: None, + } + } +} + /// Context of a script evaluation process. #[derive(Debug)] pub struct EvalContext<'a, 'x, 'px, 'm, 's, 't, 'pt> { @@ -903,86 +923,50 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool { fn_name.starts_with(FN_ANONYMOUS) } -/// Print to stdout +/// Print to `stdout` #[inline(always)] -fn default_print(_s: &str) { +#[allow(unused_variables)] +fn print_to_stdout(s: &str) { #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - println!("{}", _s); + println!("{}", s); } -/// Debug to stdout +/// Debug to `stdout` #[inline(always)] -fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) { +#[allow(unused_variables)] +fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) { #[cfg(not(feature = "no_std"))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - if let Some(source) = _source { - println!("{}{:?} | {}", source, _pos, _s); - } else if _pos.is_none() { - println!("{}", _s); + if let Some(source) = source { + println!("{}{:?} | {}", source, pos, s); + } else if pos.is_none() { + println!("{}", s); } else { - println!("{:?} | {}", _pos, _s); + println!("{:?} | {}", pos, s); } } impl Engine { - /// Create a new [`Engine`] + /// Create a new [`Engine`]. #[inline] #[must_use] pub fn new() -> Self { // Create the new scripting Engine - let mut engine = Self { - global_namespace: Default::default(), - global_modules: Default::default(), - global_sub_modules: Default::default(), + let mut engine = Self::new_raw(); - #[cfg(not(feature = "no_module"))] - #[cfg(not(feature = "no_std"))] - #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] - module_resolver: Some(Box::new(crate::module::resolvers::FileModuleResolver::new())), - #[cfg(not(feature = "no_module"))] - #[cfg(any(feature = "no_std", target_arch = "wasm32",))] - module_resolver: None, + #[cfg(not(feature = "no_module"))] + #[cfg(not(feature = "no_std"))] + #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] + { + engine.module_resolver = + Some(Box::new(crate::module::resolvers::FileModuleResolver::new())); + } - type_names: Default::default(), - empty_string: Default::default(), - disabled_symbols: Default::default(), - custom_keywords: Default::default(), - custom_syntax: Default::default(), + // default print/debug implementations + engine.print = Some(Box::new(print_to_stdout)); + engine.debug = Some(Box::new(debug_to_stdout)); - // variable resolver - resolve_var: None, - - // default print/debug implementations - print: Some(Box::new(default_print)), - debug: Some(Box::new(default_debug)), - - // progress callback - #[cfg(not(feature = "unchecked"))] - progress: None, - - // optimization level - optimization_level: Default::default(), - - #[cfg(not(feature = "unchecked"))] - limits: Limits { - #[cfg(not(feature = "no_function"))] - max_call_stack_depth: MAX_CALL_STACK_DEPTH, - max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH), - #[cfg(not(feature = "no_function"))] - max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH), - max_operations: None, - #[cfg(not(feature = "no_module"))] - max_modules: usize::MAX, - max_string_size: None, - #[cfg(not(feature = "no_index"))] - max_array_size: None, - #[cfg(not(feature = "no_object"))] - max_map_size: None, - }, - }; - - engine.global_namespace.internal = true; engine.register_global_module(StandardPackage::new().as_shared_module()); engine @@ -1019,21 +1003,7 @@ impl Engine { optimization_level: Default::default(), #[cfg(not(feature = "unchecked"))] - limits: Limits { - #[cfg(not(feature = "no_function"))] - max_call_stack_depth: MAX_CALL_STACK_DEPTH, - max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH), - #[cfg(not(feature = "no_function"))] - max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH), - max_operations: None, - #[cfg(not(feature = "no_module"))] - max_modules: usize::MAX, - max_string_size: None, - #[cfg(not(feature = "no_index"))] - max_array_size: None, - #[cfg(not(feature = "no_object"))] - max_map_size: None, - }, + limits: Default::default(), }; engine.global_namespace.internal = true; From 08828dd8c1294c9100e22392e82e3b8dd34942cd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 18:25:20 +0800 Subject: [PATCH 13/25] Use .into() instead of Box::new() --- src/ast.rs | 2 +- src/custom_syntax.rs | 5 +-- src/dynamic.rs | 25 +++---------- src/error_parsing.rs | 2 +- src/fn_call.rs | 5 +-- src/module/mod.rs | 10 +++--- src/optimize.rs | 6 ++-- src/parse.rs | 84 ++++++++++++++++++++++---------------------- 8 files changed, 64 insertions(+), 75 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 7d6309f8..2c6c1297 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1924,7 +1924,7 @@ impl Expr { #[cfg(not(feature = "no_float"))] Union::Float(f, _, _) => Self::FloatConstant(f, pos), - _ => Self::DynamicConstant(Box::new(value), pos), + _ => Self::DynamicConstant(value.into(), pos), } } /// Is the expression a simple variable access? diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 3b3b18a0..adcd1a12 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -318,11 +318,12 @@ impl Engine { ) -> &mut Self { self.custom_syntax.insert( key.into(), - Box::new(CustomSyntax { + CustomSyntax { parse: Box::new(parse), func: (Box::new(func) as Box).into(), scope_changed, - }), + } + .into(), ); self } diff --git a/src/dynamic.rs b/src/dynamic.rs index 5e16cedf..3ae9aae4 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1887,11 +1887,7 @@ impl From> for Dynamic { impl From for Dynamic { #[inline(always)] fn from(value: Decimal) -> Self { - Self(Union::Decimal( - Box::new(value.into()), - DEFAULT_TAG_VALUE, - ReadWrite, - )) + Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } impl From for Dynamic { @@ -1912,19 +1908,12 @@ impl From<&ImmutableString> for Dynamic { value.clone().into() } } -#[cfg(not(feature = "no_smartstring"))] -impl From<&crate::Identifier> for Dynamic { - #[inline(always)] - fn from(value: &crate::Identifier) -> Self { - value.to_string().into() - } -} #[cfg(not(feature = "no_index"))] impl Dynamic { /// Create a [`Dynamic`] from an [`Array`]. #[inline(always)] pub(crate) fn from_array(array: Array) -> Self { - Self(Union::Array(Box::new(array), DEFAULT_TAG_VALUE, ReadWrite)) + Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_index"))] @@ -1965,7 +1954,7 @@ impl Dynamic { /// Create a [`Dynamic`] from a [`Map`]. #[inline(always)] pub(crate) fn from_map(map: Map) -> Self { - Self(Union::Map(Box::new(map), DEFAULT_TAG_VALUE, ReadWrite)) + Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_object"))] @@ -2008,7 +1997,7 @@ impl, T: Variant + Clone> From for Dynamic { #[inline(always)] fn from(value: FnPtr) -> Self { - Self(Union::FnPtr(Box::new(value), DEFAULT_TAG_VALUE, ReadWrite)) + Self(Union::FnPtr(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } impl From> for Dynamic { @@ -2021,11 +2010,7 @@ impl From> for Dynamic { impl From for Dynamic { #[inline(always)] fn from(value: Instant) -> Self { - Self(Union::TimeStamp( - Box::new(value), - DEFAULT_TAG_VALUE, - ReadWrite, - )) + Self(Union::TimeStamp(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) } } #[cfg(not(feature = "no_closure"))] diff --git a/src/error_parsing.rs b/src/error_parsing.rs index 84e9392d..5b797baa 100644 --- a/src/error_parsing.rs +++ b/src/error_parsing.rs @@ -183,7 +183,7 @@ impl ParseErrorType { #[inline(always)] #[must_use] pub(crate) fn into_err(self, pos: Position) -> ParseError { - ParseError(Box::new(self), pos) + ParseError(self.into(), pos) } } diff --git a/src/fn_call.rs b/src/fn_call.rs index d4251efb..cff7f146 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1206,7 +1206,7 @@ impl Engine { } return result.map_err(|err| { - Box::new(EvalAltResult::ErrorInFunctionCall( + EvalAltResult::ErrorInFunctionCall( KEYWORD_EVAL.to_string(), state .source @@ -1215,7 +1215,8 @@ impl Engine { .unwrap_or_default(), err, pos, - )) + ) + .into() }); } diff --git a/src/module/mod.rs b/src/module/mod.rs index f23b883f..dbb5535b 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -479,7 +479,7 @@ impl Module { param_names.push("Dynamic".into()); self.functions.insert( hash_script, - Box::new(FuncInfo { + FuncInfo { name: fn_def.name.clone(), namespace: FnNamespace::Internal, access: fn_def.access, @@ -488,7 +488,8 @@ impl Module { #[cfg(feature = "metadata")] param_names, func: Into::::into(fn_def).into(), - }), + } + .into(), ); self.indexed = false; self.contains_indexed_global_functions = false; @@ -709,7 +710,7 @@ impl Module { self.functions.insert( hash_fn, - Box::new(FuncInfo { + FuncInfo { name: self.identifiers.get(name), namespace, access, @@ -718,7 +719,8 @@ impl Module { #[cfg(feature = "metadata")] param_names, func: func.into(), - }), + } + .into(), ); self.indexed = false; diff --git a/src/optimize.rs b/src/optimize.rs index 1282f92a..87179054 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -413,7 +413,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { *stmt = if preserve_result { // -> { expr, Noop } - Stmt::Block(Box::new([Stmt::Expr(expr), Stmt::Noop(pos)]), pos) + Stmt::Block([Stmt::Expr(expr), Stmt::Noop(pos)].into(), pos) } else { // -> expr Stmt::Expr(expr) @@ -843,7 +843,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { #[cfg(not(feature = "no_index"))] Expr::Array(_, _) if expr.is_constant() => { state.set_dirty(); - *expr = Expr::DynamicConstant(Box::new(expr.get_literal_value().unwrap()), expr.position()); + *expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position()); } // [ items .. ] #[cfg(not(feature = "no_index"))] @@ -852,7 +852,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { #[cfg(not(feature = "no_object"))] Expr::Map(_, _) if expr.is_constant() => { state.set_dirty(); - *expr = Expr::DynamicConstant(Box::new(expr.get_literal_value().unwrap()), expr.position()); + *expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position()); } // #{ key:value, .. } #[cfg(not(feature = "no_object"))] diff --git a/src/parse.rs b/src/parse.rs index 13a48ce7..ca3d601d 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -635,13 +635,13 @@ fn parse_index_chain( parse_index_chain(input, state, lib, idx_expr, settings.level_up())?; // Indexing binds to right Ok(Expr::Index( - Box::new(BinaryExpr { lhs, rhs: idx_expr }), + BinaryExpr { lhs, rhs: idx_expr }.into(), prev_pos, )) } // Otherwise terminate the indexing chain _ => Ok(Expr::Index( - Box::new(BinaryExpr { lhs, rhs: idx_expr }), + BinaryExpr { lhs, rhs: idx_expr }.into(), settings.pos, )), } @@ -841,7 +841,7 @@ fn parse_map_literal( map.shrink_to_fit(); - Ok(Expr::Map(Box::new((map, template)), settings.pos)) + Ok(Expr::Map((map, template).into(), settings.pos)) } /// Parse a switch expression. @@ -951,7 +951,7 @@ fn parse_switch( let need_comma = !stmt.is_self_terminated(); def_stmt = if let Some(hash) = hash { - table.insert(hash, Box::new((condition, stmt.into()))); + table.insert(hash, (condition, stmt.into()).into()); None } else { Some(stmt.into()) @@ -980,12 +980,11 @@ fn parse_switch( } } + let def_stmt_block = def_stmt.unwrap_or_else(|| Stmt::Noop(Position::NONE).into()); + Ok(Stmt::Switch( item, - Box::new(( - table, - def_stmt.unwrap_or_else(|| Stmt::Noop(Position::NONE).into()), - )), + (table, def_stmt_block).into(), settings.pos, )) } @@ -1162,7 +1161,7 @@ fn parse_primary( Expr::Variable( None, settings.pos, - Box::new((None, None, state.get_identifier(s))), + (None, None, state.get_identifier(s)).into(), ) } // Namespace qualification @@ -1176,7 +1175,7 @@ fn parse_primary( Expr::Variable( None, settings.pos, - Box::new((None, None, state.get_identifier(s))), + (None, None, state.get_identifier(s)).into(), ) } // Normal variable access @@ -1192,7 +1191,7 @@ fn parse_primary( Expr::Variable( short_index, settings.pos, - Box::new((index, None, state.get_identifier(s))), + (index, None, state.get_identifier(s)).into(), ) } } @@ -1210,13 +1209,13 @@ fn parse_primary( Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable( None, settings.pos, - Box::new((None, None, state.get_identifier(s))), + (None, None, state.get_identifier(s)).into(), ), // Access to `this` as a variable is OK within a function scope _ if s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable( None, settings.pos, - Box::new((None, None, state.get_identifier(s))), + (None, None, state.get_identifier(s)).into(), ), // Cannot access to `this` as a variable not in a function scope _ if s == KEYWORD_THIS => { @@ -1307,7 +1306,7 @@ fn parse_primary( Expr::Variable( None, pos2, - Box::new((None, namespace, state.get_identifier(id2))), + (None, namespace, state.get_identifier(id2)).into(), ) } // Indexing @@ -1500,7 +1499,7 @@ fn make_assignment_stmt<'a>( } // var (non-indexed) = rhs Expr::Variable(None, _, ref x) if x.0.is_none() => { - Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) + Ok(Stmt::Assignment((lhs, op_info, rhs).into(), op_pos)) } // var (indexed) = rhs Expr::Variable(i, var_pos, ref x) => { @@ -1516,9 +1515,7 @@ fn make_assignment_stmt<'a>( |n| n.get() as usize, ); match state.stack[state.stack.len() - index].1 { - AccessMode::ReadWrite => { - Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) - } + AccessMode::ReadWrite => Ok(Stmt::Assignment((lhs, op_info, rhs).into(), op_pos)), // Constant values cannot be assigned to AccessMode::ReadOnly => { Err(PERR::AssignmentToConstant(name.to_string()).into_err(var_pos)) @@ -1531,7 +1528,7 @@ fn make_assignment_stmt<'a>( None => match x.lhs { // var[???] = rhs, var.??? = rhs Expr::Variable(_, _, _) => { - Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) + Ok(Stmt::Assignment((lhs, op_info, rhs).into(), op_pos)) } // expr[???] = rhs, expr.??? = rhs ref expr => { @@ -1608,7 +1605,7 @@ fn make_dot_expr( (state.get_identifier(ident).into(), var_pos), ))); - Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) } // lhs.module::id - syntax error (_, Expr::Variable(_, _, x)) if x.1.is_some() => { @@ -1616,20 +1613,19 @@ fn make_dot_expr( .into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos)) } // lhs.prop - (lhs, prop @ Expr::Property(_)) => { - Expr::Dot(Box::new(BinaryExpr { lhs, rhs: prop }), op_pos) - } + (lhs, prop @ Expr::Property(_)) => Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), op_pos), // lhs.dot_lhs.dot_rhs (lhs, Expr::Dot(x, pos)) => match x.lhs { Expr::Variable(_, _, _) | Expr::Property(_) => { let rhs = Expr::Dot( - Box::new(BinaryExpr { + BinaryExpr { lhs: x.lhs.into_property(state), rhs: x.rhs, - }), + } + .into(), pos, ); - Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) } Expr::FnCall(mut func, func_pos) => { // Recalculate hash @@ -1639,26 +1635,28 @@ fn make_dot_expr( ); let rhs = Expr::Dot( - Box::new(BinaryExpr { + BinaryExpr { lhs: Expr::FnCall(func, func_pos), rhs: x.rhs, - }), + } + .into(), pos, ); - Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) } _ => unreachable!("invalid dot expression: {:?}", x.lhs), }, // lhs.idx_lhs[idx_rhs] (lhs, Expr::Index(x, pos)) => { let rhs = Expr::Index( - Box::new(BinaryExpr { + BinaryExpr { lhs: x.lhs.into_property(state), rhs: x.rhs, - }), + } + .into(), pos, ); - Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) } // lhs.nnn::func(...) (_, Expr::FnCall(x, _)) if x.is_qualified() => { @@ -1694,7 +1692,7 @@ fn make_dot_expr( calc_fn_hash(&func.name, func.args.len() + 1), ); let rhs = Expr::FnCall(func, func_pos); - Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) + Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) } // lhs.rhs (_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())), @@ -1818,10 +1816,11 @@ fn parse_binary_op( .pop() .expect("never fails because `||` has two arguments"); Expr::Or( - Box::new(BinaryExpr { + BinaryExpr { lhs: current_lhs, rhs, - }), + } + .into(), pos, ) } @@ -1833,10 +1832,11 @@ fn parse_binary_op( .pop() .expect("never fails because `&&` has two arguments"); Expr::And( - Box::new(BinaryExpr { + BinaryExpr { lhs: current_lhs, rhs, - }), + } + .into(), pos, ) } @@ -1927,7 +1927,7 @@ fn parse_custom_syntax( let name = state.get_identifier(name); segments.push(name.clone().into()); tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_IDENT)); - keywords.push(Expr::Variable(None, pos, Box::new((None, None, name)))); + keywords.push(Expr::Variable(None, pos, (None, None, name).into())); } CUSTOM_SYNTAX_MARKER_EXPR => { keywords.push(parse_expr(input, state, lib, settings)?); @@ -2145,7 +2145,7 @@ fn parse_if( Ok(Stmt::If( guard, - Box::new((if_body.into(), else_body.into())), + (if_body.into(), else_body.into()).into(), settings.pos, )) } @@ -2836,7 +2836,7 @@ fn parse_try_catch( let catch_body = parse_block(input, state, lib, settings.level_up())?; Ok(Stmt::TryCatch( - Box::new((body.into(), var_def, catch_body.into())), + (body.into(), var_def, catch_body.into()).into(), settings.pos, )) } @@ -2967,7 +2967,7 @@ fn make_curry_from_externals( externals .iter() .cloned() - .map(|x| Expr::Variable(None, Position::NONE, Box::new((None, None, x)))), + .map(|x| Expr::Variable(None, Position::NONE, (None, None, x).into())), ); let expr = FnCallExpr { @@ -2986,7 +2986,7 @@ fn make_curry_from_externals( let mut statements = StaticVec::with_capacity(externals.len() + 1); statements.extend(externals.into_iter().map(Stmt::Share)); statements.push(Stmt::Expr(expr)); - Expr::Stmt(Box::new(StmtBlock::new(statements, pos))) + Expr::Stmt(StmtBlock::new(statements, pos).into()) } /// Parse an anonymous function definition. From 0346bb874bde174b3cd9ffaeb3512c893bb1538b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 18:41:03 +0800 Subject: [PATCH 14/25] Make Expr::position const. --- src/ast.rs | 35 ++++++++++++----------------------- src/custom_syntax.rs | 4 ++-- src/engine.rs | 4 ++-- src/optimize.rs | 8 ++++---- src/parse.rs | 8 ++++---- 5 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 2c6c1297..e7c92ae8 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1052,7 +1052,7 @@ impl Stmt { } /// Get the [position][Position] of this statement. #[must_use] - pub fn position(&self) -> Position { + pub const fn position(&self) -> Position { match self { Self::Noop(pos) | Self::Continue(pos) @@ -1727,7 +1727,7 @@ pub enum Expr { /// [String][ImmutableString] constant. StringConstant(ImmutableString, Position), /// An interpolated [string][ImmutableString]. - InterpolatedString(Box>), + InterpolatedString(Box>, Position), /// [ expr, ... ] Array(Box>, Position), /// #{ name:expr, ... } @@ -1798,7 +1798,7 @@ impl fmt::Debug for Expr { Self::StringConstant(value, _) => write!(f, "{:?}", value), Self::Unit(_) => f.write_str("()"), - Self::InterpolatedString(x) => { + Self::InterpolatedString(x, _) => { f.write_str("InterpolatedString")?; return f.debug_list().entries(x.iter()).finish(); } @@ -1948,7 +1948,7 @@ impl Expr { /// Get the [position][Position] of the expression. #[inline] #[must_use] - pub fn position(&self) -> Position { + pub const fn position(&self) -> Position { match self { #[cfg(not(feature = "no_float"))] Self::FloatConstant(_, pos) => *pos, @@ -1964,14 +1964,8 @@ impl Expr { | Self::Variable(_, pos, _) | Self::Stack(_, pos) | Self::FnCall(_, pos) - | Self::Custom(_, pos) => *pos, - - Self::InterpolatedString(x) => x - .first() - .expect( - "never fails because an interpolated string always contains at least one item", - ) - .position(), + | Self::Custom(_, pos) + | Self::InterpolatedString(_, pos) => *pos, Self::Property(x) => (x.2).1, Self::Stmt(x) => x.1, @@ -2003,13 +1997,8 @@ impl Expr { | Self::Variable(_, pos, _) | Self::Stack(_, pos) | Self::FnCall(_, pos) - | Self::Custom(_, pos) => *pos = new_pos, - - Self::InterpolatedString(x) => { - x.first_mut() - .expect("never fails because an interpolated string always contains at least one item") - .set_position(new_pos); - } + | Self::Custom(_, pos) + | Self::InterpolatedString(_, pos) => *pos = new_pos, Self::Property(x) => (x.2).1 = new_pos, Self::Stmt(x) => x.1 = new_pos, @@ -2024,7 +2013,7 @@ impl Expr { #[must_use] pub fn is_pure(&self) -> bool { match self { - Self::InterpolatedString(x) | Self::Array(x, _) => x.iter().all(Self::is_pure), + Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_pure), Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure), @@ -2062,7 +2051,7 @@ impl Expr { | Self::Unit(_) | Self::Stack(_, _) => true, - Self::InterpolatedString(x) | Self::Array(x, _) => x.iter().all(Self::is_constant), + Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_constant), Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant), @@ -2092,7 +2081,7 @@ impl Expr { Self::IntegerConstant(_, _) | Self::StringConstant(_, _) - | Self::InterpolatedString(_) + | Self::InterpolatedString(_, _) | Self::FnCall(_, _) | Self::Stmt(_) | Self::Dot(_, _) @@ -2147,7 +2136,7 @@ impl Expr { } } } - Self::InterpolatedString(x) | Self::Array(x, _) => { + Self::InterpolatedString(x, _) | Self::Array(x, _) => { for e in x.as_ref() { if !e.walk(path, on_node) { return false; diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index adcd1a12..766720a7 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -67,13 +67,13 @@ impl Expression<'_> { /// Get the expression. #[inline(always)] #[must_use] - pub(crate) fn expr(&self) -> &Expr { + pub(crate) const fn expr(&self) -> &Expr { &self.0 } /// Get the position of this expression. #[inline(always)] #[must_use] - pub fn position(&self) -> Position { + pub const fn position(&self) -> Position { self.0.position() } /// Get the value of this expression if it is a literal constant. diff --git a/src/engine.rs b/src/engine.rs index 99d9ff8a..45b63da4 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2009,8 +2009,8 @@ impl Engine { } // `... ${...} ...` - Expr::InterpolatedString(x) => { - let mut pos = expr.position(); + Expr::InterpolatedString(x, pos) => { + let mut pos = *pos; let mut result: Dynamic = self.empty_string.clone().into(); for expr in x.iter() { diff --git a/src/optimize.rs b/src/optimize.rs index 87179054..60a3596b 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -787,17 +787,17 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { #[cfg(not(feature = "no_index"))] Expr::Index(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); } // `` - Expr::InterpolatedString(x) if x.is_empty() => { + Expr::InterpolatedString(x, pos) if x.is_empty() => { state.set_dirty(); - *expr = Expr::StringConstant(state.engine.empty_string.clone(), Position::NONE); + *expr = Expr::StringConstant(state.engine.empty_string.clone(), *pos); } // `...` - Expr::InterpolatedString(x) if x.len() == 1 && matches!(x[0], Expr::StringConstant(_, _)) => { + Expr::InterpolatedString(x, _) if x.len() == 1 && matches!(x[0], Expr::StringConstant(_, _)) => { state.set_dirty(); *expr = mem::take(&mut x[0]); } // `... ${ ... } ...` - Expr::InterpolatedString(x) => { + Expr::InterpolatedString(x, _) => { let mut n = 0; // Merge consecutive strings diff --git a/src/parse.rs b/src/parse.rs index ca3d601d..7786d489 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -515,7 +515,7 @@ fn parse_index_chain( Expr::IntegerConstant(_, _) | Expr::Array(_, _) | Expr::StringConstant(_, _) - | Expr::InterpolatedString(_) => (), + | Expr::InterpolatedString(_, _) => (), Expr::Map(_, _) => { return Err(PERR::MalformedIndexExpr( @@ -547,10 +547,10 @@ fn parse_index_chain( }, // lhs[string] - Expr::StringConstant(_, _) | Expr::InterpolatedString(_) => match lhs { + Expr::StringConstant(_, _) | Expr::InterpolatedString(_, _) => match lhs { Expr::Map(_, _) => (), - Expr::Array(_, _) | Expr::StringConstant(_, _) | Expr::InterpolatedString(_) => { + Expr::Array(_, _) | Expr::StringConstant(_, _) | Expr::InterpolatedString(_, _) => { return Err(PERR::MalformedIndexExpr( "Array or string expects numeric index, not a string".into(), ) @@ -1132,7 +1132,7 @@ fn parse_primary( } segments.shrink_to_fit(); - Expr::InterpolatedString(segments.into()) + Expr::InterpolatedString(segments.into(), settings.pos) } // Array literal From d146de4ff987f23689e7940e16b82a447b3c7ee3 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 21:47:27 +0800 Subject: [PATCH 15/25] Make FnPtr::fn_name_raw const. --- src/fn_call.rs | 4 ++-- src/fn_ptr.rs | 4 ++-- src/immutable_string.rs | 13 +++++++++++++ src/packages/fn_basic.rs | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/fn_call.rs b/src/fn_call.rs index cff7f146..36594362 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -951,7 +951,7 @@ impl Engine { fn_ptr.clone() } else { FnPtr::new_unchecked( - fn_ptr.get_fn_name().clone(), + fn_ptr.fn_name_raw().clone(), fn_ptr .curry() .iter() @@ -981,7 +981,7 @@ impl Engine { if let Some(val) = map.get(fn_name) { if let Some(fn_ptr) = val.read_lock::() { // Remap the function name - _redirected = fn_ptr.get_fn_name().clone(); + _redirected = fn_ptr.fn_name_raw().clone(); fn_name = &_redirected; // Add curried arguments if fn_ptr.is_curried() { diff --git a/src/fn_ptr.rs b/src/fn_ptr.rs index 77544d38..5c22208d 100644 --- a/src/fn_ptr.rs +++ b/src/fn_ptr.rs @@ -33,12 +33,12 @@ impl FnPtr { #[inline(always)] #[must_use] pub fn fn_name(&self) -> &str { - self.get_fn_name().as_ref() + self.fn_name_raw().as_ref() } /// Get the name of the function. #[inline(always)] #[must_use] - pub(crate) const fn get_fn_name(&self) -> &Identifier { + pub(crate) const fn fn_name_raw(&self) -> &Identifier { &self.0 } /// Get the underlying data of the function pointer. diff --git a/src/immutable_string.rs b/src/immutable_string.rs index 212ecc24..48c7e387 100644 --- a/src/immutable_string.rs +++ b/src/immutable_string.rs @@ -106,12 +106,25 @@ impl From for ImmutableString { } } #[cfg(not(feature = "no_smartstring"))] +impl From<&SmartString> for ImmutableString { + #[inline(always)] + fn from(value: &SmartString) -> Self { + Self(Into::::into(value.as_str()).into()) + } +} +#[cfg(not(feature = "no_smartstring"))] impl From for ImmutableString { #[inline(always)] fn from(value: SmartString) -> Self { Self(value.into()) } } +impl From<&ImmutableString> for SmartString { + #[inline(always)] + fn from(value: &ImmutableString) -> Self { + value.as_str().into() + } +} impl From for SmartString { #[inline(always)] fn from(mut value: ImmutableString) -> Self { diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index f88089c5..76e9d52d 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -11,7 +11,7 @@ def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, { mod fn_ptr_functions { #[rhai_fn(name = "name", get = "name", pure)] pub fn name(f: &mut FnPtr) -> ImmutableString { - f.get_fn_name().as_str().into() + f.fn_name_raw().into() } #[cfg(not(feature = "no_function"))] From 8b0299077b83ee6dfad759b3cb15b28015480bfa Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 21:47:55 +0800 Subject: [PATCH 16/25] Simplify FuncArgs::parse. --- src/fn_args.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/fn_args.rs b/src/fn_args.rs index e575ec19..6e6f4621 100644 --- a/src/fn_args.rs +++ b/src/fn_args.rs @@ -4,7 +4,7 @@ #![allow(non_snake_case)] use crate::dynamic::Variant; -use crate::{Dynamic, StaticVec}; +use crate::Dynamic; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -70,13 +70,10 @@ macro_rules! impl_args { impl<$($p: Variant + Clone),*> FuncArgs for ($($p,)*) { #[inline(always)] + #[allow(unused_variables)] fn parse>(self, container: &mut CONTAINER) { let ($($p,)*) = self; - - let mut _v = StaticVec::new(); - $(_v.push($p.into_dynamic());)* - - container.extend(_v.into_iter()); + $(container.extend(Some($p.into_dynamic()));)* } } From bd35999b75a771cfd84ff9d7f2d23505dd52a587 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 21:58:05 +0800 Subject: [PATCH 17/25] Remove unnecessary Box::new(). --- src/module/resolvers/file.rs | 26 ++++++++++++++------------ src/packages/iter_basic.rs | 9 +++++---- src/packages/lang_core.rs | 10 ++++++---- tests/syntax.rs | 10 +++------- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/module/resolvers/file.rs b/src/module/resolvers/file.rs index 25f196f2..441114f3 100644 --- a/src/module/resolvers/file.rs +++ b/src/module/resolvers/file.rs @@ -345,17 +345,19 @@ impl ModuleResolver for FileModuleResolver { let file_path = self.get_file_path(path, source_path); // Load the script file and compile it - match engine.compile_file(file_path).map_err(|err| match *err { - EvalAltResult::ErrorSystem(_, err) if err.is::() => { - Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos)) - } - _ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)), - }) { - Ok(mut ast) => { - ast.set_source(path); - Some(Ok(ast)) - } - err => Some(err), - } + Some( + engine + .compile_file(file_path) + .map(|mut ast| { + ast.set_source(path); + ast + }) + .map_err(|err| match *err { + EvalAltResult::ErrorSystem(_, err) if err.is::() => { + EvalAltResult::ErrorModuleNotFound(path.to_string(), pos).into() + } + _ => EvalAltResult::ErrorInModule(path.to_string(), err, pos).into(), + }), + ) } } diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index c4b51b1a..835030fe 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -27,10 +27,11 @@ where return EvalAltResult::ErrorInFunctionCall( "range".to_string(), Default::default(), - Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( "step value cannot be zero".to_string(), crate::Position::NONE, - )), + ) + .into(), crate::Position::NONE, ) .into(); @@ -314,7 +315,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { #[cfg(not(feature = "unchecked"))] if step == 0.0 { return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), - Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)), + EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), crate::Position::NONE, ).into(); } @@ -376,7 +377,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { #[cfg(not(feature = "unchecked"))] if step.is_zero() { return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), - Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)), + EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(), crate::Position::NONE, ).into(); } diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index e20f76db..26d4c5b0 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -14,7 +14,7 @@ mod core_functions { #[rhai_fn(name = "set_tag", set = "tag", return_raw)] pub fn set_tag(value: &mut Dynamic, tag: INT) -> Result<(), Box> { if tag < Tag::MIN as INT { - Err(Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!( "{} is too small to fit into a tag (must be between {} and {})", tag, @@ -22,9 +22,10 @@ mod core_functions { Tag::MAX ), Position::NONE, - ))) + ) + .into() } else if tag > Tag::MAX as INT { - Err(Box::new(EvalAltResult::ErrorArithmetic( + EvalAltResult::ErrorArithmetic( format!( "{} is too large to fit into a tag (must be between {} and {})", tag, @@ -32,7 +33,8 @@ mod core_functions { Tag::MAX ), Position::NONE, - ))) + ) + .into() } else { value.set_tag(tag as Tag); Ok(()) diff --git a/tests/syntax.rs b/tests/syntax.rs index a0ce0ab8..8c44ae4b 100644 --- a/tests/syntax.rs +++ b/tests/syntax.rs @@ -130,13 +130,9 @@ fn test_custom_syntax_raw() -> Result<(), Box> { 1 => Ok(Some("$ident$".into())), 2 => match stream[1].as_str() { "world" | "kitty" => Ok(None), - s => Err(ParseError( - Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol( - s.to_string(), - Default::default(), - ))), - Position::NONE, - )), + s => Err(LexError::ImproperSymbol(s.to_string(), Default::default()) + .into_err(Position::NONE) + .into()), }, _ => unreachable!(), }, From fc349f67f85265bee15b0983c86a031bc421a58f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 23:17:31 +0800 Subject: [PATCH 18/25] Make more functions const. --- src/custom_syntax.rs | 2 +- src/engine.rs | 16 ++++++++-------- src/engine_settings.rs | 4 ++-- src/optimize.rs | 2 +- src/parse.rs | 2 +- tests/syntax.rs | 2 +- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/custom_syntax.rs b/src/custom_syntax.rs index 766720a7..36c97003 100644 --- a/src/custom_syntax.rs +++ b/src/custom_syntax.rs @@ -127,7 +127,7 @@ impl Expression<'_> { } } -impl EvalContext<'_, '_, '_, '_, '_, '_, '_> { +impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> { /// Evaluate an [expression tree][Expression]. /// /// # WARNING - Low Level API diff --git a/src/engine.rs b/src/engine.rs index 45b63da4..7cd672bb 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -740,21 +740,21 @@ impl Default for Limits { /// Context of a script evaluation process. #[derive(Debug)] -pub struct EvalContext<'a, 'x, 'px, 'm, 's, 't, 'pt> { +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) lib: &'a [&'a Module], + 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> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, '_> { /// The current [`Engine`]. #[inline(always)] #[must_use] - pub fn engine(&self) -> &Engine { + pub const fn engine(&self) -> &Engine { self.engine } /// The current source. @@ -766,7 +766,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> { /// The current [`Scope`]. #[inline(always)] #[must_use] - pub fn scope(&self) -> &Scope { + pub const fn scope(&self) -> &Scope<'px> { self.scope } /// Mutable reference to the current [`Scope`]. @@ -788,7 +788,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> { #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn imports(&self) -> &Imports { + pub const fn imports(&self) -> &Imports { self.mods } /// Get an iterator over the namespaces containing definition of all script-defined functions. @@ -802,7 +802,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> { #[cfg(feature = "internals")] #[inline(always)] #[must_use] - pub fn namespaces(&self) -> &[&Module] { + pub const fn namespaces(&self) -> &[&Module] { self.lib } /// The current bound `this` pointer, if any. @@ -814,7 +814,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> { /// The current nesting level of function calls. #[inline(always)] #[must_use] - pub fn call_level(&self) -> usize { + pub const fn call_level(&self) -> usize { self.level } } diff --git a/src/engine_settings.rs b/src/engine_settings.rs index dbadafd9..e4176e21 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -29,7 +29,7 @@ impl Engine { #[cfg(not(feature = "no_optimize"))] #[inline(always)] #[must_use] - pub fn optimization_level(&self) -> crate::OptimizationLevel { + pub const fn optimization_level(&self) -> crate::OptimizationLevel { self.optimization_level } /// Set the maximum levels of function calls allowed for a script in order to avoid @@ -179,7 +179,7 @@ impl Engine { #[cfg(not(feature = "no_index"))] #[inline(always)] #[must_use] - pub fn max_array_size(&self) -> usize { + pub const fn max_array_size(&self) -> usize { if let Some(n) = self.limits.max_array_size { n.get() } else { diff --git a/src/optimize.rs b/src/optimize.rs index 60a3596b..7830efb7 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -68,7 +68,7 @@ impl<'a> State<'a> { ) -> Self { Self { changed: false, - variables: vec![], + variables: Vec::new(), propagate_constants: true, engine, lib, diff --git a/src/parse.rs b/src/parse.rs index 7786d489..0de53e8c 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -143,7 +143,7 @@ impl<'e> ParseState<'e> { /// /// Return `None` when the variable name is not found in the `stack`. #[inline(always)] - fn access_var(&mut self, name: &str, _pos: Position) -> Option { + pub fn access_var(&mut self, name: &str, _pos: Position) -> Option { let mut barrier = false; let index = self diff --git a/tests/syntax.rs b/tests/syntax.rs index 8c44ae4b..34c84115 100644 --- a/tests/syntax.rs +++ b/tests/syntax.rs @@ -1,4 +1,4 @@ -use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseError, ParseErrorType, Position, INT}; +use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseErrorType, Position, INT}; #[test] fn test_custom_syntax() -> Result<(), Box> { From 69352d74c28176087829c82c31c1a8672735f8a1 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 23:22:54 +0800 Subject: [PATCH 19/25] Optimize Scope layout. --- src/ast.rs | 2 +- src/scope.rs | 30 ++++++++++++++---------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index e7c92ae8..87af9821 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -2206,7 +2206,7 @@ mod tests { 96 } ); - assert_eq!(size_of::(), 160); + assert_eq!(size_of::(), 464); assert_eq!(size_of::(), 56); assert_eq!( size_of::(), diff --git a/src/scope.rs b/src/scope.rs index 2fe9bcfb..9fa87088 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -49,14 +49,14 @@ const SCOPE_ENTRIES_INLINED: usize = 8; // look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether. // // Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed. -// -// The alias is `Box`'ed because it occurs infrequently. #[derive(Debug, Clone, Hash)] pub struct Scope<'a> { /// Current value of the entry. values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, /// (Name, aliases) of the entry. - names: Vec<(Cow<'a, str>, Option>>)>, + names: smallvec::SmallVec< + [(Cow<'a, str>, Option>>); SCOPE_ENTRIES_INLINED], + >, } impl Default for Scope<'_> { @@ -253,7 +253,7 @@ impl<'a> Scope<'a> { access: AccessMode, mut value: Dynamic, ) -> &mut Self { - self.names.push((name.into(), None)); + self.names.push((name.into(), Default::default())); value.set_access_mode(access); self.values.push(value.into()); self @@ -436,20 +436,18 @@ impl<'a> Scope<'a> { #[cfg(not(feature = "no_module"))] #[inline(always)] pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self { - let entry = self + let (_, aliases) = self .names .get_mut(index) .expect("never fails unless the index is out of bounds"); - if entry.1.is_none() { - // Initialize the alias list if it is empty. - entry.1 = Some(Default::default()); - } - let list = entry - .1 - .as_mut() - .expect("never fails because the list is initialized"); - if !list.iter().any(|a| a == &alias) { - list.push(alias); + match aliases { + None => { + let mut list = StaticVec::new(); + list.push(alias); + *aliases = Some(list.into()); + } + Some(aliases) if !aliases.iter().any(|a| a == &alias) => aliases.push(alias), + Some(_) => (), } self } @@ -534,7 +532,7 @@ impl<'a, K: Into>> Extend<(K, Dynamic)> for Scope<'a> { #[inline(always)] fn extend>(&mut self, iter: T) { iter.into_iter().for_each(|(name, value)| { - self.names.push((name.into(), None)); + self.names.push((name.into(), Default::default())); self.values.push(value); }); } From c87645ba775d495513d5cb80cbfc0e973dd84ec4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 29 Jun 2021 23:29:05 +0800 Subject: [PATCH 20/25] Fix unchecked build. --- src/engine.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine.rs b/src/engine.rs index 7cd672bb..aadecc52 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -718,6 +718,7 @@ pub struct Limits { pub max_map_size: Option, } +#[cfg(not(feature = "unchecked"))] impl Default for Limits { fn default() -> Self { Self { From fc0256aff9469d427365d3dd2199c685fecb4145 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 30 Jun 2021 10:13:45 +0800 Subject: [PATCH 21/25] Refine ChainArgument. --- src/engine.rs | 87 +++++++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index aadecc52..fc6c1b44 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -274,50 +274,34 @@ enum ChainArgument { #[cfg(not(feature = "no_object"))] Property(Position), /// Arguments to a dot method call. + /// Wrapped values are the arguments plus the [position][Position] of the first argument. #[cfg(not(feature = "no_object"))] MethodCallArgs(StaticVec, Position), - /// Index value. + /// Index value and [position][Position]. #[cfg(not(feature = "no_index"))] IndexValue(Dynamic, Position), } #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] impl ChainArgument { - /// Return the `Dynamic` value. - /// - /// # Panics - /// - /// Panics if not `ChainArgument::IndexValue`. + /// Return the index value. #[inline(always)] #[cfg(not(feature = "no_index"))] #[must_use] - pub fn as_index_value(self) -> Dynamic { + pub fn as_index_value(self) -> Option { match self { - #[cfg(not(feature = "no_object"))] - Self::Property(_) | Self::MethodCallArgs(_, _) => { - panic!("expecting ChainArgument::IndexValue") - } - Self::IndexValue(value, _) => value, + Self::IndexValue(value, _) => Some(value), + _ => None, } } - /// Return the `StaticVec` value. - /// - /// # Panics - /// - /// Panics if not `ChainArgument::MethodCallArgs`. + /// Return the list of method call arguments. #[inline(always)] #[cfg(not(feature = "no_object"))] #[must_use] - pub fn as_fn_call_args(self) -> (StaticVec, Position) { + pub fn as_fn_call_args(self) -> Option<(StaticVec, Position)> { match self { - Self::Property(_) => { - panic!("expecting ChainArgument::MethodCallArgs") - } - #[cfg(not(feature = "no_index"))] - Self::IndexValue(_, _) => { - panic!("expecting ChainArgument::MethodCallArgs") - } - Self::MethodCallArgs(values, pos) => (values, pos), + Self::MethodCallArgs(values, pos) => Some((values, pos)), + _ => None, } } } @@ -1208,12 +1192,14 @@ impl Engine { #[cfg(not(feature = "no_index"))] ChainType::Index => { let pos = rhs.position(); + let idx_val = idx_val + .as_index_value() + .expect("never fails because `chain_type` is `ChainType::Index`"); match rhs { // xxx[idx].expr... | xxx[idx][expr]... Expr::Dot(x, x_pos) | Expr::Index(x, x_pos) => { let idx_pos = x.lhs.position(); - let idx_val = idx_val.as_index_value(); let obj_ptr = &mut self.get_indexed_mut( mods, state, lib, target, idx_val, idx_pos, false, true, level, )?; @@ -1229,7 +1215,6 @@ impl Engine { _ if new_val.is_some() => { let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("never fails because `new_val` is `Some`"); - let idx_val = idx_val.as_index_value(); let mut idx_val_for_setter = idx_val.clone(); let try_setter = match self.get_indexed_mut( @@ -1271,13 +1256,9 @@ impl Engine { Ok((Dynamic::UNIT, true)) } // xxx[rhs] - _ => { - let idx_val = idx_val.as_index_value(); - self.get_indexed_mut( - mods, state, lib, target, idx_val, pos, false, true, level, - ) - .map(|v| (v.take_or_clone(), false)) - } + _ => self + .get_indexed_mut(mods, state, lib, target, idx_val, pos, false, true, level) + .map(|v| (v.take_or_clone(), false)), } } @@ -1287,9 +1268,11 @@ impl Engine { // 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 args = &mut idx_val.as_fn_call_args(); + let call_args = &mut idx_val + .as_fn_call_args() + .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); self.make_method_call( - mods, state, lib, name, *hashes, target, args, *pos, level, + mods, state, lib, name, *hashes, target, call_args, *pos, level, ) } // xxx.fn_name(...) = ??? @@ -1450,9 +1433,11 @@ impl Engine { // {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 args = &mut idx_val.as_fn_call_args(); + let call_args = &mut idx_val + .as_fn_call_args() + .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); let (val, _) = self.make_method_call( - mods, state, lib, name, *hashes, target, args, pos, level, + mods, state, lib, name, *hashes, target, call_args, pos, level, )?; val.into() } @@ -1569,7 +1554,9 @@ impl Engine { Expr::FnCall(ref f, pos) if !f.is_qualified() => { let FnCallExpr { name, hashes, .. } = f.as_ref(); let rhs_chain = match_chain_type(rhs); - let args = &mut idx_val.as_fn_call_args(); + let args = &mut idx_val + .as_fn_call_args() + .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); let (mut val, _) = self.make_method_call( mods, state, lib, name, *hashes, target, args, pos, level, )?; @@ -1688,17 +1675,19 @@ impl Engine { args, constants, .. } = x.as_ref(); let mut arg_values = StaticVec::with_capacity(args.len()); + let mut first_arg_pos = Position::NONE; for index in 0..args.len() { - let (value, _) = self.get_arg_value( + let (value, pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, args, constants, index, )?; arg_values.push(value.flatten()); + if index == 0 { + first_arg_pos = pos + } } - let pos = x.args.get(0).map(Expr::position).unwrap_or_default(); - - idx_values.push((arg_values, pos).into()); + idx_values.push((arg_values, first_arg_pos).into()); } #[cfg(not(feature = "no_object"))] Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => { @@ -1730,17 +1719,19 @@ impl Engine { args, constants, .. } = x.as_ref(); let mut arg_values = StaticVec::with_capacity(args.len()); + let mut first_arg_pos = Position::NONE; for index in 0..args.len() { - let (value, _) = self.get_arg_value( + let (value, pos) = self.get_arg_value( scope, mods, state, lib, this_ptr, level, args, constants, index, )?; arg_values.push(value.flatten()); + if index == 0 { + first_arg_pos = pos; + } } - let pos = x.args.get(0).map(Expr::position).unwrap_or_default(); - - (arg_values, pos).into() + (arg_values, first_arg_pos).into() } #[cfg(not(feature = "no_object"))] Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => { From f033896fece922c4d290c3ce73de2bd0318459e9 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 30 Jun 2021 11:08:29 +0800 Subject: [PATCH 22/25] Add From and From for Dynamic. --- CHANGELOG.md | 3 ++- src/dynamic.rs | 33 +++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15cfc23c..d1c9fac5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Version 1.0.0 The official version `1.0`. -Almost the same version as `0.20.3` but with deprecated API removed. +Almost the same version as `0.20.3` but with deprecated API's removed. Breaking changes ---------------- @@ -18,6 +18,7 @@ Enhancements ------------ * New methods `is_odd`, `is_even` for integers, and `is_zero` for all numbers. +* `From` and `From` are added for `Dynamic`, which create object maps with `()` values. Version 0.20.3 diff --git a/src/dynamic.rs b/src/dynamic.rs index 3ae9aae4..81dde6f6 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -1977,6 +1977,23 @@ impl, T: Variant + Clone> From> From> for Dynamic { + #[inline(always)] + fn from(value: std::collections::HashSet) -> Self { + Self(Union::Map( + Box::new( + value + .into_iter() + .map(|k| (k.into(), Dynamic::UNIT)) + .collect(), + ), + DEFAULT_TAG_VALUE, + ReadWrite, + )) + } +} +#[cfg(not(feature = "no_object"))] impl, T: Variant + Clone> From> for Dynamic { @@ -1994,6 +2011,22 @@ impl, T: Variant + Clone> From> From> for Dynamic { + #[inline(always)] + fn from(value: std::collections::BTreeSet) -> Self { + Self(Union::Map( + Box::new( + value + .into_iter() + .map(|k| (k.into(), Dynamic::UNIT)) + .collect(), + ), + DEFAULT_TAG_VALUE, + ReadWrite, + )) + } +} impl From for Dynamic { #[inline(always)] fn from(value: FnPtr) -> Self { diff --git a/src/lib.rs b/src/lib.rs index 905e5655..9bd8e204 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -191,7 +191,7 @@ pub use ast::ScriptFnMetadata; #[cfg(not(feature = "no_index"))] pub type Array = Vec; -/// Hash map of [`Dynamic`] values with [`ImmutableString`] keys. +/// Hash map of [`Dynamic`] values with [`SmartString`](https://crates.io/crates/smartstring) keys. /// Not available under `no_object`. #[cfg(not(feature = "no_object"))] pub type Map = std::collections::BTreeMap; From a0f51a1a39350740ee7d9fe56da1f296b39a7800 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 30 Jun 2021 16:28:37 +0800 Subject: [PATCH 23/25] Split calc_qualified_var_hash. --- src/fn_hash.rs | 27 +++++++++++++++++++++++++++ src/lib.rs | 3 ++- src/module/mod.rs | 4 ++-- src/parse.rs | 9 ++++----- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/fn_hash.rs b/src/fn_hash.rs index c503daf5..887ebceb 100644 --- a/src/fn_hash.rs +++ b/src/fn_hash.rs @@ -52,6 +52,33 @@ pub fn get_hasher() -> ahash::AHasher { Default::default() } +/// Calculate a [`u64`] hash key from a namespace-qualified variable name. +/// +/// Module names are passed in via `&str` references from an iterator. +/// Parameter types are passed in via [`TypeId`] values from an iterator. +/// +/// # Note +/// +/// The first module name is skipped. Hashing starts from the _second_ module in the chain. +#[inline] +#[must_use] +pub fn calc_qualified_var_hash<'a>( + modules: impl Iterator, + var_name: impl AsRef, +) -> u64 { + let s = &mut get_hasher(); + + // We always skip the first module + let mut len = 0; + modules + .inspect(|_| len += 1) + .skip(1) + .for_each(|m| m.hash(s)); + len.hash(s); + var_name.as_ref().hash(s); + s.finish() +} + /// Calculate a [`u64`] hash key from a namespace-qualified function name /// and the number of parameters, but no parameter types. /// diff --git a/src/lib.rs b/src/lib.rs index 9bd8e204..6d1a9aae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -172,7 +172,8 @@ pub use fn_native::Shared; use fn_native::Locked; pub(crate) use fn_hash::{ - calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, + calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, calc_qualified_var_hash, + combine_hashes, }; pub use rhai_codegen::*; diff --git a/src/module/mod.rs b/src/module/mod.rs index dbb5535b..19d674e5 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -447,7 +447,7 @@ impl Module { let value = Dynamic::from(value); if self.indexed { - let hash_var = crate::calc_qualified_fn_hash(once(""), &ident, 0); + let hash_var = crate::calc_qualified_var_hash(once(""), &ident); self.all_variables.insert(hash_var, value.clone()); } self.variables.insert(ident, value); @@ -1529,7 +1529,7 @@ impl Module { // Index all variables module.variables.iter().for_each(|(var_name, value)| { - let hash_var = crate::calc_qualified_fn_hash(path.iter().map(|&v| v), var_name, 0); + let hash_var = crate::calc_qualified_var_hash(path.iter().map(|&v| v), var_name); variables.insert(hash_var, value.clone()); }); diff --git a/src/parse.rs b/src/parse.rs index 0de53e8c..33380e50 100644 --- a/src/parse.rs +++ b/src/parse.rs @@ -10,16 +10,15 @@ use crate::custom_syntax::{ }; use crate::dynamic::{AccessMode, Union}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; +use crate::fn_hash::get_hasher; use crate::module::NamespaceRef; use crate::optimize::{optimize_into_ast, OptimizationLevel}; - -use crate::fn_hash::get_hasher; use crate::token::{ is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl, }; use crate::{ - calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError, - ParseErrorType, Position, Scope, Shared, StaticVec, AST, + calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, Identifier, + LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -1354,7 +1353,7 @@ fn parse_primary( } .map(|x| match x { (_, Some((namespace, hash)), name) => { - *hash = calc_qualified_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0); + *hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name); #[cfg(not(feature = "no_module"))] namespace.set_index(state.find_module(&namespace[0].name)); From 8c802dcd587d487387c99b057163becc827e0108 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 1 Jul 2021 12:27:29 +0800 Subject: [PATCH 24/25] Fix optimizer bug. --- CHANGELOG.md | 5 +++++ src/optimize.rs | 8 +++++++- tests/optimizer.rs | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1c9fac5..3680c038 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ The official version `1.0`. Almost the same version as `0.20.3` but with deprecated API's removed. +Bug fixes +--------- + +* Fixed infinite loop in certain script optimizations. + Breaking changes ---------------- diff --git a/src/optimize.rs b/src/optimize.rs index 7830efb7..22a67ac8 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -297,8 +297,14 @@ fn optimize_stmt_block( Stmt::Noop(pos) }; } + // { ...; stmt; noop } -> done [.., ref second_last_stmt, Stmt::Noop(_)] - if second_last_stmt.returns_value() => {} + if second_last_stmt.returns_value() => + { + break + } + // { ...; stmt_that_returns; pure_non_value_stmt } -> { ...; stmt_that_returns; noop } + // { ...; stmt; pure_non_value_stmt } -> { ...; stmt } [.., ref second_last_stmt, ref last_stmt] if !last_stmt.returns_value() && is_pure(last_stmt) => { diff --git a/tests/optimizer.rs b/tests/optimizer.rs index 844577b5..dc25ab23 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -2,6 +2,25 @@ use rhai::{Engine, EvalAltResult, OptimizationLevel, INT}; +#[test] +fn test_optimizer() -> Result<(), Box> { + let mut engine = Engine::new(); + engine.set_optimization_level(OptimizationLevel::Full); + + assert_eq!( + engine.eval::( + " + fn foo(x) { print(x); return; } + fn foo2(x) { if x > 0 {} return; } + 42 + " + )?, + 42 + ); + + Ok(()) +} + #[test] fn test_optimizer_run() -> Result<(), Box> { fn run_test(engine: &mut Engine) -> Result<(), Box> { From 3e0fd33db4a07e7f74e2bffd907c0f7de6f5e082 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 1 Jul 2021 12:33:16 +0800 Subject: [PATCH 25/25] Fix optimizer test. --- tests/optimizer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/optimizer.rs b/tests/optimizer.rs index dc25ab23..ac2d5f7f 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -7,6 +7,7 @@ fn test_optimizer() -> Result<(), Box> { let mut engine = Engine::new(); engine.set_optimization_level(OptimizationLevel::Full); + #[cfg(not(feature = "no_function"))] assert_eq!( engine.eval::( "