Merge pull request #418 from schungx/master

General code cleanup.
This commit is contained in:
Stephen Chung 2021-06-16 18:45:37 +08:00 committed by GitHub
commit 81b5903feb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1466 additions and 1048 deletions

View File

@ -1,10 +1,10 @@
//! Module defining the AST (abstract syntax tree). //! Module defining the AST (abstract syntax tree).
use crate::calc_fn_hash;
use crate::dynamic::Union; use crate::dynamic::Union;
use crate::fn_native::shared_make_mut; use crate::fn_native::shared_make_mut;
use crate::module::NamespaceRef; use crate::module::NamespaceRef;
use crate::token::Token; use crate::token::Token;
use crate::utils::calc_fn_hash;
use crate::{ use crate::{
Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT, Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT,
}; };
@ -216,6 +216,7 @@ impl Default for AST {
impl AST { impl AST {
/// Create a new [`AST`]. /// Create a new [`AST`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new( pub fn new(
statements: impl IntoIterator<Item = Stmt>, statements: impl IntoIterator<Item = Stmt>,
functions: impl Into<Shared<Module>>, functions: impl Into<Shared<Module>>,
@ -230,6 +231,7 @@ impl AST {
} }
/// Create a new [`AST`] with a source name. /// Create a new [`AST`] with a source name.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new_with_source( pub fn new_with_source(
statements: impl IntoIterator<Item = Stmt>, statements: impl IntoIterator<Item = Stmt>,
functions: impl Into<Shared<Module>>, functions: impl Into<Shared<Module>>,
@ -245,11 +247,13 @@ impl AST {
} }
/// Get the source, if any. /// Get the source, if any.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn source(&self) -> Option<&str> { pub fn source(&self) -> Option<&str> {
self.source.as_ref().map(|s| s.as_str()) self.source.as_ref().map(|s| s.as_str())
} }
/// Clone the source, if any. /// Clone the source, if any.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn clone_source(&self) -> Option<Identifier> { pub(crate) fn clone_source(&self) -> Option<Identifier> {
self.source.clone() self.source.clone()
} }
@ -272,6 +276,7 @@ impl AST {
/// Get the statements. /// Get the statements.
#[cfg(not(feature = "internals"))] #[cfg(not(feature = "internals"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn statements(&self) -> &[Stmt] { pub(crate) fn statements(&self) -> &[Stmt] {
&self.body.0 &self.body.0
} }
@ -280,12 +285,14 @@ impl AST {
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated = "this method is volatile and may change"] #[deprecated = "this method is volatile and may change"]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn statements(&self) -> &[Stmt] { pub fn statements(&self) -> &[Stmt] {
&self.body.0 &self.body.0
} }
/// Get a mutable reference to the statements. /// Get a mutable reference to the statements.
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn statements_mut(&mut self) -> &mut StaticVec<Stmt> { pub(crate) fn statements_mut(&mut self) -> &mut StaticVec<Stmt> {
&mut self.body.0 &mut self.body.0
} }
@ -294,6 +301,7 @@ impl AST {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn shared_lib(&self) -> Shared<Module> { pub(crate) fn shared_lib(&self) -> Shared<Module> {
self.functions.clone() self.functions.clone()
} }
@ -306,12 +314,14 @@ impl AST {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn shared_lib(&self) -> Shared<Module> { pub fn shared_lib(&self) -> Shared<Module> {
self.functions.clone() self.functions.clone()
} }
/// Get the internal [`Module`] containing all script-defined functions. /// Get the internal [`Module`] containing all script-defined functions.
#[cfg(not(feature = "internals"))] #[cfg(not(feature = "internals"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn lib(&self) -> &Module { pub(crate) fn lib(&self) -> &Module {
&self.functions &self.functions
} }
@ -322,6 +332,7 @@ impl AST {
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated = "this method is volatile and may change"] #[deprecated = "this method is volatile and may change"]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn lib(&self) -> &Module { pub fn lib(&self) -> &Module {
&self.functions &self.functions
} }
@ -329,6 +340,7 @@ impl AST {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "internals"))] #[cfg(not(feature = "internals"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn resolver( pub(crate) fn resolver(
&self, &self,
) -> Option<Shared<crate::module::resolvers::StaticModuleResolver>> { ) -> Option<Shared<crate::module::resolvers::StaticModuleResolver>> {
@ -341,6 +353,7 @@ impl AST {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn resolver(&self) -> Option<Shared<crate::module::resolvers::StaticModuleResolver>> { pub fn resolver(&self) -> Option<Shared<crate::module::resolvers::StaticModuleResolver>> {
self.resolver.clone() self.resolver.clone()
} }
@ -362,6 +375,7 @@ impl AST {
/// This operation is cheap because functions are shared. /// This operation is cheap because functions are shared.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn clone_functions_only(&self) -> Self { pub fn clone_functions_only(&self) -> Self {
self.clone_functions_only_filtered(|_, _, _, _, _| true) self.clone_functions_only_filtered(|_, _, _, _, _| true)
} }
@ -373,6 +387,7 @@ impl AST {
/// This operation is cheap because functions are shared. /// This operation is cheap because functions are shared.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn clone_functions_only_filtered( pub fn clone_functions_only_filtered(
&self, &self,
filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool, filter: impl Fn(FnNamespace, FnAccess, bool, &str, usize) -> bool,
@ -390,6 +405,7 @@ impl AST {
/// Clone the [`AST`]'s script statements into a new [`AST`]. /// Clone the [`AST`]'s script statements into a new [`AST`].
/// No functions are cloned. /// No functions are cloned.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn clone_statements_only(&self) -> Self { pub fn clone_statements_only(&self) -> Self {
Self { Self {
source: self.source.clone(), source: self.source.clone(),
@ -449,6 +465,7 @@ impl AST {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn merge(&self, other: &Self) -> Self { pub fn merge(&self, other: &Self) -> Self {
self.merge_filtered(other, |_, _, _, _, _| true) self.merge_filtered(other, |_, _, _, _, _| true)
} }
@ -557,6 +574,7 @@ impl AST {
/// # } /// # }
/// ``` /// ```
#[inline] #[inline]
#[must_use]
pub fn merge_filtered( pub fn merge_filtered(
&self, &self,
other: &Self, other: &Self,
@ -694,6 +712,7 @@ impl AST {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &ScriptFnDef> { pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &ScriptFnDef> {
self.functions self.functions
.iter_script_fn() .iter_script_fn()
@ -704,6 +723,7 @@ impl AST {
/// Not available under `no_function`. /// Not available under `no_function`.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = ScriptFnMetadata> + 'a { pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = ScriptFnMetadata> + 'a {
self.functions self.functions
.iter_script_fn() .iter_script_fn()
@ -714,13 +734,15 @@ impl AST {
/// Not available under `no_function`. /// Not available under `no_function`.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
pub fn clear_functions(&mut self) { pub fn clear_functions(&mut self) -> &mut Self {
self.functions = Default::default(); self.functions = Default::default();
self
} }
/// Clear all statements in the [`AST`], leaving only function definitions. /// Clear all statements in the [`AST`], leaving only function definitions.
#[inline(always)] #[inline(always)]
pub fn clear_statements(&mut self) { pub fn clear_statements(&mut self) -> &mut Self {
self.body = Default::default(); self.body = Default::default();
self
} }
/// Recursively walk the [`AST`], including function bodies (if any). /// Recursively walk the [`AST`], including function bodies (if any).
/// Return `false` from the callback to terminate the walk. /// Return `false` from the callback to terminate the walk.
@ -869,26 +891,33 @@ pub struct StmtBlock(StaticVec<Stmt>, Position);
impl StmtBlock { impl StmtBlock {
/// Create a new [`StmtBlock`]. /// Create a new [`StmtBlock`].
#[must_use]
pub fn new(mut statements: StaticVec<Stmt>, pos: Position) -> Self { pub fn new(mut statements: StaticVec<Stmt>, pos: Position) -> Self {
statements.shrink_to_fit(); statements.shrink_to_fit();
Self(statements, pos) Self(statements, pos)
} }
/// Is this statements block empty? /// Is this statements block empty?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.is_empty() self.0.is_empty()
} }
/// Number of statements in this statements block. /// Number of statements in this statements block.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.0.len()
} }
/// Get the position of this statements block. /// Get the position of this statements block.
#[inline(always)]
#[must_use]
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
self.1 self.1
} }
/// Get the statements of this statements block. /// Get the statements of this statements block.
pub fn statements(&mut self) -> &mut StaticVec<Stmt> { #[inline(always)]
#[must_use]
pub fn statements_mut(&mut self) -> &mut StaticVec<Stmt> {
&mut self.0 &mut self.0
} }
} }
@ -896,12 +925,14 @@ impl StmtBlock {
impl Deref for StmtBlock { impl Deref for StmtBlock {
type Target = StaticVec<Stmt>; type Target = StaticVec<Stmt>;
#[inline(always)]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
impl DerefMut for StmtBlock { impl DerefMut for StmtBlock {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0 &mut self.0
} }
@ -917,6 +948,7 @@ impl fmt::Debug for StmtBlock {
} }
impl From<StmtBlock> for Stmt { impl From<StmtBlock> for Stmt {
#[inline(always)]
fn from(block: StmtBlock) -> Self { fn from(block: StmtBlock) -> Self {
let block_pos = block.position(); let block_pos = block.position();
Self::Block(block.0.into_boxed_slice(), block_pos) Self::Block(block.0.into_boxed_slice(), block_pos)
@ -961,11 +993,7 @@ pub enum Stmt {
/// `{` stmt`;` ... `}` /// `{` stmt`;` ... `}`
Block(Box<[Stmt]>, Position), Block(Box<[Stmt]>, Position),
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
TryCatch( TryCatch(Box<(StmtBlock, Option<Ident>, StmtBlock)>, Position),
Box<(StmtBlock, Option<Ident>, StmtBlock)>,
Position,
Position,
),
/// [expression][Expr] /// [expression][Expr]
Expr(Expr), Expr(Expr),
/// `continue` /// `continue`
@ -983,7 +1011,7 @@ pub enum Stmt {
/// ///
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Export(Box<[(Ident, Option<Ident>)]>, Position), Export(Box<[(Ident, Ident)]>, Position),
/// Convert a variable to shared. /// Convert a variable to shared.
/// ///
/// Not available under `no_closure`. /// Not available under `no_closure`.
@ -1015,6 +1043,7 @@ impl From<Stmt> for StmtBlock {
impl Stmt { impl Stmt {
/// Is this statement [`Noop`][Stmt::Noop]? /// Is this statement [`Noop`][Stmt::Noop]?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_noop(&self) -> bool { pub fn is_noop(&self) -> bool {
match self { match self {
Self::Noop(_) => true, Self::Noop(_) => true,
@ -1022,6 +1051,7 @@ impl Stmt {
} }
} }
/// Get the [position][Position] of this statement. /// Get the [position][Position] of this statement.
#[must_use]
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
Self::Noop(pos) Self::Noop(pos)
@ -1038,7 +1068,7 @@ impl Stmt {
| Self::Return(_, _, pos) | Self::Return(_, _, pos)
| Self::Let(_, _, _, pos) | Self::Let(_, _, _, pos)
| Self::Const(_, _, _, pos) | Self::Const(_, _, _, pos)
| Self::TryCatch(_, pos, _) => *pos, | Self::TryCatch(_, pos) => *pos,
Self::Expr(x) => x.position(), Self::Expr(x) => x.position(),
@ -1068,7 +1098,7 @@ impl Stmt {
| Self::Return(_, _, pos) | Self::Return(_, _, pos)
| Self::Let(_, _, _, pos) | Self::Let(_, _, _, pos)
| Self::Const(_, _, _, pos) | Self::Const(_, _, _, pos)
| Self::TryCatch(_, pos, _) => *pos = new_pos, | Self::TryCatch(_, pos) => *pos = new_pos,
Self::Expr(x) => { Self::Expr(x) => {
x.set_position(new_pos); x.set_position(new_pos);
@ -1086,6 +1116,7 @@ impl Stmt {
self self
} }
/// Does this statement return a value? /// Does this statement return a value?
#[must_use]
pub fn returns_value(&self) -> bool { pub fn returns_value(&self) -> bool {
match self { match self {
Self::If(_, _, _) Self::If(_, _, _)
@ -1098,7 +1129,7 @@ impl Stmt {
| Self::While(_, _, _) | Self::While(_, _, _)
| Self::Do(_, _, _, _) | Self::Do(_, _, _, _)
| Self::For(_, _, _) | Self::For(_, _, _)
| Self::TryCatch(_, _, _) => false, | Self::TryCatch(_, _) => false,
Self::Let(_, _, _, _) Self::Let(_, _, _, _)
| Self::Const(_, _, _, _) | Self::Const(_, _, _, _)
@ -1115,6 +1146,7 @@ impl Stmt {
} }
} }
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)? /// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
#[must_use]
pub fn is_self_terminated(&self) -> bool { pub fn is_self_terminated(&self) -> bool {
match self { match self {
Self::If(_, _, _) Self::If(_, _, _)
@ -1122,7 +1154,7 @@ impl Stmt {
| Self::While(_, _, _) | Self::While(_, _, _)
| Self::For(_, _, _) | Self::For(_, _, _)
| Self::Block(_, _) | Self::Block(_, _)
| Self::TryCatch(_, _, _) => true, | Self::TryCatch(_, _) => true,
// A No-op requires a semicolon in order to know it is an empty statement! // A No-op requires a semicolon in order to know it is an empty statement!
Self::Noop(_) => false, Self::Noop(_) => false,
@ -1147,6 +1179,7 @@ impl Stmt {
/// Is this statement _pure_? /// Is this statement _pure_?
/// ///
/// A pure statement has no side effects. /// A pure statement has no side effects.
#[must_use]
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { match self {
Self::Noop(_) => true, Self::Noop(_) => true,
@ -1174,7 +1207,7 @@ impl Stmt {
| Self::FnCall(_, _) => false, | Self::FnCall(_, _) => false,
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,
Self::TryCatch(x, _, _) => { Self::TryCatch(x, _) => {
(x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure) (x.0).0.iter().all(Stmt::is_pure) && (x.2).0.iter().all(Stmt::is_pure)
} }
@ -1194,6 +1227,7 @@ impl Stmt {
/// Only variable declarations (i.e. `let` and `const`) and `import`/`export` statements /// Only variable declarations (i.e. `let` and `const`) and `import`/`export` statements
/// are internally pure. /// are internally pure.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_internally_pure(&self) -> bool { pub fn is_internally_pure(&self) -> bool {
match self { match self {
Self::Let(expr, _, _, _) | Self::Const(expr, _, _, _) => expr.is_pure(), Self::Let(expr, _, _, _) | Self::Const(expr, _, _, _) => expr.is_pure(),
@ -1212,6 +1246,7 @@ impl Stmt {
/// ///
/// All statements following this statement will essentially be dead code. /// All statements following this statement will essentially be dead code.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_control_flow_break(&self) -> bool { pub fn is_control_flow_break(&self) -> bool {
match self { match self {
Self::Return(_, _, _) | Self::Break(_) | Self::Continue(_) => true, Self::Return(_, _, _) | Self::Break(_) | Self::Continue(_) => true,
@ -1315,7 +1350,7 @@ impl Stmt {
} }
} }
} }
Self::TryCatch(x, _, _) => { Self::TryCatch(x, _) => {
for s in &(x.0).0 { for s in &(x.0).0 {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
@ -1364,6 +1399,15 @@ pub struct CustomExpr {
pub tokens: StaticVec<Identifier>, pub tokens: StaticVec<Identifier>,
} }
impl CustomExpr {
/// Convert this into a [`Expr::Custom`].
#[inline(always)]
#[must_use]
pub fn into_custom_syntax_expr(self, pos: Position) -> Expr {
Expr::Custom(self.into(), pos)
}
}
/// _(INTERNALS)_ A binary expression. /// _(INTERNALS)_ A binary expression.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
@ -1397,6 +1441,7 @@ impl OpAssignment<'_> {
/// # Panics /// # Panics
/// ///
/// Panics if the operator name is not an op-assignment operator. /// Panics if the operator name is not an op-assignment operator.
#[must_use]
pub fn new(op: Token) -> Self { pub fn new(op: Token) -> Self {
let op_raw = op let op_raw = op
.map_op_assignment() .map_op_assignment()
@ -1466,6 +1511,7 @@ impl fmt::Debug for FnCallHashes {
impl FnCallHashes { impl FnCallHashes {
/// Create a [`FnCallHashes`] with only the native Rust hash. /// Create a [`FnCallHashes`] with only the native Rust hash.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn from_native(hash: u64) -> Self { pub fn from_native(hash: u64) -> Self {
Self { Self {
script: None, script: None,
@ -1474,6 +1520,7 @@ impl FnCallHashes {
} }
/// Create a [`FnCallHashes`] with both native Rust and script function hashes set to the same value. /// Create a [`FnCallHashes`] with both native Rust and script function hashes set to the same value.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn from_script(hash: u64) -> Self { pub fn from_script(hash: u64) -> Self {
Self { Self {
script: Some(hash), script: Some(hash),
@ -1482,6 +1529,7 @@ impl FnCallHashes {
} }
/// Create a [`FnCallHashes`] with both native Rust and script function hashes. /// Create a [`FnCallHashes`] with both native Rust and script function hashes.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn from_script_and_native(script: u64, native: u64) -> Self { pub fn from_script_and_native(script: u64, native: u64) -> Self {
Self { Self {
script: Some(script), script: Some(script),
@ -1490,19 +1538,10 @@ impl FnCallHashes {
} }
/// Is this [`FnCallHashes`] native Rust only? /// Is this [`FnCallHashes`] native Rust only?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_native_only(&self) -> bool { pub fn is_native_only(&self) -> bool {
self.script.is_none() self.script.is_none()
} }
/// Get the script function hash from this [`FnCallHashes`].
#[inline(always)]
pub fn script_hash(&self) -> Option<u64> {
self.script
}
/// Get the naive Rust function hash from this [`FnCallHashes`].
#[inline(always)]
pub fn native_hash(&self) -> u64 {
self.native
}
} }
/// _(INTERNALS)_ A function call. /// _(INTERNALS)_ A function call.
@ -1530,9 +1569,16 @@ pub struct FnCallExpr {
impl FnCallExpr { impl FnCallExpr {
/// Does this function call contain a qualified namespace? /// Does this function call contain a qualified namespace?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_qualified(&self) -> bool { pub fn is_qualified(&self) -> bool {
self.namespace.is_some() self.namespace.is_some()
} }
/// Convert this into a [`FnCall`][Expr::FnCall].
#[inline(always)]
#[must_use]
pub fn into_fn_call_expr(self, pos: Position) -> Expr {
Expr::FnCall(self.into(), pos)
}
} }
/// A type that wraps a floating-point number and implements [`Hash`]. /// A type that wraps a floating-point number and implements [`Hash`].
@ -1638,6 +1684,7 @@ impl<F: Float> FloatWrapper<F> {
pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001; pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001;
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new(value: F) -> Self { pub fn new(value: F) -> Self {
Self(value) Self(value)
} }
@ -1646,6 +1693,7 @@ impl<F: Float> FloatWrapper<F> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
impl FloatWrapper<FLOAT> { impl FloatWrapper<FLOAT> {
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) const fn const_new(value: FLOAT) -> Self { pub(crate) const fn const_new(value: FLOAT) -> Self {
Self(value) Self(value)
} }
@ -1697,7 +1745,7 @@ pub enum Expr {
Position, Position,
Box<( Box<(
Option<NonZeroUsize>, Option<NonZeroUsize>,
Option<(u64, NamespaceRef)>, Option<(NamespaceRef, u64)>,
Identifier, Identifier,
)>, )>,
), ),
@ -1823,6 +1871,7 @@ impl Expr {
/// ///
/// Returns [`None`] if the expression is not a literal constant. /// Returns [`None`] if the expression is not a literal constant.
#[inline] #[inline]
#[must_use]
pub fn get_literal_value(&self) -> Option<Dynamic> { pub fn get_literal_value(&self) -> Option<Dynamic> {
Some(match self { Some(match self {
Self::DynamicConstant(x, _) => x.as_ref().clone(), Self::DynamicConstant(x, _) => x.as_ref().clone(),
@ -1861,6 +1910,7 @@ impl Expr {
} }
/// Create an [`Expr`] from a [`Dynamic`] value. /// Create an [`Expr`] from a [`Dynamic`] value.
#[inline] #[inline]
#[must_use]
pub fn from_dynamic(value: Dynamic, pos: Position) -> Self { pub fn from_dynamic(value: Dynamic, pos: Position) -> Self {
match value.0 { match value.0 {
Union::Unit(_, _, _) => Self::Unit(pos), Union::Unit(_, _, _) => Self::Unit(pos),
@ -1877,6 +1927,7 @@ impl Expr {
} }
/// Is the expression a simple variable access? /// Is the expression a simple variable access?
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool { pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool {
match self { match self {
Self::Variable(_, _, x) => !non_qualified || x.1.is_none(), Self::Variable(_, _, x) => !non_qualified || x.1.is_none(),
@ -1885,6 +1936,7 @@ impl Expr {
} }
/// Return the variable name if the expression a simple variable access. /// Return the variable name if the expression a simple variable access.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> { pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> {
match self { match self {
Self::Variable(_, _, x) if !non_qualified || x.1.is_none() => Some(x.2.as_str()), Self::Variable(_, _, x) if !non_qualified || x.1.is_none() => Some(x.2.as_str()),
@ -1893,6 +1945,7 @@ impl Expr {
} }
/// Get the [position][Position] of the expression. /// Get the [position][Position] of the expression.
#[inline] #[inline]
#[must_use]
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -1966,6 +2019,7 @@ impl Expr {
/// ///
/// A pure expression has no side effects. /// A pure expression has no side effects.
#[inline] #[inline]
#[must_use]
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { 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),
@ -1983,6 +2037,7 @@ impl Expr {
} }
/// Is the expression the unit `()` literal? /// Is the expression the unit `()` literal?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_unit(&self) -> bool { pub fn is_unit(&self) -> bool {
match self { match self {
Self::Unit(_) => true, Self::Unit(_) => true,
@ -1991,6 +2046,7 @@ impl Expr {
} }
/// Is the expression a constant? /// Is the expression a constant?
#[inline] #[inline]
#[must_use]
pub fn is_constant(&self) -> bool { pub fn is_constant(&self) -> bool {
match self { match self {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -2013,6 +2069,7 @@ impl Expr {
} }
/// Is a particular [token][Token] allowed as a postfix operator to this expression? /// Is a particular [token][Token] allowed as a postfix operator to this expression?
#[inline] #[inline]
#[must_use]
pub fn is_valid_postfix(&self, token: &Token) -> bool { pub fn is_valid_postfix(&self, token: &Token) -> bool {
match token { match token {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]

View File

@ -1,13 +1,16 @@
//! Module implementing custom syntax for [`Engine`]. //! Module implementing custom syntax for [`Engine`].
use crate::ast::Expr; use crate::ast::Expr;
use crate::dynamic::Variant;
use crate::engine::EvalContext; use crate::engine::EvalContext;
use crate::fn_native::SendSync; use crate::fn_native::SendSync;
use crate::r#unsafe::unsafe_try_cast;
use crate::token::{is_valid_identifier, Token}; use crate::token::{is_valid_identifier, Token};
use crate::{ use crate::{
Dynamic, Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, Engine, Identifier, ImmutableString, LexError, ParseError, Position, RhaiResult, Shared,
Shared, StaticVec, StaticVec, INT,
}; };
use std::any::TypeId;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -50,23 +53,70 @@ impl<'a> From<&'a Expr> for Expression<'a> {
impl Expression<'_> { impl Expression<'_> {
/// If this expression is a variable name, return it. Otherwise [`None`]. /// If this expression is a variable name, return it. Otherwise [`None`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_variable_name(&self) -> Option<&str> { pub fn get_variable_name(&self) -> Option<&str> {
self.0.get_variable_name(true) self.0.get_variable_name(true)
} }
/// Get the expression. /// Get the expression.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn expr(&self) -> &Expr { pub(crate) fn expr(&self) -> &Expr {
&self.0 &self.0
} }
/// Get the position of this expression. /// Get the position of this expression.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
self.0.position() self.0.position()
} }
/// Get the value of this expression if it is a literal constant. /// Get the value of this expression if it is a literal constant.
/// Supports [`INT`][crate::INT], [`FLOAT`][crate::FLOAT], `()`, `char`, `bool` and
/// [`ImmutableString`][crate::ImmutableString].
///
/// Returns [`None`] also if the constant is not of the specified type.
#[inline(always)] #[inline(always)]
pub fn get_literal_value(&self) -> Option<Dynamic> { #[must_use]
self.0.get_literal_value() pub fn get_literal_value<T: Variant>(&self) -> Option<T> {
// Coded this way in order to maximally leverage potentials for dead-code removal.
if TypeId::of::<T>() == TypeId::of::<INT>() {
return match self.0 {
Expr::IntegerConstant(x, _) => unsafe_try_cast(*x).ok(),
_ => None,
};
}
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
return match self.0 {
Expr::FloatConstant(x, _) => unsafe_try_cast(*x).ok(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<char>() {
return match self.0 {
Expr::CharConstant(x, _) => unsafe_try_cast(*x).ok(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return match self.0 {
Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()).ok(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<bool>() {
return match self.0 {
Expr::BoolConstant(x, _) => unsafe_try_cast(*x).ok(),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<()>() {
return match self.0 {
Expr::Unit(_) => unsafe_try_cast(()).ok(),
_ => None,
};
}
None
} }
} }
@ -77,6 +127,7 @@ impl EvalContext<'_, '_, '_, '_, '_, '_, '_> {
/// ///
/// This function is very low level. It evaluates an expression from an [`AST`][crate::AST]. /// This function is very low level. It evaluates an expression from an [`AST`][crate::AST].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult { pub fn eval_expression_tree(&mut self, expr: &Expression) -> RhaiResult {
self.engine.eval_expr( self.engine.eval_expr(
self.scope, self.scope,
@ -120,6 +171,7 @@ impl Engine {
/// since they all go away at the end of the block. /// since they all go away at the end of the block.
/// ///
/// Variables in parent blocks should be left untouched as they persist beyond the current block. /// Variables in parent blocks should be left untouched as they persist beyond the current block.
#[must_use]
pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>( pub fn register_custom_syntax<S: AsRef<str> + Into<Identifier>>(
&mut self, &mut self,
keywords: &[S], keywords: &[S],

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,9 @@
//! Main module defining the script evaluation [`Engine`]. //! Main module defining the script evaluation [`Engine`].
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt}; use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt};
use crate::custom_syntax::CustomSyntax;
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
use crate::fn_hash::get_hasher;
use crate::fn_native::{ use crate::fn_native::{
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnVarCallback, CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnVarCallback,
}; };
@ -9,9 +11,7 @@ use crate::module::NamespaceRef;
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::packages::{Package, StandardPackage}; use crate::packages::{Package, StandardPackage};
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::syntax::CustomSyntax;
use crate::token::Token; use crate::token::Token;
use crate::utils::get_hasher;
use crate::{ use crate::{
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope, Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
Shared, StaticVec, INT, Shared, StaticVec, INT,
@ -64,27 +64,32 @@ pub struct Imports {
impl Imports { impl Imports {
/// Get the length of this stack of imported [modules][Module]. /// Get the length of this stack of imported [modules][Module].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.keys.len() self.keys.len()
} }
/// Is this stack of imported [modules][Module] empty? /// Is this stack of imported [modules][Module] empty?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.keys.is_empty() self.keys.is_empty()
} }
/// Get the imported [modules][Module] at a particular index. /// Get the imported [modules][Module] at a particular index.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get(&self, index: usize) -> Option<Shared<Module>> { pub fn get(&self, index: usize) -> Option<Shared<Module>> {
self.modules.get(index).cloned() self.modules.get(index).cloned()
} }
/// Get the imported [modules][Module] at a particular index. /// Get the imported [modules][Module] at a particular index.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared<Module>> { pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared<Module>> {
self.modules.get_mut(index) self.modules.get_mut(index)
} }
/// Get the index of an imported [modules][Module] by name. /// Get the index of an imported [modules][Module] by name.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn find(&self, name: &str) -> Option<usize> { pub fn find(&self, name: &str) -> Option<usize> {
self.keys self.keys
.iter() .iter()
@ -107,6 +112,7 @@ impl Imports {
/// Get an iterator to this stack of imported [modules][Module] in reverse order. /// Get an iterator to this stack of imported [modules][Module] in reverse order.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> { pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
self.keys self.keys
.iter() .iter()
@ -117,17 +123,20 @@ impl Imports {
/// Get an iterator to this stack of imported [modules][Module] in reverse order. /// Get an iterator to this stack of imported [modules][Module] in reverse order.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> { pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
self.keys.iter().rev().zip(self.modules.iter().rev()) self.keys.iter().rev().zip(self.modules.iter().rev())
} }
/// Get an iterator to this stack of imported [modules][Module] in forward order. /// Get an iterator to this stack of imported [modules][Module] in forward order.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> { pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
self.keys.iter().zip(self.modules.iter()) self.keys.iter().zip(self.modules.iter())
} }
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order. /// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> { pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
self.keys self.keys
.into_iter() .into_iter()
@ -137,11 +146,13 @@ impl Imports {
/// Does the specified function hash key exist in this stack of imported [modules][Module]? /// Does the specified function hash key exist in this stack of imported [modules][Module]?
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_fn(&self, hash: u64) -> bool { pub fn contains_fn(&self, hash: u64) -> bool {
self.modules.iter().any(|m| m.contains_qualified_fn(hash)) self.modules.iter().any(|m| m.contains_qualified_fn(hash))
} }
/// Get specified function via its hash key. /// Get specified function via its hash key.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> { pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> {
self.modules self.modules
.iter() .iter()
@ -152,11 +163,13 @@ impl Imports {
/// imported [modules][Module]? /// imported [modules][Module]?
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_iter(&self, id: TypeId) -> bool { pub fn contains_iter(&self, id: TypeId) -> bool {
self.modules.iter().any(|m| m.contains_qualified_iter(id)) self.modules.iter().any(|m| m.contains_qualified_iter(id))
} }
/// Get the specified [`TypeId`][std::any::TypeId] iterator. /// Get the specified [`TypeId`][std::any::TypeId] iterator.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> { pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.modules self.modules
.iter() .iter()
@ -278,6 +291,7 @@ impl ChainArgument {
/// Panics if not `ChainArgument::IndexValue`. /// Panics if not `ChainArgument::IndexValue`.
#[inline(always)] #[inline(always)]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
#[must_use]
pub fn as_index_value(self) -> Dynamic { pub fn as_index_value(self) -> Dynamic {
match self { match self {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -294,6 +308,7 @@ impl ChainArgument {
/// Panics if not `ChainArgument::MethodCallArgs`. /// Panics if not `ChainArgument::MethodCallArgs`.
#[inline(always)] #[inline(always)]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[must_use]
pub fn as_fn_call_args(self) -> (StaticVec<Dynamic>, Position) { pub fn as_fn_call_args(self) -> (StaticVec<Dynamic>, Position) {
match self { match self {
Self::Property(_) => { Self::Property(_) => {
@ -349,6 +364,7 @@ impl<'a> Target<'a> {
/// Is the `Target` a reference pointing to other data? /// Is the `Target` a reference pointing to other data?
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_ref(&self) -> bool { pub fn is_ref(&self) -> bool {
match self { match self {
Self::RefMut(_) => true, Self::RefMut(_) => true,
@ -363,6 +379,7 @@ impl<'a> Target<'a> {
} }
/// Is the `Target` a temp value? /// Is the `Target` a temp value?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_temp_value(&self) -> bool { pub fn is_temp_value(&self) -> bool {
match self { match self {
Self::RefMut(_) => false, Self::RefMut(_) => false,
@ -378,6 +395,7 @@ impl<'a> Target<'a> {
/// Is the `Target` a shared value? /// Is the `Target` a shared value?
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_shared(&self) -> bool { pub fn is_shared(&self) -> bool {
match self { match self {
Self::RefMut(r) => r.is_shared(), Self::RefMut(r) => r.is_shared(),
@ -393,6 +411,7 @@ impl<'a> Target<'a> {
/// Is the `Target` a specific type? /// Is the `Target` a specific type?
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is<T: Variant + Clone>(&self) -> bool { pub fn is<T: Variant + Clone>(&self) -> bool {
match self { match self {
Self::RefMut(r) => r.is::<T>(), Self::RefMut(r) => r.is::<T>(),
@ -407,6 +426,7 @@ impl<'a> Target<'a> {
} }
/// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary. /// Get the value of the `Target` as a `Dynamic`, cloning a referenced value if necessary.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn take_or_clone(self) -> Dynamic { pub fn take_or_clone(self) -> Dynamic {
match self { match self {
Self::RefMut(r) => r.clone(), // Referenced value is cloned Self::RefMut(r) => r.clone(), // Referenced value is cloned
@ -421,6 +441,7 @@ impl<'a> Target<'a> {
} }
/// Take a `&mut Dynamic` reference from the `Target`. /// Take a `&mut Dynamic` reference from the `Target`.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn take_ref(self) -> Option<&'a mut Dynamic> { pub fn take_ref(self) -> Option<&'a mut Dynamic> {
match self { match self {
Self::RefMut(r) => Some(r), Self::RefMut(r) => Some(r),
@ -429,12 +450,14 @@ impl<'a> Target<'a> {
} }
/// Convert a shared or reference `Target` into a target with an owned value. /// Convert a shared or reference `Target` into a target with an owned value.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn into_owned(self) -> Target<'static> { pub fn into_owned(self) -> Target<'static> {
self.take_or_clone().into() self.take_or_clone().into()
} }
/// Propagate a changed value back to the original source. /// Propagate a changed value back to the original source.
/// This has no effect except for string indexing. /// This has no effect except for string indexing.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn propagate_changed_value(&mut self) -> Result<(), Box<EvalAltResult>> { pub fn propagate_changed_value(&mut self) -> Result<(), Box<EvalAltResult>> {
match self { match self {
Self::RefMut(_) | Self::TempValue(_) => (), Self::RefMut(_) | Self::TempValue(_) => (),
@ -566,6 +589,7 @@ impl AsMut<Dynamic> for Target<'_> {
impl<T: Into<Dynamic>> From<T> for Target<'_> { impl<T: Into<Dynamic>> From<T> for Target<'_> {
#[inline(always)] #[inline(always)]
#[must_use]
fn from(value: T) -> Self { fn from(value: T) -> Self {
Self::TempValue(value.into()) Self::TempValue(value.into())
} }
@ -614,11 +638,13 @@ pub struct State {
impl State { impl State {
/// Is the state currently at global (root) level? /// Is the state currently at global (root) level?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_global(&self) -> bool { pub fn is_global(&self) -> bool {
self.scope_level == 0 self.scope_level == 0
} }
/// Get a mutable reference to the current function resolution cache. /// Get a mutable reference to the current function resolution cache.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
if self.fn_resolution_caches.0.is_empty() { if self.fn_resolution_caches.0.is_empty() {
// Push a new function resolution cache if the stack is empty // Push a new function resolution cache if the stack is empty
@ -714,27 +740,32 @@ pub struct EvalContext<'a, 'x, 'px, 'm, 's, 't, 'pt> {
impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> { impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
/// The current [`Engine`]. /// The current [`Engine`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn engine(&self) -> &Engine { pub fn engine(&self) -> &Engine {
self.engine self.engine
} }
/// The current source. /// The current source.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn source(&self) -> Option<&str> { pub fn source(&self) -> Option<&str> {
self.state.source.as_ref().map(|s| s.as_str()) self.state.source.as_ref().map(|s| s.as_str())
} }
/// The current [`Scope`]. /// The current [`Scope`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn scope(&self) -> &Scope { pub fn scope(&self) -> &Scope {
self.scope self.scope
} }
/// Mutable reference to the current [`Scope`]. /// Mutable reference to the current [`Scope`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn scope_mut(&mut self) -> &mut &'x mut Scope<'px> { pub fn scope_mut(&mut self) -> &mut &'x mut Scope<'px> {
&mut self.scope &mut self.scope
} }
/// Get an iterator over the current set of modules imported via `import` statements. /// Get an iterator over the current set of modules imported via `import` statements.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> { pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
self.mods.iter() self.mods.iter()
} }
@ -743,11 +774,13 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn imports(&self) -> &Imports { pub fn imports(&self) -> &Imports {
self.mods self.mods
} }
/// Get an iterator over the namespaces containing definition of all script-defined functions. /// Get an iterator over the namespaces containing definition of all script-defined functions.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> { pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
self.lib.iter().cloned() self.lib.iter().cloned()
} }
@ -755,16 +788,19 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn namespaces(&self) -> &[&Module] { pub fn namespaces(&self) -> &[&Module] {
self.lib self.lib
} }
/// The current bound `this` pointer, if any. /// The current bound `this` pointer, if any.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn this_ptr(&self) -> Option<&Dynamic> { pub fn this_ptr(&self) -> Option<&Dynamic> {
self.this_ptr.as_ref().map(|v| &**v) self.this_ptr.as_ref().map(|v| &**v)
} }
/// The current nesting level of function calls. /// The current nesting level of function calls.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn call_level(&self) -> usize { pub fn call_level(&self) -> usize {
self.level self.level
} }
@ -853,6 +889,7 @@ impl Default for Engine {
/// Make getter function /// Make getter function
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn make_getter(id: &str) -> String { pub fn make_getter(id: &str) -> String {
format!("{}{}", FN_GET, id) format!("{}{}", FN_GET, id)
} }
@ -860,6 +897,7 @@ pub fn make_getter(id: &str) -> String {
/// Make setter function /// Make setter function
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn make_setter(id: &str) -> String { pub fn make_setter(id: &str) -> String {
format!("{}{}", FN_SET, id) format!("{}{}", FN_SET, id)
} }
@ -867,6 +905,7 @@ pub fn make_setter(id: &str) -> String {
/// Is this function an anonymous function? /// Is this function an anonymous function?
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_anonymous_fn(fn_name: &str) -> bool { pub fn is_anonymous_fn(fn_name: &str) -> bool {
fn_name.starts_with(FN_ANONYMOUS) fn_name.starts_with(FN_ANONYMOUS)
} }
@ -896,6 +935,7 @@ fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) {
impl Engine { impl Engine {
/// Create a new [`Engine`] /// Create a new [`Engine`]
#[inline] #[inline]
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
// Create the new scripting Engine // Create the new scripting Engine
let mut engine = Self { let mut engine = Self {
@ -959,6 +999,7 @@ impl Engine {
/// ///
/// Use [`register_global_module`][Engine::register_global_module] to add packages of functions. /// Use [`register_global_module`][Engine::register_global_module] to add packages of functions.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new_raw() -> Self { pub fn new_raw() -> Self {
let mut engine = Self { let mut engine = Self {
global_namespace: Default::default(), global_namespace: Default::default(),
@ -1009,6 +1050,7 @@ impl Engine {
/// Search for a module within an imports stack. /// Search for a module within an imports stack.
#[inline] #[inline]
#[must_use]
pub(crate) fn search_imports( pub(crate) fn search_imports(
&self, &self,
mods: &Imports, mods: &Imports,
@ -1042,6 +1084,7 @@ impl Engine {
/// Search for a variable within the scope or within imports, /// Search for a variable within the scope or within imports,
/// depending on whether the variable name is namespace-qualified. /// depending on whether the variable name is namespace-qualified.
#[must_use]
pub(crate) fn search_namespace<'s>( pub(crate) fn search_namespace<'s>(
&self, &self,
scope: &'s mut Scope, scope: &'s mut Scope,
@ -1059,17 +1102,17 @@ impl Engine {
// Normal variable access // Normal variable access
(_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr), (_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
// Qualified variable // Qualified variable
(_, Some((hash_var, modules)), var_name) => { (_, Some((namespace, hash_var)), var_name) => {
let module = self.search_imports(mods, state, modules).ok_or_else(|| { let module = self.search_imports(mods, state, namespace).ok_or_else(|| {
EvalAltResult::ErrorModuleNotFound( EvalAltResult::ErrorModuleNotFound(
modules[0].name.to_string(), namespace[0].name.to_string(),
modules[0].pos, namespace[0].pos,
) )
})?; })?;
let target = module.get_qualified_var(*hash_var).map_err(|mut err| { let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
match *err { match *err {
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
*err_name = format!("{}{}", modules, var_name); *err_name = format!("{}{}", namespace, var_name);
} }
_ => (), _ => (),
} }
@ -1091,6 +1134,7 @@ impl Engine {
/// # Panics /// # Panics
/// ///
/// Panics if `expr` is not [`Expr::Variable`]. /// Panics if `expr` is not [`Expr::Variable`].
#[must_use]
pub(crate) fn search_scope_only<'s>( pub(crate) fn search_scope_only<'s>(
&self, &self,
scope: &'s mut Scope, scope: &'s mut Scope,
@ -1128,17 +1172,19 @@ impl Engine {
this_ptr, this_ptr,
level: 0, level: 0,
}; };
if let Some(mut result) = resolve_var( match resolve_var(
expr.get_variable_name(true) expr.get_variable_name(true)
.expect("`expr` should be `Variable`"), .expect("`expr` should be `Variable`"),
index, index,
&context, &context,
) ) {
.map_err(|err| err.fill_position(var_pos))? Ok(Some(mut result)) => {
{
result.set_access_mode(AccessMode::ReadOnly); result.set_access_mode(AccessMode::ReadOnly);
return Ok((result.into(), var_pos)); return Ok((result.into(), var_pos));
} }
Ok(None) => (),
Err(err) => return Err(err.fill_position(var_pos)),
}
} }
let index = if index > 0 { let index = if index > 0 {
@ -1162,6 +1208,7 @@ impl Engine {
/// Chain-evaluate a dot/index chain. /// Chain-evaluate a dot/index chain.
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards. /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and must be set afterwards.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[must_use]
fn eval_dot_index_chain_helper( fn eval_dot_index_chain_helper(
&self, &self,
mods: &mut Imports, mods: &mut Imports,
@ -1275,9 +1322,9 @@ impl Engine {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => { Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
let FnCallExpr { name, hashes, .. } = x.as_ref(); let FnCallExpr { name, hashes, .. } = x.as_ref();
let mut args = idx_val.as_fn_call_args(); let args = &mut idx_val.as_fn_call_args();
self.make_method_call( self.make_method_call(
mods, state, lib, name, *hashes, target, &mut args, *pos, level, mods, state, lib, name, *hashes, target, args, *pos, level,
) )
} }
// xxx.fn_name(...) = ??? // xxx.fn_name(...) = ???
@ -1295,11 +1342,11 @@ impl Engine {
new_val.expect("never fails because `new_val` is `Some`"); new_val.expect("never fails because `new_val` is `Some`");
let index = name.into(); let index = name.into();
{ {
let mut val = self.get_indexed_mut( let val_target = &mut self.get_indexed_mut(
mods, state, lib, target, index, *pos, true, false, level, mods, state, lib, target, index, *pos, true, false, level,
)?; )?;
self.eval_op_assignment( self.eval_op_assignment(
mods, state, lib, op_info, op_pos, &mut val, root, new_val, mods, state, lib, op_info, op_pos, val_target, root, new_val,
) )
.map_err(|err| err.fill_position(new_pos))?; .map_err(|err| err.fill_position(new_pos))?;
} }
@ -1324,11 +1371,11 @@ impl Engine {
if op_info.is_some() { if op_info.is_some() {
let hash = FnCallHashes::from_native(*hash_get); let hash = FnCallHashes::from_native(*hash_get);
let mut args = [target.as_mut()]; let args = &mut [target.as_mut()];
let (mut orig_val, _) = self let (mut orig_val, _) = self
.exec_fn_call( .exec_fn_call(
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, mods, state, lib, getter, hash, args, is_ref, true, *pos, None,
None, level, level,
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// Try an indexer if property does not exist // Try an indexer if property does not exist
@ -1368,18 +1415,17 @@ impl Engine {
} }
let hash = FnCallHashes::from_native(*hash_set); let hash = FnCallHashes::from_native(*hash_set);
let mut args = [target.as_mut(), &mut new_val]; let args = &mut [target.as_mut(), &mut new_val];
self.exec_fn_call( self.exec_fn_call(
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None, mods, state, lib, setter, hash, args, is_ref, true, *pos, None, level,
level,
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// Try an indexer if property does not exist // Try an indexer if property does not exist
EvalAltResult::ErrorDotExpr(_, _) => { EvalAltResult::ErrorDotExpr(_, _) => {
let mut prop = name.into(); let args = &mut [target, &mut name.into(), &mut new_val];
let args = &mut [target, &mut prop, &mut new_val];
let hash_set = let hash_set =
FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3)); FnCallHashes::from_native(crate::calc_fn_hash(FN_IDX_SET, 3));
self.exec_fn_call( self.exec_fn_call(
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true, mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
*pos, None, level, *pos, None, level,
@ -1398,10 +1444,9 @@ impl Engine {
Expr::Property(x) => { Expr::Property(x) => {
let ((getter, hash_get), _, (name, pos)) = x.as_ref(); let ((getter, hash_get), _, (name, pos)) = x.as_ref();
let hash = FnCallHashes::from_native(*hash_get); let hash = FnCallHashes::from_native(*hash_get);
let mut args = [target.as_mut()]; let args = &mut [target.as_mut()];
self.exec_fn_call( self.exec_fn_call(
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None, mods, state, lib, getter, hash, args, is_ref, true, *pos, None, level,
level,
) )
.map_or_else( .map_or_else(
|err| match *err { |err| match *err {
@ -1426,8 +1471,8 @@ impl Engine {
} }
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
let mut val = match &x.lhs { let val_target = &mut match x.lhs {
Expr::Property(p) => { Expr::Property(ref p) => {
let (name, pos) = &p.2; let (name, pos) = &p.2;
let index = name.into(); let index = name.into();
self.get_indexed_mut( self.get_indexed_mut(
@ -1435,11 +1480,11 @@ impl Engine {
)? )?
} }
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
Expr::FnCall(x, pos) if !x.is_qualified() => { Expr::FnCall(ref x, pos) if !x.is_qualified() => {
let FnCallExpr { name, hashes, .. } = x.as_ref(); let FnCallExpr { name, hashes, .. } = x.as_ref();
let mut args = idx_val.as_fn_call_args(); let args = &mut idx_val.as_fn_call_args();
let (val, _) = self.make_method_call( let (val, _) = self.make_method_call(
mods, state, lib, name, *hashes, target, &mut args, *pos, level, mods, state, lib, name, *hashes, target, args, pos, level,
)?; )?;
val.into() val.into()
} }
@ -1448,21 +1493,21 @@ impl Engine {
"function call in dot chain should not be namespace-qualified" "function call in dot chain should not be namespace-qualified"
), ),
// Others - syntax error // Others - syntax error
expr => unreachable!("invalid dot expression: {:?}", expr), ref expr => unreachable!("invalid dot expression: {:?}", expr),
}; };
let rhs_chain = match_chain_type(rhs); let rhs_chain = match_chain_type(rhs);
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values, mods, state, lib, this_ptr, val_target, root, &x.rhs, idx_values,
rhs_chain, level, new_val, rhs_chain, level, new_val,
) )
.map_err(|err| err.fill_position(*x_pos)) .map_err(|err| err.fill_position(*x_pos))
} }
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr // xxx.sub_lhs[expr] | xxx.sub_lhs.expr
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) => { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) => {
match &x.lhs { match x.lhs {
// xxx.prop[expr] | xxx.prop.expr // xxx.prop[expr] | xxx.prop.expr
Expr::Property(p) => { Expr::Property(ref p) => {
let ((getter, hash_get), (setter, hash_set), (name, pos)) = let ((getter, hash_get), (setter, hash_set), (name, pos)) =
p.as_ref(); p.as_ref();
let rhs_chain = match_chain_type(rhs); let rhs_chain = match_chain_type(rhs);
@ -1525,8 +1570,8 @@ impl Engine {
|err| match *err { |err| match *err {
// Try an indexer if property does not exist // Try an indexer if property does not exist
EvalAltResult::ErrorDotExpr(_, _) => { EvalAltResult::ErrorDotExpr(_, _) => {
let mut prop = name.into(); let args =
let args = &mut [target.as_mut(), &mut prop, val]; &mut [target.as_mut(), &mut name.into(), val];
let hash_set = FnCallHashes::from_native( let hash_set = FnCallHashes::from_native(
crate::calc_fn_hash(FN_IDX_SET, 3), crate::calc_fn_hash(FN_IDX_SET, 3),
); );
@ -1553,12 +1598,12 @@ impl Engine {
Ok((result, may_be_changed)) Ok((result, may_be_changed))
} }
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(f, pos) if !f.is_qualified() => { Expr::FnCall(ref f, pos) if !f.is_qualified() => {
let FnCallExpr { name, hashes, .. } = f.as_ref(); let FnCallExpr { name, hashes, .. } = f.as_ref();
let rhs_chain = match_chain_type(rhs); let rhs_chain = match_chain_type(rhs);
let mut args = idx_val.as_fn_call_args(); let args = &mut idx_val.as_fn_call_args();
let (mut val, _) = self.make_method_call( let (mut val, _) = self.make_method_call(
mods, state, lib, name, *hashes, target, &mut args, *pos, level, mods, state, lib, name, *hashes, target, args, pos, level,
)?; )?;
let val = &mut val; let val = &mut val;
let target = &mut val.into(); let target = &mut val.into();
@ -1567,14 +1612,14 @@ impl Engine {
mods, state, lib, this_ptr, target, root, &x.rhs, idx_values, mods, state, lib, this_ptr, target, root, &x.rhs, idx_values,
rhs_chain, level, new_val, rhs_chain, level, new_val,
) )
.map_err(|err| err.fill_position(*pos)) .map_err(|err| err.fill_position(pos))
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_, _) => unreachable!( Expr::FnCall(_, _) => unreachable!(
"function call in dot chain should not be namespace-qualified" "function call in dot chain should not be namespace-qualified"
), ),
// Others - syntax error // Others - syntax error
expr => unreachable!("invalid dot expression: {:?}", expr), ref expr => unreachable!("invalid dot expression: {:?}", expr),
} }
} }
// Syntax error // Syntax error
@ -1586,6 +1631,8 @@ impl Engine {
/// Evaluate a dot/index chain. /// Evaluate a dot/index chain.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[must_use]
#[must_use]
fn eval_dot_index_chain( fn eval_dot_index_chain(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1649,6 +1696,7 @@ impl Engine {
/// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of /// [`StaticVec`] is used to avoid an allocation in the overwhelming cases of
/// just a few levels of indexing. /// just a few levels of indexing.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[must_use]
fn eval_indexed_chain( fn eval_indexed_chain(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1774,6 +1822,7 @@ impl Engine {
/// Get the value at the indexed position of a base type. /// Get the value at the indexed position of a base type.
/// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards. /// [`Position`] in [`EvalAltResult`] may be [`NONE`][Position::NONE] and should be set afterwards.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[must_use]
fn get_indexed_mut<'t>( fn get_indexed_mut<'t>(
&self, &self,
mods: &mut Imports, mods: &mut Imports,
@ -1795,7 +1844,7 @@ impl Engine {
// val_array[idx] // val_array[idx]
let index = idx let index = idx
.as_int() .as_int()
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?; .map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
let arr_len = arr.len(); let arr_len = arr.len();
@ -1854,7 +1903,7 @@ impl Engine {
// val_int[idx] // val_int[idx]
let index = idx let index = idx
.as_int() .as_int()
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?; .map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
let bits = std::mem::size_of_val(value) * 8; let bits = std::mem::size_of_val(value) * 8;
@ -1891,7 +1940,7 @@ impl Engine {
// val_string[idx] // val_string[idx]
let index = idx let index = idx
.as_int() .as_int()
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?; .map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
let (ch, offset) = if index >= 0 { let (ch, offset) = if index >= 0 {
let offset = index as usize; let offset = index as usize;
@ -1939,6 +1988,7 @@ impl Engine {
} }
/// Evaluate an expression. /// Evaluate an expression.
#[must_use]
pub(crate) fn eval_expr( pub(crate) fn eval_expr(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -2059,7 +2109,7 @@ impl Engine {
let namespace = namespace let namespace = namespace
.as_ref() .as_ref()
.expect("never fails because function call is qualified"); .expect("never fails because function call is qualified");
let hash = hashes.native_hash(); let hash = hashes.native;
self.make_qualified_function_call( self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash, scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash,
*pos, level, *pos, level,
@ -2086,12 +2136,12 @@ impl Engine {
Ok((self Ok((self
.eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, x.lhs.position()))? .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.lhs.position()))?
&& // Short-circuit using && && // Short-circuit using &&
self self
.eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)? .eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, x.rhs.position()))?) .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.rhs.position()))?)
.into()) .into())
} }
@ -2099,12 +2149,12 @@ impl Engine {
Ok((self Ok((self
.eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, x.lhs.position()))? .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.lhs.position()))?
|| // Short-circuit using || || // Short-circuit using ||
self self
.eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)? .eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, x.rhs.position()))?) .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, x.rhs.position()))?)
.into()) .into())
} }
@ -2139,6 +2189,7 @@ impl Engine {
} }
/// Evaluate a statements block. /// Evaluate a statements block.
#[must_use]
pub(crate) fn eval_stmt_block( pub(crate) fn eval_stmt_block(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -2216,6 +2267,7 @@ impl Engine {
/// Evaluate an op-assignment statement. /// Evaluate an op-assignment statement.
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards. /// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards.
#[must_use]
pub(crate) fn eval_op_assignment( pub(crate) fn eval_op_assignment(
&self, &self,
mods: &mut Imports, mods: &mut Imports,
@ -2289,6 +2341,7 @@ impl Engine {
/// ///
/// This method uses some unsafe code, mainly for avoiding cloning of local variable names via /// This method uses some unsafe code, mainly for avoiding cloning of local variable names via
/// direct lifetime casting. /// direct lifetime casting.
#[must_use]
pub(crate) fn eval_stmt( pub(crate) fn eval_stmt(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -2397,7 +2450,7 @@ impl Engine {
let guard_val = self let guard_val = self
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?; .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?;
if guard_val { if guard_val {
if !x.0.is_empty() { if !x.0.is_empty() {
@ -2430,9 +2483,9 @@ impl Engine {
match self match self
.eval_expr(scope, mods, state, lib, this_ptr, &condition, level) .eval_expr(scope, mods, state, lib, this_ptr, &condition, level)
.and_then(|v| { .and_then(|v| {
v.as_bool().map_err(|err| { v.as_bool().map_err(|typ| {
self.make_type_mismatch_err::<bool>( self.make_type_mismatch_err::<bool>(
err, typ,
condition.position(), condition.position(),
) )
}) })
@ -2474,7 +2527,7 @@ impl Engine {
let condition = if !expr.is_unit() { let condition = if !expr.is_unit() {
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))? .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?
} else { } else {
true true
}; };
@ -2512,7 +2565,7 @@ impl Engine {
let condition = self let condition = self
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?; .map_err(|typ| self.make_type_mismatch_err::<bool>(typ, expr.position()))?;
if condition ^ *is_while { if condition ^ *is_while {
return Ok(Dynamic::UNIT); return Ok(Dynamic::UNIT);
@ -2649,7 +2702,7 @@ impl Engine {
let namespace = namespace let namespace = namespace
.as_ref() .as_ref()
.expect("never fails because function call is qualified"); .expect("never fails because function call is qualified");
let hash = hashes.native_hash(); let hash = hashes.native;
self.make_qualified_function_call( self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash, scope, mods, state, lib, this_ptr, namespace, name, args, constants, hash,
*pos, level, *pos, level,
@ -2673,7 +2726,7 @@ impl Engine {
} }
// Try/Catch statement // Try/Catch statement
Stmt::TryCatch(x, _, _) => { Stmt::TryCatch(x, _) => {
let (try_stmt, err_var, catch_stmt) = x.as_ref(); let (try_stmt, err_var, catch_stmt) = x.as_ref();
let result = self let result = self
@ -2898,11 +2951,13 @@ impl Engine {
// Export statement // Export statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Export(list, _) => { Stmt::Export(list, _) => {
for (Ident { name, pos, .. }, rename) in list.iter() { for (Ident { name, pos, .. }, Ident { name: rename, .. }) in list.as_ref() {
// Mark scope variables as public // Mark scope variables as public
if let Some(index) = scope.get_index(name).map(|(i, _)| i) { if let Some((index, _)) = scope.get_index(name) {
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name); scope.add_entry_alias(
scope.add_entry_alias(index, alias.clone()); index,
if rename.is_empty() { name } else { rename }.clone(),
);
} else { } else {
return EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos).into(); return EvalAltResult::ErrorVariableNotFound(name.to_string(), *pos).into();
} }
@ -2932,6 +2987,7 @@ impl Engine {
/// Check a result to ensure that the data size is within allowable limit. /// Check a result to ensure that the data size is within allowable limit.
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
#[inline(always)] #[inline(always)]
#[must_use]
fn check_return_value(&self, result: RhaiResult) -> RhaiResult { fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
result result
} }
@ -2939,17 +2995,20 @@ impl Engine {
/// Check a result to ensure that the data size is within allowable limit. /// Check a result to ensure that the data size is within allowable limit.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[inline(always)] #[inline(always)]
#[must_use]
fn check_return_value(&self, result: RhaiResult) -> RhaiResult { fn check_return_value(&self, result: RhaiResult) -> RhaiResult {
result.and_then(|r| self.check_data_size(&r).map(|_| r)) result.and_then(|r| self.check_data_size(&r).map(|_| r))
} }
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
#[inline(always)] #[inline(always)]
#[must_use]
fn check_data_size(&self, _value: &Dynamic) -> Result<(), Box<EvalAltResult>> { fn check_data_size(&self, _value: &Dynamic) -> Result<(), Box<EvalAltResult>> {
Ok(()) Ok(())
} }
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[must_use]
fn check_data_size(&self, value: &Dynamic) -> Result<(), Box<EvalAltResult>> { fn check_data_size(&self, value: &Dynamic) -> Result<(), Box<EvalAltResult>> {
// Recursively calculate the size of a value (especially `Array` and `Map`) // Recursively calculate the size of a value (especially `Array` and `Map`)
fn calc_size(value: &Dynamic) -> (usize, usize, usize) { fn calc_size(value: &Dynamic) -> (usize, usize, usize) {
@ -3052,6 +3111,7 @@ impl Engine {
/// Check if the number of operations stay within limit. /// Check if the number of operations stay within limit.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[must_use]
pub(crate) fn inc_operations( pub(crate) fn inc_operations(
&self, &self,
state: &mut State, state: &mut State,
@ -3080,6 +3140,7 @@ impl Engine {
/// If a type is registered via [`register_type_with_name`][Engine::register_type_with_name], /// If a type is registered via [`register_type_with_name`][Engine::register_type_with_name],
/// the type name provided for the registration will be used. /// the type name provided for the registration will be used.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str { pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
self.type_names self.type_names
.get(name) .get(name)
@ -3089,6 +3150,7 @@ impl Engine {
/// Make a `Box<`[`EvalAltResult<ErrorMismatchDataType>`][EvalAltResult::ErrorMismatchDataType]`>`. /// Make a `Box<`[`EvalAltResult<ErrorMismatchDataType>`][EvalAltResult::ErrorMismatchDataType]`>`.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> Box<EvalAltResult> { pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> Box<EvalAltResult> {
EvalAltResult::ErrorMismatchDataType( EvalAltResult::ErrorMismatchDataType(
self.map_type_name(type_name::<T>()).into(), self.map_type_name(type_name::<T>()).into(),

View File

@ -5,7 +5,7 @@ use crate::engine::{EvalContext, Imports, State};
use crate::fn_native::{FnCallArgs, SendSync}; use crate::fn_native::{FnCallArgs, SendSync};
use crate::fn_register::RegisterNativeFunction; use crate::fn_register::RegisterNativeFunction;
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::parser::ParseState; use crate::parse::ParseState;
use crate::{ use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module, scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module,
NativeCallContext, ParseError, Position, RhaiResult, Shared, AST, NativeCallContext, ParseError, Position, RhaiResult, Shared, AST,
@ -1031,6 +1031,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn compile(&self, script: &str) -> Result<AST, ParseError> { pub fn compile(&self, script: &str) -> Result<AST, ParseError> {
self.compile_with_scope(&Default::default(), script) self.compile_with_scope(&Default::default(), script)
} }
@ -1073,6 +1074,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> { pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, ParseError> {
self.compile_scripts_with_scope(scope, &[script]) self.compile_scripts_with_scope(scope, &[script])
} }
@ -1086,6 +1088,7 @@ impl Engine {
/// [`AST`]. When it is evaluated later, `import` statement directly recall pre-resolved /// [`AST`]. When it is evaluated later, `import` statement directly recall pre-resolved
/// [modules][Module] and the resolution process is not performed again. /// [modules][Module] and the resolution process is not performed again.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[must_use]
pub fn compile_into_self_contained( pub fn compile_into_self_contained(
&self, &self,
scope: &Scope, scope: &Scope,
@ -1197,6 +1200,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn compile_scripts_with_scope( pub fn compile_scripts_with_scope(
&self, &self,
scope: &Scope, scope: &Scope,
@ -1206,6 +1210,7 @@ impl Engine {
} }
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level. /// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn compile_with_scope_and_optimization_level( pub(crate) fn compile_with_scope_and_optimization_level(
&self, &self,
scope: &Scope, scope: &Scope,
@ -1224,6 +1229,7 @@ impl Engine {
/// Read the contents of a file into a string. /// Read the contents of a file into a string.
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[must_use]
fn read_file(path: std::path::PathBuf) -> Result<String, Box<EvalAltResult>> { fn read_file(path: std::path::PathBuf) -> Result<String, Box<EvalAltResult>> {
use std::io::Read; use std::io::Read;
@ -1279,6 +1285,7 @@ impl Engine {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn compile_file(&self, path: std::path::PathBuf) -> Result<AST, Box<EvalAltResult>> { pub fn compile_file(&self, path: std::path::PathBuf) -> Result<AST, Box<EvalAltResult>> {
self.compile_file_with_scope(&Default::default(), path) self.compile_file_with_scope(&Default::default(), path)
} }
@ -1318,6 +1325,7 @@ impl Engine {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn compile_file_with_scope( pub fn compile_file_with_scope(
&self, &self,
scope: &Scope, scope: &Scope,
@ -1368,6 +1376,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[must_use]
pub fn parse_json( pub fn parse_json(
&self, &self,
json: impl AsRef<str>, json: impl AsRef<str>,
@ -1443,6 +1452,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> { pub fn compile_expression(&self, script: &str) -> Result<AST, ParseError> {
self.compile_expression_with_scope(&Default::default(), script) self.compile_expression_with_scope(&Default::default(), script)
} }
@ -1486,6 +1496,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn compile_expression_with_scope( pub fn compile_expression_with_scope(
&self, &self,
scope: &Scope, scope: &Scope,
@ -1518,6 +1529,7 @@ impl Engine {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval_file<T: Variant + Clone>( pub fn eval_file<T: Variant + Clone>(
&self, &self,
path: std::path::PathBuf, path: std::path::PathBuf,
@ -1548,6 +1560,7 @@ impl Engine {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval_file_with_scope<T: Variant + Clone>( pub fn eval_file_with_scope<T: Variant + Clone>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1570,6 +1583,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> { pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> {
self.eval_with_scope(&mut Default::default(), script) self.eval_with_scope(&mut Default::default(), script)
} }
@ -1596,6 +1610,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval_with_scope<T: Variant + Clone>( pub fn eval_with_scope<T: Variant + Clone>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1623,6 +1638,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval_expression<T: Variant + Clone>( pub fn eval_expression<T: Variant + Clone>(
&self, &self,
script: &str, script: &str,
@ -1648,6 +1664,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval_expression_with_scope<T: Variant + Clone>( pub fn eval_expression_with_scope<T: Variant + Clone>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1686,6 +1703,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> { pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
self.eval_ast_with_scope(&mut Default::default(), ast) self.eval_ast_with_scope(&mut Default::default(), ast)
} }
@ -1719,6 +1737,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn eval_ast_with_scope<T: Variant + Clone>( pub fn eval_ast_with_scope<T: Variant + Clone>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1741,6 +1760,7 @@ impl Engine {
} }
/// Evaluate an [`AST`] with own scope. /// Evaluate an [`AST`] with own scope.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn eval_ast_with_scope_raw<'a>( pub(crate) fn eval_ast_with_scope_raw<'a>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1889,6 +1909,7 @@ impl Engine {
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn call_fn<T: Variant + Clone>( pub fn call_fn<T: Variant + Clone>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1968,6 +1989,7 @@ impl Engine {
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn call_fn_dynamic( pub fn call_fn_dynamic(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1991,6 +2013,7 @@ impl Engine {
/// clone them _before_ calling this function. /// clone them _before_ calling this function.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn call_fn_dynamic_raw( pub(crate) fn call_fn_dynamic_raw(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -2048,6 +2071,7 @@ impl Engine {
/// Then, the [`AST`] is cloned and the copy re-optimized before running. /// Then, the [`AST`] is cloned and the copy re-optimized before running.
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn optimize_ast( pub fn optimize_ast(
&self, &self,
scope: &Scope, scope: &Scope,
@ -2076,6 +2100,7 @@ impl Engine {
/// 2) Functions in registered sub-modules /// 2) Functions in registered sub-modules
/// 3) Functions in packages (optional) /// 3) Functions in packages (optional)
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[must_use]
pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> { pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> {
let mut signatures: Vec<_> = Default::default(); let mut signatures: Vec<_> = Default::default();

View File

@ -28,6 +28,7 @@ impl Engine {
/// Not available under `no_optimize`. /// Not available under `no_optimize`.
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn optimization_level(&self) -> crate::OptimizationLevel { pub fn optimization_level(&self) -> crate::OptimizationLevel {
self.optimization_level self.optimization_level
} }
@ -48,6 +49,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn max_call_levels(&self) -> usize { pub fn max_call_levels(&self) -> usize {
self.limits.max_call_stack_depth self.limits.max_call_stack_depth
} }
@ -66,6 +68,7 @@ impl Engine {
/// Not available under `unchecked`. /// Not available under `unchecked`.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn max_operations(&self) -> u64 { pub fn max_operations(&self) -> u64 {
self.limits.max_operations.map_or(0, NonZeroU64::get) self.limits.max_operations.map_or(0, NonZeroU64::get)
} }
@ -85,6 +88,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn max_modules(&self) -> usize { pub fn max_modules(&self) -> usize {
self.limits.max_modules self.limits.max_modules
} }
@ -110,6 +114,7 @@ impl Engine {
/// Not available under `unchecked`. /// Not available under `unchecked`.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn max_expr_depth(&self) -> usize { pub fn max_expr_depth(&self) -> usize {
self.limits.max_expr_depth.map_or(0, NonZeroUsize::get) self.limits.max_expr_depth.map_or(0, NonZeroUsize::get)
} }
@ -119,6 +124,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn max_function_expr_depth(&self) -> usize { pub fn max_function_expr_depth(&self) -> usize {
self.limits self.limits
.max_function_expr_depth .max_function_expr_depth
@ -138,6 +144,7 @@ impl Engine {
/// Not available under `unchecked`. /// Not available under `unchecked`.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn max_string_size(&self) -> usize { pub fn max_string_size(&self) -> usize {
self.limits.max_string_size.map_or(0, NonZeroUsize::get) self.limits.max_string_size.map_or(0, NonZeroUsize::get)
} }
@ -157,6 +164,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn max_array_size(&self) -> usize { pub fn max_array_size(&self) -> usize {
self.limits.max_array_size.map_or(0, NonZeroUsize::get) self.limits.max_array_size.map_or(0, NonZeroUsize::get)
} }
@ -176,6 +184,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn max_map_size(&self) -> usize { pub fn max_map_size(&self) -> usize {
self.limits.max_map_size.map_or(0, NonZeroUsize::get) self.limits.max_map_size.map_or(0, NonZeroUsize::get)
} }
@ -260,6 +269,7 @@ impl Engine {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[must_use]
pub fn register_custom_operator( pub fn register_custom_operator(
&mut self, &mut self,
keyword: impl AsRef<str> + Into<Identifier>, keyword: impl AsRef<str> + Into<Identifier>,

View File

@ -95,6 +95,7 @@ pub enum EvalAltResult {
} }
impl EvalAltResult { impl EvalAltResult {
#[must_use]
pub(crate) fn desc(&self) -> &str { pub(crate) fn desc(&self) -> &str {
match self { match self {
#[allow(deprecated)] #[allow(deprecated)]
@ -275,6 +276,7 @@ impl EvalAltResult {
/// Is this a pseudo error? A pseudo error is one that does not occur naturally. /// Is this a pseudo error? A pseudo error is one that does not occur naturally.
/// ///
/// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors. /// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors.
#[must_use]
pub fn is_pseudo_error(&self) -> bool { pub fn is_pseudo_error(&self) -> bool {
match self { match self {
Self::LoopBreak(_, _) | Self::Return(_, _) => true, Self::LoopBreak(_, _) | Self::Return(_, _) => true,
@ -286,6 +288,7 @@ impl EvalAltResult {
/// # Panics /// # Panics
/// ///
/// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return]. /// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return].
#[must_use]
pub fn is_catchable(&self) -> bool { pub fn is_catchable(&self) -> bool {
match self { match self {
Self::ErrorSystem(_, _) => false, Self::ErrorSystem(_, _) => false,
@ -325,6 +328,7 @@ impl EvalAltResult {
/// # Panics /// # Panics
/// ///
/// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return]. /// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return].
#[must_use]
pub fn is_system_exception(&self) -> bool { pub fn is_system_exception(&self) -> bool {
match self { match self {
Self::ErrorSystem(_, _) => true, Self::ErrorSystem(_, _) => true,
@ -412,6 +416,7 @@ impl EvalAltResult {
}; };
} }
/// Get the [position][Position] of this error. /// Get the [position][Position] of this error.
#[must_use]
pub fn position(&self) -> Position { pub fn position(&self) -> Position {
match self { match self {
Self::ErrorSystem(_, _) => Position::NONE, Self::ErrorSystem(_, _) => Position::NONE,
@ -444,6 +449,12 @@ impl EvalAltResult {
| Self::Return(_, pos) => *pos, | Self::Return(_, pos) => *pos,
} }
} }
/// Remove the [position][Position] information from this error.
///
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
pub fn clear_position(&mut self) -> &mut Self {
self.set_position(Position::NONE)
}
/// Remove the [position][Position] information from this error and return it. /// Remove the [position][Position] information from this error and return it.
/// ///
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards. /// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
@ -453,7 +464,7 @@ impl EvalAltResult {
pos pos
} }
/// Override the [position][Position] of this error. /// Override the [position][Position] of this error.
pub fn set_position(&mut self, new_position: Position) { pub fn set_position(&mut self, new_position: Position) -> &mut Self {
match self { match self {
Self::ErrorSystem(_, _) => (), Self::ErrorSystem(_, _) => (),
@ -484,10 +495,12 @@ impl EvalAltResult {
| Self::LoopBreak(_, pos) | Self::LoopBreak(_, pos)
| Self::Return(_, pos) => *pos = new_position, | Self::Return(_, pos) => *pos = new_position,
} }
self
} }
/// Consume the current [`EvalAltResult`] and return a new one with the specified [`Position`] /// Consume the current [`EvalAltResult`] and return a new one with the specified [`Position`]
/// if the current position is [`Position::None`]. /// if the current position is [`Position::None`].
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn fill_position(mut self: Box<Self>, new_position: Position) -> Box<Self> { pub(crate) fn fill_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
if self.position().is_none() { if self.position().is_none() {
self.set_position(new_position); self.set_position(new_position);

View File

@ -58,6 +58,7 @@ impl fmt::Display for LexError {
} }
impl LexError { impl LexError {
#[must_use]
pub(crate) fn desc(&self) -> &str { pub(crate) fn desc(&self) -> &str {
match self { match self {
Self::UnexpectedInput(_) => "Unexpected character encountered", Self::UnexpectedInput(_) => "Unexpected character encountered",
@ -72,6 +73,7 @@ impl LexError {
} }
/// Convert a [`LexError`] into a [`ParseError`]. /// Convert a [`LexError`] into a [`ParseError`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn into_err(self, pos: Position) -> ParseError { pub fn into_err(self, pos: Position) -> ParseError {
ParseError(Box::new(self.into()), pos) ParseError(Box::new(self.into()), pos)
} }
@ -188,10 +190,12 @@ pub enum ParseErrorType {
impl ParseErrorType { impl ParseErrorType {
/// Make a [`ParseError`] using the current type and position. /// Make a [`ParseError`] using the current type and position.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn into_err(self, pos: Position) -> ParseError { pub(crate) fn into_err(self, pos: Position) -> ParseError {
ParseError(Box::new(self), pos) ParseError(Box::new(self), pos)
} }
#[must_use]
pub(crate) fn desc(&self) -> &str { pub(crate) fn desc(&self) -> &str {
match self { match self {
Self::UnexpectedEOF => "Script is incomplete", Self::UnexpectedEOF => "Script is incomplete",

View File

@ -17,6 +17,7 @@ const BUILTIN: &str = "never fails because this is built-in code and the type is
/// Is the type a numeric type? /// Is the type a numeric type?
#[inline(always)] #[inline(always)]
#[must_use]
fn is_numeric(type_id: TypeId) -> bool { fn is_numeric(type_id: TypeId) -> bool {
let result = type_id == TypeId::of::<u8>() let result = type_id == TypeId::of::<u8>()
|| type_id == TypeId::of::<u16>() || type_id == TypeId::of::<u16>()
@ -40,6 +41,7 @@ fn is_numeric(type_id: TypeId) -> bool {
} }
/// Build in common binary operator implementations to avoid the cost of calling a registered function. /// Build in common binary operator implementations to avoid the cost of calling a registered function.
#[must_use]
pub fn get_builtin_binary_op_fn( pub fn get_builtin_binary_op_fn(
op: &str, op: &str,
x: &Dynamic, x: &Dynamic,
@ -54,91 +56,89 @@ pub fn get_builtin_binary_op_fn(
// One of the operands is a custom type, so it is never built-in // One of the operands is a custom type, so it is never built-in
if x.is_variant() || y.is_variant() { if x.is_variant() || y.is_variant() {
if is_numeric(type1) && is_numeric(type2) { return if is_numeric(type1) && is_numeric(type2) {
// Disallow comparisons between different numeric types // Disallow comparisons between different numeric types
return None; None
} } else if type1 != type2 {
// If the types are not the same, default to not compare // If the types are not the same, default to not compare
if type1 != type2 { match op {
return match op {
"!=" => Some(|_, _| Ok(Dynamic::TRUE)), "!=" => Some(|_, _| Ok(Dynamic::TRUE)),
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
_ => None, _ => None,
};
} }
} else {
// Disallow comparisons between the same type // Disallow comparisons between the same type
return None; None
};
} }
let types_pair = (type1, type2); let types_pair = (type1, type2);
macro_rules! impl_op { macro_rules! impl_op {
($xx:ident $op:tt $yy:ident) => { ($xx:ident $op:tt $yy:ident) => {
return Some(|_, args| { Some(|_, args| {
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
Ok((x $op y).into()) Ok((x $op y).into())
}) })
}; };
($xx:ident . $func:ident ( $yy:ty )) => { ($xx:ident . $func:ident ( $yy:ty )) => {
return Some(|_, args| { Some(|_, args| {
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
Ok(x.$func(y).into()) Ok(x.$func(y).into())
}) })
}; };
($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { ($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => {
return Some(|_, args| { Some(|_, args| {
let x = &*args[0].read_lock::<$xx>().expect(BUILTIN); let x = &*args[0].read_lock::<$xx>().expect(BUILTIN);
let y = &*args[1].read_lock::<$yy>().expect(BUILTIN); let y = &*args[1].read_lock::<$yy>().expect(BUILTIN);
Ok(x.$func(y.$yyy()).into()) Ok(x.$func(y.$yyy()).into())
}) })
}; };
($func:ident ( $op:tt )) => { ($func:ident ( $op:tt )) => {
return Some(|_, args| { Some(|_, args| {
let (x, y) = $func(args); let (x, y) = $func(args);
Ok((x $op y).into()) Ok((x $op y).into())
}) })
}; };
($base:ty => $xx:ident $op:tt $yy:ident) => { ($base:ty => $xx:ident $op:tt $yy:ident) => {
return Some(|_, args| { Some(|_, args| {
let x = args[0].$xx().expect(BUILTIN) as $base; let x = args[0].$xx().expect(BUILTIN) as $base;
let y = args[1].$yy().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base;
Ok((x $op y).into()) Ok((x $op y).into())
}) })
}; };
($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { ($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => {
return Some(|_, args| { Some(|_, args| {
let x = args[0].$xx().expect(BUILTIN) as $base; let x = args[0].$xx().expect(BUILTIN) as $base;
let y = args[1].$yy().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base;
Ok(x.$func(y as $yyy).into()) Ok(x.$func(y as $yyy).into())
}) })
}; };
($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { ($base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
return Some(|_, args| { Some(|_, args| {
let x = args[0].$xx().expect(BUILTIN) as $base; let x = args[0].$xx().expect(BUILTIN) as $base;
let y = args[1].$yy().expect(BUILTIN) as $base; let y = args[1].$yy().expect(BUILTIN) as $base;
$func(x, y).map(Into::<Dynamic>::into) $func(x, y).map(Into::<Dynamic>::into)
}) })
}; };
(from $base:ty => $xx:ident $op:tt $yy:ident) => { (from $base:ty => $xx:ident $op:tt $yy:ident) => {
return Some(|_, args| { Some(|_, args| {
let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let x = <$base>::from(args[0].$xx().expect(BUILTIN));
let y = <$base>::from(args[1].$yy().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN));
Ok((x $op y).into()) Ok((x $op y).into())
}) })
}; };
(from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { (from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => {
return Some(|_, args| { Some(|_, args| {
let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let x = <$base>::from(args[0].$xx().expect(BUILTIN));
let y = <$base>::from(args[1].$yy().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN));
Ok(x.$func(y).into()) Ok(x.$func(y).into())
}) })
}; };
(from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => {
return Some(|_, args| { Some(|_, args| {
let x = <$base>::from(args[0].$xx().expect(BUILTIN)); let x = <$base>::from(args[0].$xx().expect(BUILTIN));
let y = <$base>::from(args[1].$yy().expect(BUILTIN)); let y = <$base>::from(args[1].$yy().expect(BUILTIN));
$func(x, y).map(Into::<Dynamic>::into) $func(x, y).map(Into::<Dynamic>::into)
@ -150,7 +150,7 @@ pub fn get_builtin_binary_op_fn(
($x:ty, $xx:ident, $y:ty, $yy:ident) => { ($x:ty, $xx:ident, $y:ty, $yy:ident) => {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) { if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
match op { return match op {
"+" => impl_op!(FLOAT => $xx + $yy), "+" => impl_op!(FLOAT => $xx + $yy),
"-" => impl_op!(FLOAT => $xx - $yy), "-" => impl_op!(FLOAT => $xx - $yy),
"*" => impl_op!(FLOAT => $xx * $yy), "*" => impl_op!(FLOAT => $xx * $yy),
@ -163,8 +163,8 @@ pub fn get_builtin_binary_op_fn(
">=" => impl_op!(FLOAT => $xx >= $yy), ">=" => impl_op!(FLOAT => $xx >= $yy),
"<" => impl_op!(FLOAT => $xx < $yy), "<" => impl_op!(FLOAT => $xx < $yy),
"<=" => impl_op!(FLOAT => $xx <= $yy), "<=" => impl_op!(FLOAT => $xx <= $yy),
_ => return None, _ => None,
} };
} }
}; };
} }
@ -177,41 +177,43 @@ pub fn get_builtin_binary_op_fn(
($x:ty, $xx:ident, $y:ty, $yy:ident) => { ($x:ty, $xx:ident, $y:ty, $yy:ident) => {
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) { if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
if cfg!(not(feature = "unBUILTIN")) { #[cfg(not(feature = "unchecked"))]
use crate::packages::arithmetic::decimal_functions::*; use crate::packages::arithmetic::decimal_functions::*;
#[cfg(not(feature = "unchecked"))]
match op { match op {
"+" => impl_op!(from Decimal => add($xx, $yy)), "+" => return impl_op!(from Decimal => add($xx, $yy)),
"-" => impl_op!(from Decimal => subtract($xx, $yy)), "-" => return impl_op!(from Decimal => subtract($xx, $yy)),
"*" => impl_op!(from Decimal => multiply($xx, $yy)), "*" => return impl_op!(from Decimal => multiply($xx, $yy)),
"/" => impl_op!(from Decimal => divide($xx, $yy)), "/" => return impl_op!(from Decimal => divide($xx, $yy)),
"%" => impl_op!(from Decimal => modulo($xx, $yy)), "%" => return impl_op!(from Decimal => modulo($xx, $yy)),
"**" => impl_op!(from Decimal => power($xx, $yy)), "**" => return impl_op!(from Decimal => power($xx, $yy)),
_ => () _ => ()
} }
} else {
#[cfg(feature = "unchecked")]
use rust_decimal::MathematicalOps; use rust_decimal::MathematicalOps;
#[cfg(feature = "unchecked")]
match op { match op {
"+" => impl_op!(from Decimal => $xx + $yy), "+" => return impl_op!(from Decimal => $xx + $yy),
"-" => impl_op!(from Decimal => $xx - $yy), "-" => return impl_op!(from Decimal => $xx - $yy),
"*" => impl_op!(from Decimal => $xx * $yy), "*" => return impl_op!(from Decimal => $xx * $yy),
"/" => impl_op!(from Decimal => $xx / $yy), "/" => return impl_op!(from Decimal => $xx / $yy),
"%" => impl_op!(from Decimal => $xx % $yy), "%" => return impl_op!(from Decimal => $xx % $yy),
"**" => impl_op!(from Decimal => $xx.powd($yy)), "**" => return impl_op!(from Decimal => $xx.powd($yy)),
_ => () _ => ()
} }
}
match op { return match op {
"==" => impl_op!(from Decimal => $xx == $yy), "==" => impl_op!(from Decimal => $xx == $yy),
"!=" => impl_op!(from Decimal => $xx != $yy), "!=" => impl_op!(from Decimal => $xx != $yy),
">" => impl_op!(from Decimal => $xx > $yy), ">" => impl_op!(from Decimal => $xx > $yy),
">=" => impl_op!(from Decimal => $xx >= $yy), ">=" => impl_op!(from Decimal => $xx >= $yy),
"<" => impl_op!(from Decimal => $xx < $yy), "<" => impl_op!(from Decimal => $xx < $yy),
"<=" => impl_op!(from Decimal => $xx <= $yy), "<=" => impl_op!(from Decimal => $xx <= $yy),
_ => return None _ => None
} };
} }
}; };
} }
@ -232,22 +234,20 @@ pub fn get_builtin_binary_op_fn(
(s1, s2) (s1, s2)
} }
match op { return match op {
"+" => { "+" => Some(|_, args| {
return Some(|_, args| {
let x = args[0].as_char().expect(BUILTIN); let x = args[0].as_char().expect(BUILTIN);
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN); let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
Ok(format!("{}{}", x, y).into()) Ok(format!("{}{}", x, y).into())
}) }),
}
"==" => impl_op!(get_s1s2(==)), "==" => impl_op!(get_s1s2(==)),
"!=" => impl_op!(get_s1s2(!=)), "!=" => impl_op!(get_s1s2(!=)),
">" => impl_op!(get_s1s2(>)), ">" => impl_op!(get_s1s2(>)),
">=" => impl_op!(get_s1s2(>=)), ">=" => impl_op!(get_s1s2(>=)),
"<" => impl_op!(get_s1s2(<)), "<" => impl_op!(get_s1s2(<)),
"<=" => impl_op!(get_s1s2(<=)), "<=" => impl_op!(get_s1s2(<=)),
_ => return None, _ => None,
} };
} }
// string op char // string op char
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) { if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
@ -261,36 +261,30 @@ pub fn get_builtin_binary_op_fn(
(s1, s2) (s1, s2)
} }
match op { return match op {
"+" => { "+" => Some(|_, args| {
return Some(|_, args| {
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN); let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
Ok((x + y).into()) Ok((x + y).into())
}) }),
} "-" => Some(|_, args| {
"-" => {
return Some(|_, args| {
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN); let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
Ok((x - y).into()) Ok((x - y).into())
}) }),
}
"==" => impl_op!(get_s1s2(==)), "==" => impl_op!(get_s1s2(==)),
"!=" => impl_op!(get_s1s2(!=)), "!=" => impl_op!(get_s1s2(!=)),
">" => impl_op!(get_s1s2(>)), ">" => impl_op!(get_s1s2(>)),
">=" => impl_op!(get_s1s2(>=)), ">=" => impl_op!(get_s1s2(>=)),
"<" => impl_op!(get_s1s2(<)), "<" => impl_op!(get_s1s2(<)),
"<=" => impl_op!(get_s1s2(<=)), "<=" => impl_op!(get_s1s2(<=)),
OP_CONTAINS => { OP_CONTAINS => Some(|_, args| {
return Some(|_, args| {
let s = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN); let s = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let c = args[1].as_char().expect(BUILTIN); let c = args[1].as_char().expect(BUILTIN);
Ok((s.contains(c)).into()) Ok((s.contains(c)).into())
}) }),
} _ => None,
_ => return None, };
}
} }
// map op string // map op string
@ -298,10 +292,10 @@ pub fn get_builtin_binary_op_fn(
if types_pair == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) { if types_pair == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) {
use crate::Map; use crate::Map;
match op { return match op {
OP_CONTAINS => impl_op!(Map.contains_key(ImmutableString.as_str())), OP_CONTAINS => impl_op!(Map.contains_key(ImmutableString.as_str())),
_ => return None, _ => None,
} };
} }
// Default comparison operators for different types // Default comparison operators for different types
@ -316,35 +310,36 @@ pub fn get_builtin_binary_op_fn(
// Beyond here, type1 == type2 // Beyond here, type1 == type2
if type1 == TypeId::of::<INT>() { if type1 == TypeId::of::<INT>() {
if cfg!(not(feature = "unBUILTIN")) { #[cfg(not(feature = "unchecked"))]
use crate::packages::arithmetic::arith_basic::INT::functions::*; use crate::packages::arithmetic::arith_basic::INT::functions::*;
#[cfg(not(feature = "unchecked"))]
match op { match op {
"+" => impl_op!(INT => add(as_int, as_int)), "+" => return impl_op!(INT => add(as_int, as_int)),
"-" => impl_op!(INT => subtract(as_int, as_int)), "-" => return impl_op!(INT => subtract(as_int, as_int)),
"*" => impl_op!(INT => multiply(as_int, as_int)), "*" => return impl_op!(INT => multiply(as_int, as_int)),
"/" => impl_op!(INT => divide(as_int, as_int)), "/" => return impl_op!(INT => divide(as_int, as_int)),
"%" => impl_op!(INT => modulo(as_int, as_int)), "%" => return impl_op!(INT => modulo(as_int, as_int)),
"**" => impl_op!(INT => power(as_int, as_int)), "**" => return impl_op!(INT => power(as_int, as_int)),
">>" => impl_op!(INT => shift_right(as_int, as_int)), ">>" => return impl_op!(INT => shift_right(as_int, as_int)),
"<<" => impl_op!(INT => shift_left(as_int, as_int)), "<<" => return impl_op!(INT => shift_left(as_int, as_int)),
_ => (), _ => (),
} }
} else {
match op {
"+" => impl_op!(INT => as_int + as_int),
"-" => impl_op!(INT => as_int - as_int),
"*" => impl_op!(INT => as_int * as_int),
"/" => impl_op!(INT => as_int / as_int),
"%" => impl_op!(INT => as_int % as_int),
"**" => impl_op!(INT => as_int.pow(as_int as u32)),
">>" => impl_op!(INT => as_int >> as_int),
"<<" => impl_op!(INT => as_int << as_int),
_ => (),
}
}
#[cfg(feature = "unchecked")]
match op { match op {
"+" => return impl_op!(INT => as_int + as_int),
"-" => return impl_op!(INT => as_int - as_int),
"*" => return impl_op!(INT => as_int * as_int),
"/" => return impl_op!(INT => as_int / as_int),
"%" => return impl_op!(INT => as_int % as_int),
"**" => return impl_op!(INT => as_int.pow(as_int as u32)),
">>" => return impl_op!(INT => as_int >> as_int),
"<<" => return impl_op!(INT => as_int << as_int),
_ => (),
}
return match op {
"==" => impl_op!(INT => as_int == as_int), "==" => impl_op!(INT => as_int == as_int),
"!=" => impl_op!(INT => as_int != as_int), "!=" => impl_op!(INT => as_int != as_int),
">" => impl_op!(INT => as_int > as_int), ">" => impl_op!(INT => as_int > as_int),
@ -354,12 +349,12 @@ pub fn get_builtin_binary_op_fn(
"&" => impl_op!(INT => as_int & as_int), "&" => impl_op!(INT => as_int & as_int),
"|" => impl_op!(INT => as_int | as_int), "|" => impl_op!(INT => as_int | as_int),
"^" => impl_op!(INT => as_int ^ as_int), "^" => impl_op!(INT => as_int ^ as_int),
_ => return None, _ => None,
} };
} }
if type1 == TypeId::of::<bool>() { if type1 == TypeId::of::<bool>() {
match op { return match op {
"==" => impl_op!(bool => as_bool == as_bool), "==" => impl_op!(bool => as_bool == as_bool),
"!=" => impl_op!(bool => as_bool != as_bool), "!=" => impl_op!(bool => as_bool != as_bool),
">" => impl_op!(bool => as_bool > as_bool), ">" => impl_op!(bool => as_bool > as_bool),
@ -369,12 +364,12 @@ pub fn get_builtin_binary_op_fn(
"&" => impl_op!(bool => as_bool & as_bool), "&" => impl_op!(bool => as_bool & as_bool),
"|" => impl_op!(bool => as_bool | as_bool), "|" => impl_op!(bool => as_bool | as_bool),
"^" => impl_op!(bool => as_bool ^ as_bool), "^" => impl_op!(bool => as_bool ^ as_bool),
_ => return None, _ => None,
} };
} }
if type1 == TypeId::of::<ImmutableString>() { if type1 == TypeId::of::<ImmutableString>() {
match op { return match op {
"+" => impl_op!(ImmutableString + ImmutableString), "+" => impl_op!(ImmutableString + ImmutableString),
"-" => impl_op!(ImmutableString - ImmutableString), "-" => impl_op!(ImmutableString - ImmutableString),
"==" => impl_op!(ImmutableString == ImmutableString), "==" => impl_op!(ImmutableString == ImmutableString),
@ -383,48 +378,45 @@ pub fn get_builtin_binary_op_fn(
">=" => impl_op!(ImmutableString >= ImmutableString), ">=" => impl_op!(ImmutableString >= ImmutableString),
"<" => impl_op!(ImmutableString < ImmutableString), "<" => impl_op!(ImmutableString < ImmutableString),
"<=" => impl_op!(ImmutableString <= ImmutableString), "<=" => impl_op!(ImmutableString <= ImmutableString),
OP_CONTAINS => { OP_CONTAINS => Some(|_, args| {
return Some(|_, args| {
let s1 = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN); let s1 = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let s2 = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN); let s2 = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
Ok((s1.contains(s2.as_str())).into()) Ok((s1.contains(s2.as_str())).into())
}) }),
} _ => None,
_ => return None, };
}
} }
if type1 == TypeId::of::<char>() { if type1 == TypeId::of::<char>() {
match op { return match op {
"+" => { "+" => Some(|_, args| {
return Some(|_, args| {
let x = args[0].as_char().expect(BUILTIN); let x = args[0].as_char().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
Ok(format!("{}{}", x, y).into()) Ok(format!("{}{}", x, y).into())
}) }),
}
"==" => impl_op!(char => as_char == as_char), "==" => impl_op!(char => as_char == as_char),
"!=" => impl_op!(char => as_char != as_char), "!=" => impl_op!(char => as_char != as_char),
">" => impl_op!(char => as_char > as_char), ">" => impl_op!(char => as_char > as_char),
">=" => impl_op!(char => as_char >= as_char), ">=" => impl_op!(char => as_char >= as_char),
"<" => impl_op!(char => as_char < as_char), "<" => impl_op!(char => as_char < as_char),
"<=" => impl_op!(char => as_char <= as_char), "<=" => impl_op!(char => as_char <= as_char),
_ => return None, _ => None,
} };
} }
if type1 == TypeId::of::<()>() { if type1 == TypeId::of::<()>() {
match op { return match op {
"==" => return Some(|_, _| Ok(Dynamic::TRUE)), "==" => Some(|_, _| Ok(Dynamic::TRUE)),
"!=" | ">" | ">=" | "<" | "<=" => return Some(|_, _| Ok(Dynamic::FALSE)), "!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)),
_ => return None, _ => None,
} };
} }
None None
} }
/// Build in common operator assignment implementations to avoid the cost of calling a registered function. /// Build in common operator assignment implementations to avoid the cost of calling a registered function.
#[must_use]
pub fn get_builtin_op_assignment_fn( pub fn get_builtin_op_assignment_fn(
op: &str, op: &str,
x: &Dynamic, x: &Dynamic,
@ -441,53 +433,53 @@ pub fn get_builtin_op_assignment_fn(
macro_rules! impl_op { macro_rules! impl_op {
($x:ty = x $op:tt $yy:ident) => { ($x:ty = x $op:tt $yy:ident) => {
return Some(|_, args| { Some(|_, args| {
let x = args[0].$yy().expect(BUILTIN); let x = args[0].$yy().expect(BUILTIN);
let y = args[1].$yy().expect(BUILTIN) as $x; let y = args[1].$yy().expect(BUILTIN) as $x;
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x $op y).into()) Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x $op y).into())
}) })
}; };
($x:ident $op:tt $yy:ident) => { ($x:ident $op:tt $yy:ident) => {
return Some(|_, args| { Some(|_, args| {
let y = args[1].$yy().expect(BUILTIN) as $x; let y = args[1].$yy().expect(BUILTIN) as $x;
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
}) })
}; };
($x:ident $op:tt $yy:ident as $yyy:ty) => { ($x:ident $op:tt $yy:ident as $yyy:ty) => {
return Some(|_, args| { Some(|_, args| {
let y = args[1].$yy().expect(BUILTIN) as $yyy; let y = args[1].$yy().expect(BUILTIN) as $yyy;
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
}) })
}; };
($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { ($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => {
return Some(|_, args| { Some(|_, args| {
let x = args[0].$xx().expect(BUILTIN); let x = args[0].$xx().expect(BUILTIN);
let y = args[1].$yy().expect(BUILTIN) as $x; let y = args[1].$yy().expect(BUILTIN) as $x;
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y as $yyy)).into()) Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y as $yyy)).into())
}) })
}; };
($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { ($x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
return Some(|_, args| { Some(|_, args| {
let x = args[0].$xx().expect(BUILTIN); let x = args[0].$xx().expect(BUILTIN);
let y = args[1].$yy().expect(BUILTIN) as $x; let y = args[1].$yy().expect(BUILTIN) as $x;
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into()) Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
}) })
}; };
(from $x:ident $op:tt $yy:ident) => { (from $x:ident $op:tt $yy:ident) => {
return Some(|_, args| { Some(|_, args| {
let y = <$x>::from(args[1].$yy().expect(BUILTIN)); let y = <$x>::from(args[1].$yy().expect(BUILTIN));
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into()) Ok((*args[0].write_lock::<$x>().expect(BUILTIN) $op y).into())
}) })
}; };
(from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { (from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => {
return Some(|_, args| { Some(|_, args| {
let x = args[0].$xx().expect(BUILTIN); let x = args[0].$xx().expect(BUILTIN);
let y = <$x>::from(args[1].$yy().expect(BUILTIN)); let y = <$x>::from(args[1].$yy().expect(BUILTIN));
Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y)).into()) Ok((*args[0].write_lock::<$x>().expect(BUILTIN) = x.$func(y)).into())
}) })
}; };
(from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => {
return Some(|_, args| { Some(|_, args| {
let x = args[0].$xx().expect(BUILTIN); let x = args[0].$xx().expect(BUILTIN);
let y = <$x>::from(args[1].$yy().expect(BUILTIN)); let y = <$x>::from(args[1].$yy().expect(BUILTIN));
Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into()) Ok((*args[0].write_lock().expect(BUILTIN) = $func(x, y)?).into())
@ -499,15 +491,15 @@ pub fn get_builtin_op_assignment_fn(
($x:ident, $xx:ident, $y:ty, $yy:ident) => { ($x:ident, $xx:ident, $y:ty, $yy:ident) => {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) { if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
match op { return match op {
"+=" => impl_op!($x += $yy), "+=" => impl_op!($x += $yy),
"-=" => impl_op!($x -= $yy), "-=" => impl_op!($x -= $yy),
"*=" => impl_op!($x *= $yy), "*=" => impl_op!($x *= $yy),
"/=" => impl_op!($x /= $yy), "/=" => impl_op!($x /= $yy),
"%=" => impl_op!($x %= $yy), "%=" => impl_op!($x %= $yy),
"**=" => impl_op!($x => $xx.powf($yy as $x)), "**=" => impl_op!($x => $xx.powf($yy as $x)),
_ => return None, _ => None,
} };
} }
} }
} }
@ -519,31 +511,33 @@ pub fn get_builtin_op_assignment_fn(
($x:ident, $xx:ident, $y:ty, $yy:ident) => { ($x:ident, $xx:ident, $y:ty, $yy:ident) => {
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) { if types_pair == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
if cfg!(not(feature = "unBUILTIN")) { #[cfg(not(feature = "unchecked"))]
use crate::packages::arithmetic::decimal_functions::*; use crate::packages::arithmetic::decimal_functions::*;
match op { #[cfg(not(feature = "unchecked"))]
return match op {
"+=" => impl_op!(from $x => add($xx, $yy)), "+=" => impl_op!(from $x => add($xx, $yy)),
"-=" => impl_op!(from $x => subtract($xx, $yy)), "-=" => impl_op!(from $x => subtract($xx, $yy)),
"*=" => impl_op!(from $x => multiply($xx, $yy)), "*=" => impl_op!(from $x => multiply($xx, $yy)),
"/=" => impl_op!(from $x => divide($xx, $yy)), "/=" => impl_op!(from $x => divide($xx, $yy)),
"%=" => impl_op!(from $x => modulo($xx, $yy)), "%=" => impl_op!(from $x => modulo($xx, $yy)),
"**=" => impl_op!(from $x => power($xx, $yy)), "**=" => impl_op!(from $x => power($xx, $yy)),
_ => return None, _ => None,
} };
} else {
#[cfg(feature = "unchecked")]
use rust_decimal::MathematicalOps; use rust_decimal::MathematicalOps;
match op { #[cfg(feature = "unchecked")]
return match op {
"+=" => impl_op!(from $x += $yy), "+=" => impl_op!(from $x += $yy),
"-=" => impl_op!(from $x -= $yy), "-=" => impl_op!(from $x -= $yy),
"*=" => impl_op!(from $x *= $yy), "*=" => impl_op!(from $x *= $yy),
"/=" => impl_op!(from $x /= $yy), "/=" => impl_op!(from $x /= $yy),
"%=" => impl_op!(from $x %= $yy), "%=" => impl_op!(from $x %= $yy),
"**=" => impl_op!(from $x => $xx.powd($yy)), "**=" => impl_op!(from $x => $xx.powd($yy)),
_ => return None, _ => None,
} };
}
} }
}; };
} }
@ -553,17 +547,16 @@ pub fn get_builtin_op_assignment_fn(
// string op= char // string op= char
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) { if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
match op { return match op {
"+=" => impl_op!(ImmutableString += as_char as char), "+=" => impl_op!(ImmutableString += as_char as char),
"-=" => impl_op!(ImmutableString -= as_char as char), "-=" => impl_op!(ImmutableString -= as_char as char),
_ => return None, _ => None,
} };
} }
// char op= string // char op= string
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) { if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
match op { return match op {
"+=" => { "+=" => Some(|_, args| {
return Some(|_, args| {
let mut ch = args[0].as_char().expect(BUILTIN).to_string(); let mut ch = args[0].as_char().expect(BUILTIN).to_string();
ch.push_str( ch.push_str(
args[1] args[1]
@ -574,10 +567,9 @@ pub fn get_builtin_op_assignment_fn(
let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN); let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN);
Ok((*x = ch.into()).into()) Ok((*x = ch.into()).into())
}) }),
} _ => None,
_ => return None, };
}
} }
// No built-in op-assignments for different types. // No built-in op-assignments for different types.
@ -587,83 +579,78 @@ pub fn get_builtin_op_assignment_fn(
// Beyond here, type1 == type2 // Beyond here, type1 == type2
if type1 == TypeId::of::<INT>() { if type1 == TypeId::of::<INT>() {
if cfg!(not(feature = "unBUILTIN")) { #[cfg(not(feature = "unchecked"))]
use crate::packages::arithmetic::arith_basic::INT::functions::*; use crate::packages::arithmetic::arith_basic::INT::functions::*;
#[cfg(not(feature = "unchecked"))]
match op { match op {
"+=" => impl_op!(INT => add(as_int, as_int)), "+=" => return impl_op!(INT => add(as_int, as_int)),
"-=" => impl_op!(INT => subtract(as_int, as_int)), "-=" => return impl_op!(INT => subtract(as_int, as_int)),
"*=" => impl_op!(INT => multiply(as_int, as_int)), "*=" => return impl_op!(INT => multiply(as_int, as_int)),
"/=" => impl_op!(INT => divide(as_int, as_int)), "/=" => return impl_op!(INT => divide(as_int, as_int)),
"%=" => impl_op!(INT => modulo(as_int, as_int)), "%=" => return impl_op!(INT => modulo(as_int, as_int)),
"**=" => impl_op!(INT => power(as_int, as_int)), "**=" => return impl_op!(INT => power(as_int, as_int)),
">>=" => impl_op!(INT => shift_right(as_int, as_int)), ">>=" => return impl_op!(INT => shift_right(as_int, as_int)),
"<<=" => impl_op!(INT => shift_left(as_int, as_int)), "<<=" => return impl_op!(INT => shift_left(as_int, as_int)),
_ => (), _ => (),
} }
} else {
match op {
"+=" => impl_op!(INT += as_int),
"-=" => impl_op!(INT -= as_int),
"*=" => impl_op!(INT *= as_int),
"/=" => impl_op!(INT /= as_int),
"%=" => impl_op!(INT %= as_int),
"**=" => impl_op!(INT => as_int.pow(as_int as u32)),
">>=" => impl_op!(INT >>= as_int),
"<<=" => impl_op!(INT <<= as_int),
_ => (),
}
}
#[cfg(feature = "unchecked")]
match op { match op {
"+=" => return impl_op!(INT += as_int),
"-=" => return impl_op!(INT -= as_int),
"*=" => return impl_op!(INT *= as_int),
"/=" => return impl_op!(INT /= as_int),
"%=" => return impl_op!(INT %= as_int),
"**=" => return impl_op!(INT => as_int.pow(as_int as u32)),
">>=" => return impl_op!(INT >>= as_int),
"<<=" => return impl_op!(INT <<= as_int),
_ => (),
}
return match op {
"&=" => impl_op!(INT &= as_int), "&=" => impl_op!(INT &= as_int),
"|=" => impl_op!(INT |= as_int), "|=" => impl_op!(INT |= as_int),
"^=" => impl_op!(INT ^= as_int), "^=" => impl_op!(INT ^= as_int),
_ => (), _ => None,
} };
} }
if type1 == TypeId::of::<bool>() { if type1 == TypeId::of::<bool>() {
match op { return match op {
"&=" => impl_op!(bool = x && as_bool), "&=" => impl_op!(bool = x && as_bool),
"|=" => impl_op!(bool = x || as_bool), "|=" => impl_op!(bool = x || as_bool),
_ => return None, _ => None,
} };
} }
if type1 == TypeId::of::<char>() { if type1 == TypeId::of::<char>() {
match op { return match op {
"+=" => { "+=" => Some(|_, args| {
return Some(|_, args| {
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN); let mut x = args[0].write_lock::<Dynamic>().expect(BUILTIN);
Ok((*x = format!("{}{}", *x, y).into()).into()) Ok((*x = format!("{}{}", *x, y).into()).into())
}) }),
} _ => None,
_ => return None, };
}
} }
if type1 == TypeId::of::<ImmutableString>() { if type1 == TypeId::of::<ImmutableString>() {
match op { return match op {
"+=" => { "+=" => Some(|_, args| {
return Some(|_, args| {
let (first, second) = args.split_first_mut().expect(BUILTIN); let (first, second) = args.split_first_mut().expect(BUILTIN);
let mut x = first.write_lock::<ImmutableString>().expect(BUILTIN); let mut x = first.write_lock::<ImmutableString>().expect(BUILTIN);
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN); let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
Ok((*x += y).into()) Ok((*x += y).into())
}) }),
} "-=" => Some(|_, args| {
"-=" => {
return Some(|_, args| {
let (first, second) = args.split_first_mut().expect(BUILTIN); let (first, second) = args.split_first_mut().expect(BUILTIN);
let mut x = first.write_lock::<ImmutableString>().expect(BUILTIN); let mut x = first.write_lock::<ImmutableString>().expect(BUILTIN);
let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN); let y = &*second[0].read_lock::<ImmutableString>().expect(BUILTIN);
Ok((*x -= y).into()) Ok((*x -= y).into())
}) }),
} _ => None,
_ => return None, };
}
} }
None None

View File

@ -98,6 +98,7 @@ impl Drop for ArgBackup<'_> {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn ensure_no_data_race( pub fn ensure_no_data_race(
fn_name: &str, fn_name: &str,
args: &FnCallArgs, args: &FnCallArgs,
@ -123,6 +124,7 @@ pub fn ensure_no_data_race(
impl Engine { impl Engine {
/// Generate the signature for a function call. /// Generate the signature for a function call.
#[inline] #[inline]
#[must_use]
fn gen_call_signature( fn gen_call_signature(
&self, &self,
namespace: Option<&NamespaceRef>, namespace: Option<&NamespaceRef>,
@ -153,7 +155,8 @@ impl Engine {
/// 4) Imported modules - functions marked with global namespace /// 4) Imported modules - functions marked with global namespace
/// 5) Global sub-modules - functions marked with global namespace /// 5) Global sub-modules - functions marked with global namespace
#[inline(always)] #[inline(always)]
fn resolve_function<'s>( #[must_use]
fn resolve_fn<'s>(
&self, &self,
mods: &Imports, mods: &Imports,
state: &'s mut State, state: &'s mut State,
@ -286,16 +289,17 @@ impl Engine {
/// Function call arguments be _consumed_ when the function requires them to be passed by value. /// Function call arguments be _consumed_ when the function requires them to be passed by value.
/// All function arguments not in the first position are always passed by value and thus consumed. /// All function arguments not in the first position are always passed by value and thus consumed.
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
#[must_use]
pub(crate) fn call_native_fn( pub(crate) fn call_native_fn(
&self, &self,
mods: &Imports, mods: &Imports,
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
fn_name: &str, name: &str,
hash_native: u64, hash: u64,
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
is_op_assignment: bool, is_op_assign: bool,
pos: Position, pos: Position,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -304,16 +308,7 @@ impl Engine {
let state_source = state.source.clone(); let state_source = state.source.clone();
// Check if function access already in the cache // Check if function access already in the cache
let func = self.resolve_function( let func = self.resolve_fn(mods, state, lib, name, hash, Some(args), true, is_op_assign);
mods,
state,
lib,
fn_name,
hash_native,
Some(args),
true,
is_op_assignment,
);
if let Some(f) = func { if let Some(f) = func {
let FnResolutionCacheEntry { func, source } = f.as_ref(); let FnResolutionCacheEntry { func, source } = f.as_ref();
@ -334,9 +329,9 @@ impl Engine {
let result = if func.is_plugin_fn() { let result = if func.is_plugin_fn() {
func.get_plugin_fn() func.get_plugin_fn()
.call((self, fn_name, source, mods, lib).into(), args) .call((self, name, source, mods, lib).into(), args)
} else { } else {
func.get_native_fn()((self, fn_name, source, mods, lib).into(), args) func.get_native_fn()((self, name, source, mods, lib).into(), args)
}; };
// Restore the original reference // Restore the original reference
@ -345,7 +340,7 @@ impl Engine {
let result = result.map_err(|err| err.fill_position(pos))?; let result = result.map_err(|err| err.fill_position(pos))?;
// See if the function match print/debug (which requires special processing) // See if the function match print/debug (which requires special processing)
return Ok(match fn_name { return Ok(match name {
KEYWORD_PRINT => { KEYWORD_PRINT => {
let text = result.as_immutable_string().map_err(|typ| { let text = result.as_immutable_string().map_err(|typ| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
@ -371,7 +366,7 @@ impl Engine {
}); });
} }
match fn_name { match name {
// index getter function not found? // index getter function not found?
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
crate::engine::FN_IDX_GET => { crate::engine::FN_IDX_GET => {
@ -398,13 +393,13 @@ impl Engine {
// Getter function not found? // Getter function not found?
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
_ if fn_name.starts_with(crate::engine::FN_GET) => { _ if name.starts_with(crate::engine::FN_GET) => {
assert!(args.len() == 1); assert!(args.len() == 1);
EvalAltResult::ErrorDotExpr( EvalAltResult::ErrorDotExpr(
format!( format!(
"Unknown property '{}' - a getter is not registered for type '{}'", "Unknown property '{}' - a getter is not registered for type '{}'",
&fn_name[crate::engine::FN_GET.len()..], &name[crate::engine::FN_GET.len()..],
self.map_type_name(args[0].type_name()) self.map_type_name(args[0].type_name())
), ),
pos, pos,
@ -414,13 +409,13 @@ impl Engine {
// Setter function not found? // Setter function not found?
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
_ if fn_name.starts_with(crate::engine::FN_SET) => { _ if name.starts_with(crate::engine::FN_SET) => {
assert!(args.len() == 2); assert!(args.len() == 2);
EvalAltResult::ErrorDotExpr( EvalAltResult::ErrorDotExpr(
format!( format!(
"No writable property '{}' - a setter is not registered for type '{}' to handle '{}'", "No writable property '{}' - a setter is not registered for type '{}' to handle '{}'",
&fn_name[crate::engine::FN_SET.len()..], &name[crate::engine::FN_SET.len()..],
self.map_type_name(args[0].type_name()), self.map_type_name(args[0].type_name()),
self.map_type_name(args[1].type_name()), self.map_type_name(args[1].type_name()),
), ),
@ -431,7 +426,7 @@ impl Engine {
// Raise error // Raise error
_ => EvalAltResult::ErrorFunctionNotFound( _ => EvalAltResult::ErrorFunctionNotFound(
self.gen_call_signature(None, fn_name, args.as_ref()), self.gen_call_signature(None, name, args.as_ref()),
pos, pos,
) )
.into(), .into(),
@ -446,6 +441,7 @@ impl Engine {
/// All function arguments not in the first position are always passed by value and thus consumed. /// All function arguments not in the first position are always passed by value and thus consumed.
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[must_use]
pub(crate) fn call_script_fn( pub(crate) fn call_script_fn(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -576,6 +572,7 @@ impl Engine {
// Does a scripted function exist? // Does a scripted function exist?
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn has_script_fn( pub(crate) fn has_script_fn(
&self, &self,
mods: Option<&Imports>, mods: Option<&Imports>,
@ -614,13 +611,14 @@ impl Engine {
/// Function call arguments may be _consumed_ when the function requires them to be passed by value. /// Function call arguments may be _consumed_ when the function requires them to be passed by value.
/// All function arguments not in the first position are always passed by value and thus consumed. /// All function arguments not in the first position are always passed by value and thus consumed.
/// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`! /// **DO NOT** reuse the argument values unless for the first `&mut` argument - all others are silently replaced by `()`!
#[must_use]
pub(crate) fn exec_fn_call( pub(crate) fn exec_fn_call(
&self, &self,
mods: &mut Imports, mods: &mut Imports,
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
fn_name: &str, fn_name: &str,
hash: FnCallHashes, hashes: FnCallHashes,
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
_is_method: bool, _is_method: bool,
@ -691,11 +689,11 @@ impl Engine {
// Scripted function call? // Scripted function call?
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let hash_script = hash.script_hash(); let hash_script = hashes.script;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
if let Some(f) = hash_script.and_then(|hash| { if let Some(f) = hash_script.and_then(|hash| {
self.resolve_function(mods, state, lib, fn_name, hash, None, false, false) self.resolve_fn(mods, state, lib, fn_name, hash, None, false, false)
.clone() .clone()
}) { }) {
let FnResolutionCacheEntry { func, source } = *f; let FnResolutionCacheEntry { func, source } = *f;
@ -781,13 +779,14 @@ impl Engine {
} }
// Native function call // Native function call
let hash = hash.native_hash(); let hash = hashes.native;
self.call_native_fn(mods, state, lib, fn_name, hash, args, is_ref, false, pos) self.call_native_fn(mods, state, lib, fn_name, hash, args, is_ref, false, pos)
} }
/// Evaluate a list of statements with no `this` pointer. /// Evaluate a list of statements with no `this` pointer.
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body. /// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn eval_global_statements( pub(crate) fn eval_global_statements(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -808,6 +807,7 @@ impl Engine {
} }
/// Evaluate a text script in place - used primarily for 'eval'. /// Evaluate a text script in place - used primarily for 'eval'.
#[must_use]
fn eval_script_expr_in_place( fn eval_script_expr_in_place(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -859,6 +859,7 @@ impl Engine {
/// Call a dot method. /// Call a dot method.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[must_use]
pub(crate) fn make_method_call( pub(crate) fn make_method_call(
&self, &self,
mods: &mut Imports, mods: &mut Imports,
@ -900,7 +901,7 @@ impl Engine {
if call_args.len() > 0 { if call_args.len() > 0 {
if !call_args[0].is::<FnPtr>() { if !call_args[0].is::<FnPtr>() {
return Err(self.make_type_mismatch_err::<FnPtr>( return Err(self.make_type_mismatch_err::<FnPtr>(
self.map_type_name(target.type_name()), self.map_type_name(call_args[0].type_name()),
*call_arg_pos, *call_arg_pos,
)); ));
} }
@ -1020,6 +1021,7 @@ impl Engine {
/// Evaluate an argument. /// Evaluate an argument.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_arg_value( pub(crate) fn get_arg_value(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1041,6 +1043,7 @@ impl Engine {
} }
/// Call a function in normal function-call style. /// Call a function in normal function-call style.
#[must_use]
pub(crate) fn make_function_call( pub(crate) fn make_function_call(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1155,7 +1158,7 @@ impl Engine {
let fn_name = arg let fn_name = arg
.as_immutable_string() .as_immutable_string()
.map_err(|err| self.make_type_mismatch_err::<ImmutableString>(err, arg_pos))?; .map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
let (arg, arg_pos) = self.get_arg_value( let (arg, arg_pos) = self.get_arg_value(
scope, mods, state, lib, this_ptr, level, args_expr, constants, 1, scope, mods, state, lib, this_ptr, level, args_expr, constants, 1,
@ -1163,7 +1166,7 @@ impl Engine {
let num_params = arg let num_params = arg
.as_int() .as_int()
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, arg_pos))?; .map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
return Ok(if num_params < 0 { return Ok(if num_params < 0 {
Dynamic::FALSE Dynamic::FALSE
@ -1181,7 +1184,7 @@ impl Engine {
)?; )?;
let var_name = arg let var_name = arg
.as_immutable_string() .as_immutable_string()
.map_err(|err| self.make_type_mismatch_err::<ImmutableString>(err, arg_pos))?; .map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
return Ok(scope.contains(&var_name).into()); return Ok(scope.contains(&var_name).into());
} }
@ -1291,6 +1294,7 @@ impl Engine {
} }
/// Call a namespace-qualified function in normal function-call style. /// Call a namespace-qualified function in normal function-call style.
#[must_use]
pub(crate) fn make_qualified_function_call( pub(crate) fn make_qualified_function_call(
&self, &self,
scope: &mut Scope, scope: &mut Scope,

113
src/fn_hash.rs Normal file
View File

@ -0,0 +1,113 @@
//! Module containing utilities to hash functions and function calls.
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
any::TypeId,
hash::{BuildHasher, Hash, Hasher},
iter::empty,
};
/// A hasher that only takes one single [`u64`] and returns it as a hash key.
///
/// # Panics
///
/// Panics when hashing any data type other than a [`u64`].
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct StraightHasher(u64);
impl Hasher for StraightHasher {
#[inline(always)]
fn finish(&self) -> u64 {
self.0
}
#[inline(always)]
fn write(&mut self, bytes: &[u8]) {
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values");
let mut key = [0_u8; 8];
key.copy_from_slice(bytes);
self.0 = u64::from_ne_bytes(key);
}
}
/// A hash builder for `StraightHasher`.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct StraightHasherBuilder;
impl BuildHasher for StraightHasherBuilder {
type Hasher = StraightHasher;
#[inline(always)]
fn build_hasher(&self) -> Self::Hasher {
StraightHasher(42)
}
}
/// Create an instance of the default hasher.
#[inline(always)]
#[must_use]
pub fn get_hasher() -> ahash::AHasher {
Default::default()
}
/// Calculate a [`u64`] hash key from a namespace-qualified function name
/// and the number of parameters, but no parameter types.
///
/// 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_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>,
num: usize,
) -> 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);
fn_name.as_ref().hash(s);
num.hash(s);
s.finish()
}
/// Calculate a [`u64`] hash key from a non-namespace-qualified function name
/// and the number of parameters, but no parameter types.
///
/// Parameter types are passed in via [`TypeId`] values from an iterator.
#[inline(always)]
#[must_use]
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
calc_qualified_fn_hash(empty(), fn_name, num)
}
/// Calculate a [`u64`] hash key from a list of parameter types.
///
/// Parameter types are passed in via [`TypeId`] values from an iterator.
#[inline]
#[must_use]
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
let s = &mut get_hasher();
let mut len = 0;
params.inspect(|_| len += 1).for_each(|t| t.hash(s));
len.hash(s);
s.finish()
}
/// Combine two [`u64`] hashes by taking the XOR of them.
#[inline(always)]
#[must_use]
pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
a ^ b
}

View File

@ -92,6 +92,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized> From<(&'a Engine, &'a str, &'a M)>
impl<'a> NativeCallContext<'a> { impl<'a> NativeCallContext<'a> {
/// Create a new [`NativeCallContext`]. /// Create a new [`NativeCallContext`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self { pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self {
Self { Self {
engine, engine,
@ -108,6 +109,7 @@ impl<'a> NativeCallContext<'a> {
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new_with_all_fields( pub fn new_with_all_fields(
engine: &'a Engine, engine: &'a Engine,
fn_name: &'a str, fn_name: &'a str,
@ -125,16 +127,19 @@ impl<'a> NativeCallContext<'a> {
} }
/// The current [`Engine`]. /// The current [`Engine`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn engine(&self) -> &Engine { pub fn engine(&self) -> &Engine {
self.engine self.engine
} }
/// Name of the function called. /// Name of the function called.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn fn_name(&self) -> &str { pub fn fn_name(&self) -> &str {
self.fn_name self.fn_name
} }
/// The current source. /// The current source.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn source(&self) -> Option<&str> { pub fn source(&self) -> Option<&str> {
self.source self.source
} }
@ -143,6 +148,7 @@ impl<'a> NativeCallContext<'a> {
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> { pub fn iter_imports(&self) -> impl Iterator<Item = (&str, &Module)> {
self.mods.iter().flat_map(|&m| m.iter()) self.mods.iter().flat_map(|&m| m.iter())
} }
@ -150,6 +156,7 @@ impl<'a> NativeCallContext<'a> {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn iter_imports_raw( pub(crate) fn iter_imports_raw(
&self, &self,
) -> impl Iterator<Item = (&crate::Identifier, &Shared<Module>)> { ) -> impl Iterator<Item = (&crate::Identifier, &Shared<Module>)> {
@ -162,11 +169,13 @@ impl<'a> NativeCallContext<'a> {
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn imports(&self) -> Option<&Imports> { pub fn imports(&self) -> Option<&Imports> {
self.mods self.mods
} }
/// Get an iterator over the namespaces containing definitions of all script-defined functions. /// Get an iterator over the namespaces containing definitions of all script-defined functions.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> { pub fn iter_namespaces(&self) -> impl Iterator<Item = &Module> {
self.lib.iter().cloned() self.lib.iter().cloned()
} }
@ -174,6 +183,7 @@ impl<'a> NativeCallContext<'a> {
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn namespaces(&self) -> &[&Module] { pub fn namespaces(&self) -> &[&Module] {
self.lib self.lib
} }
@ -190,6 +200,7 @@ impl<'a> NativeCallContext<'a> {
/// If `is_method` is [`true`], the first argument is assumed to be passed /// If `is_method` is [`true`], the first argument is assumed to be passed
/// by reference and is not consumed. /// by reference and is not consumed.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn call_fn_dynamic_raw( pub fn call_fn_dynamic_raw(
&self, &self,
fn_name: impl AsRef<str>, fn_name: impl AsRef<str>,
@ -228,18 +239,21 @@ impl<'a> NativeCallContext<'a> {
/// Consume a [`Shared`] resource and return a mutable reference to the wrapped value. /// Consume a [`Shared`] resource and return a mutable reference to the wrapped value.
/// If the resource is shared (i.e. has other outstanding references), a cloned copy is used. /// If the resource is shared (i.e. has other outstanding references), a cloned copy is used.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T { pub fn shared_make_mut<T: Clone>(value: &mut Shared<T>) -> &mut T {
Shared::make_mut(value) Shared::make_mut(value)
} }
/// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise. /// Consume a [`Shared`] resource if is unique (i.e. not shared), or clone it otherwise.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T { pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone()) shared_try_take(value).unwrap_or_else(|v| v.as_ref().clone())
} }
/// Consume a [`Shared`] resource if is unique (i.e. not shared). /// Consume a [`Shared`] resource if is unique (i.e. not shared).
#[inline(always)] #[inline(always)]
#[must_use]
pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> { pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
Shared::try_unwrap(value) Shared::try_unwrap(value)
} }
@ -250,6 +264,7 @@ pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
/// ///
/// Panics if the resource is shared (i.e. has other outstanding references). /// Panics if the resource is shared (i.e. has other outstanding references).
#[inline(always)] #[inline(always)]
#[must_use]
pub fn shared_take<T>(value: Shared<T>) -> T { pub fn shared_take<T>(value: Shared<T>) -> T {
shared_try_take(value) shared_try_take(value)
.ok() .ok()
@ -267,31 +282,37 @@ pub struct FnPtr(Identifier, StaticVec<Dynamic>);
impl FnPtr { impl FnPtr {
/// Create a new function pointer. /// Create a new function pointer.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new(name: impl Into<Identifier>) -> Result<Self, Box<EvalAltResult>> { pub fn new(name: impl Into<Identifier>) -> Result<Self, Box<EvalAltResult>> {
name.into().try_into() name.into().try_into()
} }
/// Create a new function pointer without checking its parameters. /// Create a new function pointer without checking its parameters.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self { pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self {
Self(name.into(), curry) Self(name.into(), curry)
} }
/// Get the name of the function. /// Get the name of the function.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn fn_name(&self) -> &str { pub fn fn_name(&self) -> &str {
self.get_fn_name().as_ref() self.get_fn_name().as_ref()
} }
/// Get the name of the function. /// Get the name of the function.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_fn_name(&self) -> &Identifier { pub(crate) fn get_fn_name(&self) -> &Identifier {
&self.0 &self.0
} }
/// Get the underlying data of the function pointer. /// Get the underlying data of the function pointer.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn take_data(self) -> (Identifier, StaticVec<Dynamic>) { pub(crate) fn take_data(self) -> (Identifier, StaticVec<Dynamic>) {
(self.0, self.1) (self.0, self.1)
} }
/// Get the curried arguments. /// Get the curried arguments.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn curry(&self) -> &[Dynamic] { pub fn curry(&self) -> &[Dynamic] {
self.1.as_ref() self.1.as_ref()
} }
@ -309,11 +330,13 @@ impl FnPtr {
} }
/// Is the function pointer curried? /// Is the function pointer curried?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_curried(&self) -> bool { pub fn is_curried(&self) -> bool {
!self.1.is_empty() !self.1.is_empty()
} }
/// Get the number of curried arguments. /// Get the number of curried arguments.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn num_curried(&self) -> usize { pub fn num_curried(&self) -> usize {
self.1.len() self.1.len()
} }
@ -322,6 +345,7 @@ impl FnPtr {
/// Not available under `no_function`. /// Not available under `no_function`.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_anonymous(&self) -> bool { pub fn is_anonymous(&self) -> bool {
self.0.starts_with(crate::engine::FN_ANONYMOUS) self.0.starts_with(crate::engine::FN_ANONYMOUS)
} }
@ -336,6 +360,7 @@ impl FnPtr {
/// Do not use the arguments after this call. If they are needed afterwards, /// Do not use the arguments after this call. If they are needed afterwards,
/// clone them _before_ calling this function. /// clone them _before_ calling this function.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn call_dynamic( pub fn call_dynamic(
&self, &self,
ctx: &NativeCallContext, ctx: &NativeCallContext,
@ -518,6 +543,7 @@ impl fmt::Display for CallableFunction {
impl CallableFunction { impl CallableFunction {
/// Is this a pure native Rust function? /// Is this a pure native Rust function?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { match self {
Self::Pure(_) => true, Self::Pure(_) => true,
@ -531,6 +557,7 @@ impl CallableFunction {
} }
/// Is this a native Rust method function? /// Is this a native Rust method function?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_method(&self) -> bool { pub fn is_method(&self) -> bool {
match self { match self {
Self::Method(_) => true, Self::Method(_) => true,
@ -544,6 +571,7 @@ impl CallableFunction {
} }
/// Is this an iterator function? /// Is this an iterator function?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_iter(&self) -> bool { pub fn is_iter(&self) -> bool {
match self { match self {
Self::Iterator(_) => true, Self::Iterator(_) => true,
@ -555,6 +583,7 @@ impl CallableFunction {
} }
/// Is this a Rhai-scripted function? /// Is this a Rhai-scripted function?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_script(&self) -> bool { pub fn is_script(&self) -> bool {
match self { match self {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -565,6 +594,7 @@ impl CallableFunction {
} }
/// Is this a plugin function? /// Is this a plugin function?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_plugin_fn(&self) -> bool { pub fn is_plugin_fn(&self) -> bool {
match self { match self {
Self::Plugin(_) => true, Self::Plugin(_) => true,
@ -576,6 +606,7 @@ impl CallableFunction {
} }
/// Is this a native Rust function? /// Is this a native Rust function?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_native(&self) -> bool { pub fn is_native(&self) -> bool {
match self { match self {
Self::Pure(_) | Self::Method(_) => true, Self::Pure(_) | Self::Method(_) => true,
@ -588,6 +619,7 @@ impl CallableFunction {
} }
/// Get the access mode. /// Get the access mode.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn access(&self) -> FnAccess { pub fn access(&self) -> FnAccess {
match self { match self {
Self::Plugin(_) => FnAccess::Public, Self::Plugin(_) => FnAccess::Public,
@ -604,6 +636,7 @@ impl CallableFunction {
/// Panics if the [`CallableFunction`] is not [`Pure`][CallableFunction::Pure] or /// Panics if the [`CallableFunction`] is not [`Pure`][CallableFunction::Pure] or
/// [`Method`][CallableFunction::Method]. /// [`Method`][CallableFunction::Method].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_native_fn(&self) -> &Shared<FnAny> { pub fn get_native_fn(&self) -> &Shared<FnAny> {
match self { match self {
Self::Pure(f) | Self::Method(f) => f, Self::Pure(f) | Self::Method(f) => f,
@ -622,6 +655,7 @@ impl CallableFunction {
/// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script]. /// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script].
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_fn_def(&self) -> &Shared<crate::ast::ScriptFnDef> { pub fn get_fn_def(&self) -> &Shared<crate::ast::ScriptFnDef> {
match self { match self {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => { Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => {
@ -636,6 +670,7 @@ impl CallableFunction {
/// ///
/// Panics if the [`CallableFunction`] is not [`Iterator`][CallableFunction::Iterator]. /// Panics if the [`CallableFunction`] is not [`Iterator`][CallableFunction::Iterator].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_iter_fn(&self) -> IteratorFn { pub fn get_iter_fn(&self) -> IteratorFn {
match self { match self {
Self::Iterator(f) => *f, Self::Iterator(f) => *f,
@ -653,6 +688,7 @@ impl CallableFunction {
/// ///
/// Panics if the [`CallableFunction`] is not [`Plugin`][CallableFunction::Plugin]. /// Panics if the [`CallableFunction`] is not [`Plugin`][CallableFunction::Plugin].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_plugin_fn<'s>(&'s self) -> &Shared<FnPlugin> { pub fn get_plugin_fn<'s>(&'s self) -> &Shared<FnPlugin> {
match self { match self {
Self::Plugin(f) => f, Self::Plugin(f) => f,
@ -666,16 +702,19 @@ impl CallableFunction {
} }
/// Create a new [`CallableFunction::Pure`]. /// Create a new [`CallableFunction::Pure`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn from_pure(func: Box<FnAny>) -> Self { pub fn from_pure(func: Box<FnAny>) -> Self {
Self::Pure(func.into()) Self::Pure(func.into())
} }
/// Create a new [`CallableFunction::Method`]. /// Create a new [`CallableFunction::Method`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn from_method(func: Box<FnAny>) -> Self { pub fn from_method(func: Box<FnAny>) -> Self {
Self::Method(func.into()) Self::Method(func.into())
} }
/// Create a new [`CallableFunction::Plugin`]. /// Create a new [`CallableFunction::Plugin`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self { pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
Self::Plugin((Box::new(func) as Box<FnPlugin>).into()) Self::Plugin((Box::new(func) as Box<FnPlugin>).into())
} }

View File

@ -30,6 +30,7 @@ pub struct Mut<T>(T);
/// Dereference into DynamicWriteLock /// Dereference into DynamicWriteLock
#[inline(always)] #[inline(always)]
#[must_use]
pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> { pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
// Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data. // Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data.
data.write_lock::<T>() data.write_lock::<T>()
@ -38,6 +39,7 @@ pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
/// Dereference into value. /// Dereference into value.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T { pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
if TypeId::of::<T>() == TypeId::of::<&str>() { if TypeId::of::<T>() == TypeId::of::<&str>() {
// If T is `&str`, data must be `ImmutableString`, so map directly to it // If T is `&str`, data must be `ImmutableString`, so map directly to it
@ -63,24 +65,30 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
/// Trait to register custom Rust functions. /// Trait to register custom Rust functions.
pub trait RegisterNativeFunction<Args, Result> { pub trait RegisterNativeFunction<Args, Result> {
/// Convert this function into a [`CallableFunction`]. /// Convert this function into a [`CallableFunction`].
#[must_use]
fn into_callable_function(self) -> CallableFunction; fn into_callable_function(self) -> CallableFunction;
/// Get the type ID's of this function's parameters. /// Get the type ID's of this function's parameters.
#[must_use]
fn param_types() -> Box<[TypeId]>; fn param_types() -> Box<[TypeId]>;
/// _(METADATA)_ Get the type names of this function's parameters. /// _(METADATA)_ Get the type names of this function's parameters.
/// Exported under the `metadata` feature only. /// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[must_use]
fn param_names() -> Box<[&'static str]>; fn param_names() -> Box<[&'static str]>;
/// _(METADATA)_ Get the type ID of this function's return value. /// _(METADATA)_ Get the type ID of this function's return value.
/// Exported under the `metadata` feature only. /// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[must_use]
fn return_type() -> TypeId; fn return_type() -> TypeId;
/// _(METADATA)_ Get the type name of this function's return value. /// _(METADATA)_ Get the type name of this function's return value.
/// Exported under the `metadata` feature only. /// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[must_use]
fn return_type_name() -> &'static str; fn return_type_name() -> &'static str;
} }
#[inline(always)] #[inline(always)]
#[must_use]
fn is_setter(_fn_name: &str) -> bool { fn is_setter(_fn_name: &str) -> bool {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
if _fn_name.starts_with(crate::engine::FN_SET) { if _fn_name.starts_with(crate::engine::FN_SET) {

View File

@ -1,119 +1,19 @@
//! Module containing various utility types and functions. //! The `ImmutableString` type.
use crate::fn_native::{shared_make_mut, shared_take}; use crate::fn_native::{shared_make_mut, shared_take};
use crate::{Identifier, Shared, SmartString}; use crate::{Shared, SmartString};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
any::TypeId,
borrow::Borrow, borrow::Borrow,
cmp::Ordering, cmp::Ordering,
fmt, fmt,
hash::{BuildHasher, Hash, Hasher}, hash::Hash,
iter::{empty, FromIterator}, iter::FromIterator,
ops::{Add, AddAssign, Deref, Sub, SubAssign}, ops::{Add, AddAssign, Deref, Sub, SubAssign},
str::FromStr, str::FromStr,
}; };
/// A hasher that only takes one single [`u64`] and returns it as a hash key.
///
/// # Panics
///
/// Panics when hashing any data type other than a [`u64`].
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct StraightHasher(u64);
impl Hasher for StraightHasher {
#[inline(always)]
fn finish(&self) -> u64 {
self.0
}
#[inline(always)]
fn write(&mut self, bytes: &[u8]) {
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values");
let mut key = [0_u8; 8];
key.copy_from_slice(bytes);
self.0 = u64::from_ne_bytes(key);
}
}
/// A hash builder for `StraightHasher`.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
pub struct StraightHasherBuilder;
impl BuildHasher for StraightHasherBuilder {
type Hasher = StraightHasher;
#[inline(always)]
fn build_hasher(&self) -> Self::Hasher {
StraightHasher(42)
}
}
/// Create an instance of the default hasher.
#[inline(always)]
pub fn get_hasher() -> ahash::AHasher {
Default::default()
}
/// Calculate a [`u64`] hash key from a namespace-qualified function name
/// and the number of parameters, but no parameter types.
///
/// 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]
pub fn calc_qualified_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>,
num: usize,
) -> 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);
fn_name.as_ref().hash(s);
num.hash(s);
s.finish()
}
/// Calculate a [`u64`] hash key from a non-namespace-qualified function name
/// and the number of parameters, but no parameter types.
///
/// Parameter types are passed in via [`TypeId`] values from an iterator.
#[inline(always)]
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
calc_qualified_fn_hash(empty(), fn_name, num)
}
/// Calculate a [`u64`] hash key from a list of parameter types.
///
/// Parameter types are passed in via [`TypeId`] values from an iterator.
#[inline]
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
let s = &mut get_hasher();
let mut len = 0;
params.inspect(|_| len += 1).for_each(|t| t.hash(s));
len.hash(s);
s.finish()
}
/// Combine two [`u64`] hashes by taking the XOR of them.
#[inline(always)]
pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
a ^ b
}
/// The system immutable string type. /// The system immutable string type.
/// ///
/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>` /// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>`
@ -623,32 +523,3 @@ impl ImmutableString {
shared_make_mut(&mut self.0) shared_make_mut(&mut self.0)
} }
} }
/// A factory of identifiers from text strings.
///
/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`],
/// this just returns one because most identifiers in Rhai are short and ASCII-based.
///
/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a
/// collection of strings and returns shared instances, only creating a new string when it is not
/// yet interned.
#[derive(Debug, Clone, Default, Hash)]
pub struct IdentifierBuilder(
#[cfg(feature = "no_smartstring")] std::collections::BTreeSet<Identifier>,
);
impl IdentifierBuilder {
/// Get an identifier from a text string.
#[inline(always)]
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
#[cfg(not(feature = "no_smartstring"))]
return text.as_ref().into();
#[cfg(feature = "no_smartstring")]
return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
let s: Identifier = text.into();
self.0.insert(s.clone());
s
});
}
}

View File

@ -70,28 +70,29 @@ use std::prelude::v1::*;
// Internal modules // Internal modules
mod ast; mod ast;
mod custom_syntax;
mod dynamic; mod dynamic;
mod engine; mod engine;
mod engine_api; mod engine_api;
mod engine_settings; mod engine_settings;
mod error;
mod error_parsing;
mod fn_args; mod fn_args;
mod fn_builtin; mod fn_builtin;
mod fn_call; mod fn_call;
mod fn_func; mod fn_func;
mod fn_hash;
mod fn_native; mod fn_native;
mod fn_register; mod fn_register;
mod immutable_string;
mod module; mod module;
mod optimize; mod optimize;
pub mod packages; pub mod packages;
mod parse_error; mod parse;
mod parser;
pub mod plugin; pub mod plugin;
mod result;
mod scope; mod scope;
mod syntax;
mod token; mod token;
mod r#unsafe; mod r#unsafe;
mod utils;
type RhaiResult = Result<Dynamic, Box<EvalAltResult>>; type RhaiResult = Result<Dynamic, Box<EvalAltResult>>;
@ -126,17 +127,17 @@ pub type FLOAT = f64;
pub type FLOAT = f32; pub type FLOAT = f32;
pub use ast::{FnAccess, AST}; pub use ast::{FnAccess, AST};
pub use custom_syntax::Expression;
pub use dynamic::Dynamic; pub use dynamic::Dynamic;
pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS}; pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS};
pub use error::EvalAltResult;
pub use error_parsing::{LexError, ParseError, ParseErrorType};
pub use fn_native::{FnPtr, NativeCallContext}; pub use fn_native::{FnPtr, NativeCallContext};
pub use fn_register::RegisterNativeFunction; pub use fn_register::RegisterNativeFunction;
pub use immutable_string::ImmutableString;
pub use module::{FnNamespace, Module}; pub use module::{FnNamespace, Module};
pub use parse_error::{LexError, ParseError, ParseErrorType};
pub use result::EvalAltResult;
pub use scope::Scope; pub use scope::Scope;
pub use syntax::Expression;
pub use token::Position; pub use token::Position;
pub use utils::ImmutableString;
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most /// 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. /// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
@ -169,7 +170,9 @@ pub use fn_native::Shared;
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
use fn_native::Locked; use fn_native::Locked;
pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes}; pub(crate) use fn_hash::{
calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes,
};
pub use rhai_codegen::*; pub use rhai_codegen::*;

View File

@ -4,8 +4,8 @@ use crate::ast::{FnAccess, Ident};
use crate::dynamic::Variant; use crate::dynamic::Variant;
use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync};
use crate::fn_register::RegisterNativeFunction; use crate::fn_register::RegisterNativeFunction;
use crate::parse::IdentifierBuilder;
use crate::token::Token; use crate::token::Token;
use crate::utils::IdentifierBuilder;
use crate::{ use crate::{
calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult,
Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec, Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec,
@ -67,6 +67,7 @@ impl FuncInfo {
/// Generate a signature of the function. /// Generate a signature of the function.
/// Exported under the `metadata` feature only. /// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[must_use]
pub fn gen_signature(&self) -> String { pub fn gen_signature(&self) -> String {
let mut sig = format!("{}(", self.name); let mut sig = format!("{}(", self.name);
@ -240,6 +241,7 @@ impl Module {
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42); /// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
id: None, id: None,
@ -269,6 +271,7 @@ impl Module {
/// assert_eq!(module.id(), Some("hello")); /// assert_eq!(module.id(), Some("hello"));
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn id(&self) -> Option<&str> { pub fn id(&self) -> Option<&str> {
self.id_raw().map(|s| s.as_str()) self.id_raw().map(|s| s.as_str())
} }
@ -285,6 +288,7 @@ impl Module {
/// assert_eq!(module.id_raw().map(|s| s.as_str()), Some("hello")); /// assert_eq!(module.id_raw().map(|s| s.as_str()), Some("hello"));
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn id_raw(&self) -> Option<&Identifier> { pub fn id_raw(&self) -> Option<&Identifier> {
self.id.as_ref() self.id.as_ref()
} }
@ -317,6 +321,7 @@ impl Module {
/// assert!(module.is_empty()); /// assert!(module.is_empty());
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.functions.is_empty() self.functions.is_empty()
&& self.all_functions.is_empty() && self.all_functions.is_empty()
@ -349,6 +354,7 @@ impl Module {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_indexed(&self) -> bool { pub fn is_indexed(&self) -> bool {
self.indexed self.indexed
} }
@ -357,6 +363,7 @@ impl Module {
/// Exported under the `metadata` feature only. /// Exported under the `metadata` feature only.
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ { pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
self.functions self.functions
.values() .values()
@ -379,6 +386,7 @@ impl Module {
/// assert!(module.contains_var("answer")); /// assert!(module.contains_var("answer"));
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_var(&self, name: &str) -> bool { pub fn contains_var(&self, name: &str) -> bool {
self.variables.contains_key(name) self.variables.contains_key(name)
} }
@ -395,6 +403,7 @@ impl Module {
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42); /// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> { pub fn get_var_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.get_var(name).and_then(Dynamic::try_cast::<T>) self.get_var(name).and_then(Dynamic::try_cast::<T>)
} }
@ -411,6 +420,7 @@ impl Module {
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42); /// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_var(&self, name: &str) -> Option<Dynamic> { pub fn get_var(&self, name: &str) -> Option<Dynamic> {
self.variables.get(name).cloned() self.variables.get(name).cloned()
} }
@ -448,6 +458,7 @@ impl Module {
/// Get a reference to a namespace-qualified variable. /// Get a reference to a namespace-qualified variable.
/// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards. /// Name and Position in [`EvalAltResult`] are [`None`] and [`NONE`][Position::NONE] and must be set afterwards.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> { pub(crate) fn get_qualified_var(&self, hash_var: u64) -> Result<&Dynamic, Box<EvalAltResult>> {
self.all_variables.get(&hash_var).ok_or_else(|| { self.all_variables.get(&hash_var).ok_or_else(|| {
EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into() EvalAltResult::ErrorVariableNotFound(String::new(), Position::NONE).into()
@ -489,6 +500,7 @@ impl Module {
/// and number of parameters. /// and number of parameters.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_script_fn( pub fn get_script_fn(
&self, &self,
name: &str, name: &str,
@ -508,6 +520,7 @@ impl Module {
/// Thus the [`Module`] is automatically set to be non-indexed. /// Thus the [`Module`] is automatically set to be non-indexed.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> { pub(crate) fn sub_modules_mut(&mut self) -> &mut BTreeMap<Identifier, Shared<Module>> {
// We must assume that the user has changed the sub-modules // We must assume that the user has changed the sub-modules
// (otherwise why take a mutable reference?) // (otherwise why take a mutable reference?)
@ -533,6 +546,7 @@ impl Module {
/// assert!(module.contains_sub_module("question")); /// assert!(module.contains_sub_module("question"));
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_sub_module(&self, name: &str) -> bool { pub fn contains_sub_module(&self, name: &str) -> bool {
self.modules.contains_key(name) self.modules.contains_key(name)
} }
@ -550,6 +564,7 @@ impl Module {
/// assert!(module.get_sub_module("question").is_some()); /// assert!(module.get_sub_module("question").is_some());
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_sub_module(&self, name: &str) -> Option<&Module> { pub fn get_sub_module(&self, name: &str) -> Option<&Module> {
self.modules.get(name).map(|m| m.as_ref()) self.modules.get(name).map(|m| m.as_ref())
} }
@ -594,6 +609,7 @@ impl Module {
/// assert!(module.contains_fn(hash)); /// assert!(module.contains_fn(hash));
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_fn(&self, hash_fn: u64) -> bool { pub fn contains_fn(&self, hash_fn: u64) -> bool {
self.functions.contains_key(&hash_fn) self.functions.contains_key(&hash_fn)
} }
@ -641,6 +657,7 @@ impl Module {
/// Remap type ID. /// Remap type ID.
#[inline(always)] #[inline(always)]
#[must_use]
fn map_type(map: bool, type_id: TypeId) -> TypeId { fn map_type(map: bool, type_id: TypeId) -> TypeId {
if !map { if !map {
return type_id; return type_id;
@ -1104,6 +1121,7 @@ impl Module {
/// ///
/// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call. /// The [`u64`] hash is returned by the [`set_native_fn`][Module::set_native_fn] call.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_fn(&self, hash_fn: u64) -> Option<&CallableFunction> { pub(crate) fn get_fn(&self, hash_fn: u64) -> Option<&CallableFunction> {
self.functions.get(&hash_fn).map(|f| f.func.as_ref()) self.functions.get(&hash_fn).map(|f| f.func.as_ref())
} }
@ -1112,6 +1130,7 @@ impl Module {
/// ///
/// The [`u64`] hash is calculated by [`build_index`][Module::build_index]. /// The [`u64`] hash is calculated by [`build_index`][Module::build_index].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool { pub fn contains_qualified_fn(&self, hash_fn: u64) -> bool {
self.all_functions.contains_key(&hash_fn) self.all_functions.contains_key(&hash_fn)
} }
@ -1120,6 +1139,7 @@ impl Module {
/// ///
/// The [`u64`] hash is calculated by [`build_index`][Module::build_index]. /// The [`u64`] hash is calculated by [`build_index`][Module::build_index].
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> { pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> {
self.all_functions self.all_functions
.get(&hash_qualified_fn) .get(&hash_qualified_fn)
@ -1266,6 +1286,7 @@ impl Module {
/// Get the number of variables, functions and type iterators in the [`Module`]. /// Get the number of variables, functions and type iterators in the [`Module`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn count(&self) -> (usize, usize, usize) { pub fn count(&self) -> (usize, usize, usize) {
( (
self.variables.len(), self.variables.len(),
@ -1276,12 +1297,14 @@ impl Module {
/// Get an iterator to the sub-modules in the [`Module`]. /// Get an iterator to the sub-modules in the [`Module`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, Shared<Module>)> { pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, Shared<Module>)> {
self.modules.iter().map(|(k, m)| (k.as_str(), m.clone())) self.modules.iter().map(|(k, m)| (k.as_str(), m.clone()))
} }
/// Get an iterator to the variables in the [`Module`]. /// Get an iterator to the variables in the [`Module`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> { pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
self.variables.iter().map(|(k, v)| (k.as_str(), v)) self.variables.iter().map(|(k, v)| (k.as_str(), v))
} }
@ -1289,6 +1312,7 @@ impl Module {
/// Get an iterator to the functions in the [`Module`]. /// Get an iterator to the functions in the [`Module`].
#[inline(always)] #[inline(always)]
#[allow(dead_code)] #[allow(dead_code)]
#[must_use]
pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> { pub(crate) fn iter_fn(&self) -> impl Iterator<Item = &FuncInfo> {
self.functions.values().map(Box::as_ref) self.functions.values().map(Box::as_ref)
} }
@ -1303,6 +1327,7 @@ impl Module {
/// 5) Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef]. /// 5) Shared reference to function definition [`ScriptFnDef`][crate::ast::ScriptFnDef].
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn iter_script_fn( pub(crate) fn iter_script_fn(
&self, &self,
) -> impl Iterator< ) -> impl Iterator<
@ -1338,6 +1363,7 @@ impl Module {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "internals"))] #[cfg(not(feature = "internals"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_script_fn_info( pub fn iter_script_fn_info(
&self, &self,
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize)> { ) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize)> {
@ -1359,6 +1385,7 @@ impl Module {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_script_fn_info( pub fn iter_script_fn_info(
&self, &self,
) -> impl Iterator< ) -> impl Iterator<
@ -1395,6 +1422,7 @@ impl Module {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[must_use]
pub fn eval_ast_as_new( pub fn eval_ast_as_new(
mut scope: crate::Scope, mut scope: crate::Scope,
ast: &crate::AST, ast: &crate::AST,
@ -1463,6 +1491,7 @@ impl Module {
/// ///
/// Panics if the [`Module`] is not yet indexed via [`build_index`][Module::build_index]. /// Panics if the [`Module`] is not yet indexed via [`build_index`][Module::build_index].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_indexed_global_functions(&self) -> bool { pub fn contains_indexed_global_functions(&self) -> bool {
self.contains_indexed_global_functions self.contains_indexed_global_functions
} }
@ -1562,12 +1591,14 @@ impl Module {
/// Does a type iterator exist in the entire module tree? /// Does a type iterator exist in the entire module tree?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_qualified_iter(&self, id: TypeId) -> bool { pub fn contains_qualified_iter(&self, id: TypeId) -> bool {
self.all_type_iterators.contains_key(&id) self.all_type_iterators.contains_key(&id)
} }
/// Does a type iterator exist in the module? /// Does a type iterator exist in the module?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_iter(&self, id: TypeId) -> bool { pub fn contains_iter(&self, id: TypeId) -> bool {
self.type_iterators.contains_key(&id) self.type_iterators.contains_key(&id)
} }
@ -1609,12 +1640,14 @@ impl Module {
/// Get the specified type iterator. /// Get the specified type iterator.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<IteratorFn> { pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.all_type_iterators.get(&id).cloned() self.all_type_iterators.get(&id).cloned()
} }
/// Get the specified type iterator. /// Get the specified type iterator.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> { pub(crate) fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
self.type_iterators.get(&id).cloned() self.type_iterators.get(&id).cloned()
} }
@ -1693,6 +1726,7 @@ impl From<StaticVec<Ident>> for NamespaceRef {
impl NamespaceRef { impl NamespaceRef {
/// Get the [`Scope`][crate::Scope] index offset. /// Get the [`Scope`][crate::Scope] index offset.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn index(&self) -> Option<NonZeroUsize> { pub(crate) fn index(&self) -> Option<NonZeroUsize> {
self.index self.index
} }

View File

@ -41,6 +41,7 @@ impl ModuleResolversCollection {
/// engine.set_module_resolver(collection); /// engine.set_module_resolver(collection);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
@ -76,11 +77,13 @@ impl ModuleResolversCollection {
} }
/// Get an iterator of all the [module resolvers][ModuleResolver]. /// Get an iterator of all the [module resolvers][ModuleResolver].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> { pub fn iter(&self) -> impl Iterator<Item = &dyn ModuleResolver> {
self.0.iter().map(|v| v.as_ref()) self.0.iter().map(|v| v.as_ref())
} }
/// Get a mutable iterator of all the [module resolvers][ModuleResolver]. /// Get a mutable iterator of all the [module resolvers][ModuleResolver].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn into_iter(self) -> impl Iterator<Item = Box<dyn ModuleResolver>> { pub fn into_iter(self) -> impl Iterator<Item = Box<dyn ModuleResolver>> {
self.0.into_iter() self.0.into_iter()
} }
@ -92,11 +95,13 @@ impl ModuleResolversCollection {
} }
/// Is this [`ModuleResolversCollection`] empty? /// Is this [`ModuleResolversCollection`] empty?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.is_empty() self.0.is_empty()
} }
/// Get the number of [module resolvers][ModuleResolver] in this [`ModuleResolversCollection`]. /// Get the number of [module resolvers][ModuleResolver] in this [`ModuleResolversCollection`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.0.len()
} }

View File

@ -77,6 +77,7 @@ impl FileModuleResolver {
/// engine.set_module_resolver(resolver); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self::new_with_extension(RHAI_SCRIPT_EXTENSION) Self::new_with_extension(RHAI_SCRIPT_EXTENSION)
} }
@ -99,6 +100,7 @@ impl FileModuleResolver {
/// engine.set_module_resolver(resolver); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new_with_path(path: impl Into<PathBuf>) -> Self { pub fn new_with_path(path: impl Into<PathBuf>) -> Self {
Self::new_with_path_and_extension(path, RHAI_SCRIPT_EXTENSION) Self::new_with_path_and_extension(path, RHAI_SCRIPT_EXTENSION)
} }
@ -118,6 +120,7 @@ impl FileModuleResolver {
/// engine.set_module_resolver(resolver); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new_with_extension(extension: impl Into<Identifier>) -> Self { pub fn new_with_extension(extension: impl Into<Identifier>) -> Self {
Self { Self {
base_path: None, base_path: None,
@ -143,6 +146,7 @@ impl FileModuleResolver {
/// engine.set_module_resolver(resolver); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new_with_path_and_extension( pub fn new_with_path_and_extension(
path: impl Into<PathBuf>, path: impl Into<PathBuf>,
extension: impl Into<Identifier>, extension: impl Into<Identifier>,
@ -157,6 +161,7 @@ impl FileModuleResolver {
/// Get the base path for script files. /// Get the base path for script files.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn base_path(&self) -> Option<&Path> { pub fn base_path(&self) -> Option<&Path> {
self.base_path.as_ref().map(PathBuf::as_ref) self.base_path.as_ref().map(PathBuf::as_ref)
} }
@ -169,6 +174,7 @@ impl FileModuleResolver {
/// Get the script file extension. /// Get the script file extension.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn extension(&self) -> &str { pub fn extension(&self) -> &str {
&self.extension &self.extension
} }
@ -188,12 +194,14 @@ impl FileModuleResolver {
} }
/// Is the cache enabled? /// Is the cache enabled?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_cache_enabled(&self) -> bool { pub fn is_cache_enabled(&self) -> bool {
self.cache_enabled self.cache_enabled
} }
/// Is a particular path cached? /// Is a particular path cached?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_cached(&self, path: &str, source_path: Option<&str>) -> bool { pub fn is_cached(&self, path: &str, source_path: Option<&str>) -> bool {
if !self.cache_enabled { if !self.cache_enabled {
return false; return false;
@ -208,16 +216,19 @@ impl FileModuleResolver {
} }
/// Empty the internal cache. /// Empty the internal cache.
#[inline(always)] #[inline(always)]
pub fn clear_cache(&mut self) { pub fn clear_cache(&mut self) -> &mut Self {
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
self.cache.borrow_mut().clear(); self.cache.borrow_mut().clear();
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
self.cache.write().unwrap().clear(); self.cache.write().unwrap().clear();
self
} }
/// Remove the specified path from internal cache. /// Remove the specified path from internal cache.
/// ///
/// The next time this path is resolved, the script file will be loaded once again. /// The next time this path is resolved, the script file will be loaded once again.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn clear_cache_for_path( pub fn clear_cache_for_path(
&mut self, &mut self,
path: &str, path: &str,
@ -240,6 +251,8 @@ impl FileModuleResolver {
.map(|(_, v)| v); .map(|(_, v)| v);
} }
/// Construct a full file path. /// Construct a full file path.
#[must_use]
#[must_use]
fn get_file_path(&self, path: &str, source_path: Option<&str>) -> PathBuf { fn get_file_path(&self, path: &str, source_path: Option<&str>) -> PathBuf {
let path = Path::new(path); let path = Path::new(path);

View File

@ -23,6 +23,7 @@ pub use stat::StaticModuleResolver;
/// Trait that encapsulates a module resolution service. /// Trait that encapsulates a module resolution service.
pub trait ModuleResolver: SendSync { pub trait ModuleResolver: SendSync {
/// Resolve a module based on a path string. /// Resolve a module based on a path string.
#[must_use]
fn resolve( fn resolve(
&self, &self,
engine: &Engine, engine: &Engine,
@ -41,6 +42,7 @@ pub trait ModuleResolver: SendSync {
/// Override the default implementation of this method if the module resolver /// Override the default implementation of this method if the module resolver
/// serves modules based on compiled Rhai scripts. /// serves modules based on compiled Rhai scripts.
#[allow(unused_variables)] #[allow(unused_variables)]
#[must_use]
fn resolve_ast( fn resolve_ast(
&self, &self,
engine: &Engine, engine: &Engine,

View File

@ -41,6 +41,7 @@ impl StaticModuleResolver {
/// engine.set_module_resolver(resolver); /// engine.set_module_resolver(resolver);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Default::default()
} }
@ -57,38 +58,45 @@ impl StaticModuleResolver {
} }
/// Does the path exist? /// Does the path exist?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains_path(&self, path: &str) -> bool { pub fn contains_path(&self, path: &str) -> bool {
self.0.contains_key(path) self.0.contains_key(path)
} }
/// Get an iterator of all the [modules][Module]. /// Get an iterator of all the [modules][Module].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> { pub fn iter(&self) -> impl Iterator<Item = (&str, &Shared<Module>)> {
self.0.iter().map(|(k, v)| (k.as_str(), v)) self.0.iter().map(|(k, v)| (k.as_str(), v))
} }
/// Get a mutable iterator of all the [modules][Module]. /// Get a mutable iterator of all the [modules][Module].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Shared<Module>)> { pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Shared<Module>)> {
self.0.iter_mut().map(|(k, v)| (k.as_str(), v)) self.0.iter_mut().map(|(k, v)| (k.as_str(), v))
} }
/// Get a mutable iterator of all the modules. /// Get a mutable iterator of all the modules.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> { pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
self.0.into_iter() self.0.into_iter()
} }
/// Get an iterator of all the [module][Module] paths. /// Get an iterator of all the [module][Module] paths.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn paths(&self) -> impl Iterator<Item = &str> { pub fn paths(&self) -> impl Iterator<Item = &str> {
self.0.keys().map(|s| s.as_str()) self.0.keys().map(|s| s.as_str())
} }
/// Get an iterator of all the [modules][Module]. /// Get an iterator of all the [modules][Module].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn values(&self) -> impl Iterator<Item = &Shared<Module>> { pub fn values(&self) -> impl Iterator<Item = &Shared<Module>> {
self.0.values().map(|m| m) self.0.values().map(|m| m)
} }
/// Remove all [modules][Module]. /// Remove all [modules][Module].
#[inline(always)] #[inline(always)]
pub fn clear(&mut self) { pub fn clear(&mut self) -> &mut Self {
self.0.clear(); self.0.clear();
self
} }
/// Is this [`StaticModuleResolver`] empty? /// Is this [`StaticModuleResolver`] empty?
#[inline(always)] #[inline(always)]
@ -97,6 +105,7 @@ impl StaticModuleResolver {
} }
/// Get the number of [modules][Module] in this [`StaticModuleResolver`]. /// Get the number of [modules][Module] in this [`StaticModuleResolver`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.0.len() self.0.len()
} }
@ -105,10 +114,11 @@ impl StaticModuleResolver {
/// ///
/// Existing modules of the same path name are overwritten. /// Existing modules of the same path name are overwritten.
#[inline(always)] #[inline(always)]
pub fn merge(&mut self, other: Self) { pub fn merge(&mut self, other: Self) -> &mut Self {
if !other.is_empty() { if !other.is_empty() {
self.0.extend(other.0.into_iter()); self.0.extend(other.0.into_iter());
} }
self
} }
} }

View File

@ -4,8 +4,8 @@ use crate::ast::{Expr, OpAssignment, Stmt};
use crate::dynamic::AccessMode; use crate::dynamic::AccessMode;
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
use crate::fn_builtin::get_builtin_binary_op_fn; use crate::fn_builtin::get_builtin_binary_op_fn;
use crate::fn_hash::get_hasher;
use crate::token::Token; use crate::token::Token;
use crate::utils::get_hasher;
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString,
Module, Position, Scope, StaticVec, AST, Module, Position, Scope, StaticVec, AST,
@ -248,12 +248,12 @@ fn optimize_stmt_block(
if preserve_result && index >= statements.len() - 1 { if preserve_result && index >= statements.len() - 1 {
break; break;
} else { } else {
match &statements[index] { match statements[index] {
stmt if is_pure(stmt) && index >= first_non_constant => { ref stmt if is_pure(stmt) && index >= first_non_constant => {
state.set_dirty(); state.set_dirty();
statements.remove(index); statements.remove(index);
} }
stmt if stmt.is_pure() => { ref stmt if stmt.is_pure() => {
state.set_dirty(); state.set_dirty();
if index < first_non_constant { if index < first_non_constant {
first_non_constant -= 1; first_non_constant -= 1;
@ -269,36 +269,37 @@ fn optimize_stmt_block(
// We cannot remove anything for non-pure statements due to potential side-effects. // We cannot remove anything for non-pure statements due to potential side-effects.
if preserve_result { if preserve_result {
loop { loop {
match &mut statements[..] { match statements[..] {
// { return; } -> {} // { return; } -> {}
[Stmt::Return(crate::ast::ReturnType::Return, None, _)] if reduce_return => { [Stmt::Return(crate::ast::ReturnType::Return, None, _)] if reduce_return => {
state.set_dirty(); state.set_dirty();
statements.clear(); statements.clear();
} }
[stmt] if !stmt.returns_value() && is_pure(stmt) => { [ref stmt] if !stmt.returns_value() && is_pure(stmt) => {
state.set_dirty(); state.set_dirty();
statements.clear(); statements.clear();
} }
// { ...; return; } -> { ... } // { ...; return; } -> { ... }
[.., last_stmt, Stmt::Return(crate::ast::ReturnType::Return, None, _)] [.., ref last_stmt, Stmt::Return(crate::ast::ReturnType::Return, None, _)]
if reduce_return && !last_stmt.returns_value() => if reduce_return && !last_stmt.returns_value() =>
{ {
state.set_dirty(); state.set_dirty();
statements.pop().unwrap(); statements.pop().unwrap();
} }
// { ...; return val; } -> { ...; val } // { ...; return val; } -> { ...; val }
[.., Stmt::Return(crate::ast::ReturnType::Return, expr, pos)] [.., Stmt::Return(crate::ast::ReturnType::Return, ref mut expr, pos)]
if reduce_return => if reduce_return =>
{ {
state.set_dirty(); state.set_dirty();
*statements.last_mut().unwrap() = if let Some(expr) = expr { *statements.last_mut().unwrap() = if let Some(expr) = expr {
Stmt::Expr(mem::take(expr)) Stmt::Expr(mem::take(expr))
} else { } else {
Stmt::Noop(*pos) Stmt::Noop(pos)
}; };
} }
[.., second_last_stmt, Stmt::Noop(_)] if second_last_stmt.returns_value() => (), [.., ref second_last_stmt, Stmt::Noop(_)]
[.., second_last_stmt, last_stmt] if second_last_stmt.returns_value() => {}
[.., ref second_last_stmt, ref last_stmt]
if !last_stmt.returns_value() && is_pure(last_stmt) => if !last_stmt.returns_value() && is_pure(last_stmt) =>
{ {
state.set_dirty(); state.set_dirty();
@ -313,8 +314,8 @@ fn optimize_stmt_block(
} }
} else { } else {
loop { loop {
match &statements[..] { match statements[..] {
[stmt] if is_pure(stmt) => { [ref stmt] if is_pure(stmt) => {
state.set_dirty(); state.set_dirty();
statements.clear(); statements.clear();
} }
@ -326,13 +327,13 @@ fn optimize_stmt_block(
statements.pop().unwrap(); statements.pop().unwrap();
} }
// { ...; return pure_val; } -> { ... } // { ...; return pure_val; } -> { ... }
[.., Stmt::Return(crate::ast::ReturnType::Return, Some(expr), _)] [.., Stmt::Return(crate::ast::ReturnType::Return, Some(ref expr), _)]
if reduce_return && expr.is_pure() => if reduce_return && expr.is_pure() =>
{ {
state.set_dirty(); state.set_dirty();
statements.pop().unwrap(); statements.pop().unwrap();
} }
[.., last_stmt] if is_pure(last_stmt) => { [.., ref last_stmt] if is_pure(last_stmt) => {
state.set_dirty(); state.set_dirty();
statements.pop().unwrap(); statements.pop().unwrap();
} }
@ -373,8 +374,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
&& x2.args[0].get_variable_name(true) == x.0.get_variable_name(true) && x2.args[0].get_variable_name(true) == x.0.get_variable_name(true)
) => ) =>
{ {
match &mut x.2 { match x.2 {
Expr::FnCall(x2, _) => { Expr::FnCall(ref mut x2, _) => {
state.set_dirty(); state.set_dirty();
let op = Token::lookup_from_syntax(&x2.name).unwrap(); let op = Token::lookup_from_syntax(&x2.name).unwrap();
let op_assignment = op.make_op_assignment().unwrap(); let op_assignment = op.make_op_assignment().unwrap();
@ -444,11 +445,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
Stmt::If(condition, x, _) => { Stmt::If(condition, x, _) => {
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
let if_block = mem::take(x.0.statements()).into_vec(); let if_block = mem::take(x.0.statements_mut()).into_vec();
*x.0.statements() = *x.0.statements_mut() =
optimize_stmt_block(if_block, state, preserve_result, true, false).into(); optimize_stmt_block(if_block, state, preserve_result, true, false).into();
let else_block = mem::take(x.1.statements()).into_vec(); let else_block = mem::take(x.1.statements_mut()).into_vec();
*x.1.statements() = *x.1.statements_mut() =
optimize_stmt_block(else_block, state, preserve_result, true, false).into(); optimize_stmt_block(else_block, state, preserve_result, true, false).into();
} }
@ -520,8 +521,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
_ => { _ => {
block.0 = Some(condition); block.0 = Some(condition);
*block.1.statements() = optimize_stmt_block( *block.1.statements_mut() = optimize_stmt_block(
mem::take(block.1.statements()).into_vec(), mem::take(block.1.statements_mut()).into_vec(),
state, state,
preserve_result, preserve_result,
true, true,
@ -541,8 +542,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
x.0.remove(&key); x.0.remove(&key);
} }
let def_block = mem::take(x.1.statements()).into_vec(); let def_block = mem::take(x.1.statements_mut()).into_vec();
*x.1.statements() = *x.1.statements_mut() =
optimize_stmt_block(def_block, state, preserve_result, true, false).into(); optimize_stmt_block(def_block, state, preserve_result, true, false).into();
} }
@ -554,8 +555,8 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
// while expr { block } // while expr { block }
Stmt::While(condition, body, _) => { Stmt::While(condition, body, _) => {
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
let block = mem::take(body.statements()).into_vec(); let block = mem::take(body.statements_mut()).into_vec();
*body.statements() = optimize_stmt_block(block, state, false, true, false).into(); *body.statements_mut() = optimize_stmt_block(block, state, false, true, false).into();
if body.len() == 1 { if body.len() == 1 {
match body[0] { match body[0] {
@ -582,7 +583,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
| Stmt::Do(body, Expr::BoolConstant(false, _), true, _) => { | Stmt::Do(body, Expr::BoolConstant(false, _), true, _) => {
state.set_dirty(); state.set_dirty();
let block_pos = body.position(); let block_pos = body.position();
let block = mem::take(body.statements()).into_vec(); let block = mem::take(body.statements_mut()).into_vec();
*stmt = Stmt::Block( *stmt = Stmt::Block(
optimize_stmt_block(block, state, false, true, false).into_boxed_slice(), optimize_stmt_block(block, state, false, true, false).into_boxed_slice(),
block_pos, block_pos,
@ -591,14 +592,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
// do { block } while|until expr // do { block } while|until expr
Stmt::Do(body, condition, _, _) => { Stmt::Do(body, condition, _, _) => {
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
let block = mem::take(body.statements()).into_vec(); let block = mem::take(body.statements_mut()).into_vec();
*body.statements() = optimize_stmt_block(block, state, false, true, false).into(); *body.statements_mut() = optimize_stmt_block(block, state, false, true, false).into();
} }
// for id in expr { block } // for id in expr { block }
Stmt::For(iterable, x, _) => { Stmt::For(iterable, x, _) => {
optimize_expr(iterable, state, false); optimize_expr(iterable, state, false);
let body = mem::take(x.2.statements()).into_vec(); let body = mem::take(x.2.statements_mut()).into_vec();
*x.2.statements() = optimize_stmt_block(body, state, false, true, false).into(); *x.2.statements_mut() = optimize_stmt_block(body, state, false, true, false).into();
} }
// let id = expr; // let id = expr;
Stmt::Let(expr, _, _, _) => optimize_expr(expr, state, false), Stmt::Let(expr, _, _, _) => optimize_expr(expr, state, false),
@ -624,7 +625,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
} }
} }
// try { pure try_block } catch ( var ) { catch_block } -> try_block // try { pure try_block } catch ( var ) { catch_block } -> try_block
Stmt::TryCatch(x, _, _) if x.0.iter().all(Stmt::is_pure) => { Stmt::TryCatch(x, _) if x.0.iter().all(Stmt::is_pure) => {
// If try block is pure, there will never be any exceptions // If try block is pure, there will never be any exceptions
state.set_dirty(); state.set_dirty();
let try_pos = x.0.position(); let try_pos = x.0.position();
@ -635,11 +636,13 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
); );
} }
// try { try_block } catch ( var ) { catch_block } // try { try_block } catch ( var ) { catch_block }
Stmt::TryCatch(x, _, _) => { Stmt::TryCatch(x, _) => {
let try_block = mem::take(x.0.statements()).into_vec(); let try_block = mem::take(x.0.statements_mut()).into_vec();
*x.0.statements() = optimize_stmt_block(try_block, state, false, true, false).into(); *x.0.statements_mut() =
let catch_block = mem::take(x.2.statements()).into_vec(); optimize_stmt_block(try_block, state, false, true, false).into();
*x.2.statements() = optimize_stmt_block(catch_block, state, false, true, false).into(); let catch_block = mem::take(x.2.statements_mut()).into_vec();
*x.2.statements_mut() =
optimize_stmt_block(catch_block, state, false, true, false).into();
} }
// func(...) // func(...)
Stmt::Expr(expr @ Expr::FnCall(_, _)) => { Stmt::Expr(expr @ Expr::FnCall(_, _)) => {
@ -686,7 +689,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
Expr::Stmt(x) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.position()) } Expr::Stmt(x) if x.is_empty() => { state.set_dirty(); *expr = Expr::Unit(x.position()) }
// { stmt; ... } - do not count promotion as dirty because it gets turned back into an array // { stmt; ... } - do not count promotion as dirty because it gets turned back into an array
Expr::Stmt(x) => { Expr::Stmt(x) => {
*x.statements() = optimize_stmt_block(mem::take(x.statements()).into_vec(), state, true, true, false).into(); *x.statements_mut() = optimize_stmt_block(mem::take(x.statements_mut()).into_vec(), state, true, true, false).into();
// { Stmt(Expr) } - promote // { Stmt(Expr) } - promote
match x.as_mut().as_mut() { match x.as_mut().as_mut() {
@ -726,7 +729,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
let mut result = a.remove(*i as usize); let mut result = mem::take(&mut a[*i as usize]);
result.set_position(*pos); result.set_position(*pos);
*expr = result; *expr = result;
} }
@ -737,7 +740,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
let mut result = a.remove(a.len() - i.abs() as usize); let index = a.len() - i.abs() as usize;
let mut result = mem::take(&mut a[index]);
result.set_position(*pos); result.set_position(*pos);
*expr = result; *expr = result;
} }
@ -794,9 +798,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
} }
// `... ${ ... } ...` // `... ${ ... } ...`
Expr::InterpolatedString(x) => { Expr::InterpolatedString(x) => {
x.iter_mut().for_each(|expr| optimize_expr(expr, state, false)); let mut n = 0;
let mut n= 0;
// Merge consecutive strings // Merge consecutive strings
while n < x.len()-1 { while n < x.len()-1 {
@ -948,7 +950,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
// Search for overloaded operators (can override built-in). // Search for overloaded operators (can override built-in).
if !has_native_fn(state, x.hashes.native_hash(), arg_types.as_ref()) { if !has_native_fn(state, x.hashes.native, arg_types.as_ref()) {
if let Some(mut result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) if let Some(mut result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| { .and_then(|f| {
let ctx = (state.engine, x.name.as_ref(), state.lib).into(); let ctx = (state.engine, x.name.as_ref(), state.lib).into();
@ -1136,9 +1138,9 @@ pub fn optimize_into_ast(
// Optimize the function body // Optimize the function body
let state = &mut State::new(engine, lib2, level); let state = &mut State::new(engine, lib2, level);
let body = mem::take(fn_def.body.statements()).into_vec(); let body = mem::take(fn_def.body.statements_mut()).into_vec();
*fn_def.body.statements() = *fn_def.body.statements_mut() =
optimize_stmt_block(body, state, true, true, true).into(); optimize_stmt_block(body, state, true, true, true).into();
fn_def fn_def

View File

@ -287,10 +287,9 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
{ {
reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64); reg_range!(lib | "range" => i8, u8, i16, u16, i32, u32, i64, u64);
if cfg!(not(target_arch = "wasm32")) { #[cfg(not(target_arch = "wasm32"))]
reg_range!(lib | "range" => i128, u128); reg_range!(lib | "range" => i128, u128);
} }
}
reg_range!(lib | step "range" => INT); reg_range!(lib | step "range" => INT);
@ -299,10 +298,9 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
{ {
reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64); reg_range!(lib | step "range" => i8, u8, i16, u16, i32, u32, i64, u64);
if cfg!(not(target_arch = "wasm32")) { #[cfg(not(target_arch = "wasm32"))]
reg_range!(lib | step "range" => i128, u128); reg_range!(lib | step "range" => i128, u128);
} }
}
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
{ {

View File

@ -9,7 +9,7 @@ use std::prelude::v1::*;
use crate::FLOAT; use crate::FLOAT;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::result::EvalAltResult; use crate::error::EvalAltResult;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]

View File

@ -38,11 +38,13 @@ pub trait Package {
fn init(lib: &mut Module); fn init(lib: &mut Module);
/// Retrieve the generic package library from this package. /// Retrieve the generic package library from this package.
#[must_use]
fn as_shared_module(&self) -> Shared<Module>; fn as_shared_module(&self) -> Shared<Module>;
/// Retrieve the generic package library from this package. /// Retrieve the generic package library from this package.
/// This method is deprecated and will be removed in the future. /// This method is deprecated and will be removed in the future.
/// Use [`as_shared_module`][Package::as_shared_module] instead. /// Use [`as_shared_module`][Package::as_shared_module] instead.
#[must_use]
#[deprecated(since = "0.19.9", note = "use `as_shared_module` instead")] #[deprecated(since = "0.19.9", note = "use `as_shared_module` instead")]
fn get(&self) -> Shared<Module> { fn get(&self) -> Shared<Module> {
self.as_shared_module() self.as_shared_module()

View File

@ -4,19 +4,18 @@ use crate::ast::{
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType, BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType,
ScriptFnDef, Stmt, StmtBlock, ScriptFnDef, Stmt, StmtBlock,
}; };
use crate::custom_syntax::{
CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING,
};
use crate::dynamic::{AccessMode, Union}; use crate::dynamic::{AccessMode, Union};
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
use crate::module::NamespaceRef; use crate::module::NamespaceRef;
use crate::optimize::optimize_into_ast; use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::optimize::OptimizationLevel;
use crate::syntax::{
CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING,
};
use crate::fn_hash::get_hasher;
use crate::token::{ use crate::token::{
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl, is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
}; };
use crate::utils::{get_hasher, IdentifierBuilder};
use crate::{ use crate::{
calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError, calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError,
ParseErrorType, Position, Scope, Shared, StaticVec, AST, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
@ -30,7 +29,7 @@ use std::{
}; };
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::{syntax::MARKER_FLOAT, FLOAT}; use crate::{custom_syntax::MARKER_FLOAT, FLOAT};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::FnAccess; use crate::FnAccess;
@ -41,6 +40,36 @@ type FunctionsLib = BTreeMap<u64, Shared<ScriptFnDef>>;
const NEVER_ENDS: &str = "never fails because `TokenStream` never ends"; const NEVER_ENDS: &str = "never fails because `TokenStream` never ends";
/// A factory of identifiers from text strings.
///
/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`],
/// this just returns a copy because most identifiers in Rhai are short and ASCII-based.
///
/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a
/// collection of strings and returns shared instances, only creating a new string when it is not
/// yet interned.
#[derive(Debug, Clone, Default, Hash)]
pub struct IdentifierBuilder(
#[cfg(feature = "no_smartstring")] std::collections::BTreeSet<Identifier>,
);
impl IdentifierBuilder {
/// Get an identifier from a text string.
#[inline(always)]
#[must_use]
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
#[cfg(not(feature = "no_smartstring"))]
return text.into();
#[cfg(feature = "no_smartstring")]
return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
let s: Identifier = text.into();
self.0.insert(s.clone());
s
});
}
}
/// A type that encapsulates the current state of the parser. /// A type that encapsulates the current state of the parser.
#[derive(Debug)] #[derive(Debug)]
pub struct ParseState<'e> { pub struct ParseState<'e> {
@ -59,8 +88,8 @@ pub struct ParseState<'e> {
external_vars: BTreeMap<Identifier, Position>, external_vars: BTreeMap<Identifier, Position>,
/// An indicator that disables variable capturing into externals one single time /// An indicator that disables variable capturing into externals one single time
/// up until the nearest consumed Identifier token. /// up until the nearest consumed Identifier token.
/// If set to false the next call to `access_var` will not capture the variable. /// If set to false the next call to [`access_var`][ParseState::access_var] will not capture the variable.
/// All consequent calls to `access_var` will not be affected /// All consequent calls to [`access_var`][ParseState::access_var] will not be affected
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
allow_capture: bool, allow_capture: bool,
/// Encapsulates a local stack with imported [module][crate::Module] names. /// Encapsulates a local stack with imported [module][crate::Module] names.
@ -78,6 +107,7 @@ pub struct ParseState<'e> {
impl<'e> ParseState<'e> { impl<'e> ParseState<'e> {
/// Create a new [`ParseState`]. /// Create a new [`ParseState`].
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new(engine: &'e Engine, tokenizer_control: TokenizerControl) -> Self { pub fn new(engine: &'e Engine, tokenizer_control: TokenizerControl) -> Self {
Self { Self {
engine, engine,
@ -103,8 +133,8 @@ impl<'e> ParseState<'e> {
/// ///
/// If the variable is not present in the scope adds it to the list of external variables /// If the variable is not present in the scope adds it to the list of external variables
/// ///
/// The return value is the offset to be deducted from `Stack::len`, /// The return value is the offset to be deducted from `ParseState::stack::len`,
/// i.e. the top element of the [`ParseState`] is offset 1. /// i.e. the top element of [`ParseState`]'s variables stack is offset 1.
/// ///
/// Return `None` when the variable name is not found in the `stack`. /// Return `None` when the variable name is not found in the `stack`.
#[inline(always)] #[inline(always)]
@ -155,6 +185,7 @@ impl<'e> ParseState<'e> {
/// Panics when called under `no_module`. /// Panics when called under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> { pub fn find_module(&self, name: &str) -> Option<NonZeroUsize> {
self.modules self.modules
.iter() .iter()
@ -166,6 +197,7 @@ impl<'e> ParseState<'e> {
/// Get an interned string, creating one if it is not yet interned. /// Get an interned string, creating one if it is not yet interned.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_identifier(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier { pub fn get_identifier(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
self.interned_strings.get(text) self.interned_strings.get(text)
} }
@ -197,6 +229,7 @@ struct ParseSettings {
impl ParseSettings { impl ParseSettings {
/// Create a new `ParseSettings` with one higher expression level. /// Create a new `ParseSettings` with one higher expression level.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn level_up(&self) -> Self { pub fn level_up(&self) -> Self {
Self { Self {
level: self.level + 1, level: self.level + 1,
@ -206,6 +239,7 @@ impl ParseSettings {
/// Make sure that the current level of expression nesting is within the maximum limit. /// Make sure that the current level of expression nesting is within the maximum limit.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn ensure_level_within_max_limit( pub fn ensure_level_within_max_limit(
&self, &self,
limit: Option<NonZeroUsize>, limit: Option<NonZeroUsize>,
@ -222,6 +256,7 @@ impl Expr {
/// All other variants are untouched. /// All other variants are untouched.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline(always)]
#[must_use]
fn into_property(self, state: &mut ParseState) -> Self { fn into_property(self, state: &mut ParseState) -> Self {
match self { match self {
Self::Variable(_, pos, x) if x.1.is_none() => { Self::Variable(_, pos, x) if x.1.is_none() => {
@ -366,17 +401,15 @@ fn parse_fn_call(
args.shrink_to_fit(); args.shrink_to_fit();
return Ok(Expr::FnCall( return Ok(FnCallExpr {
Box::new(FnCallExpr {
name: state.get_identifier(id), name: state.get_identifier(id),
capture, capture,
namespace, namespace,
hashes, hashes,
args, args,
..Default::default() ..Default::default()
}), }
settings.pos, .into_fn_call_expr(settings.pos));
));
} }
// id... // id...
_ => (), _ => (),
@ -418,17 +451,15 @@ fn parse_fn_call(
args.shrink_to_fit(); args.shrink_to_fit();
return Ok(Expr::FnCall( return Ok(FnCallExpr {
Box::new(FnCallExpr {
name: state.get_identifier(id), name: state.get_identifier(id),
capture, capture,
namespace, namespace,
hashes, hashes,
args, args,
..Default::default() ..Default::default()
}), }
settings.pos, .into_fn_call_expr(settings.pos));
));
} }
// id(...args, // id(...args,
(Token::Comma, _) => { (Token::Comma, _) => {
@ -472,7 +503,7 @@ fn parse_index_chain(
let idx_expr = parse_expr(input, state, lib, settings.level_up())?; let idx_expr = parse_expr(input, state, lib, settings.level_up())?;
// Check type of indexing - must be integer or string // Check type of indexing - must be integer or string
match &idx_expr { match idx_expr {
Expr::IntegerConstant(_, pos) => match lhs { Expr::IntegerConstant(_, pos) => match lhs {
Expr::IntegerConstant(_, _) Expr::IntegerConstant(_, _)
| Expr::Array(_, _) | Expr::Array(_, _)
@ -483,7 +514,7 @@ fn parse_index_chain(
return Err(PERR::MalformedIndexExpr( return Err(PERR::MalformedIndexExpr(
"Object map access expects string index, not a number".into(), "Object map access expects string index, not a number".into(),
) )
.into_err(*pos)) .into_err(pos))
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
@ -686,7 +717,7 @@ fn parse_array_literal(
arr.shrink_to_fit(); arr.shrink_to_fit();
Ok(Expr::Array(Box::new(arr), settings.pos)) Ok(Expr::Array(arr.into(), settings.pos))
} }
/// Parse a map literal. /// Parse a map literal.
@ -1095,7 +1126,7 @@ fn parse_primary(
} }
segments.shrink_to_fit(); segments.shrink_to_fit();
Expr::InterpolatedString(Box::new(segments)) Expr::InterpolatedString(segments.into())
} }
// Array literal // Array literal
@ -1239,14 +1270,14 @@ fn parse_primary(
let (_, namespace, name) = *x; let (_, namespace, name) = *x;
settings.pos = var_pos; settings.pos = var_pos;
let ns = namespace.map(|(_, ns)| ns); let ns = namespace.map(|(ns, _)| ns);
parse_fn_call(input, state, lib, name, true, ns, settings.level_up())? parse_fn_call(input, state, lib, name, true, ns, settings.level_up())?
} }
// Function call // Function call
(Expr::Variable(_, var_pos, x), Token::LeftParen) => { (Expr::Variable(_, var_pos, x), Token::LeftParen) => {
let (_, namespace, name) = *x; let (_, namespace, name) = *x;
settings.pos = var_pos; settings.pos = var_pos;
let ns = namespace.map(|(_, ns)| ns); let ns = namespace.map(|(ns, _)| ns);
parse_fn_call(input, state, lib, name, false, ns, settings.level_up())? parse_fn_call(input, state, lib, name, false, ns, settings.level_up())?
} }
// module access // module access
@ -1258,12 +1289,12 @@ fn parse_primary(
pos: var_pos, pos: var_pos,
}; };
if let Some((_, ref mut namespace)) = namespace { if let Some((ref mut namespace, _)) = namespace {
namespace.push(var_name_def); namespace.push(var_name_def);
} else { } else {
let mut ns: NamespaceRef = Default::default(); let mut ns: NamespaceRef = Default::default();
ns.push(var_name_def); ns.push(var_name_def);
namespace = Some((42, ns)); namespace = Some((ns, 42));
} }
Expr::Variable( Expr::Variable(
@ -1308,15 +1339,15 @@ fn parse_primary(
// Cache the hash key for namespace-qualified variables // Cache the hash key for namespace-qualified variables
match root_expr { match root_expr {
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x), Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
Expr::Index(ref mut x, _) | Expr::Dot(ref mut x, _) => match &mut x.lhs { Expr::Index(ref mut x, _) | Expr::Dot(ref mut x, _) => match x.lhs {
Expr::Variable(_, _, x) if x.1.is_some() => Some(x), Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
_ => None, _ => None,
}, },
_ => None, _ => None,
} }
.map(|x| match x.as_mut() { .map(|x| match x {
(_, Some((hash, namespace)), name) => { (_, Some((namespace, hash)), name) => {
*hash = calc_qualified_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0); *hash = calc_qualified_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -1370,15 +1401,13 @@ fn parse_unary(
args.push(expr); args.push(expr);
args.shrink_to_fit(); args.shrink_to_fit();
Ok(Expr::FnCall( Ok(FnCallExpr {
Box::new(FnCallExpr {
name: state.get_identifier("-"), name: state.get_identifier("-"),
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)), hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
args, args,
..Default::default() ..Default::default()
}), }
pos, .into_fn_call_expr(pos))
))
} }
} }
} }
@ -1397,15 +1426,13 @@ fn parse_unary(
args.push(expr); args.push(expr);
args.shrink_to_fit(); args.shrink_to_fit();
Ok(Expr::FnCall( Ok(FnCallExpr {
Box::new(FnCallExpr {
name: state.get_identifier("+"), name: state.get_identifier("+"),
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)), hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
args, args,
..Default::default() ..Default::default()
}), }
pos, .into_fn_call_expr(pos))
))
} }
} }
} }
@ -1416,15 +1443,13 @@ fn parse_unary(
args.push(parse_unary(input, state, lib, settings.level_up())?); args.push(parse_unary(input, state, lib, settings.level_up())?);
args.shrink_to_fit(); args.shrink_to_fit();
Ok(Expr::FnCall( Ok(FnCallExpr {
Box::new(FnCallExpr {
name: state.get_identifier("!"), name: state.get_identifier("!"),
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)), hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
args, args,
..Default::default() ..Default::default()
}), }
pos, .into_fn_call_expr(pos))
))
} }
// <EOF> // <EOF>
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)), Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
@ -1441,6 +1466,7 @@ fn make_assignment_stmt<'a>(
rhs: Expr, rhs: Expr,
op_pos: Position, op_pos: Position,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
#[must_use]
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> { fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> {
match expr { match expr {
Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs { Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs {
@ -1460,17 +1486,17 @@ fn make_assignment_stmt<'a>(
let op_info = op.map(|v| OpAssignment::new(v)); let op_info = op.map(|v| OpAssignment::new(v));
match &lhs { match lhs {
// const_expr = rhs // const_expr = rhs
expr if expr.is_constant() => { ref expr if expr.is_constant() => {
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position())) Err(PERR::AssignmentToConstant("".into()).into_err(lhs.position()))
} }
// var (non-indexed) = rhs // var (non-indexed) = rhs
Expr::Variable(None, _, x) if x.0.is_none() => { Expr::Variable(None, _, ref x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
} }
// var (indexed) = rhs // var (indexed) = rhs
Expr::Variable(i, var_pos, x) => { Expr::Variable(i, var_pos, ref x) => {
let (index, _, name) = x.as_ref(); let (index, _, name) = x.as_ref();
let index = i.map_or_else( let index = i.map_or_else(
|| { || {
@ -1488,20 +1514,20 @@ fn make_assignment_stmt<'a>(
} }
// Constant values cannot be assigned to // Constant values cannot be assigned to
AccessMode::ReadOnly => { AccessMode::ReadOnly => {
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*var_pos)) Err(PERR::AssignmentToConstant(name.to_string()).into_err(var_pos))
} }
} }
} }
// xxx[???]... = rhs, xxx.prop... = rhs // xxx[???]... = rhs, xxx.prop... = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => { Expr::Index(ref x, _) | Expr::Dot(ref x, _) => {
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) { match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
None => match &x.lhs { None => match x.lhs {
// var[???] = rhs, var.??? = rhs // var[???] = rhs, var.??? = rhs
Expr::Variable(_, _, _) => { Expr::Variable(_, _, _) => {
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
} }
// expr[???] = rhs, expr.??? = rhs // expr[???] = rhs, expr.??? = rhs
expr => { ref expr => {
Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position())) Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position()))
} }
}, },
@ -1580,7 +1606,7 @@ fn make_dot_expr(
// lhs.module::id - syntax error // lhs.module::id - syntax error
(_, Expr::Variable(_, _, x)) if x.1.is_some() => { (_, Expr::Variable(_, _, x)) if x.1.is_some() => {
return Err(PERR::PropertyExpected return Err(PERR::PropertyExpected
.into_err(x.1.expect("never fails because the namespace is `Some`").1[0].pos)) .into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos))
} }
// lhs.prop // lhs.prop
(lhs, prop @ Expr::Property(_)) => { (lhs, prop @ Expr::Property(_)) => {
@ -1765,19 +1791,17 @@ fn parse_binary_op(
| Token::PowerOf | Token::PowerOf
| Token::Ampersand | Token::Ampersand
| Token::Pipe | Token::Pipe
| Token::XOr => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos), | Token::XOr => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
// '!=' defaults to true when passed invalid operands // '!=' defaults to true when passed invalid operands
Token::NotEqualsTo => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos), Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
// Comparison operators default to false when passed invalid operands // Comparison operators default to false when passed invalid operands
Token::EqualsTo Token::EqualsTo
| Token::LessThan | Token::LessThan
| Token::LessThanEqualsTo | Token::LessThanEqualsTo
| Token::GreaterThan | Token::GreaterThan
| Token::GreaterThanEqualsTo => { | Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos)
}
Token::Or => { Token::Or => {
let rhs = args let rhs = args
@ -1816,16 +1840,13 @@ fn parse_binary_op(
args.shrink_to_fit(); args.shrink_to_fit();
// Convert into a call to `contains` // Convert into a call to `contains`
let hash = calc_fn_hash(OP_CONTAINS, 2); FnCallExpr {
Expr::FnCall( hashes: FnCallHashes::from_script(calc_fn_hash(OP_CONTAINS, 2)),
Box::new(FnCallExpr {
hashes: FnCallHashes::from_script(hash),
args, args,
name: state.get_identifier(OP_CONTAINS), name: state.get_identifier(OP_CONTAINS),
..op_base ..op_base
}), }
pos, .into_fn_call_expr(pos)
)
} }
Token::Custom(s) Token::Custom(s)
@ -1837,8 +1858,7 @@ fn parse_binary_op(
{ {
let hash = calc_fn_hash(&s, 2); let hash = calc_fn_hash(&s, 2);
Expr::FnCall( FnCallExpr {
Box::new(FnCallExpr {
hashes: if is_valid_identifier(s.chars()) { hashes: if is_valid_identifier(s.chars()) {
FnCallHashes::from_script(hash) FnCallHashes::from_script(hash)
} else { } else {
@ -1846,9 +1866,8 @@ fn parse_binary_op(
}, },
args, args,
..op_base ..op_base
}), }
pos, .into_fn_call_expr(pos)
)
} }
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)), op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)),
@ -1889,12 +1908,10 @@ fn parse_custom_syntax(
settings.pos = *fwd_pos; settings.pos = *fwd_pos;
let settings = settings.level_up(); let settings = settings.level_up();
let required_token = if let Some(seg) = parse_func(&segments, fwd_token.syntax().as_ref()) let required_token = match parse_func(&segments, fwd_token.syntax().as_ref()) {
.map_err(|err| err.0.into_err(settings.pos))? Ok(Some(seg)) => seg,
{ Ok(None) => break,
seg Err(err) => return Err(err.0.into_err(settings.pos)),
} else {
break;
}; };
match required_token.as_str() { match required_token.as_str() {
@ -1994,14 +2011,12 @@ fn parse_custom_syntax(
keywords.shrink_to_fit(); keywords.shrink_to_fit();
tokens.shrink_to_fit(); tokens.shrink_to_fit();
Ok(Expr::Custom( Ok(CustomExpr {
Box::new(CustomExpr {
keywords, keywords,
tokens, tokens,
scope_changed: syntax.scope_changed, scope_changed: syntax.scope_changed,
}), }
pos, .into_custom_syntax_expr(pos))
))
} }
/// Parse an expression. /// Parse an expression.
@ -2407,23 +2422,19 @@ fn parse_export(
_ => (), _ => (),
} }
let mut exports = Vec::with_capacity(4); let mut exports = Vec::<(Ident, Ident)>::with_capacity(4);
loop { loop {
let (id, id_pos) = parse_var_name(input)?; let (id, id_pos) = parse_var_name(input)?;
let rename = if match_token(input, Token::As).0 { let (rename, rename_pos) = if match_token(input, Token::As).0 {
let (name, pos) = parse_var_name(input)?; let (name, pos) = parse_var_name(input)?;
if exports.iter().any(|(_, alias)| match alias { if exports.iter().any(|(_, alias)| alias.name == name) {
Some(Ident { name: alias, .. }) if alias == &name => true,
_ => false,
}) {
return Err(PERR::DuplicatedVariable(name).into_err(pos)); return Err(PERR::DuplicatedVariable(name).into_err(pos));
} }
let name = state.get_identifier(name); (name, pos)
Some(Ident { name, pos })
} else { } else {
None (Default::default(), Position::NONE)
}; };
exports.push(( exports.push((
@ -2431,7 +2442,10 @@ fn parse_export(
name: state.get_identifier(id), name: state.get_identifier(id),
pos: id_pos, pos: id_pos,
}, },
rename, Ident {
name: state.get_identifier(rename),
pos: rename_pos,
},
)); ));
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
@ -2817,7 +2831,6 @@ fn parse_try_catch(
Ok(Stmt::TryCatch( Ok(Stmt::TryCatch(
Box::new((body.into(), var_def, catch_body.into())), Box::new((body.into(), var_def, catch_body.into())),
settings.pos, settings.pos,
catch_pos,
)) ))
} }
@ -2838,12 +2851,11 @@ fn parse_fn(
let (token, pos) = input.next().expect(NEVER_ENDS); let (token, pos) = input.next().expect(NEVER_ENDS);
let name = token let name = match token.into_function_name_for_override() {
.into_function_name_for_override() Ok(r) => r,
.map_err(|t| match t { Err(Token::Reserved(s)) => return Err(PERR::Reserved(s).into_err(pos)),
Token::Reserved(s) => PERR::Reserved(s).into_err(pos), Err(_) => return Err(PERR::FnMissingName.into_err(pos)),
_ => PERR::FnMissingName.into_err(pos), };
})?;
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
(Token::LeftParen, _) => eat_token(input, Token::LeftParen), (Token::LeftParen, _) => eat_token(input, Token::LeftParen),
@ -2940,22 +2952,18 @@ fn make_curry_from_externals(
} }
let num_externals = externals.len(); let num_externals = externals.len();
let mut args: StaticVec<_> = Default::default(); let mut args = StaticVec::with_capacity(externals.len() + 1);
args.push(fn_expr); args.push(fn_expr);
externals.iter().for_each(|x| { args.extend(
args.push(Expr::Variable( externals
None, .iter()
Position::NONE, .cloned()
Box::new((None, None, x.clone())), .map(|x| Expr::Variable(None, Position::NONE, Box::new((None, None, x)))),
)); );
});
args.shrink_to_fit(); let expr = FnCallExpr {
let expr = Expr::FnCall(
Box::new(FnCallExpr {
name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY), name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY),
hashes: FnCallHashes::from_native(calc_fn_hash( hashes: FnCallHashes::from_native(calc_fn_hash(
crate::engine::KEYWORD_FN_PTR_CURRY, crate::engine::KEYWORD_FN_PTR_CURRY,
@ -2963,13 +2971,12 @@ fn make_curry_from_externals(
)), )),
args, args,
..Default::default() ..Default::default()
}), }
pos, .into_fn_call_expr(pos);
);
// Convert the entire expression into a statement block, then insert the relevant // Convert the entire expression into a statement block, then insert the relevant
// [`Share`][Stmt::Share] statements. // [`Share`][Stmt::Share] statements.
let mut statements: StaticVec<_> = Default::default(); let mut statements = StaticVec::with_capacity(externals.len() + 1);
statements.extend(externals.into_iter().map(Stmt::Share)); statements.extend(externals.into_iter().map(Stmt::Share));
statements.push(Stmt::Expr(expr)); statements.push(Stmt::Expr(expr));
Expr::Stmt(Box::new(StmtBlock::new(statements, pos))) Expr::Stmt(Box::new(StmtBlock::new(statements, pos)))
@ -2986,7 +2993,7 @@ fn parse_anon_fn(
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
let mut params: StaticVec<_> = Default::default(); let mut params_list: StaticVec<_> = Default::default();
if input.next().expect(NEVER_ENDS).0 != Token::Or { if input.next().expect(NEVER_ENDS).0 != Token::Or {
if !match_token(input, Token::Pipe).0 { if !match_token(input, Token::Pipe).0 {
@ -2994,12 +3001,12 @@ fn parse_anon_fn(
match input.next().expect(NEVER_ENDS) { match input.next().expect(NEVER_ENDS) {
(Token::Pipe, _) => break, (Token::Pipe, _) => break,
(Token::Identifier(s), pos) => { (Token::Identifier(s), pos) => {
if params.iter().any(|(p, _)| p == &s) { if params_list.iter().any(|p| p == &s) {
return Err(PERR::FnDuplicatedParam("".to_string(), s).into_err(pos)); return Err(PERR::FnDuplicatedParam("".to_string(), s).into_err(pos));
} }
let s = state.get_identifier(s); let s = state.get_identifier(s);
state.stack.push((s.clone(), AccessMode::ReadWrite)); state.stack.push((s.clone(), AccessMode::ReadWrite));
params.push((s, pos)) params_list.push(s)
} }
(Token::LexError(err), pos) => return Err(err.into_err(pos)), (Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => { (_, pos) => {
@ -3033,29 +3040,22 @@ fn parse_anon_fn(
// External variables may need to be processed in a consistent order, // External variables may need to be processed in a consistent order,
// so extract them into a list. // so extract them into a list.
let externals: StaticVec<Identifier> = {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
{ let externals: StaticVec<Identifier> = state
state
.external_vars .external_vars
.iter() .iter()
.map(|(name, _)| name.clone()) .map(|(name, _)| name.clone())
.collect() .collect();
}
#[cfg(feature = "no_closure")]
Default::default()
};
let mut params: StaticVec<_> = if cfg!(not(feature = "no_closure")) { #[cfg(not(feature = "no_closure"))]
externals let mut params = StaticVec::with_capacity(params_list.len() + externals.len());
.iter() #[cfg(feature = "no_closure")]
.cloned() let mut params = StaticVec::with_capacity(params_list.len());
.chain(params.into_iter().map(|(v, _)| v))
.collect() #[cfg(not(feature = "no_closure"))]
} else { params.extend(externals.iter().cloned());
params.into_iter().map(|(v, _)| v).collect()
}; params.append(&mut params_list);
params.shrink_to_fit();
// Create unique function name by hashing the script body plus the parameters. // Create unique function name by hashing the script body plus the parameters.
let hasher = &mut get_hasher(); let hasher = &mut get_hasher();
@ -3091,6 +3091,8 @@ fn parse_anon_fn(
} }
impl Engine { impl Engine {
/// Parse a global level expression.
#[must_use]
pub(crate) fn parse_global_expr( pub(crate) fn parse_global_expr(
&self, &self,
input: &mut TokenStream, input: &mut TokenStream,
@ -3132,6 +3134,7 @@ impl Engine {
} }
/// Parse the global level statements. /// Parse the global level statements.
#[must_use]
fn parse_global_level( fn parse_global_level(
&self, &self,
input: &mut TokenStream, input: &mut TokenStream,
@ -3193,6 +3196,7 @@ impl Engine {
/// Run the parser on an input stream, returning an AST. /// Run the parser on an input stream, returning an AST.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn parse( pub(crate) fn parse(
&self, &self,
input: &mut TokenStream, input: &mut TokenStream,

View File

@ -21,8 +21,10 @@ pub use rhai_codegen::{export_fn, register_exported_fn};
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead. /// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
pub trait PluginFunction { pub trait PluginFunction {
/// Call the plugin function with the arguments provided. /// Call the plugin function with the arguments provided.
#[must_use]
fn call(&self, context: NativeCallContext, args: &mut FnCallArgs) -> RhaiResult; fn call(&self, context: NativeCallContext, args: &mut FnCallArgs) -> RhaiResult;
/// Is this plugin function a method? /// Is this plugin function a method?
#[must_use]
fn is_method_call(&self) -> bool; fn is_method_call(&self) -> bool;
} }

View File

@ -95,6 +95,7 @@ impl<'a> Scope<'a> {
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42); /// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
values: Default::default(), values: Default::default(),
@ -121,6 +122,7 @@ impl<'a> Scope<'a> {
/// assert!(my_scope.is_empty()); /// assert!(my_scope.is_empty());
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn clear(&mut self) -> &mut Self { pub fn clear(&mut self) -> &mut Self {
self.names.clear(); self.names.clear();
self.values.clear(); self.values.clear();
@ -140,6 +142,7 @@ impl<'a> Scope<'a> {
/// assert_eq!(my_scope.len(), 1); /// assert_eq!(my_scope.len(), 1);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.values.len() self.values.len()
} }
@ -157,6 +160,7 @@ impl<'a> Scope<'a> {
/// assert!(!my_scope.is_empty()); /// assert!(!my_scope.is_empty());
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.values.len() == 0 self.values.len() == 0
} }
@ -301,6 +305,7 @@ impl<'a> Scope<'a> {
/// assert!(!my_scope.contains("y")); /// assert!(!my_scope.contains("y"));
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn contains(&self, name: &str) -> bool { pub fn contains(&self, name: &str) -> bool {
self.names self.names
.iter() .iter()
@ -309,6 +314,7 @@ impl<'a> Scope<'a> {
} }
/// Find an entry in the [`Scope`], starting from the last. /// Find an entry in the [`Scope`], starting from the last.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> { pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> {
self.names self.names
.iter() .iter()
@ -335,6 +341,7 @@ impl<'a> Scope<'a> {
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42); /// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 42);
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> { pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
self.names self.names
.iter() .iter()
@ -402,6 +409,7 @@ impl<'a> Scope<'a> {
/// ///
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 123); /// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 123);
/// ``` /// ```
#[must_use]
pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> { pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
self.get_index(name) self.get_index(name)
.and_then(move |(index, access)| match access { .and_then(move |(index, access)| match access {
@ -415,6 +423,7 @@ impl<'a> Scope<'a> {
/// ///
/// Panics if the index is out of bounds. /// Panics if the index is out of bounds.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic { pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
self.values self.values
.get_mut(index) .get_mut(index)
@ -448,6 +457,7 @@ impl<'a> Scope<'a> {
/// Clone the [`Scope`], keeping only the last instances of each variable name. /// Clone the [`Scope`], keeping only the last instances of each variable name.
/// Shadowed variables are omitted in the copy. /// Shadowed variables are omitted in the copy.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn clone_visible(&self) -> Self { pub(crate) fn clone_visible(&self) -> Self {
let mut entries: Self = Default::default(); let mut entries: Self = Default::default();
@ -467,6 +477,7 @@ impl<'a> Scope<'a> {
/// Get an iterator to entries in the [`Scope`]. /// Get an iterator to entries in the [`Scope`].
#[inline(always)] #[inline(always)]
#[allow(dead_code)] #[allow(dead_code)]
#[must_use]
pub(crate) fn into_iter( pub(crate) fn into_iter(
self, self,
) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<Identifier>)> { ) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<Identifier>)> {
@ -503,6 +514,7 @@ impl<'a> Scope<'a> {
/// assert_eq!(value.cast::<String>(), "hello"); /// assert_eq!(value.cast::<String>(), "hello");
/// ``` /// ```
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Dynamic)> { pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Dynamic)> {
self.iter_raw() self.iter_raw()
.map(|(name, constant, value)| (name, constant, value.flatten_clone())) .map(|(name, constant, value)| (name, constant, value.flatten_clone()))
@ -510,6 +522,7 @@ impl<'a> Scope<'a> {
/// Get an iterator to entries in the [`Scope`]. /// Get an iterator to entries in the [`Scope`].
/// Shared values are not expanded. /// Shared values are not expanded.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> { pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
self.names self.names
.iter() .iter()

View File

@ -28,14 +28,17 @@ impl<'de> DynamicDeserializer<'de> {
/// ///
/// The reference is necessary because the deserialized type may hold references /// The reference is necessary because the deserialized type may hold references
/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic]. /// (especially `&str`) to the source [`Dynamic`][crate::Dynamic].
#[must_use]
pub fn from_dynamic(value: &'de Dynamic) -> Self { pub fn from_dynamic(value: &'de Dynamic) -> Self {
Self { value } Self { value }
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
#[must_use]
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> { fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> {
self.type_error_str(type_name::<T>()) self.type_error_str(type_name::<T>())
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
#[must_use]
fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> { fn type_error_str<T>(&self, error: &str) -> Result<T, Box<EvalAltResult>> {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
error.into(), error.into(),
@ -44,6 +47,7 @@ impl<'de> DynamicDeserializer<'de> {
) )
.into() .into()
} }
#[must_use]
fn deserialize_int<V: Visitor<'de>>( fn deserialize_int<V: Visitor<'de>>(
&mut self, &mut self,
v: crate::INT, v: crate::INT,
@ -107,6 +111,7 @@ impl<'de> DynamicDeserializer<'de> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[must_use]
pub fn from_dynamic<'de, T: Deserialize<'de>>( pub fn from_dynamic<'de, T: Deserialize<'de>>(
value: &'de Dynamic, value: &'de Dynamic,
) -> Result<T, Box<EvalAltResult>> { ) -> Result<T, Box<EvalAltResult>> {
@ -125,7 +130,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
type Error = Box<EvalAltResult>; type Error = Box<EvalAltResult>;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> { fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
match &self.value.0 { match self.value.0 {
Union::Unit(_, _, _) => self.deserialize_unit(visitor), Union::Unit(_, _, _) => self.deserialize_unit(visitor),
Union::Bool(_, _, _) => self.deserialize_bool(visitor), Union::Bool(_, _, _) => self.deserialize_bool(visitor),
Union::Str(_, _, _) => self.deserialize_str(visitor), Union::Str(_, _, _) => self.deserialize_str(visitor),
@ -158,16 +163,16 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
Union::TimeStamp(_, _, _) => self.type_error(), Union::TimeStamp(_, _, _) => self.type_error(),
Union::Variant(value, _, _) if value.is::<i8>() => self.deserialize_i8(visitor), Union::Variant(ref value, _, _) if value.is::<i8>() => self.deserialize_i8(visitor),
Union::Variant(value, _, _) if value.is::<i16>() => self.deserialize_i16(visitor), Union::Variant(ref value, _, _) if value.is::<i16>() => self.deserialize_i16(visitor),
Union::Variant(value, _, _) if value.is::<i32>() => self.deserialize_i32(visitor), Union::Variant(ref value, _, _) if value.is::<i32>() => self.deserialize_i32(visitor),
Union::Variant(value, _, _) if value.is::<i64>() => self.deserialize_i64(visitor), Union::Variant(ref value, _, _) if value.is::<i64>() => self.deserialize_i64(visitor),
Union::Variant(value, _, _) if value.is::<i128>() => self.deserialize_i128(visitor), Union::Variant(ref value, _, _) if value.is::<i128>() => self.deserialize_i128(visitor),
Union::Variant(value, _, _) if value.is::<u8>() => self.deserialize_u8(visitor), Union::Variant(ref value, _, _) if value.is::<u8>() => self.deserialize_u8(visitor),
Union::Variant(value, _, _) if value.is::<u16>() => self.deserialize_u16(visitor), Union::Variant(ref value, _, _) if value.is::<u16>() => self.deserialize_u16(visitor),
Union::Variant(value, _, _) if value.is::<u32>() => self.deserialize_u32(visitor), Union::Variant(ref value, _, _) if value.is::<u32>() => self.deserialize_u32(visitor),
Union::Variant(value, _, _) if value.is::<u64>() => self.deserialize_u64(visitor), Union::Variant(ref value, _, _) if value.is::<u64>() => self.deserialize_u64(visitor),
Union::Variant(value, _, _) if value.is::<u128>() => self.deserialize_u128(visitor), Union::Variant(ref value, _, _) if value.is::<u128>() => self.deserialize_u128(visitor),
Union::Variant(_, _, _) => self.type_error(), Union::Variant(_, _, _) => self.type_error(),
@ -494,6 +499,7 @@ struct IterateArray<'a, ITER: Iterator<Item = &'a Dynamic>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateArray<'a, ITER> { impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateArray<'a, ITER> {
#[must_use]
pub fn new(iter: ITER) -> Self { pub fn new(iter: ITER) -> Self {
Self { iter } Self { iter }
} }
@ -534,6 +540,7 @@ where
KEYS: Iterator<Item = &'a str>, KEYS: Iterator<Item = &'a str>,
VALUES: Iterator<Item = &'a Dynamic>, VALUES: Iterator<Item = &'a Dynamic>,
{ {
#[must_use]
pub fn new(keys: KEYS, values: VALUES) -> Self { pub fn new(keys: KEYS, values: VALUES) -> Self {
Self { keys, values } Self { keys, values }
} }

View File

@ -214,6 +214,7 @@ impl Engine {
/// 2) Functions registered into the global namespace /// 2) Functions registered into the global namespace
/// 3) Functions in static modules /// 3) Functions in static modules
/// 4) Functions in global modules (optional) /// 4) Functions in global modules (optional)
#[must_use]
pub fn gen_fn_metadata_with_ast_to_json( pub fn gen_fn_metadata_with_ast_to_json(
&self, &self,
ast: &AST, ast: &AST,
@ -253,6 +254,7 @@ impl Engine {
/// 1) Functions registered into the global namespace /// 1) Functions registered into the global namespace
/// 2) Functions in static modules /// 2) Functions in static modules
/// 3) Functions in global modules (optional) /// 3) Functions in global modules (optional)
#[must_use]
pub fn gen_fn_metadata_to_json(&self, include_global: bool) -> serde_json::Result<String> { pub fn gen_fn_metadata_to_json(&self, include_global: bool) -> serde_json::Result<String> {
self.gen_fn_metadata_with_ast_to_json(&Default::default(), include_global) self.gen_fn_metadata_with_ast_to_json(&Default::default(), include_global)
} }

View File

@ -25,6 +25,7 @@ struct DynamicSerializer {
impl DynamicSerializer { impl DynamicSerializer {
/// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value. /// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value.
#[must_use]
pub fn new(_value: Dynamic) -> Self { pub fn new(_value: Dynamic) -> Self {
Self { Self {
_key: Default::default(), _key: Default::default(),
@ -81,6 +82,7 @@ impl DynamicSerializer {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[must_use]
pub fn to_dynamic<T: Serialize>(value: T) -> RhaiResult { pub fn to_dynamic<T: Serialize>(value: T) -> RhaiResult {
let mut s = DynamicSerializer::new(Default::default()); let mut s = DynamicSerializer::new(Default::default());
value.serialize(&mut s) value.serialize(&mut s)

View File

@ -11,23 +11,23 @@ use serde::ser::SerializeMap;
impl Serialize for Dynamic { impl Serialize for Dynamic {
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> { fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
match &self.0 { match self.0 {
Union::Unit(_, _, _) => ser.serialize_unit(), Union::Unit(_, _, _) => ser.serialize_unit(),
Union::Bool(x, _, _) => ser.serialize_bool(*x), Union::Bool(x, _, _) => ser.serialize_bool(x),
Union::Str(s, _, _) => ser.serialize_str(s.as_str()), Union::Str(ref s, _, _) => ser.serialize_str(s.as_str()),
Union::Char(c, _, _) => ser.serialize_str(&c.to_string()), Union::Char(c, _, _) => ser.serialize_str(&c.to_string()),
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
Union::Int(x, _, _) => ser.serialize_i64(*x), Union::Int(x, _, _) => ser.serialize_i64(x),
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
Union::Int(x, _, _) => ser.serialize_i32(*x), Union::Int(x, _, _) => ser.serialize_i32(x),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
Union::Float(x, _, _) => ser.serialize_f64(**x), Union::Float(x, _, _) => ser.serialize_f64(*x),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(feature = "f32_float")] #[cfg(feature = "f32_float")]
Union::Float(x, _, _) => ser.serialize_f32(**x), Union::Float(x, _, _) => ser.serialize_f32(*x),
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
@ -53,27 +53,27 @@ impl Serialize for Dynamic {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(a, _, _) => (**a).serialize(ser), Union::Array(ref a, _, _) => (**a).serialize(ser),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(m, _, _) => { Union::Map(ref m, _, _) => {
let mut map = ser.serialize_map(Some(m.len()))?; let mut map = ser.serialize_map(Some(m.len()))?;
for (k, v) in m.iter() { for (k, v) in m.iter() {
map.serialize_entry(k.as_str(), v)?; map.serialize_entry(k.as_str(), v)?;
} }
map.end() map.end()
} }
Union::FnPtr(f, _, _) => ser.serialize_str(f.fn_name()), Union::FnPtr(ref f, _, _) => ser.serialize_str(f.fn_name()),
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
Union::TimeStamp(x, _, _) => ser.serialize_str(x.as_ref().type_name()), Union::TimeStamp(ref x, _, _) => ser.serialize_str(x.as_ref().type_name()),
Union::Variant(v, _, _) => ser.serialize_str((***v).type_name()), Union::Variant(ref v, _, _) => ser.serialize_str((***v).type_name()),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
Union::Shared(cell, _, _) => cell.borrow().serialize(ser), Union::Shared(ref cell, _, _) => cell.borrow().serialize(ser),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
Union::Shared(cell, _, _) => cell.read().unwrap().serialize(ser), Union::Shared(ref cell, _, _) => cell.read().unwrap().serialize(ser),
} }
} }
} }

View File

@ -13,10 +13,12 @@ pub struct StringSliceDeserializer<'a> {
impl<'a> StringSliceDeserializer<'a> { impl<'a> StringSliceDeserializer<'a> {
/// Create an `ImmutableStringDeserializer` from an `&str` reference. /// Create an `ImmutableStringDeserializer` from an `&str` reference.
#[must_use]
pub fn from_str(value: &'a str) -> Self { pub fn from_str(value: &'a str) -> Self {
Self { value } Self { value }
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
#[must_use]
fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> { fn type_error<T>(&self) -> Result<T, Box<EvalAltResult>> {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
type_name::<T>().into(), type_name::<T>().into(),

View File

@ -89,6 +89,7 @@ impl Position {
/// ///
/// Panics if `line` is zero. /// Panics if `line` is zero.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn new(line: u16, _position: u16) -> Self { pub fn new(line: u16, _position: u16) -> Self {
assert!(line != 0, "line cannot be zero"); assert!(line != 0, "line cannot be zero");
@ -101,6 +102,7 @@ impl Position {
} }
/// Get the line number (1-based), or [`None`] if there is no position. /// Get the line number (1-based), or [`None`] if there is no position.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn line(self) -> Option<usize> { pub fn line(self) -> Option<usize> {
if self.is_none() { if self.is_none() {
None None
@ -113,6 +115,7 @@ impl Position {
} }
/// Get the character position (1-based), or [`None`] if at beginning of a line. /// Get the character position (1-based), or [`None`] if at beginning of a line.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn position(self) -> Option<usize> { pub fn position(self) -> Option<usize> {
if self.is_none() { if self.is_none() {
None None
@ -171,6 +174,7 @@ impl Position {
} }
/// Is this [`Position`] at the beginning of a line? /// Is this [`Position`] at the beginning of a line?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_beginning_of_line(self) -> bool { pub fn is_beginning_of_line(self) -> bool {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
return self.pos == 0 && !self.is_none(); return self.pos == 0 && !self.is_none();
@ -179,6 +183,7 @@ impl Position {
} }
/// Is there no [`Position`]? /// Is there no [`Position`]?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_none(self) -> bool { pub fn is_none(self) -> bool {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
return self == Self::NONE; return self == Self::NONE;
@ -187,6 +192,7 @@ impl Position {
} }
/// Print this [`Position`] for debug purposes. /// Print this [`Position`] for debug purposes.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
if !self.is_none() { if !self.is_none() {
@ -465,6 +471,7 @@ impl Token {
/// # Panics /// # Panics
/// ///
/// Panics if the token is not a keyword. /// Panics if the token is not a keyword.
#[must_use]
pub fn keyword_syntax(&self) -> &'static str { pub fn keyword_syntax(&self) -> &'static str {
use Token::*; use Token::*;
@ -554,6 +561,7 @@ impl Token {
} }
/// Get the syntax of the token. /// Get the syntax of the token.
#[must_use]
pub fn syntax(&self) -> Cow<'static, str> { pub fn syntax(&self) -> Cow<'static, str> {
use Token::*; use Token::*;
@ -580,6 +588,7 @@ impl Token {
/// Is this token an op-assignment operator? /// Is this token an op-assignment operator?
#[inline] #[inline]
#[must_use]
pub fn is_op_assignment(&self) -> bool { pub fn is_op_assignment(&self) -> bool {
match self { match self {
Self::PlusAssign Self::PlusAssign
@ -598,6 +607,7 @@ impl Token {
} }
/// Get the corresponding operator of the token if it is an op-assignment operator. /// Get the corresponding operator of the token if it is an op-assignment operator.
#[must_use]
pub fn map_op_assignment(&self) -> Option<Self> { pub fn map_op_assignment(&self) -> Option<Self> {
Some(match self { Some(match self {
Self::PlusAssign => Self::Plus, Self::PlusAssign => Self::Plus,
@ -617,6 +627,7 @@ impl Token {
/// Has this token a corresponding op-assignment operator? /// Has this token a corresponding op-assignment operator?
#[inline] #[inline]
#[must_use]
pub fn has_op_assignment(&self) -> bool { pub fn has_op_assignment(&self) -> bool {
match self { match self {
Self::Plus Self::Plus
@ -635,6 +646,7 @@ impl Token {
} }
/// Get the corresponding op-assignment operator of the token. /// Get the corresponding op-assignment operator of the token.
#[must_use]
pub fn make_op_assignment(&self) -> Option<Self> { pub fn make_op_assignment(&self) -> Option<Self> {
Some(match self { Some(match self {
Self::Plus => Self::PlusAssign, Self::Plus => Self::PlusAssign,
@ -653,6 +665,7 @@ impl Token {
} }
/// Reverse lookup a token from a piece of syntax. /// Reverse lookup a token from a piece of syntax.
#[must_use]
pub fn lookup_from_syntax(syntax: &str) -> Option<Self> { pub fn lookup_from_syntax(syntax: &str) -> Option<Self> {
use Token::*; use Token::*;
@ -763,6 +776,7 @@ impl Token {
// Is this token [`EOF`][Token::EOF]? // Is this token [`EOF`][Token::EOF]?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_eof(&self) -> bool { pub fn is_eof(&self) -> bool {
use Token::*; use Token::*;
@ -774,6 +788,7 @@ impl Token {
// If another operator is after these, it's probably an unary operator // If another operator is after these, it's probably an unary operator
// (not sure about `fn` name). // (not sure about `fn` name).
#[must_use]
pub fn is_next_unary(&self) -> bool { pub fn is_next_unary(&self) -> bool {
use Token::*; use Token::*;
@ -834,6 +849,7 @@ impl Token {
} }
/// Get the precedence number of the token. /// Get the precedence number of the token.
#[must_use]
pub fn precedence(&self) -> Option<Precedence> { pub fn precedence(&self) -> Option<Precedence> {
use Token::*; use Token::*;
@ -868,6 +884,7 @@ impl Token {
} }
/// Does an expression bind to the right (instead of left)? /// Does an expression bind to the right (instead of left)?
#[must_use]
pub fn is_bind_right(&self) -> bool { pub fn is_bind_right(&self) -> bool {
use Token::*; use Token::*;
@ -888,6 +905,7 @@ impl Token {
} }
/// Is this token a standard symbol used in the language? /// Is this token a standard symbol used in the language?
#[must_use]
pub fn is_symbol(&self) -> bool { pub fn is_symbol(&self) -> bool {
use Token::*; use Token::*;
@ -905,6 +923,7 @@ impl Token {
} }
/// Is this token an active standard keyword? /// Is this token an active standard keyword?
#[must_use]
pub fn is_keyword(&self) -> bool { pub fn is_keyword(&self) -> bool {
use Token::*; use Token::*;
@ -924,6 +943,7 @@ impl Token {
/// Is this token a reserved symbol? /// Is this token a reserved symbol?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_reserved(&self) -> bool { pub fn is_reserved(&self) -> bool {
match self { match self {
Self::Reserved(_) => true, Self::Reserved(_) => true,
@ -933,6 +953,7 @@ impl Token {
/// Convert a token into a function name, if possible. /// Convert a token into a function name, if possible.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[must_use]
pub(crate) fn into_function_name_for_override(self) -> Result<String, Self> { pub(crate) fn into_function_name_for_override(self) -> Result<String, Self> {
match self { match self {
Self::Custom(s) | Self::Identifier(s) if is_valid_identifier(s.chars()) => Ok(s), Self::Custom(s) | Self::Identifier(s) if is_valid_identifier(s.chars()) => Ok(s),
@ -942,6 +963,7 @@ impl Token {
/// Is this token a custom keyword? /// Is this token a custom keyword?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_custom(&self) -> bool { pub fn is_custom(&self) -> bool {
match self { match self {
Self::Custom(_) => true, Self::Custom(_) => true,
@ -991,6 +1013,7 @@ pub trait InputStream {
/// Get the next character from the `InputStream`. /// Get the next character from the `InputStream`.
fn get_next(&mut self) -> Option<char>; fn get_next(&mut self) -> Option<char>;
/// Peek the next character in the `InputStream`. /// Peek the next character in the `InputStream`.
#[must_use]
fn peek_next(&mut self) -> Option<char>; fn peek_next(&mut self) -> Option<char>;
} }
@ -1028,6 +1051,7 @@ pub trait InputStream {
/// # Volatile API /// # Volatile API
/// ///
/// This function is volatile and may change. /// This function is volatile and may change.
#[must_use]
pub fn parse_string_literal( pub fn parse_string_literal(
stream: &mut impl InputStream, stream: &mut impl InputStream,
state: &mut TokenizeState, state: &mut TokenizeState,
@ -1287,6 +1311,7 @@ fn scan_block_comment(
/// ///
/// This function is volatile and may change. /// This function is volatile and may change.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn get_next_token( pub fn get_next_token(
stream: &mut impl InputStream, stream: &mut impl InputStream,
state: &mut TokenizeState, state: &mut TokenizeState,
@ -1326,12 +1351,14 @@ fn is_numeric_digit(c: char) -> bool {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_doc_comment(comment: &str) -> bool { pub fn is_doc_comment(comment: &str) -> bool {
(comment.starts_with("///") && !comment.starts_with("////")) (comment.starts_with("///") && !comment.starts_with("////"))
|| (comment.starts_with("/**") && !comment.starts_with("/***")) || (comment.starts_with("/**") && !comment.starts_with("/***"))
} }
/// Get the next token. /// Get the next token.
#[must_use]
fn get_next_token_inner( fn get_next_token_inner(
stream: &mut impl InputStream, stream: &mut impl InputStream,
state: &mut TokenizeState, state: &mut TokenizeState,
@ -1962,6 +1989,7 @@ fn get_identifier(
/// Is this keyword allowed as a function? /// Is this keyword allowed as a function?
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_keyword_function(name: &str) -> bool { pub fn is_keyword_function(name: &str) -> bool {
match name { match name {
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
@ -1975,6 +2003,7 @@ pub fn is_keyword_function(name: &str) -> bool {
} }
/// Is a text string a valid identifier? /// Is a text string a valid identifier?
#[must_use]
pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool { pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
let mut first_alphabetic = false; let mut first_alphabetic = false;
@ -1994,6 +2023,7 @@ pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
/// Is a character valid to start an identifier? /// Is a character valid to start an identifier?
#[cfg(feature = "unicode-xid-ident")] #[cfg(feature = "unicode-xid-ident")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_id_first_alphabetic(x: char) -> bool { pub fn is_id_first_alphabetic(x: char) -> bool {
unicode_xid::UnicodeXID::is_xid_start(x) unicode_xid::UnicodeXID::is_xid_start(x)
} }
@ -2001,6 +2031,7 @@ pub fn is_id_first_alphabetic(x: char) -> bool {
/// Is a character valid for an identifier? /// Is a character valid for an identifier?
#[cfg(feature = "unicode-xid-ident")] #[cfg(feature = "unicode-xid-ident")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_id_continue(x: char) -> bool { pub fn is_id_continue(x: char) -> bool {
unicode_xid::UnicodeXID::is_xid_continue(x) unicode_xid::UnicodeXID::is_xid_continue(x)
} }
@ -2008,6 +2039,7 @@ pub fn is_id_continue(x: char) -> bool {
/// Is a character valid to start an identifier? /// Is a character valid to start an identifier?
#[cfg(not(feature = "unicode-xid-ident"))] #[cfg(not(feature = "unicode-xid-ident"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_id_first_alphabetic(x: char) -> bool { pub fn is_id_first_alphabetic(x: char) -> bool {
x.is_ascii_alphabetic() x.is_ascii_alphabetic()
} }
@ -2015,6 +2047,7 @@ pub fn is_id_first_alphabetic(x: char) -> bool {
/// Is a character valid for an identifier? /// Is a character valid for an identifier?
#[cfg(not(feature = "unicode-xid-ident"))] #[cfg(not(feature = "unicode-xid-ident"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn is_id_continue(x: char) -> bool { pub fn is_id_continue(x: char) -> bool {
x.is_ascii_alphanumeric() || x == '_' x.is_ascii_alphanumeric() || x == '_'
} }
@ -2201,6 +2234,7 @@ impl Engine {
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn lex<'a>( pub fn lex<'a>(
&'a self, &'a self,
input: impl IntoIterator<Item = &'a &'a str>, input: impl IntoIterator<Item = &'a &'a str>,
@ -2211,6 +2245,7 @@ impl Engine {
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn lex_with_map<'a>( pub fn lex_with_map<'a>(
&'a self, &'a self,
input: impl IntoIterator<Item = &'a &'a str>, input: impl IntoIterator<Item = &'a &'a str>,
@ -2220,6 +2255,7 @@ impl Engine {
} }
/// Tokenize an input text stream with an optional mapping function. /// Tokenize an input text stream with an optional mapping function.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn lex_raw<'a>( pub(crate) fn lex_raw<'a>(
&'a self, &'a self,
input: impl IntoIterator<Item = &'a &'a str>, input: impl IntoIterator<Item = &'a &'a str>,

View File

@ -9,6 +9,7 @@ use std::{
/// Cast a type into another type. /// Cast a type into another type.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Result<B, A> { pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Result<B, A> {
if TypeId::of::<B>() == a.type_id() { if TypeId::of::<B>() == a.type_id() {
// SAFETY: Just checked we have the right type. We explicitly forget the // SAFETY: Just checked we have the right type. We explicitly forget the
@ -26,6 +27,7 @@ pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Result<B, A> {
/// Cast a Boxed type into another type. /// Cast a Boxed type into another type.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Result<Box<T>, Box<X>> { pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Result<Box<T>, Box<X>> {
// Only allow casting to the exact same type // Only allow casting to the exact same type
if TypeId::of::<X>() == TypeId::of::<T>() { if TypeId::of::<X>() == TypeId::of::<T>() {
@ -50,6 +52,7 @@ pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Result<Box<T>, Box<X>> {
/// Force-casting a local variable's lifetime to the current [`Scope`][crate::Scope]'s larger lifetime saves /// Force-casting a local variable's lifetime to the current [`Scope`][crate::Scope]'s larger lifetime saves
/// on allocations and string cloning, thus avoids us having to maintain a chain of [`Scope`][crate::Scope]'s. /// on allocations and string cloning, thus avoids us having to maintain a chain of [`Scope`][crate::Scope]'s.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str { pub fn unsafe_cast_var_name_to_lifetime<'s>(name: &str) -> &'s str {
// WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it // WARNING - force-cast the variable name into the scope's lifetime to avoid cloning it
// this is safe because all local variables are cleared at the end of the block // this is safe because all local variables are cleared at the end of the block

View File

@ -24,7 +24,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
true, true,
|context, inputs| { |context, inputs| {
let var_name = inputs[0].get_variable_name().unwrap().to_string(); let var_name = inputs[0].get_variable_name().unwrap().to_string();
let max = inputs[1].get_literal_value().unwrap().as_int().unwrap(); let max = inputs[1].get_literal_value::<INT>().unwrap();
let stmt = inputs.get(2).unwrap(); let stmt = inputs.get(2).unwrap();
let condition = inputs.get(3).unwrap(); let condition = inputs.get(3).unwrap();