Merge pull request #430 from schungx/master

Fix bug in indexing.
This commit is contained in:
Stephen Chung 2021-07-24 14:33:31 +08:00 committed by GitHub
commit fe5af8a272
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 986 additions and 789 deletions

View File

@ -1,6 +1,15 @@
Rhai Release Notes Rhai Release Notes
================== ==================
Version 1.0.1
=============
Bug fixes
---------
* Fixed bug in using indexing/dotting inside index bracket.
Version 1.0.0 Version 1.0.0
============= =============

View File

@ -19,11 +19,11 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"]
smallvec = { version = "1.6", default-features = false, features = ["union"] } smallvec = { version = "1.6", default-features = false, features = ["union"] }
ahash = { version = "0.7", default-features = false } ahash = { version = "0.7", default-features = false }
num-traits = { version = "0.2", default-features = false } num-traits = { version = "0.2", default-features = false }
smartstring = { version = "0.2.6", default-features = false } smartstring = { version = "0.2.7", default-features = false }
rhai_codegen = { version = ">=0.4.0", path = "codegen", default-features = false } rhai_codegen = { version = "1.0", path = "codegen", default-features = false }
[features] [features]
default = ["smartstring/std", "ahash/std", "num-traits/std"] # remove 'smartstring/std' when smartstring is updated to support no-std default = ["ahash/std", "num-traits/std"]
unchecked = [] # unchecked arithmetic unchecked = [] # unchecked arithmetic
sync = [] # restrict to only types that implement Send + Sync sync = [] # restrict to only types that implement Send + Sync
no_position = [] # do not track position in the parser no_position = [] # do not track position in the parser
@ -32,7 +32,7 @@ no_float = [] # no floating-point
f32_float = [] # set FLOAT=f32 f32_float = [] # set FLOAT=f32
only_i32 = [] # set INT=i32 (useful for 32-bit systems) only_i32 = [] # set INT=i32 (useful for 32-bit systems)
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
decimal = ["rust_decimal/std"] # add the Decimal number type decimal = ["rust_decimal"] # add the Decimal number type
no_index = [] # no arrays and indexing no_index = [] # no arrays and indexing
no_object = [] # no custom objects no_object = [] # no custom objects
no_function = ["no_closure"] # no script-defined functions (meaning no closures) no_function = ["no_closure"] # no script-defined functions (meaning no closures)
@ -40,7 +40,7 @@ no_closure = [] # no automatic sharing and capture of anonymous
no_module = [] # no modules no_module = [] # no modules
internals = [] # expose internal data structures internals = [] # expose internal data structures
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers. unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
metadata = ["serde_json", "rhai_codegen/metadata"] # enable exporting functions metadata metadata = ["serde", "serde_json", "rhai_codegen/metadata"] # enable exporting functions metadata
no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"] no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]

View File

@ -132,7 +132,7 @@ pub fn inner_item_attributes<T: ExportedParams>(
} }
} }
pub fn deny_cfg_attr(attrs: &Vec<syn::Attribute>) -> syn::Result<()> { pub fn deny_cfg_attr(attrs: &[syn::Attribute]) -> syn::Result<()> {
if let Some(cfg_attr) = attrs if let Some(cfg_attr) = attrs
.iter() .iter()
.find(|a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false)) .find(|a| a.path.get_ident().map(|i| *i == "cfg").unwrap_or(false))

View File

@ -152,13 +152,13 @@ impl ExportedParams for ExportedFnParams {
("get", None) | ("set", None) | ("name", None) => { ("get", None) | ("set", None) | ("name", None) => {
return Err(syn::Error::new(key.span(), "requires value")) return Err(syn::Error::new(key.span(), "requires value"))
} }
("name", Some(s)) if &s.value() == FN_IDX_GET => { ("name", Some(s)) if s.value() == FN_IDX_GET => {
return Err(syn::Error::new( return Err(syn::Error::new(
item_span, item_span,
"use attribute 'index_get' instead", "use attribute 'index_get' instead",
)) ))
} }
("name", Some(s)) if &s.value() == FN_IDX_SET => { ("name", Some(s)) if s.value() == FN_IDX_SET => {
return Err(syn::Error::new( return Err(syn::Error::new(
item_span, item_span,
"use attribute 'index_set' instead", "use attribute 'index_set' instead",
@ -268,7 +268,6 @@ impl ExportedParams for ExportedFnParams {
special, special,
namespace, namespace,
span: Some(span), span: Some(span),
..Default::default()
}) })
} }
} }
@ -317,7 +316,7 @@ impl Parse for ExportedFn {
let skip_slots = if pass_context { 1 } else { 0 }; let skip_slots = if pass_context { 1 } else { 0 };
// Determine whether function generates a special calling convention for a mutable receiver. // Determine whether function generates a special calling convention for a mutable receiver.
let mut_receiver = match fn_all.sig.inputs.iter().skip(skip_slots).next() { let mut_receiver = match fn_all.sig.inputs.iter().nth(skip_slots) {
Some(syn::FnArg::Receiver(syn::Receiver { Some(syn::FnArg::Receiver(syn::Receiver {
reference: Some(_), .. reference: Some(_), ..
})) => true, })) => true,
@ -474,7 +473,7 @@ impl ExportedFn {
literals literals
} }
pub fn exported_name<'n>(&'n self) -> Cow<'n, str> { pub fn exported_name(&self) -> Cow<str> {
self.params self.params
.name .name
.last() .last()
@ -629,7 +628,7 @@ impl ExportedFn {
let return_span = self let return_span = self
.return_type() .return_type()
.map(|r| r.span()) .map(|r| r.span())
.unwrap_or_else(|| proc_macro2::Span::call_site()); .unwrap_or_else(proc_macro2::Span::call_site);
if self.params.return_raw.is_some() { if self.params.return_raw.is_some() {
quote_spanned! { return_span => quote_spanned! { return_span =>
pub #dynamic_signature { pub #dynamic_signature {
@ -691,7 +690,7 @@ impl ExportedFn {
}) })
.unwrap(), .unwrap(),
); );
if !self.params().pure.is_some() { if self.params().pure.is_none() {
let arg_lit_str = let arg_lit_str =
syn::LitStr::new(&pat.to_token_stream().to_string(), pat.span()); syn::LitStr::new(&pat.to_token_stream().to_string(), pat.span());
unpack_statements.push( unpack_statements.push(
@ -812,8 +811,8 @@ impl ExportedFn {
let return_span = self let return_span = self
.return_type() .return_type()
.map(|r| r.span()) .map(|r| r.span())
.unwrap_or_else(|| proc_macro2::Span::call_site()); .unwrap_or_else(proc_macro2::Span::call_site);
let return_expr = if !self.params.return_raw.is_some() { let return_expr = if self.params.return_raw.is_none() {
quote_spanned! { return_span => quote_spanned! { return_span =>
Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*))) Ok(Dynamic::from(#sig_name(#(#unpack_exprs),*)))
} }

View File

@ -91,7 +91,6 @@ impl ExportedParams for ExportedModParams {
name, name,
skip, skip,
scope: scope.unwrap_or_default(), scope: scope.unwrap_or_default(),
..Default::default()
}) })
} }
} }

View File

@ -214,7 +214,7 @@ pub fn generate_body(
} }
} }
pub fn check_rename_collisions(fns: &Vec<ExportedFn>) -> Result<(), syn::Error> { pub fn check_rename_collisions(fns: &[ExportedFn]) -> Result<(), syn::Error> {
fn make_key(name: impl ToString, item_fn: &ExportedFn) -> String { fn make_key(name: impl ToString, item_fn: &ExportedFn) -> String {
item_fn item_fn
.arg_list() .arg_list()

View File

@ -0,0 +1,38 @@
cargo-features = ["named-profiles"]
[workspace]
[package]
name = "no_std_repl"
version = "0.1.0"
edition = "2018"
authors = ["Stephen Chung"]
description = "no-std REPL application"
homepage = "https://github.com/rhaiscript/rhai/tree/no_std/no_std_repl"
repository = "https://github.com/rhaiscript/rhai"
[dependencies]
rhai = { path = "../../", default_features = false, features = [ "no_std", "decimal", "metadata" ] }
wee_alloc = { version = "0.4.5", default_features = false }
[profile.dev]
panic = "abort"
[profile.release]
opt-level = "z" # optimize for size
debug = false
rpath = false
debug-assertions = false
codegen-units = 1
panic = "abort"
[profile.unix]
inherits = "release"
lto = true
[profile.windows]
inherits = "release"
[profile.macos]
inherits = "release"
lto = "fat"

View File

@ -0,0 +1,26 @@
`no-std` REPL Sample
====================
This sample application is the same version as the `rhai-repl` tool, compiled for `no-std`.
[`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator.
To Build
--------
The nightly compiler is required:
```bash
cargo +nightly build --release
```
A specific profile can also be used:
```bash
cargo +nightly build --profile unix -Z unstable-options
```
Three profiles are defined: `unix`, `windows` and `macos`.
The release build is optimized for size. It can be changed to optimize on speed instead.

View File

@ -0,0 +1,214 @@
use rhai::{Dynamic, Engine, EvalAltResult, Scope, AST};
use std::{
env,
io::{stdin, stdout, Write},
};
/// Pretty-print error.
fn print_error(input: &str, mut err: EvalAltResult) {
let lines: Vec<_> = input.trim().split('\n').collect();
let pos = err.take_position();
let line_no = if lines.len() > 1 {
if pos.is_none() {
"".to_string()
} else {
format!("{}: ", pos.line().unwrap())
}
} else {
"".to_string()
};
// Print error position
if pos.is_none() {
// No position
println!("{}", err);
} else {
// Specific position - print line text
println!("{}{}", line_no, lines[pos.line().unwrap() - 1]);
// Display position marker
println!(
"{0:>1$} {2}",
"^",
line_no.len() + pos.position().unwrap(),
err
);
}
}
/// Print help text.
fn print_help() {
println!("help => print this help");
println!("quit, exit => quit");
println!("scope => print all variables in the scope");
println!("functions => print all functions defined");
println!("json => output all functions in JSON format");
println!("ast => print the last AST (optimized)");
println!("astu => print the last raw, un-optimized AST");
println!(r"end a line with '\' to continue to the next line.");
println!();
}
fn main() {
let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION"));
println!("{}", title);
println!("{0:=<1$}", "", title.len());
print_help();
// Initialize scripting engine
let mut engine = Engine::new();
// Setup Engine
#[cfg(not(feature = "no_optimize"))]
engine.set_optimization_level(rhai::OptimizationLevel::None);
// Make Engine immutable
let engine = engine;
// Create scope
let mut scope = Scope::new();
// REPL loop
let mut input = String::new();
let mut main_ast: AST = Default::default();
let mut ast_u: AST = Default::default();
let mut ast: AST = Default::default();
'main_loop: loop {
print!("rhai-repl> ");
stdout().flush().expect("couldn't flush stdout");
input.clear();
loop {
match stdin().read_line(&mut input) {
Ok(0) => break 'main_loop,
Ok(_) => (),
Err(err) => panic!("input error: {}", err),
}
let line = input.as_str().trim_end();
// Allow line continuation
if line.ends_with('\\') {
let len = line.len();
input.truncate(len - 1);
input.push('\n');
} else {
break;
}
print!("> ");
stdout().flush().expect("couldn't flush stdout");
}
let script = input.trim();
if script.is_empty() {
continue;
}
// Implement standard commands
match script {
"help" => {
print_help();
continue;
}
"exit" | "quit" => break, // quit
"scope" => {
scope
.iter_raw()
.enumerate()
.for_each(|(i, (name, constant, value))| {
#[cfg(not(feature = "no_closure"))]
let value_is_shared = if value.is_shared() { " (shared" } else { "" };
#[cfg(feature = "no_closure")]
let value_is_shared = "";
println!(
"[{}] {}{}{} = {:?}",
i + 1,
if constant { "const " } else { "" },
name,
value_is_shared,
*value.read_lock::<Dynamic>().unwrap(),
)
});
println!();
continue;
}
"astu" => {
// print the last un-optimized AST
println!("{:#?}\n", ast_u);
continue;
}
"ast" => {
// print the last AST
println!("{:#?}\n", ast);
continue;
}
"functions" => {
// print a list of all registered functions
engine
.gen_fn_signatures(false)
.into_iter()
.for_each(|f| println!("{}", f));
#[cfg(not(feature = "no_function"))]
main_ast.iter_functions().for_each(|f| println!("{}", f));
println!();
continue;
}
"json" => {
println!(
"{}",
engine
.gen_fn_metadata_with_ast_to_json(&main_ast, true)
.unwrap()
);
continue;
}
_ => (),
}
match engine
.compile_with_scope(&scope, &script)
.map_err(Into::into)
.and_then(|r| {
ast_u = r.clone();
#[cfg(not(feature = "no_optimize"))]
{
ast = engine.optimize_ast(&scope, r, rhai::OptimizationLevel::Simple);
}
#[cfg(feature = "no_optimize")]
{
ast = r;
}
// Merge the AST into the main
main_ast += ast.clone();
// Evaluate
engine.eval_ast_with_scope::<Dynamic>(&mut scope, &main_ast)
}) {
Ok(result) if !result.is::<()>() => {
println!("=> {:?}", result);
println!();
}
Ok(_) => (),
Err(err) => {
println!();
print_error(&input, *err);
println!();
}
}
// Throw away all the statements, leaving only the functions
main_ast.clear_statements();
}
}

View File

@ -6,15 +6,21 @@ This sample application is a bare-bones `no-std` build for testing.
[`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator. [`wee_alloc`](https://crates.io/crates/wee_alloc) is used as the allocator.
To Compile To Build
---------- --------
The nightly compiler is required: The nightly compiler is required:
```bash
cargo +nightly build --release
```
A specific profile can also be used:
```bash ```bash
cargo +nightly build --profile unix -Z unstable-options cargo +nightly build --profile unix -Z unstable-options
``` ```
Available profiles are: `unix`, `windows` and `macos`. Three profiles are defined: `unix`, `windows` and `macos`.
The release build is optimized for size. It can be changed to optimize on speed instead. The release build is optimized for size. It can be changed to optimize on speed instead.

View File

@ -78,7 +78,6 @@ pub struct ScriptFnDef {
} }
impl fmt::Display for ScriptFnDef { impl fmt::Display for ScriptFnDef {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
@ -129,7 +128,6 @@ pub struct ScriptFnMetadata<'a> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
impl fmt::Display for ScriptFnMetadata<'_> { impl fmt::Display for ScriptFnMetadata<'_> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
@ -149,16 +147,16 @@ impl fmt::Display for ScriptFnMetadata<'_> {
} }
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
impl<'a> Into<ScriptFnMetadata<'a>> for &'a ScriptFnDef { impl<'a> From<&'a ScriptFnDef> for ScriptFnMetadata<'a> {
#[inline(always)] #[inline]
fn into(self) -> ScriptFnMetadata<'a> { fn from(value: &'a ScriptFnDef) -> Self {
ScriptFnMetadata { Self {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
comments: self.comments.iter().map(|s| s.as_str()).collect(), comments: value.comments.iter().map(|s| s.as_str()).collect(),
access: self.access, access: value.access,
name: &self.name, name: &value.name,
params: self.params.iter().map(|s| s.as_str()).collect(), params: value.params.iter().map(|s| s.as_str()).collect(),
} }
} }
} }
@ -215,7 +213,7 @@ impl Default for AST {
impl AST { impl AST {
/// Create a new [`AST`]. /// Create a new [`AST`].
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn new( pub fn new(
statements: impl IntoIterator<Item = Stmt>, statements: impl IntoIterator<Item = Stmt>,
@ -223,7 +221,7 @@ impl AST {
) -> Self { ) -> Self {
Self { Self {
source: None, source: None,
body: StmtBlock::new(statements.into_iter().collect(), Position::NONE), body: StmtBlock::new(statements, Position::NONE),
functions: functions.into(), functions: functions.into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: None, resolver: None,
@ -239,7 +237,7 @@ impl AST {
) -> Self { ) -> Self {
Self { Self {
source: Some(source.into()), source: Some(source.into()),
body: StmtBlock::new(statements.into_iter().collect(), Position::NONE), body: StmtBlock::new(statements, Position::NONE),
functions: functions.into(), functions: functions.into(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
resolver: None, resolver: None,
@ -258,7 +256,7 @@ impl AST {
self.source.as_ref() self.source.as_ref()
} }
/// Set the source. /// Set the source.
#[inline(always)] #[inline]
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self { pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
let source = source.into(); let source = source.into();
Shared::get_mut(&mut self.functions) Shared::get_mut(&mut self.functions)
@ -386,7 +384,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]
#[must_use] #[must_use]
pub fn clone_functions_only_filtered( pub fn clone_functions_only_filtered(
&self, &self,
@ -657,7 +655,7 @@ impl AST {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline]
pub fn combine_filtered( pub fn combine_filtered(
&mut self, &mut self,
other: Self, other: Self,
@ -696,7 +694,7 @@ impl AST {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline]
pub fn retain_functions( pub fn retain_functions(
&mut self, &mut self,
filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool, filter: impl Fn(FnNamespace, FnAccess, &str, usize) -> bool,
@ -712,7 +710,6 @@ 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()
@ -723,7 +720,6 @@ 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()
@ -748,7 +744,7 @@ impl AST {
/// Return `false` from the callback to terminate the walk. /// Return `false` from the callback to terminate the walk.
#[cfg(not(feature = "internals"))] #[cfg(not(feature = "internals"))]
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline]
pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool { pub(crate) fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
let path = &mut Default::default(); let path = &mut Default::default();
@ -770,7 +766,7 @@ impl AST {
/// Return `false` from the callback to terminate the walk. /// Return `false` from the callback to terminate the walk.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[inline(always)] #[inline]
pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool { pub fn walk(&self, on_node: &mut impl FnMut(&[ASTNode]) -> bool) -> bool {
let path = &mut Default::default(); let path = &mut Default::default();
@ -835,7 +831,6 @@ pub struct Ident {
} }
impl fmt::Debug for Ident { impl fmt::Debug for Ident {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.name)?; write!(f, "{:?}", self.name)?;
self.pos.debug_print(f) self.pos.debug_print(f)
@ -892,7 +887,8 @@ pub struct StmtBlock(StaticVec<Stmt>, Position);
impl StmtBlock { impl StmtBlock {
/// Create a new [`StmtBlock`]. /// Create a new [`StmtBlock`].
#[must_use] #[must_use]
pub fn new(mut statements: StaticVec<Stmt>, pos: Position) -> Self { pub fn new(statements: impl IntoIterator<Item = Stmt>, pos: Position) -> Self {
let mut statements: StaticVec<_> = statements.into_iter().collect();
statements.shrink_to_fit(); statements.shrink_to_fit();
Self(statements, pos) Self(statements, pos)
} }
@ -939,7 +935,6 @@ impl DerefMut for StmtBlock {
} }
impl fmt::Debug for StmtBlock { impl fmt::Debug for StmtBlock {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Block")?; f.write_str("Block")?;
fmt::Debug::fmt(&self.0, f)?; fmt::Debug::fmt(&self.0, f)?;
@ -1027,7 +1022,7 @@ impl Default for Stmt {
} }
impl From<Stmt> for StmtBlock { impl From<Stmt> for StmtBlock {
#[inline(always)] #[inline]
fn from(stmt: Stmt) -> Self { fn from(stmt: Stmt) -> Self {
match stmt { match stmt {
Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos), Stmt::Block(mut block, pos) => Self(block.iter_mut().map(mem::take).collect(), pos),
@ -1045,10 +1040,7 @@ impl Stmt {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_noop(&self) -> bool { pub const fn is_noop(&self) -> bool {
match self { matches!(self, Self::Noop(_))
Self::Noop(_) => true,
_ => false,
}
} }
/// Get the [position][Position] of this statement. /// Get the [position][Position] of this statement.
#[must_use] #[must_use]
@ -1226,7 +1218,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]
#[must_use] #[must_use]
pub fn is_internally_pure(&self) -> bool { pub fn is_internally_pure(&self) -> bool {
match self { match self {
@ -1245,7 +1237,7 @@ impl Stmt {
/// Currently this is only true for `return`, `throw`, `break` and `continue`. /// Currently this is only true for `return`, `throw`, `break` and `continue`.
/// ///
/// All statements following this statement will essentially be dead code. /// All statements following this statement will essentially be dead code.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub const fn is_control_flow_break(&self) -> bool { pub const fn is_control_flow_break(&self) -> bool {
match self { match self {
@ -1634,7 +1626,6 @@ impl<F: Float + fmt::Debug> fmt::Debug for FloatWrapper<F> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
impl<F: Float + fmt::Display + fmt::LowerExp + From<f32>> fmt::Display for FloatWrapper<F> { impl<F: Float + fmt::Display + fmt::LowerExp + From<f32>> fmt::Display for FloatWrapper<F> {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let abs = self.0.abs(); let abs = self.0.abs();
if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into() if abs > Self::MAX_NATURAL_FLOAT_FOR_DISPLAY.into()
@ -1690,7 +1681,7 @@ impl FloatWrapper<FLOAT> {
/// Create a new [`FloatWrapper`]. /// Create a new [`FloatWrapper`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub(crate) const fn const_new(value: FLOAT) -> Self { pub const fn new_const(value: FLOAT) -> Self {
Self(value) Self(value)
} }
} }
@ -1759,10 +1750,10 @@ pub enum Expr {
Stmt(Box<StmtBlock>), Stmt(Box<StmtBlock>),
/// func `(` expr `,` ... `)` /// func `(` expr `,` ... `)`
FnCall(Box<FnCallExpr>, Position), FnCall(Box<FnCallExpr>, Position),
/// lhs `.` rhs /// lhs `.` rhs - bool variable is a dummy
Dot(Box<BinaryExpr>, Position), Dot(Box<BinaryExpr>, bool, Position),
/// expr `[` expr `]` /// expr `[` expr `]` - boolean indicates whether the dotting/indexing chain stops
Index(Box<BinaryExpr>, Position), Index(Box<BinaryExpr>, bool, Position),
/// lhs `&&` rhs /// lhs `&&` rhs
And(Box<BinaryExpr>, Position), And(Box<BinaryExpr>, Position),
/// lhs `||` rhs /// lhs `||` rhs
@ -1839,10 +1830,18 @@ impl fmt::Debug for Expr {
} }
ff.finish() ff.finish()
} }
Self::Dot(x, pos) | Self::Index(x, pos) | Self::And(x, pos) | Self::Or(x, pos) => { Self::Index(x, term, pos) => {
display_pos = *pos;
f.debug_struct("Index")
.field("lhs", &x.lhs)
.field("rhs", &x.rhs)
.field("terminate", term)
.finish()
}
Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => {
let op_name = match self { let op_name = match self {
Self::Dot(_, _) => "Dot", Self::Dot(_, _, _) => "Dot",
Self::Index(_, _) => "Index",
Self::And(_, _) => "And", Self::And(_, _) => "And",
Self::Or(_, _) => "Or", Self::Or(_, _) => "Or",
_ => unreachable!(), _ => unreachable!(),
@ -1915,9 +1914,18 @@ impl Expr {
Union::Char(c, _, _) => Self::CharConstant(c, pos), Union::Char(c, _, _) => Self::CharConstant(c, pos),
Union::Int(i, _, _) => Self::IntegerConstant(i, pos), Union::Int(i, _, _) => Self::IntegerConstant(i, pos),
#[cfg(feature = "decimal")]
Union::Decimal(value, _, _) => Self::DynamicConstant(Box::new((*value).into()), pos),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Union::Float(f, _, _) => Self::FloatConstant(f, pos), Union::Float(f, _, _) => Self::FloatConstant(f, pos),
#[cfg(not(feature = "no_index"))]
Union::Array(a, _, _) => Self::DynamicConstant(Box::new((*a).into()), pos),
#[cfg(not(feature = "no_object"))]
Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos),
_ => Self::DynamicConstant(value.into(), pos), _ => Self::DynamicConstant(value.into(), pos),
} }
} }
@ -1964,7 +1972,7 @@ impl Expr {
Self::Property(x) => (x.2).1, Self::Property(x) => (x.2).1,
Self::Stmt(x) => x.1, Self::Stmt(x) => x.1,
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => { Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _, _) | Self::Index(x, _, _) => {
x.lhs.position() x.lhs.position()
} }
} }
@ -1986,8 +1994,8 @@ impl Expr {
| Self::Map(_, pos) | Self::Map(_, pos)
| Self::And(_, pos) | Self::And(_, pos)
| Self::Or(_, pos) | Self::Or(_, pos)
| Self::Dot(_, pos) | Self::Dot(_, _, pos)
| Self::Index(_, pos) | Self::Index(_, _, pos)
| Self::Variable(_, pos, _) | Self::Variable(_, pos, _)
| Self::Stack(_, pos) | Self::Stack(_, pos)
| Self::FnCall(_, pos) | Self::FnCall(_, pos)
@ -2024,10 +2032,7 @@ impl Expr {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_unit(&self) -> bool { pub const fn is_unit(&self) -> bool {
match self { matches!(self, Self::Unit(_))
Self::Unit(_) => true,
_ => false,
}
} }
/// Is the expression a constant? /// Is the expression a constant?
#[inline] #[inline]
@ -2078,8 +2083,8 @@ impl Expr {
| Self::InterpolatedString(_, _) | Self::InterpolatedString(_, _)
| Self::FnCall(_, _) | Self::FnCall(_, _)
| Self::Stmt(_) | Self::Stmt(_)
| Self::Dot(_, _) | Self::Dot(_, _, _)
| Self::Index(_, _) | Self::Index(_, _, _)
| Self::Array(_, _) | Self::Array(_, _)
| Self::Map(_, _) => match token { | Self::Map(_, _) => match token {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -2144,7 +2149,7 @@ impl Expr {
} }
} }
} }
Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => { Self::Index(x, _, _) | Self::Dot(x, _, _) | Expr::And(x, _) | Expr::Or(x, _) => {
if !x.lhs.walk(path, on_node) { if !x.lhs.walk(path, on_node) {
return false; return false;
} }

View File

@ -48,6 +48,8 @@ fn print_help() {
println!("scope => print all variables in the scope"); println!("scope => print all variables in the scope");
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
println!("functions => print all functions defined"); println!("functions => print all functions defined");
#[cfg(feature = "metadata")]
println!("json => output all functions in JSON format");
println!("ast => print the last AST (optimized)"); println!("ast => print the last AST (optimized)");
println!("astu => print the last raw, un-optimized AST"); println!("astu => print the last raw, un-optimized AST");
println!(r"end a line with '\' to continue to the next line."); println!(r"end a line with '\' to continue to the next line.");
@ -76,7 +78,12 @@ fn main() {
eprintln!("Error script file path: {}\n{}", filename, err); eprintln!("Error script file path: {}\n{}", filename, err);
exit(1); exit(1);
} }
Ok(f) => f, Ok(f) => {
match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap()) {
Ok(f) => f.into(),
_ => f,
}
}
}; };
contents.clear(); contents.clear();
@ -207,7 +214,7 @@ fn main() {
.enumerate() .enumerate()
.for_each(|(i, (name, constant, value))| { .for_each(|(i, (name, constant, value))| {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
let value_is_shared = if value.is_shared() { " (shared" } else { "" }; let value_is_shared = if value.is_shared() { " (shared)" } else { "" };
#[cfg(feature = "no_closure")] #[cfg(feature = "no_closure")]
let value_is_shared = ""; let value_is_shared = "";
@ -247,15 +254,16 @@ fn main() {
println!(); println!();
continue; continue;
} }
// "json" => { #[cfg(feature = "metadata")]
// println!( "json" => {
// "{}", println!(
// engine "{}",
// .gen_fn_metadata_with_ast_to_json(&main_ast, true) engine
// .unwrap() .gen_fn_metadata_with_ast_to_json(&main_ast, true)
// ); .unwrap()
// continue; );
// } continue;
}
_ => (), _ => (),
} }

View File

@ -43,7 +43,11 @@ fn main() {
eprintln!("Error script file path: {}\n{}", filename, err); eprintln!("Error script file path: {}\n{}", filename, err);
exit(1); exit(1);
} }
Ok(f) => f, Ok(f) => match f.strip_prefix(std::env::current_dir().unwrap().canonicalize().unwrap())
{
Ok(f) => f.into(),
_ => f,
},
}; };
let mut engine = Engine::new(); let mut engine = Engine::new();

View File

@ -134,7 +134,6 @@ 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,
@ -179,15 +178,12 @@ impl Engine {
/// Replacing one variable with another (i.e. adding a new variable and removing one variable at /// Replacing one variable with another (i.e. adding a new variable and removing one variable at
/// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also /// the same time so that the total _size_ of the [`Scope`][crate::Scope] is unchanged) also
/// does NOT count, so `false` should be passed. /// does NOT count, so `false` should be passed.
#[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],
scope_may_be_changed: bool, scope_may_be_changed: bool,
func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static, func: impl Fn(&mut EvalContext, &[Expression]) -> RhaiResult + SendSync + 'static,
) -> Result<&mut Self, ParseError> { ) -> Result<&mut Self, ParseError> {
let keywords = keywords.as_ref();
let mut segments: StaticVec<ImmutableString> = Default::default(); let mut segments: StaticVec<ImmutableString> = Default::default();
for s in keywords { for s in keywords {
@ -240,8 +236,7 @@ impl Engine {
s s
), ),
) )
.into_err(Position::NONE) .into_err(Position::NONE));
.into());
} }
// Identifier in first position // Identifier in first position
s if segments.is_empty() && is_valid_identifier(s.chars()) => { s if segments.is_empty() && is_valid_identifier(s.chars()) => {
@ -264,8 +259,7 @@ impl Engine {
s s
), ),
) )
.into_err(Position::NONE) .into_err(Position::NONE));
.into());
} }
}; };

View File

@ -212,6 +212,7 @@ pub enum Union {
TimeStamp(Box<Instant>, Tag, AccessMode), TimeStamp(Box<Instant>, Tag, AccessMode),
/// Any type as a trait object. /// Any type as a trait object.
#[allow(clippy::redundant_allocation)]
Variant(Box<Box<dyn Variant>>, Tag, AccessMode), Variant(Box<Box<dyn Variant>>, Tag, AccessMode),
/// A _shared_ value of any type. /// A _shared_ value of any type.
@ -378,10 +379,7 @@ impl Dynamic {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_variant(&self) -> bool { pub const fn is_variant(&self) -> bool {
match self.0 { matches!(self.0, Union::Variant(_, _, _))
Union::Variant(_, _, _) => true,
_ => false,
}
} }
/// Is the value held by this [`Dynamic`] shared? /// Is the value held by this [`Dynamic`] shared?
/// ///
@ -390,13 +388,11 @@ impl Dynamic {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_shared(&self) -> bool { pub const fn is_shared(&self) -> bool {
#[cfg(not(feature = "no_closure"))]
match self.0 { match self.0 {
Union::Shared(_, _, _) => return true, #[cfg(not(feature = "no_closure"))]
_ => (), Union::Shared(_, _, _) => true,
_ => false,
} }
false
} }
/// Is the value held by this [`Dynamic`] a particular type? /// Is the value held by this [`Dynamic`] a particular type?
/// ///
@ -405,13 +401,11 @@ impl Dynamic {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn is<T: Any + Clone>(&self) -> bool { pub fn is<T: Any + Clone>(&self) -> bool {
let mut target_type_id = TypeId::of::<T>(); if TypeId::of::<T>() == TypeId::of::<String>() {
self.type_id() == TypeId::of::<ImmutableString>()
if target_type_id == TypeId::of::<String>() { } else {
target_type_id = TypeId::of::<ImmutableString>(); self.type_id() == TypeId::of::<T>()
} }
self.type_id() == target_type_id
} }
/// Get the [`TypeId`] of the value held by this [`Dynamic`]. /// Get the [`TypeId`] of the value held by this [`Dynamic`].
/// ///
@ -577,7 +571,7 @@ impl Hash for Dynamic {
} }
/// Map the name of a standard type into a friendly form. /// Map the name of a standard type into a friendly form.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub(crate) fn map_std_type_name(name: &str) -> &str { pub(crate) fn map_std_type_name(name: &str) -> &str {
if name == type_name::<String>() { if name == type_name::<String>() {
@ -845,7 +839,7 @@ impl Dynamic {
/// Not available under `no_float`. /// Not available under `no_float`.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
pub const FLOAT_ZERO: Dynamic = Self(Union::Float( pub const FLOAT_ZERO: Dynamic = Self(Union::Float(
FloatWrapper::const_new(0.0), FloatWrapper::new_const(0.0),
DEFAULT_TAG_VALUE, DEFAULT_TAG_VALUE,
ReadWrite, ReadWrite,
)); ));
@ -854,7 +848,7 @@ impl Dynamic {
/// Not available under `no_float`. /// Not available under `no_float`.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
pub const FLOAT_ONE: Dynamic = Self(Union::Float( pub const FLOAT_ONE: Dynamic = Self(Union::Float(
FloatWrapper::const_new(1.0), FloatWrapper::new_const(1.0),
DEFAULT_TAG_VALUE, DEFAULT_TAG_VALUE,
ReadWrite, ReadWrite,
)); ));
@ -863,7 +857,7 @@ impl Dynamic {
/// Not available under `no_float`. /// Not available under `no_float`.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
pub const FLOAT_TWO: Dynamic = Self(Union::Float( pub const FLOAT_TWO: Dynamic = Self(Union::Float(
FloatWrapper::const_new(2.0), FloatWrapper::new_const(2.0),
DEFAULT_TAG_VALUE, DEFAULT_TAG_VALUE,
ReadWrite, ReadWrite,
)); ));
@ -872,7 +866,7 @@ impl Dynamic {
/// Not available under `no_float`. /// Not available under `no_float`.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
pub const FLOAT_TEN: Dynamic = Self(Union::Float( pub const FLOAT_TEN: Dynamic = Self(Union::Float(
FloatWrapper::const_new(10.0), FloatWrapper::new_const(10.0),
DEFAULT_TAG_VALUE, DEFAULT_TAG_VALUE,
ReadWrite, ReadWrite,
)); ));
@ -881,7 +875,7 @@ impl Dynamic {
/// Not available under `no_float`. /// Not available under `no_float`.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float( pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(
FloatWrapper::const_new(-1.0), FloatWrapper::new_const(-1.0),
DEFAULT_TAG_VALUE, DEFAULT_TAG_VALUE,
ReadWrite, ReadWrite,
)); ));
@ -1056,21 +1050,21 @@ impl Dynamic {
let val = value.as_any(); let val = value.as_any();
if TypeId::of::<T>() == TypeId::of::<INT>() { if TypeId::of::<T>() == TypeId::of::<INT>() {
return val.downcast_ref::<INT>().expect(CHECKED).clone().into(); return (*val.downcast_ref::<INT>().expect(CHECKED)).into();
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<FLOAT>() { if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
return val.downcast_ref::<FLOAT>().expect(CHECKED).clone().into(); return (*val.downcast_ref::<FLOAT>().expect(CHECKED)).into();
} }
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
if TypeId::of::<T>() == TypeId::of::<Decimal>() { if TypeId::of::<T>() == TypeId::of::<Decimal>() {
return val.downcast_ref::<Decimal>().expect(CHECKED).clone().into(); return (*val.downcast_ref::<Decimal>().expect(CHECKED)).into();
} }
if TypeId::of::<T>() == TypeId::of::<bool>() { if TypeId::of::<T>() == TypeId::of::<bool>() {
return val.downcast_ref::<bool>().expect(CHECKED).clone().into(); return (*val.downcast_ref::<bool>().expect(CHECKED)).into();
} }
if TypeId::of::<T>() == TypeId::of::<char>() { if TypeId::of::<T>() == TypeId::of::<char>() {
return val.downcast_ref::<char>().expect(CHECKED).clone().into(); return (*val.downcast_ref::<char>().expect(CHECKED)).into();
} }
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() { if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return val return val
@ -1321,7 +1315,7 @@ impl Dynamic {
/// ///
/// assert_eq!(x.cast::<u32>(), 42); /// assert_eq!(x.cast::<u32>(), 42);
/// ``` /// ```
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn cast<T: Any + Clone>(self) -> T { pub fn cast<T: Any + Clone>(self) -> T {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
@ -1481,7 +1475,7 @@ impl Dynamic {
/// ///
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
/// Otherwise, this call panics if the data is currently borrowed for write. /// Otherwise, this call panics if the data is currently borrowed for write.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn read_lock<T: Any + Clone>(&self) -> Option<DynamicReadLock<T>> { pub fn read_lock<T: Any + Clone>(&self) -> Option<DynamicReadLock<T>> {
match self.0 { match self.0 {
@ -1515,7 +1509,7 @@ impl Dynamic {
/// ///
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1). /// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
/// Otherwise, this call panics if the data is currently borrowed for write. /// Otherwise, this call panics if the data is currently borrowed for write.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn write_lock<T: Any + Clone>(&mut self) -> Option<DynamicWriteLock<T>> { pub fn write_lock<T: Any + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
match self.0 { match self.0 {
@ -1735,7 +1729,6 @@ impl Dynamic {
/// Cast the [`Dynamic`] as a unit `()` and return it. /// Cast the [`Dynamic`] as a unit `()` and return it.
/// Returns the name of the actual type if the cast fails. /// Returns the name of the actual type if the cast fails.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn as_unit(&self) -> Result<(), &'static str> { pub fn as_unit(&self) -> Result<(), &'static str> {
match self.0 { match self.0 {
Union::Unit(value, _, _) => Ok(value), Union::Unit(value, _, _) => Ok(value),
@ -1747,7 +1740,6 @@ impl Dynamic {
/// Cast the [`Dynamic`] as the system integer type [`INT`] and return it. /// Cast the [`Dynamic`] as the system integer type [`INT`] and return it.
/// Returns the name of the actual type if the cast fails. /// Returns the name of the actual type if the cast fails.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn as_int(&self) -> Result<INT, &'static str> { pub fn as_int(&self) -> Result<INT, &'static str> {
match self.0 { match self.0 {
Union::Int(n, _, _) => Ok(n), Union::Int(n, _, _) => Ok(n),
@ -1762,7 +1754,6 @@ impl Dynamic {
/// Not available under `no_float`. /// Not available under `no_float`.
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn as_float(&self) -> Result<FLOAT, &'static str> { pub fn as_float(&self) -> Result<FLOAT, &'static str> {
match self.0 { match self.0 {
Union::Float(n, _, _) => Ok(*n), Union::Float(n, _, _) => Ok(*n),
@ -1777,7 +1768,6 @@ impl Dynamic {
/// Exported under the `decimal` feature only. /// Exported under the `decimal` feature only.
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn as_decimal(&self) -> Result<Decimal, &'static str> { pub fn as_decimal(&self) -> Result<Decimal, &'static str> {
match self.0 { match self.0 {
Union::Decimal(ref n, _, _) => Ok(**n), Union::Decimal(ref n, _, _) => Ok(**n),
@ -1789,7 +1779,6 @@ impl Dynamic {
/// Cast the [`Dynamic`] as a [`bool`] and return it. /// Cast the [`Dynamic`] as a [`bool`] and return it.
/// Returns the name of the actual type if the cast fails. /// Returns the name of the actual type if the cast fails.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn as_bool(&self) -> Result<bool, &'static str> { pub fn as_bool(&self) -> Result<bool, &'static str> {
match self.0 { match self.0 {
Union::Bool(b, _, _) => Ok(b), Union::Bool(b, _, _) => Ok(b),
@ -1801,7 +1790,6 @@ impl Dynamic {
/// Cast the [`Dynamic`] as a [`char`] and return it. /// Cast the [`Dynamic`] as a [`char`] and return it.
/// Returns the name of the actual type if the cast fails. /// Returns the name of the actual type if the cast fails.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn as_char(&self) -> Result<char, &'static str> { pub fn as_char(&self) -> Result<char, &'static str> {
match self.0 { match self.0 {
Union::Char(n, _, _) => Ok(n), Union::Char(n, _, _) => Ok(n),
@ -1817,7 +1805,6 @@ impl Dynamic {
/// ///
/// Panics if the value is shared. /// Panics if the value is shared.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> { pub(crate) fn as_str_ref(&self) -> Result<&str, &'static str> {
match self.0 { match self.0 {
Union::Str(ref s, _, _) => Ok(s), Union::Str(ref s, _, _) => Ok(s),
@ -1830,14 +1817,12 @@ impl Dynamic {
/// If there are other references to the same string, a cloned copy is returned. /// If there are other references to the same string, a cloned copy is returned.
/// Returns the name of the actual type if the cast fails. /// Returns the name of the actual type if the cast fails.
#[inline(always)] #[inline(always)]
#[must_use]
pub fn as_string(self) -> Result<String, &'static str> { pub fn as_string(self) -> Result<String, &'static str> {
self.as_immutable_string().map(ImmutableString::into_owned) self.as_immutable_string().map(ImmutableString::into_owned)
} }
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it. /// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
/// Returns the name of the actual type if the cast fails. /// Returns the name of the actual type if the cast fails.
#[inline(always)] #[inline]
#[must_use]
pub fn as_immutable_string(self) -> Result<ImmutableString, &'static str> { pub fn as_immutable_string(self) -> Result<ImmutableString, &'static str> {
match self.0 { match self.0 {
Union::Str(s, _, _) => Ok(s), Union::Str(s, _, _) => Ok(s),
@ -2057,6 +2042,6 @@ impl From<Instant> for Dynamic {
impl From<crate::Shared<crate::Locked<Dynamic>>> for Dynamic { impl From<crate::Shared<crate::Locked<Dynamic>>> for Dynamic {
#[inline(always)] #[inline(always)]
fn from(value: crate::Shared<crate::Locked<Self>>) -> Self { fn from(value: crate::Shared<crate::Locked<Self>>) -> Self {
Self(Union::Shared(value.into(), DEFAULT_TAG_VALUE, ReadWrite)) Self(Union::Shared(value, DEFAULT_TAG_VALUE, ReadWrite))
} }
} }

View File

@ -67,7 +67,10 @@ impl Imports {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Default::default() Self {
keys: StaticVec::new(),
modules: StaticVec::new(),
}
} }
/// Get the length of this stack of imported [modules][Module]. /// Get the length of this stack of imported [modules][Module].
#[inline(always)] #[inline(always)]
@ -119,7 +122,6 @@ 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()
@ -130,14 +132,12 @@ 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())
} }
@ -290,10 +290,10 @@ pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign;
enum ChainType { enum ChainType {
/// Indexing. /// Indexing.
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Index, Indexing,
/// Dotting. /// Dotting.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Dot, Dotting,
} }
/// Value of a chaining argument. /// Value of a chaining argument.
@ -318,9 +318,10 @@ impl ChainArgument {
#[inline(always)] #[inline(always)]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
#[must_use] #[must_use]
pub fn as_index_value(self) -> Option<Dynamic> { pub fn into_index_value(self) -> Option<Dynamic> {
match self { match self {
Self::IndexValue(value, _) => Some(value), Self::IndexValue(value, _) => Some(value),
#[cfg(not(feature = "no_object"))]
_ => None, _ => None,
} }
} }
@ -328,7 +329,7 @@ impl ChainArgument {
#[inline(always)] #[inline(always)]
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[must_use] #[must_use]
pub fn as_fn_call_args(self) -> Option<(StaticVec<Dynamic>, Position)> { pub fn into_fn_call_args(self) -> Option<(StaticVec<Dynamic>, Position)> {
match self { match self {
Self::MethodCallArgs(values, pos) => Some((values, pos)), Self::MethodCallArgs(values, pos) => Some((values, pos)),
_ => None, _ => None,
@ -352,6 +353,18 @@ impl From<(Dynamic, Position)> for ChainArgument {
} }
} }
/// Get the chaining type for an [`Expr`].
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
fn match_chaining_type(expr: &Expr) -> ChainType {
match expr {
#[cfg(not(feature = "no_index"))]
Expr::Index(_, _, _) => ChainType::Indexing,
#[cfg(not(feature = "no_object"))]
Expr::Dot(_, _, _) => ChainType::Dotting,
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
}
}
/// A type that encapsulates a mutation target for an expression with side effects. /// A type that encapsulates a mutation target for an expression with side effects.
#[derive(Debug)] #[derive(Debug)]
pub enum Target<'a> { pub enum Target<'a> {
@ -376,7 +389,7 @@ pub enum Target<'a> {
impl<'a> Target<'a> { 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]
#[must_use] #[must_use]
pub const fn is_ref(&self) -> bool { pub const fn is_ref(&self) -> bool {
match self { match self {
@ -391,7 +404,7 @@ impl<'a> Target<'a> {
} }
} }
/// Is the `Target` a temp value? /// Is the `Target` a temp value?
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub const fn is_temp_value(&self) -> bool { pub const fn is_temp_value(&self) -> bool {
match self { match self {
@ -407,7 +420,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]
#[must_use] #[must_use]
pub fn is_shared(&self) -> bool { pub fn is_shared(&self) -> bool {
match self { match self {
@ -423,7 +436,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]
#[must_use] #[must_use]
pub fn is<T: Variant + Clone>(&self) -> bool { pub fn is<T: Variant + Clone>(&self) -> bool {
match self { match self {
@ -438,7 +451,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]
#[must_use] #[must_use]
pub fn take_or_clone(self) -> Dynamic { pub fn take_or_clone(self) -> Dynamic {
match self { match self {
@ -469,8 +482,7 @@ impl<'a> Target<'a> {
} }
/// 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]
#[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(_) => (),
@ -534,7 +546,7 @@ impl<'a> Target<'a> {
} }
impl<'a> From<&'a mut Dynamic> for Target<'a> { impl<'a> From<&'a mut Dynamic> for Target<'a> {
#[inline(always)] #[inline]
fn from(value: &'a mut Dynamic) -> Self { fn from(value: &'a mut Dynamic) -> Self {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
if value.is_shared() { if value.is_shared() {
@ -555,7 +567,7 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
impl Deref for Target<'_> { impl Deref for Target<'_> {
type Target = Dynamic; type Target = Dynamic;
#[inline(always)] #[inline]
fn deref(&self) -> &Dynamic { fn deref(&self) -> &Dynamic {
match self { match self {
Self::RefMut(r) => *r, Self::RefMut(r) => *r,
@ -578,7 +590,7 @@ impl AsRef<Dynamic> for Target<'_> {
} }
impl DerefMut for Target<'_> { impl DerefMut for Target<'_> {
#[inline(always)] #[inline]
fn deref_mut(&mut self) -> &mut Dynamic { fn deref_mut(&mut self) -> &mut Dynamic {
match self { match self {
Self::RefMut(r) => *r, Self::RefMut(r) => *r,
@ -818,7 +830,6 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> {
/// 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()
} }
@ -833,7 +844,6 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> {
} }
/// 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()
} }
@ -947,7 +957,7 @@ impl Default for Engine {
/// Make getter function /// Make getter function
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn make_getter(id: &str) -> String { pub fn make_getter(id: &str) -> String {
format!("{}{}", FN_GET, id) format!("{}{}", FN_GET, id)
@ -955,7 +965,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]
#[must_use] #[must_use]
pub fn make_setter(id: &str) -> String { pub fn make_setter(id: &str) -> String {
format!("{}{}", FN_SET, id) format!("{}{}", FN_SET, id)
@ -970,7 +980,7 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool {
} }
/// Print to `stdout` /// Print to `stdout`
#[inline(always)] #[inline]
#[allow(unused_variables)] #[allow(unused_variables)]
fn print_to_stdout(s: &str) { fn print_to_stdout(s: &str) {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -979,7 +989,7 @@ fn print_to_stdout(s: &str) {
} }
/// Debug to `stdout` /// Debug to `stdout`
#[inline(always)] #[inline]
#[allow(unused_variables)] #[allow(unused_variables)]
fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) { fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) {
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
@ -1021,7 +1031,7 @@ impl Engine {
/// Create a new [`Engine`] with minimal built-in functions. /// Create a new [`Engine`] with minimal built-in functions.
/// ///
/// 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]
#[must_use] #[must_use]
pub fn new_raw() -> Self { pub fn new_raw() -> Self {
let mut engine = Self { let mut engine = Self {
@ -1093,7 +1103,6 @@ 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,
@ -1143,7 +1152,6 @@ 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,
@ -1217,7 +1225,6 @@ 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,
@ -1227,21 +1234,12 @@ impl Engine {
target: &mut Target, target: &mut Target,
root: (&str, Position), root: (&str, Position),
rhs: &Expr, rhs: &Expr,
_terminate_chaining: bool,
idx_values: &mut StaticVec<ChainArgument>, idx_values: &mut StaticVec<ChainArgument>,
chain_type: ChainType, chain_type: ChainType,
level: usize, level: usize,
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
fn match_chain_type(expr: &Expr) -> ChainType {
match expr {
#[cfg(not(feature = "no_index"))]
Expr::Index(_, _) => ChainType::Index,
#[cfg(not(feature = "no_object"))]
Expr::Dot(_, _) => ChainType::Dot,
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
}
}
let is_ref_mut = target.is_ref(); let is_ref_mut = target.is_ref();
// Pop the last index value // Pop the last index value
@ -1251,23 +1249,25 @@ impl Engine {
match chain_type { match chain_type {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
ChainType::Index => { ChainType::Indexing => {
let pos = rhs.position(); let pos = rhs.position();
let idx_val = idx_val let idx_val = idx_val
.as_index_value() .into_index_value()
.expect("never fails because `chain_type` is `ChainType::Index`"); .expect("never fails because `chain_type` is `ChainType::Index`");
match rhs { match rhs {
// xxx[idx].expr... | xxx[idx][expr]... // xxx[idx].expr... | xxx[idx][expr]...
Expr::Dot(x, x_pos) | Expr::Index(x, x_pos) => { Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos)
if !_terminate_chaining =>
{
let idx_pos = x.lhs.position(); let idx_pos = x.lhs.position();
let obj_ptr = &mut self.get_indexed_mut( let obj_ptr = &mut self.get_indexed_mut(
mods, state, lib, target, idx_val, idx_pos, false, true, level, mods, state, lib, target, idx_val, idx_pos, false, true, level,
)?; )?;
let rhs_chain = match_chain_type(rhs);
let rhs_chain = match_chaining_type(rhs);
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values, mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, 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))
@ -1324,13 +1324,13 @@ impl Engine {
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
ChainType::Dot => { ChainType::Dotting => {
match rhs { match rhs {
// 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 call_args = &mut idx_val let call_args = &mut idx_val
.as_fn_call_args() .into_fn_call_args()
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
self.make_method_call( self.make_method_call(
mods, state, lib, name, *hashes, target, call_args, *pos, level, mods, state, lib, name, *hashes, target, call_args, *pos, level,
@ -1482,7 +1482,9 @@ 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, term, x_pos) | Expr::Dot(x, term, x_pos)
if target.is::<Map>() =>
{
let val_target = &mut match x.lhs { let val_target = &mut match x.lhs {
Expr::Property(ref p) => { Expr::Property(ref p) => {
let (name, pos) = &p.2; let (name, pos) = &p.2;
@ -1495,7 +1497,7 @@ impl Engine {
Expr::FnCall(ref 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 call_args = &mut idx_val let call_args = &mut idx_val
.as_fn_call_args() .into_fn_call_args()
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
let (val, _) = self.make_method_call( let (val, _) = self.make_method_call(
mods, state, lib, name, *hashes, target, call_args, pos, level, mods, state, lib, name, *hashes, target, call_args, pos, level,
@ -1509,22 +1511,22 @@ impl Engine {
// Others - syntax error // Others - syntax error
ref expr => unreachable!("invalid dot expression: {:?}", expr), ref expr => unreachable!("invalid dot expression: {:?}", expr),
}; };
let rhs_chain = match_chain_type(rhs); let rhs_chain = match_chaining_type(rhs);
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, val_target, root, &x.rhs, idx_values, mods, state, lib, this_ptr, val_target, root, &x.rhs, *term,
rhs_chain, level, new_val, idx_values, 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, term, x_pos) | Expr::Dot(x, term, x_pos) => {
match x.lhs { match x.lhs {
// xxx.prop[expr] | xxx.prop.expr // xxx.prop[expr] | xxx.prop.expr
Expr::Property(ref 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_chaining_type(rhs);
let hash_get = FnCallHashes::from_native(*hash_get); let hash_get = FnCallHashes::from_native(*hash_get);
let hash_set = FnCallHashes::from_native(*hash_set); let hash_set = FnCallHashes::from_native(*hash_set);
let mut arg_values = [target.as_mut(), &mut Default::default()]; let mut arg_values = [target.as_mut(), &mut Default::default()];
@ -1564,6 +1566,7 @@ impl Engine {
&mut val.into(), &mut val.into(),
root, root,
&x.rhs, &x.rhs,
*term,
idx_values, idx_values,
rhs_chain, rhs_chain,
level, level,
@ -1614,9 +1617,9 @@ impl Engine {
// 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(ref 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_chaining_type(rhs);
let args = &mut idx_val let args = &mut idx_val
.as_fn_call_args() .into_fn_call_args()
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`"); .expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
let (mut val, _) = self.make_method_call( let (mut val, _) = self.make_method_call(
mods, state, lib, name, *hashes, target, args, pos, level, mods, state, lib, name, *hashes, target, args, pos, level,
@ -1625,8 +1628,8 @@ impl Engine {
let target = &mut val.into(); let target = &mut val.into();
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, target, root, &x.rhs, idx_values, mods, state, lib, this_ptr, target, root, &x.rhs, *term,
rhs_chain, level, new_val, idx_values, rhs_chain, level, new_val,
) )
.map_err(|err| err.fill_position(pos)) .map_err(|err| err.fill_position(pos))
} }
@ -1647,8 +1650,6 @@ 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,
@ -1660,18 +1661,18 @@ impl Engine {
level: usize, level: usize,
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>, new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
) -> RhaiResult { ) -> RhaiResult {
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr { let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, term, op_pos) = match expr {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos), Expr::Index(x, term, pos) => (x.as_ref(), ChainType::Indexing, *term, *pos),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos), Expr::Dot(x, term, pos) => (x.as_ref(), ChainType::Dotting, *term, *pos),
_ => unreachable!("index or dot chain expected, but gets {:?}", expr), _ => unreachable!("index or dot chain expected, but gets {:?}", expr),
}; };
let idx_values = &mut Default::default(); let idx_values = &mut Default::default();
self.eval_indexed_chain( self.eval_dot_index_chain_arguments(
scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, 0, level, scope, mods, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level,
)?; )?;
match lhs { match lhs {
@ -1680,13 +1681,15 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(state, *var_pos)?; self.inc_operations(state, *var_pos)?;
let (target, _) = self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?; let (mut target, _) =
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
let obj_ptr = &mut target.into(); let obj_ptr = &mut target;
let root = (x.2.as_str(), *var_pos); let root = (x.2.as_str(), *var_pos);
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, &mut None, obj_ptr, root, rhs, idx_values, chain_type, level, mods, state, lib, &mut None, obj_ptr, root, rhs, term, idx_values, chain_type,
new_val, level, new_val,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
.map_err(|err| err.fill_position(op_pos)) .map_err(|err| err.fill_position(op_pos))
@ -1699,8 +1702,8 @@ impl Engine {
let obj_ptr = &mut value.into(); let obj_ptr = &mut value.into();
let root = ("", expr.position()); let root = ("", expr.position());
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
mods, state, lib, this_ptr, obj_ptr, root, rhs, idx_values, chain_type, level, mods, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type,
new_val, level, new_val,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
.map_err(|err| err.fill_position(op_pos)) .map_err(|err| err.fill_position(op_pos))
@ -1712,8 +1715,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_dot_index_chain_arguments(
fn eval_indexed_chain(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
mods: &mut Imports, mods: &mut Imports,
@ -1721,6 +1723,7 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
expr: &Expr, expr: &Expr,
terminate_chaining: bool,
_parent_chain_type: ChainType, _parent_chain_type: ChainType,
idx_values: &mut StaticVec<ChainArgument>, idx_values: &mut StaticVec<ChainArgument>,
size: usize, size: usize,
@ -1731,7 +1734,7 @@ impl Engine {
match expr { match expr {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dot && !x.is_qualified() => { Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => {
let crate::ast::FnCallExpr { let crate::ast::FnCallExpr {
args, constants, .. args, constants, ..
} = x.as_ref(); } = x.as_ref();
@ -1751,30 +1754,30 @@ impl Engine {
idx_values.push((arg_values, first_arg_pos).into()); idx_values.push((arg_values, first_arg_pos).into());
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => { Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => {
unreachable!("function call in dot chain should not be namespace-qualified") unreachable!("function call in dot chain should not be namespace-qualified")
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(x) if _parent_chain_type == ChainType::Dot => { Expr::Property(x) if _parent_chain_type == ChainType::Dotting => {
idx_values.push(ChainArgument::Property((x.2).1)) idx_values.push(ChainArgument::Property((x.2).1))
} }
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
Expr::Index(x, _) | Expr::Dot(x, _) => { Expr::Index(x, term, _) | Expr::Dot(x, term, _) if !terminate_chaining => {
let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref(); let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref();
// Evaluate in left-to-right order // Evaluate in left-to-right order
let lhs_val = match lhs { let lhs_val = match lhs {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(x) if _parent_chain_type == ChainType::Dot => { Expr::Property(x) if _parent_chain_type == ChainType::Dotting => {
ChainArgument::Property((x.2).1) ChainArgument::Property((x.2).1)
} }
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::FnCall(x, _) Expr::FnCall(x, _)
if _parent_chain_type == ChainType::Dot && !x.is_qualified() => if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
{ {
let crate::ast::FnCallExpr { let crate::ast::FnCallExpr {
args, constants, .. args, constants, ..
@ -1795,41 +1798,37 @@ impl Engine {
(arg_values, first_arg_pos).into() (arg_values, first_arg_pos).into()
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => { Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => {
unreachable!("function call in dot chain should not be namespace-qualified") unreachable!("function call in dot chain should not be namespace-qualified")
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
expr if _parent_chain_type == ChainType::Dot => { expr if _parent_chain_type == ChainType::Dotting => {
unreachable!("invalid dot expression: {:?}", expr); unreachable!("invalid dot expression: {:?}", expr);
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
_ if _parent_chain_type == ChainType::Index => self _ if _parent_chain_type == ChainType::Indexing => self
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level) .eval_expr(scope, mods, state, lib, this_ptr, lhs, level)
.map(|v| (v.flatten(), lhs.position()).into())?, .map(|v| (v.flatten(), lhs.position()).into())?,
expr => unreachable!("unknown chained expression: {:?}", expr), expr => unreachable!("unknown chained expression: {:?}", expr),
}; };
// Push in reverse order // Push in reverse order
let chain_type = match expr { let chain_type = match_chaining_type(expr);
#[cfg(not(feature = "no_index"))]
Expr::Index(_, _) => ChainType::Index, self.eval_dot_index_chain_arguments(
#[cfg(not(feature = "no_object"))] scope, mods, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size,
Expr::Dot(_, _) => ChainType::Dot, level,
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
};
self.eval_indexed_chain(
scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, size, level,
)?; )?;
idx_values.push(lhs_val); idx_values.push(lhs_val);
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
_ if _parent_chain_type == ChainType::Dot => { _ if _parent_chain_type == ChainType::Dotting => {
unreachable!("invalid dot expression: {:?}", expr); unreachable!("invalid dot expression: {:?}", expr);
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
_ if _parent_chain_type == ChainType::Index => idx_values.push( _ if _parent_chain_type == ChainType::Indexing => idx_values.push(
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level) self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
.map(|v| (v.flatten(), expr.position()).into())?, .map(|v| (v.flatten(), expr.position()).into())?,
), ),
@ -1842,7 +1841,6 @@ 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,
@ -2013,7 +2011,6 @@ 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,
@ -2051,13 +2048,13 @@ impl Engine {
// lhs[idx_expr] // lhs[idx_expr]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(_, _) => { Expr::Index(_, _, _) => {
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
} }
// lhs.dot_rhs // lhs.dot_rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(_, _) => { Expr::Dot(_, _, _) => {
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None) self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
} }
@ -2214,7 +2211,6 @@ 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,
@ -2292,7 +2288,6 @@ 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,
@ -2366,7 +2361,6 @@ 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,
@ -2436,7 +2430,7 @@ impl Engine {
let rhs_val = self let rhs_val = self
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)? .eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
.flatten(); .flatten();
let _new_val = Some(((rhs_val, rhs_expr.position()), (op_info.clone(), *op_pos))); let _new_val = Some(((rhs_val, rhs_expr.position()), (*op_info, *op_pos)));
// Must be either `var[index] op= val` or `var.prop op= val` // Must be either `var[index] op= val` or `var.prop op= val`
match lhs_expr { match lhs_expr {
@ -2446,7 +2440,7 @@ impl Engine {
} }
// idx_lhs[idx_expr] op= rhs // idx_lhs[idx_expr] op= rhs
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(_, _) => { Expr::Index(_, _, _) => {
self.eval_dot_index_chain( self.eval_dot_index_chain(
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
)?; )?;
@ -2454,7 +2448,7 @@ impl Engine {
} }
// dot_lhs.dot_rhs op= rhs // dot_lhs.dot_rhs op= rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(_, _) => { Expr::Dot(_, _, _) => {
self.eval_dot_index_chain( self.eval_dot_index_chain(
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val, scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
)?; )?;
@ -2953,7 +2947,7 @@ impl Engine {
EvalAltResult::ErrorModuleNotFound(path.to_string(), path_pos).into() EvalAltResult::ErrorModuleNotFound(path.to_string(), path_pos).into()
})?; })?;
export.as_ref().map(|x| x.name.clone()).map(|name| { if let Some(name) = export.as_ref().map(|x| x.name.clone()) {
if !module.is_indexed() { if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed // Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module); let mut module = crate::fn_native::shared_take_or_clone(module);
@ -2962,7 +2956,7 @@ impl Engine {
} else { } else {
mods.push(name, module); mods.push(name, module);
} }
}); }
state.modules += 1; state.modules += 1;
@ -2992,14 +2986,14 @@ impl Engine {
// Share statement // Share statement
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Stmt::Share(name) => { Stmt::Share(name) => {
scope.get_index(name).map(|(index, _)| { if let Some((index, _)) = scope.get_index(name) {
let val = scope.get_mut_by_index(index); let val = scope.get_mut_by_index(index);
if !val.is_shared() { if !val.is_shared() {
// Replace the variable with a shared value. // Replace the variable with a shared value.
*val = std::mem::take(val).into_shared(); *val = std::mem::take(val).into_shared();
} }
}); }
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
}; };
@ -3011,7 +3005,6 @@ 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
} }
@ -3019,20 +3012,17 @@ 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) {
@ -3135,7 +3125,6 @@ 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 EvalState, state: &mut EvalState,

View File

@ -329,8 +329,7 @@ impl Engine {
name: &str, name: &str,
get_fn: impl Fn(&mut T) -> V + SendSync + 'static, get_fn: impl Fn(&mut T) -> V + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
use crate::engine::make_getter; self.register_fn(&crate::engine::make_getter(name), get_fn)
self.register_fn(&make_getter(name), get_fn)
} }
/// Register a getter function for a member of a registered type with the [`Engine`]. /// Register a getter function for a member of a registered type with the [`Engine`].
/// ///
@ -378,8 +377,7 @@ impl Engine {
name: &str, name: &str,
get_fn: impl Fn(&mut T) -> Result<V, Box<EvalAltResult>> + SendSync + 'static, get_fn: impl Fn(&mut T) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
use crate::engine::make_getter; self.register_result_fn(&crate::engine::make_getter(name), get_fn)
self.register_result_fn(&make_getter(name), get_fn)
} }
/// Register a setter function for a member of a registered type with the [`Engine`]. /// Register a setter function for a member of a registered type with the [`Engine`].
/// ///
@ -426,8 +424,7 @@ impl Engine {
name: &str, name: &str,
set_fn: impl Fn(&mut T, V) + SendSync + 'static, set_fn: impl Fn(&mut T, V) + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
use crate::engine::make_setter; self.register_fn(&crate::engine::make_setter(name), set_fn)
self.register_fn(&make_setter(name), set_fn)
} }
/// Register a setter function for a member of a registered type with the [`Engine`]. /// Register a setter function for a member of a registered type with the [`Engine`].
/// ///
@ -477,8 +474,7 @@ impl Engine {
name: &str, name: &str,
set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, set_fn: impl Fn(&mut T, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
use crate::engine::make_setter; self.register_result_fn(&crate::engine::make_setter(name), set_fn)
self.register_result_fn(&make_setter(name), set_fn)
} }
/// Short-hand for registering both getter and setter functions /// Short-hand for registering both getter and setter functions
/// of a registered type with the [`Engine`]. /// of a registered type with the [`Engine`].
@ -883,7 +879,7 @@ impl Engine {
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>( pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
&mut self, &mut self,
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static, get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
set_fn: impl Fn(&mut T, X, V) -> () + SendSync + 'static, set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
) -> &mut Self { ) -> &mut Self {
self.register_indexer_get(get_fn) self.register_indexer_get(get_fn)
.register_indexer_set(set_fn) .register_indexer_set(set_fn)
@ -1011,7 +1007,6 @@ 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)
} }
@ -1054,7 +1049,6 @@ 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])
} }
@ -1068,7 +1062,6 @@ 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,
@ -1107,7 +1100,7 @@ impl Engine {
let mut resolver = StaticModuleResolver::new(); let mut resolver = StaticModuleResolver::new();
let mut imports = Default::default(); let mut imports = Default::default();
collect_imports(&ast, &mut resolver, &mut imports); collect_imports(&ast, &resolver, &mut imports);
if !imports.is_empty() { if !imports.is_empty() {
while let Some(path) = imports.iter().next() { while let Some(path) = imports.iter().next() {
@ -1115,7 +1108,7 @@ impl Engine {
match module_resolver.resolve_ast(self, None, &path, Position::NONE) { match module_resolver.resolve_ast(self, None, &path, Position::NONE) {
Some(Ok(module_ast)) => { Some(Ok(module_ast)) => {
collect_imports(&module_ast, &mut resolver, &mut imports) collect_imports(&module_ast, &resolver, &mut imports)
} }
Some(err) => return err, Some(err) => return err,
None => (), None => (),
@ -1180,7 +1173,6 @@ 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,
@ -1189,8 +1181,7 @@ impl Engine {
self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level) self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
} }
/// 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]
#[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,
@ -1209,7 +1200,6 @@ 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;
@ -1265,7 +1255,6 @@ 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)
} }
@ -1305,7 +1294,6 @@ 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,
@ -1357,7 +1345,6 @@ 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>,
@ -1433,7 +1420,6 @@ 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)
} }
@ -1476,8 +1462,7 @@ impl Engine {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline]
#[must_use]
pub fn compile_expression_with_scope( pub fn compile_expression_with_scope(
&self, &self,
scope: &Scope, scope: &Scope,
@ -1510,7 +1495,6 @@ 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,
@ -1541,7 +1525,6 @@ 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,
@ -1564,7 +1547,6 @@ 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)
} }
@ -1591,7 +1573,6 @@ 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,
@ -1619,7 +1600,6 @@ 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,
@ -1644,8 +1624,7 @@ impl Engine {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline]
#[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,
@ -1684,7 +1663,6 @@ 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)
} }
@ -1717,8 +1695,7 @@ impl Engine {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline]
#[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,
@ -1730,18 +1707,17 @@ impl Engine {
let typ = self.map_type_name(result.type_name()); let typ = self.map_type_name(result.type_name());
return result.try_cast::<T>().ok_or_else(|| { result.try_cast::<T>().ok_or_else(|| {
EvalAltResult::ErrorMismatchOutputType( EvalAltResult::ErrorMismatchOutputType(
self.map_type_name(type_name::<T>()).into(), self.map_type_name(type_name::<T>()).into(),
typ.into(), typ.into(),
Position::NONE, Position::NONE,
) )
.into() .into()
}); })
} }
/// Evaluate an [`AST`] with own scope. /// Evaluate an [`AST`] with own scope.
#[inline(always)] #[inline]
#[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,
@ -1797,7 +1773,7 @@ impl Engine {
} }
/// Evaluate a string with own scope, but throw away the result and only return error (if any). /// Evaluate a string with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
#[inline(always)] #[inline]
pub fn consume_with_scope( pub fn consume_with_scope(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1824,7 +1800,7 @@ impl Engine {
} }
/// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any). /// Evaluate an [`AST`] with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors. /// Useful for when you don't need the result, but still need to keep track of possible errors.
#[inline(always)] #[inline]
pub fn consume_ast_with_scope( pub fn consume_ast_with_scope(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1889,8 +1865,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline]
#[must_use]
pub fn call_fn<T: Variant + Clone>( pub fn call_fn<T: Variant + Clone>(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1901,6 +1876,7 @@ impl Engine {
let mut arg_values: crate::StaticVec<_> = Default::default(); let mut arg_values: crate::StaticVec<_> = Default::default();
args.parse(&mut arg_values); args.parse(&mut arg_values);
let mut args: crate::StaticVec<_> = arg_values.iter_mut().collect(); let mut args: crate::StaticVec<_> = arg_values.iter_mut().collect();
let name = name.as_ref();
let result = self.call_fn_dynamic_raw(scope, ast, true, name, &mut None, &mut args)?; let result = self.call_fn_dynamic_raw(scope, ast, true, name, &mut None, &mut args)?;
@ -1969,8 +1945,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline]
#[must_use]
pub fn call_fn_dynamic( pub fn call_fn_dynamic(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1980,6 +1955,7 @@ impl Engine {
mut this_ptr: Option<&mut Dynamic>, mut this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>, mut arg_values: impl AsMut<[Dynamic]>,
) -> RhaiResult { ) -> RhaiResult {
let name = name.as_ref();
let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect(); let mut args: crate::StaticVec<_> = arg_values.as_mut().iter_mut().collect();
self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, &mut args) self.call_fn_dynamic_raw(scope, ast, eval_ast, name, &mut this_ptr, &mut args)
@ -1993,14 +1969,13 @@ impl Engine {
/// 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.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline]
#[must_use]
pub(crate) fn call_fn_dynamic_raw( pub(crate) fn call_fn_dynamic_raw(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
eval_ast: bool, eval_ast: bool,
name: impl AsRef<str>, name: &str,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
args: &mut FnCallArgs, args: &mut FnCallArgs,
) -> RhaiResult { ) -> RhaiResult {
@ -2008,7 +1983,6 @@ impl Engine {
let mods = &mut Default::default(); let mods = &mut Default::default();
let lib = &[ast.lib()]; let lib = &[ast.lib()];
let statements = ast.statements(); let statements = ast.statements();
let name = name.as_ref();
if eval_ast && !statements.is_empty() { if eval_ast && !statements.is_empty() {
self.eval_global_statements(scope, mods, state, statements, lib, 0)?; self.eval_global_statements(scope, mods, state, statements, lib, 0)?;
@ -2051,7 +2025,7 @@ impl Engine {
/// (i.e. with [`Scope::push_constant`]). /// (i.e. with [`Scope::push_constant`]).
/// 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]
#[must_use] #[must_use]
pub fn optimize_ast( pub fn optimize_ast(
&self, &self,
@ -2086,6 +2060,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")]
#[inline]
#[must_use] #[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

@ -291,7 +291,6 @@ 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>,
@ -310,18 +309,18 @@ impl Engine {
// Disabled keywords are OK // Disabled keywords are OK
Some(token) if token.is_keyword() => { Some(token) if token.is_keyword() => {
if !self.disabled_symbols.contains(token.syntax().as_ref()) { if !self.disabled_symbols.contains(token.syntax().as_ref()) {
return Err(format!("'{}' is a reserved keyword", keyword.as_ref()).into()); return Err(format!("'{}' is a reserved keyword", keyword.as_ref()));
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) if token.is_symbol() => { Some(token) if token.is_symbol() => {
if !self.disabled_symbols.contains(token.syntax().as_ref()) { if !self.disabled_symbols.contains(token.syntax().as_ref()) {
return Err(format!("'{}' is a reserved operator", keyword.as_ref()).into()); return Err(format!("'{}' is a reserved operator", keyword.as_ref()));
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => { Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => {
return Err(format!("'{}' is a reserved symbol", keyword.as_ref()).into()) return Err(format!("'{}' is a reserved symbol", keyword.as_ref()))
} }
// Disabled symbols are OK // Disabled symbols are OK
Some(_) => (), Some(_) => (),

View File

@ -39,7 +39,6 @@ pub enum LexError {
impl Error for LexError {} impl Error for LexError {}
impl fmt::Display for LexError { impl fmt::Display for LexError {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s), Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
@ -283,7 +282,6 @@ pub struct ParseError(pub Box<ParseErrorType>, pub Position);
impl Error for ParseError {} impl Error for ParseError {}
impl fmt::Display for ParseError { impl fmt::Display for ParseError {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)?; fmt::Display::fmt(&self.0, f)?;

View File

@ -18,7 +18,7 @@ use rust_decimal::Decimal;
const BUILTIN: &str = "never fails because this is built-in code and the type is already checked"; const BUILTIN: &str = "never fails because this is built-in code and the type is already checked";
/// Is the type a numeric type? /// Is the type a numeric type?
#[inline(always)] #[inline]
#[must_use] #[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>()
@ -206,7 +206,6 @@ pub fn get_builtin_binary_op_fn(
// char op string // char op string
if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) { if types_pair == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
#[inline(always)]
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) { fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
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);
@ -233,7 +232,6 @@ pub fn get_builtin_binary_op_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>()) {
#[inline(always)]
fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) { fn get_s1s2(args: &FnCallArgs) -> ([char; 2], [char; 2]) {
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);

View File

@ -84,7 +84,9 @@ impl<'a> ArgBackup<'a> {
/// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak. /// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak.
#[inline(always)] #[inline(always)]
fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) { fn restore_first_arg(mut self, args: &mut FnCallArgs<'a>) {
self.orig_mut.take().map(|p| args[0] = p); if let Some(p) = self.orig_mut.take() {
args[0] = p;
}
} }
} }
@ -100,8 +102,7 @@ impl Drop for ArgBackup<'_> {
} }
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[inline(always)] #[inline]
#[must_use]
pub fn ensure_no_data_race( pub fn ensure_no_data_race(
fn_name: &str, fn_name: &str,
args: &FnCallArgs, args: &FnCallArgs,
@ -157,7 +158,6 @@ impl Engine {
/// 3) Global modules - packages /// 3) Global modules - packages
/// 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)]
#[must_use] #[must_use]
fn resolve_fn<'s>( fn resolve_fn<'s>(
&self, &self,
@ -262,7 +262,6 @@ 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,
@ -291,7 +290,7 @@ impl Engine {
let mut backup: Option<ArgBackup> = None; let mut backup: Option<ArgBackup> = None;
if is_method_call && func.is_pure() && !args.is_empty() { if is_method_call && func.is_pure() && !args.is_empty() {
backup = Some(Default::default()); backup = Some(Default::default());
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args)); backup.as_mut().unwrap().change_first_arg_to_copy(args);
} }
// Run external function // Run external function
@ -312,7 +311,9 @@ impl Engine {
}; };
// Restore the original reference // Restore the original reference
backup.map(|bk| bk.restore_first_arg(args)); if let Some(bk) = backup {
bk.restore_first_arg(args)
}
let result = result.map_err(|err| err.fill_position(pos))?; let result = result.map_err(|err| err.fill_position(pos))?;
@ -434,7 +435,6 @@ 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,
@ -447,7 +447,6 @@ impl Engine {
pos: Position, pos: Position,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
#[inline(always)]
fn make_error( fn make_error(
name: String, name: String,
fn_def: &crate::ast::ScriptFnDef, fn_def: &crate::ast::ScriptFnDef,
@ -564,7 +563,6 @@ impl Engine {
// Does a scripted function exist? // Does a scripted function exist?
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use] #[must_use]
pub(crate) fn has_script_fn( pub(crate) fn has_script_fn(
&self, &self,
@ -604,7 +602,6 @@ 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,
@ -619,7 +616,6 @@ impl Engine {
_capture_scope: Option<Scope>, _capture_scope: Option<Scope>,
_level: usize, _level: usize,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
#[inline(always)]
fn no_method_err(name: &str, pos: Position) -> Result<(Dynamic, bool), Box<EvalAltResult>> { fn no_method_err(name: &str, pos: Position) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
let msg = format!("'{0}' should not be called this way. Try {0}(...);", name); let msg = format!("'{0}' should not be called this way. Try {0}(...);", name);
EvalAltResult::ErrorRuntime(msg.into(), pos).into() EvalAltResult::ErrorRuntime(msg.into(), pos).into()
@ -706,7 +702,7 @@ impl Engine {
// Move captured variables into scope // Move captured variables into scope
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
if !func.externals.is_empty() { if !func.externals.is_empty() {
_capture_scope.map(|captured| { if let Some(captured) = _capture_scope {
captured captured
.into_iter() .into_iter()
.filter(|(name, _, _)| func.externals.contains(name.as_ref())) .filter(|(name, _, _)| func.externals.contains(name.as_ref()))
@ -714,7 +710,7 @@ impl Engine {
// Consume the scope values. // Consume the scope values.
scope.push_dynamic(name, value); scope.push_dynamic(name, value);
}) })
}); }
} }
let result = if _is_method_call { let result = if _is_method_call {
@ -750,7 +746,7 @@ impl Engine {
let mut backup: Option<ArgBackup> = None; let mut backup: Option<ArgBackup> = None;
if is_ref_mut && !args.is_empty() { if is_ref_mut && !args.is_empty() {
backup = Some(Default::default()); backup = Some(Default::default());
backup.as_mut().map(|bk| bk.change_first_arg_to_copy(args)); backup.as_mut().unwrap().change_first_arg_to_copy(args);
} }
let orig_source = state.source.take(); let orig_source = state.source.take();
@ -765,7 +761,9 @@ impl Engine {
state.source = orig_source; state.source = orig_source;
// Restore the original reference // Restore the original reference
backup.map(|bk| bk.restore_first_arg(args)); if let Some(bk) = backup {
bk.restore_first_arg(args)
}
result? result?
}; };
@ -782,8 +780,7 @@ impl Engine {
/// 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]
#[must_use]
pub(crate) fn eval_global_statements( pub(crate) fn eval_global_statements(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -804,7 +801,6 @@ 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,
@ -856,7 +852,6 @@ 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,
@ -895,7 +890,7 @@ impl Engine {
) )
} }
KEYWORD_FN_PTR_CALL => { KEYWORD_FN_PTR_CALL => {
if call_args.len() > 0 { if !call_args.is_empty() {
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(call_args[0].type_name()), self.map_type_name(call_args[0].type_name()),
@ -1018,8 +1013,7 @@ impl Engine {
} }
/// Evaluate an argument. /// Evaluate an argument.
#[inline(always)] #[inline]
#[must_use]
pub(crate) fn get_arg_value( pub(crate) fn get_arg_value(
&self, &self,
scope: &mut Scope, scope: &mut Scope,
@ -1041,7 +1035,6 @@ 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,
@ -1107,7 +1100,7 @@ impl Engine {
return arg return arg
.as_immutable_string() .as_immutable_string()
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos)) .map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))
.and_then(|s| FnPtr::try_from(s)) .and_then(FnPtr::try_from)
.map(Into::<Dynamic>::into) .map(Into::<Dynamic>::into)
.map_err(|err| err.fill_position(arg_pos)); .map_err(|err| err.fill_position(arg_pos));
} }
@ -1293,7 +1286,6 @@ 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,
@ -1390,10 +1382,10 @@ impl Engine {
// Clone first argument if the function is not a method after-all // Clone first argument if the function is not a method after-all
if !func.map(|f| f.is_method()).unwrap_or(true) { if !func.map(|f| f.is_method()).unwrap_or(true) {
first_arg_value.map(|first| { if let Some(first) = first_arg_value {
*first = args[0].clone(); *first = args[0].clone();
args[0] = first; args[0] = first;
}); }
} }
match func { match func {

View File

@ -21,7 +21,7 @@ impl Hasher for StraightHasher {
fn finish(&self) -> u64 { fn finish(&self) -> u64 {
self.0 self.0
} }
#[inline(always)] #[inline]
fn write(&mut self, bytes: &[u8]) { fn write(&mut self, bytes: &[u8]) {
assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values"); assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values");
@ -62,10 +62,7 @@ pub fn get_hasher() -> ahash::AHasher {
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline] #[inline]
#[must_use] #[must_use]
pub fn calc_qualified_var_hash<'a>( pub fn calc_qualified_var_hash<'a>(modules: impl Iterator<Item = &'a str>, var_name: &str) -> u64 {
modules: impl Iterator<Item = &'a str>,
var_name: impl AsRef<str>,
) -> u64 {
let s = &mut get_hasher(); let s = &mut get_hasher();
// We always skip the first module // We always skip the first module
@ -75,7 +72,7 @@ pub fn calc_qualified_var_hash<'a>(
.skip(1) .skip(1)
.for_each(|m| m.hash(s)); .for_each(|m| m.hash(s));
len.hash(s); len.hash(s);
var_name.as_ref().hash(s); var_name.hash(s);
s.finish() s.finish()
} }
@ -92,7 +89,7 @@ pub fn calc_qualified_var_hash<'a>(
#[must_use] #[must_use]
pub fn calc_qualified_fn_hash<'a>( pub fn calc_qualified_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>, modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>, fn_name: &str,
num: usize, num: usize,
) -> u64 { ) -> u64 {
let s = &mut get_hasher(); let s = &mut get_hasher();
@ -104,7 +101,7 @@ pub fn calc_qualified_fn_hash<'a>(
.skip(1) .skip(1)
.for_each(|m| m.hash(s)); .for_each(|m| m.hash(s));
len.hash(s); len.hash(s);
fn_name.as_ref().hash(s); fn_name.hash(s);
num.hash(s); num.hash(s);
s.finish() s.finish()
} }
@ -115,7 +112,7 @@ pub fn calc_qualified_fn_hash<'a>(
/// Parameter types are passed in via [`TypeId`] values from an iterator. /// Parameter types are passed in via [`TypeId`] values from an iterator.
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 { pub fn calc_fn_hash(fn_name: &str, num: usize) -> u64 {
calc_qualified_fn_hash(empty(), fn_name, num) calc_qualified_fn_hash(empty(), fn_name, num)
} }
@ -127,7 +124,10 @@ pub fn calc_fn_hash(fn_name: impl AsRef<str>, num: usize) -> u64 {
pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 { pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
let s = &mut get_hasher(); let s = &mut get_hasher();
let mut len = 0; let mut len = 0;
params.inspect(|_| len += 1).for_each(|t| t.hash(s)); params.for_each(|t| {
len += 1;
t.hash(s);
});
len.hash(s); len.hash(s);
s.finish() s.finish()
} }

View File

@ -144,7 +144,6 @@ 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())
} }
@ -152,7 +151,6 @@ 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>)> {
@ -171,7 +169,6 @@ impl<'a> NativeCallContext<'a> {
} }
/// 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()
} }
@ -195,8 +192,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]
#[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>,
@ -249,7 +245,6 @@ pub fn shared_take_or_clone<T: Clone>(value: Shared<T>) -> T {
/// 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)
} }
@ -338,7 +333,6 @@ pub enum CallableFunction {
} }
impl fmt::Debug for CallableFunction { impl fmt::Debug for CallableFunction {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Pure(_) => write!(f, "NativePureFunction"), Self::Pure(_) => write!(f, "NativePureFunction"),
@ -353,7 +347,6 @@ impl fmt::Debug for CallableFunction {
} }
impl fmt::Display for CallableFunction { impl fmt::Display for CallableFunction {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Pure(_) => write!(f, "NativePureFunction"), Self::Pure(_) => write!(f, "NativePureFunction"),
@ -369,7 +362,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]
#[must_use] #[must_use]
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { match self {
@ -383,7 +376,7 @@ impl CallableFunction {
} }
} }
/// Is this a native Rust method function? /// Is this a native Rust method function?
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn is_method(&self) -> bool { pub fn is_method(&self) -> bool {
match self { match self {
@ -397,7 +390,7 @@ impl CallableFunction {
} }
} }
/// Is this an iterator function? /// Is this an iterator function?
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub const fn is_iter(&self) -> bool { pub const fn is_iter(&self) -> bool {
match self { match self {
@ -409,7 +402,7 @@ impl CallableFunction {
} }
} }
/// Is this a Rhai-scripted function? /// Is this a Rhai-scripted function?
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub const fn is_script(&self) -> bool { pub const fn is_script(&self) -> bool {
match self { match self {
@ -420,7 +413,7 @@ impl CallableFunction {
} }
} }
/// Is this a plugin function? /// Is this a plugin function?
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub const fn is_plugin_fn(&self) -> bool { pub const fn is_plugin_fn(&self) -> bool {
match self { match self {
@ -432,7 +425,7 @@ impl CallableFunction {
} }
} }
/// Is this a native Rust function? /// Is this a native Rust function?
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub const fn is_native(&self) -> bool { pub const fn is_native(&self) -> bool {
match self { match self {
@ -445,7 +438,7 @@ impl CallableFunction {
} }
} }
/// Get the access mode. /// Get the access mode.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn access(&self) -> FnAccess { pub fn access(&self) -> FnAccess {
match self { match self {
@ -457,7 +450,7 @@ impl CallableFunction {
} }
} }
/// Get a shared reference to a native Rust function. /// Get a shared reference to a native Rust function.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> { pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
match self { match self {
@ -472,7 +465,7 @@ impl CallableFunction {
/// ///
/// Not available under `no_function`. /// Not available under `no_function`.
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> { pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
match self { match self {
@ -481,7 +474,7 @@ impl CallableFunction {
} }
} }
/// Get a reference to an iterator function. /// Get a reference to an iterator function.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn get_iter_fn(&self) -> Option<IteratorFn> { pub fn get_iter_fn(&self) -> Option<IteratorFn> {
match self { match self {
@ -493,9 +486,9 @@ impl CallableFunction {
} }
} }
/// Get a shared reference to a plugin function. /// Get a shared reference to a plugin function.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn get_plugin_fn<'s>(&'s self) -> Option<&Shared<FnPlugin>> { pub fn get_plugin_fn(&self) -> Option<&Shared<FnPlugin>> {
match self { match self {
Self::Plugin(f) => Some(f), Self::Plugin(f) => Some(f),
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None, Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None,
@ -557,6 +550,6 @@ impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
impl From<Shared<FnPlugin>> for CallableFunction { impl From<Shared<FnPlugin>> for CallableFunction {
#[inline(always)] #[inline(always)]
fn from(func: Shared<FnPlugin>) -> Self { fn from(func: Shared<FnPlugin>) -> Self {
Self::Plugin(func.into()) Self::Plugin(func)
} }
} }

View File

@ -19,7 +19,6 @@ 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()
} }
@ -27,7 +26,7 @@ impl FnPtr {
#[inline(always)] #[inline(always)]
#[must_use] #[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, curry)
} }
/// Get the name of the function. /// Get the name of the function.
#[inline(always)] #[inline(always)]
@ -96,8 +95,7 @@ impl FnPtr {
/// This is to avoid unnecessarily cloning the arguments. /// This is to avoid unnecessarily cloning the arguments.
/// 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]
#[must_use]
pub fn call_dynamic( pub fn call_dynamic(
&self, &self,
ctx: &NativeCallContext, ctx: &NativeCallContext,
@ -127,7 +125,6 @@ impl FnPtr {
} }
impl fmt::Display for FnPtr { impl fmt::Display for FnPtr {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Fn({})", self.0) write!(f, "Fn({})", self.0)
} }
@ -136,7 +133,7 @@ impl fmt::Display for FnPtr {
impl TryFrom<Identifier> for FnPtr { impl TryFrom<Identifier> for FnPtr {
type Error = Box<EvalAltResult>; type Error = Box<EvalAltResult>;
#[inline(always)] #[inline]
fn try_from(value: Identifier) -> Result<Self, Self::Error> { fn try_from(value: Identifier) -> Result<Self, Self::Error> {
if is_valid_identifier(value.chars()) { if is_valid_identifier(value.chars()) {
Ok(Self(value, Default::default())) Ok(Self(value, Default::default()))

View File

@ -88,7 +88,7 @@ pub trait RegisterNativeFunction<Args, Result> {
fn return_type_name() -> &'static str; fn return_type_name() -> &'static str;
} }
#[inline(always)] #[inline]
#[must_use] #[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"))]

View File

@ -16,8 +16,8 @@ use std::{
/// 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]`<`[`SmartString`][smartstring::SmartString]`>`
/// (or [`Arc`][std::sync::Arc]`<`[`String`]`>` under the `sync` feature) /// (or [`Arc`][std::sync::Arc]`<`[`SmartString`][smartstring::SmartString]`>` under the `sync` feature)
/// so that it can be simply shared and not cloned. /// so that it can be simply shared and not cloned.
/// ///
/// # Example /// # Example
@ -255,12 +255,10 @@ impl Add<&str> for ImmutableString {
#[inline] #[inline]
fn add(mut self, rhs: &str) -> Self::Output { fn add(mut self, rhs: &str) -> Self::Output {
if rhs.is_empty() { if !rhs.is_empty() {
self
} else {
self.make_mut().push_str(rhs); self.make_mut().push_str(rhs);
self
} }
self
} }
} }
@ -322,7 +320,7 @@ impl Add<String> for &ImmutableString {
} }
impl AddAssign<String> for ImmutableString { impl AddAssign<String> for ImmutableString {
#[inline(always)] #[inline]
fn add_assign(&mut self, rhs: String) { fn add_assign(&mut self, rhs: String) {
if !rhs.is_empty() { if !rhs.is_empty() {
if self.is_empty() { if self.is_empty() {

View File

@ -112,10 +112,10 @@ impl FuncInfo {
/// # Note /// # Note
/// ///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline(always)] #[inline]
fn calc_native_fn_hash<'a>( fn calc_native_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>, modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>, fn_name: &str,
params: &[TypeId], params: &[TypeId],
) -> u64 { ) -> u64 {
let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len()); let hash_script = calc_qualified_fn_hash(modules, fn_name, params.len());
@ -324,7 +324,7 @@ impl Module {
/// let module = Module::new(); /// let module = Module::new();
/// assert!(module.is_empty()); /// assert!(module.is_empty());
/// ``` /// ```
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.functions.is_empty() self.functions.is_empty()
@ -437,7 +437,7 @@ impl Module {
/// module.set_var("answer", 42_i64); /// module.set_var("answer", 42_i64);
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42); /// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
/// ``` /// ```
#[inline(always)] #[inline]
pub fn set_var( pub fn set_var(
&mut self, &mut self,
name: impl Into<Identifier>, name: impl Into<Identifier>,
@ -457,7 +457,6 @@ 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()
@ -519,7 +518,7 @@ impl Module {
/// By taking a mutable reference, it is assumed that some sub-modules will be modified. /// By taking a mutable reference, it is assumed that some sub-modules will be modified.
/// 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]
#[must_use] #[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
@ -580,7 +579,7 @@ impl Module {
/// module.set_sub_module("question", sub_module); /// module.set_sub_module("question", sub_module);
/// assert!(module.get_sub_module("question").is_some()); /// assert!(module.get_sub_module("question").is_some());
/// ``` /// ```
#[inline(always)] #[inline]
pub fn set_sub_module( pub fn set_sub_module(
&mut self, &mut self,
name: impl Into<Identifier>, name: impl Into<Identifier>,
@ -624,7 +623,7 @@ impl Module {
/// The _last entry_ in the list should be the _return type_ of the function. /// The _last entry_ in the list should be the _return type_ of the function.
/// In other words, the number of entries should be one larger than the number of parameters. /// In other words, the number of entries should be one larger than the number of parameters.
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
#[inline(always)] #[inline]
pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self { pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self {
let param_names = arg_names let param_names = arg_names
.iter() .iter()
@ -641,7 +640,7 @@ impl Module {
/// Update the namespace of a registered function. /// Update the namespace of a registered function.
/// ///
/// 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]
pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self { pub fn update_fn_namespace(&mut self, hash_fn: u64, namespace: FnNamespace) -> &mut Self {
if let Some(f) = self.functions.get_mut(&hash_fn) { if let Some(f) = self.functions.get_mut(&hash_fn) {
f.namespace = namespace; f.namespace = namespace;
@ -652,7 +651,7 @@ impl Module {
} }
/// Remap type ID. /// Remap type ID.
#[inline(always)] #[inline]
#[must_use] #[must_use]
fn map_type(map: bool, type_id: TypeId) -> TypeId { fn map_type(map: bool, type_id: TypeId) -> TypeId {
if !map { if !map {
@ -706,7 +705,7 @@ impl Module {
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
param_names.shrink_to_fit(); param_names.shrink_to_fit();
let hash_fn = calc_native_fn_hash(empty(), &name, &param_types); let hash_fn = calc_native_fn_hash(empty(), name.as_ref(), &param_types);
self.functions.insert( self.functions.insert(
hash_fn, hash_fn,
@ -1207,7 +1206,7 @@ impl Module {
/// Merge another [`Module`] into this [`Module`]. /// Merge another [`Module`] into this [`Module`].
#[inline(always)] #[inline(always)]
pub fn merge(&mut self, other: &Self) -> &mut Self { pub fn merge(&mut self, other: &Self) -> &mut Self {
self.merge_filtered(other, &mut |_, _, _, _, _| true) self.merge_filtered(other, &|_, _, _, _, _| true)
} }
/// Merge another [`Module`] into this [`Module`] based on a filter predicate. /// Merge another [`Module`] into this [`Module`] based on a filter predicate.
@ -1292,14 +1291,12 @@ 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))
} }
@ -1307,7 +1304,6 @@ 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)
} }
@ -1322,7 +1318,6 @@ 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<
@ -1360,7 +1355,6 @@ 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)> {
@ -1419,7 +1413,6 @@ 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,
@ -1529,13 +1522,13 @@ impl Module {
// Index all variables // Index all variables
module.variables.iter().for_each(|(var_name, value)| { module.variables.iter().for_each(|(var_name, value)| {
let hash_var = crate::calc_qualified_var_hash(path.iter().map(|&v| v), var_name); let hash_var = crate::calc_qualified_var_hash(path.iter().copied(), var_name);
variables.insert(hash_var, value.clone()); variables.insert(hash_var, value.clone());
}); });
// Index type iterators // Index type iterators
module.type_iterators.iter().for_each(|(&type_id, func)| { module.type_iterators.iter().for_each(|(&type_id, func)| {
type_iterators.insert(type_id, func.clone()); type_iterators.insert(type_id, *func);
contains_indexed_global_functions = true; contains_indexed_global_functions = true;
}); });
@ -1611,10 +1604,10 @@ impl Module {
} }
/// Set a type iterator into the [`Module`]. /// Set a type iterator into the [`Module`].
#[inline(always)] #[inline]
pub fn set_iter(&mut self, type_id: TypeId, func: IteratorFn) -> &mut Self { pub fn set_iter(&mut self, type_id: TypeId, func: IteratorFn) -> &mut Self {
if self.indexed { if self.indexed {
self.all_type_iterators.insert(type_id, func.clone()); self.all_type_iterators.insert(type_id, func);
self.contains_indexed_global_functions = true; self.contains_indexed_global_functions = true;
} }
self.type_iterators.insert(type_id, func); self.type_iterators.insert(type_id, func);
@ -1678,7 +1671,6 @@ pub struct NamespaceRef {
} }
impl fmt::Debug for NamespaceRef { impl fmt::Debug for NamespaceRef {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(index) = self.index { if let Some(index) = self.index {
write!(f, "{} -> ", index)?; write!(f, "{} -> ", index)?;
@ -1696,7 +1688,6 @@ impl fmt::Debug for NamespaceRef {
} }
impl fmt::Display for NamespaceRef { impl fmt::Display for NamespaceRef {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for Ident { name, .. } in self.path.iter() { for Ident { name, .. } in self.path.iter() {
write!(f, "{}{}", name, Token::DoubleColon.syntax())?; write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
@ -1735,7 +1726,10 @@ impl NamespaceRef {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn new(&self) -> Self { pub fn new(&self) -> Self {
Default::default() Self {
index: None,
path: StaticVec::new(),
}
} }
/// Get the [`Scope`][crate::Scope] index offset. /// Get the [`Scope`][crate::Scope] index offset.
#[inline(always)] #[inline(always)]

View File

@ -77,16 +77,9 @@ 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].
#[inline(always)]
#[must_use]
pub fn into_iter(self) -> impl Iterator<Item = Box<dyn ModuleResolver>> {
self.0.into_iter()
}
/// Remove all [module resolvers][ModuleResolver]. /// Remove all [module resolvers][ModuleResolver].
#[inline(always)] #[inline(always)]
pub fn clear(&mut self) -> &mut Self { pub fn clear(&mut self) -> &mut Self {
@ -114,6 +107,16 @@ impl ModuleResolversCollection {
} }
} }
impl IntoIterator for ModuleResolversCollection {
type Item = Box<dyn ModuleResolver>;
type IntoIter = std::vec::IntoIter<Box<dyn ModuleResolver>>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl ModuleResolver for ModuleResolversCollection { impl ModuleResolver for ModuleResolversCollection {
fn resolve( fn resolve(
&self, &self,

View File

@ -200,7 +200,7 @@ impl FileModuleResolver {
} }
/// Is a particular path cached? /// Is a particular path cached?
#[inline(always)] #[inline]
#[must_use] #[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 {
@ -227,7 +227,7 @@ impl FileModuleResolver {
/// 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]
#[must_use] #[must_use]
pub fn clear_cache_for_path( pub fn clear_cache_for_path(
&mut self, &mut self,
@ -252,7 +252,6 @@ impl FileModuleResolver {
} }
/// Construct a full file path. /// Construct a full file path.
#[must_use] #[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,7 +23,6 @@ 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,

View File

@ -64,33 +64,28 @@ impl StaticModuleResolver {
} }
/// 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()
} }
/// Remove all [modules][Module]. /// Remove all [modules][Module].
#[inline(always)] #[inline(always)]

View File

@ -18,6 +18,9 @@ use std::{
mem, mem,
}; };
#[cfg(not(feature = "no_closure"))]
use crate::engine::KEYWORD_IS_SHARED;
/// Level of optimization performed. /// Level of optimization performed.
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)] #[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
pub enum OptimizationLevel { pub enum OptimizationLevel {
@ -118,42 +121,40 @@ impl<'a> OptimizerState<'a> {
} }
}) })
} }
} /// Call a registered function
#[inline(always)]
pub fn call_fn_with_constant_arguments(
&self,
fn_name: &str,
arg_values: &mut [Dynamic],
) -> Option<Dynamic> {
self.engine
.call_native_fn(
&Default::default(),
&mut Default::default(),
self.lib,
fn_name,
calc_fn_hash(fn_name, arg_values.len()),
&mut arg_values.iter_mut().collect::<StaticVec<_>>(),
false,
false,
Position::NONE,
)
.ok()
.map(|(v, _)| v)
}
// Has a system function a Rust-native override?
pub fn has_native_fn(&self, hash_script: u64, arg_types: &[TypeId]) -> bool {
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
let hash = combine_hashes(hash_script, hash_params);
// Has a system function a Rust-native override? // First check registered functions
fn has_native_fn(state: &OptimizerState, hash_script: u64, arg_types: &[TypeId]) -> bool { self.engine.global_namespace.contains_fn(hash)
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
let hash = combine_hashes(hash_script, hash_params);
// First check registered functions
state.engine.global_namespace.contains_fn(hash)
// Then check packages // Then check packages
|| state.engine.global_modules.iter().any(|m| m.contains_fn(hash)) || self.engine.global_modules.iter().any(|m| m.contains_fn(hash))
// Then check sub-modules // Then check sub-modules
|| state.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)) || self.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
} }
/// Call a registered function
fn call_fn_with_constant_arguments(
state: &OptimizerState,
fn_name: &str,
arg_values: &mut [Dynamic],
) -> Option<Dynamic> {
state
.engine
.call_native_fn(
&mut Default::default(),
&mut Default::default(),
state.lib,
fn_name,
calc_fn_hash(fn_name, arg_values.len()),
&mut arg_values.iter_mut().collect::<StaticVec<_>>(),
false,
false,
Position::NONE,
)
.ok()
.map(|(v, _)| v)
} }
/// Optimize a block of [statements][Stmt]. /// Optimize a block of [statements][Stmt].
@ -705,14 +706,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
} }
// lhs.rhs // lhs.rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(x, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) { Expr::Dot(x,_, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
// map.string // map.string
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => { (Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
let prop = p.2.0.as_str(); let prop = p.2.0.as_str();
// Map literal where everything is pure - promote the indexed item. // Map 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();
*expr = mem::take(&mut m.0).into_iter().find(|(x, _)| &x.name == prop) *expr = mem::take(&mut m.0).into_iter().find(|(x, _)| x.name == prop)
.map(|(_, mut expr)| { expr.set_position(*pos); expr }) .map(|(_, mut expr)| { expr.set_position(*pos); expr })
.unwrap_or_else(|| Expr::Unit(*pos)); .unwrap_or_else(|| Expr::Unit(*pos));
} }
@ -723,11 +724,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
} }
// ....lhs.rhs // ....lhs.rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); } Expr::Dot(x,_, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
// lhs[rhs] // lhs[rhs]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(x, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) { Expr::Index(x, _, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
// array[int] // array[int]
(Expr::Array(a, pos), Expr::IntegerConstant(i, _)) (Expr::Array(a, pos), Expr::IntegerConstant(i, _))
if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) => if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) =>
@ -791,7 +792,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
}, },
// ...[lhs][rhs] // ...[lhs][rhs]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); } Expr::Index(x, _, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
// `` // ``
Expr::InterpolatedString(x, pos) if x.is_empty() => { Expr::InterpolatedString(x, pos) if x.is_empty() => {
state.set_dirty(); state.set_dirty();
@ -944,32 +945,36 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
Expr::FnCall(x, pos) Expr::FnCall(x, pos)
if !x.is_qualified() // Non-qualified if !x.is_qualified() // Non-qualified
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations && state.optimization_level == OptimizationLevel::Simple // simple optimizations
&& x.args.len() == 2 // binary call
&& x.args.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted //&& !is_valid_identifier(x.name.chars()) // cannot be scripted
=> { => {
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e { let arg_values = &mut x.args.iter().map(|e| match e {
Expr::Stack(slot, _) => x.constants[*slot].clone(), Expr::Stack(slot, _) => x.constants[*slot].clone(),
_ => e.get_literal_value().unwrap() _ => e.get_literal_value().unwrap()
}).collect(); }).collect::<StaticVec<_>>();
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). let result = match x.name.as_str() {
if !has_native_fn(state, x.hashes.native, arg_types.as_ref()) { KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
if let Some(mut result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) #[cfg(not(feature = "no_closure"))]
.and_then(|f| { KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE),
let ctx = (state.engine, x.name.as_ref(), state.lib).into(); // Overloaded operators can override built-in.
let (first, second) = arg_values.split_first_mut().unwrap(); _ if x.args.len() == 2 && !state.has_native_fn(x.hashes.native, arg_types.as_ref()) => {
(f)(ctx, &mut [ first, &mut second[0] ]).ok() get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
}) .and_then(|f| {
.map(Expr::from) let ctx = (state.engine, x.name.as_ref(), state.lib).into();
{ let (first, second) = arg_values.split_first_mut().unwrap();
state.set_dirty(); (f)(ctx, &mut [ first, &mut second[0] ]).ok()
result.set_position(*pos); })
*expr = result;
return;
} }
_ => None
};
if let Some(result) = result {
state.set_dirty();
*expr = Expr::from_dynamic(result, *pos);
return;
} }
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
@ -997,30 +1002,21 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
let has_script_fn = false; let has_script_fn = false;
if !has_script_fn { if !has_script_fn {
let mut arg_values: StaticVec<_> = x.args.iter().map(|e| match e { let arg_values = &mut x.args.iter().map(|e| match e {
Expr::Stack(slot, _) => x.constants[*slot].clone(), Expr::Stack(slot, _) => x.constants[*slot].clone(),
_ => e.get_literal_value().unwrap() _ => e.get_literal_value().unwrap()
}).collect(); }).collect::<StaticVec<_>>();
// Save the typename of the first argument if it is `type_of()` let result = match x.name.as_str() {
// This is to avoid `call_args` being passed into the closure KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
let arg_for_type_of = if x.name == KEYWORD_TYPE_OF && arg_values.len() == 1 { #[cfg(not(feature = "no_closure"))]
state.engine.map_type_name(arg_values[0].type_name()) KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE),
} else { _ => state.call_fn_with_constant_arguments(x.name.as_ref(), arg_values)
""
}; };
if let Some(mut result) = call_fn_with_constant_arguments(&state, x.name.as_ref(), &mut arg_values) if let Some(result) = result {
.or_else(|| if !arg_for_type_of.is_empty() {
// Handle `type_of()`
Some(arg_for_type_of.to_string().into())
} else {
None
}).map(Expr::from)
{
state.set_dirty(); state.set_dirty();
result.set_position(*pos); *expr = Expr::from_dynamic(result, *pos);
*expr = result;
return; return;
} }
} }
@ -1043,9 +1039,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
// constant-name // constant-name
Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => { Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => {
// Replace constant with value // Replace constant with value
let pos = *pos; *expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos);
*expr = Expr::from(state.find_constant(&x.2).unwrap().clone());
expr.set_position(pos);
state.set_dirty(); state.set_dirty();
} }

View File

@ -155,7 +155,7 @@ mod array_functions {
len as usize len as usize
}; };
array[start..start + len].iter().cloned().collect() array[start..start + len].to_vec()
} }
#[rhai_fn(name = "extract")] #[rhai_fn(name = "extract")]
pub fn extract_tail(array: &mut Array, start: INT) -> Array { pub fn extract_tail(array: &mut Array, start: INT) -> Array {
@ -170,7 +170,7 @@ mod array_functions {
start as usize start as usize
}; };
array[start..].iter().cloned().collect() array[start..].to_vec()
} }
#[rhai_fn(name = "split")] #[rhai_fn(name = "split")]
pub fn split_at(array: &mut Array, start: INT) -> Array { pub fn split_at(array: &mut Array, start: INT) -> Array {

View File

@ -50,14 +50,11 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
let mut map = Map::new(); let mut map = Map::new();
if let Some(ns) = namespace { if let Some(ns) = namespace {
map.insert(dict.get("namespace").expect(DICT).clone().into(), ns.into()); map.insert(dict.get("namespace").expect(DICT).clone(), ns.into());
} }
map.insert(dict.get("name").expect(DICT).clone(), f.name.clone().into());
map.insert( map.insert(
dict.get("name").expect(DICT).clone().into(), dict.get("access").expect(DICT).clone(),
f.name.clone().into(),
);
map.insert(
dict.get("access").expect(DICT).clone().into(),
match f.access { match f.access {
FnAccess::Public => dict.get("public").expect(DICT).clone(), FnAccess::Public => dict.get("public").expect(DICT).clone(),
FnAccess::Private => dict.get("private").expect(DICT).clone(), FnAccess::Private => dict.get("private").expect(DICT).clone(),
@ -65,11 +62,11 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
.into(), .into(),
); );
map.insert( map.insert(
dict.get("is_anonymous").expect(DICT).clone().into(), dict.get("is_anonymous").expect(DICT).clone(),
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(), f.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
); );
map.insert( map.insert(
dict.get("params").expect(DICT).clone().into(), dict.get("params").expect(DICT).clone(),
f.params f.params
.iter() .iter()
.cloned() .cloned()
@ -78,7 +75,7 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
.into(), .into(),
); );
map.into() map
} }
// Intern strings // Intern strings

View File

@ -1,5 +1,6 @@
use crate::dynamic::Variant; use crate::dynamic::Variant;
use crate::{def_package, EvalAltResult, INT}; use crate::{def_package, EvalAltResult, INT};
use std::iter::{ExactSizeIterator, FusedIterator};
use std::ops::Range; use std::ops::Range;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -53,31 +54,19 @@ where
None None
} else if self.0 < self.1 { } else if self.0 < self.1 {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
let diff1 = if let Some(diff) = self.1.checked_sub(&self.0) { let diff1 = self.1.checked_sub(&self.0)?;
diff
} else {
return None;
};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
let diff1 = self.1 - self.0; let diff1 = self.1 - self.0;
let v = self.0; let v = self.0;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
let n = if let Some(num) = self.0.checked_add(&self.2) { let n = self.0.checked_add(&self.2)?;
num
} else {
return None;
};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
let n = self.0 + self.2; let n = self.0 + self.2;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
let diff2 = if let Some(diff) = self.1.checked_sub(&n) { let diff2 = self.1.checked_sub(&n)?;
diff
} else {
return None;
};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
let diff2 = self.1 - n; let diff2 = self.1 - n;
@ -89,31 +78,19 @@ where
} }
} else { } else {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
let diff1 = if let Some(diff) = self.0.checked_sub(&self.1) { let diff1 = self.0.checked_sub(&self.1)?;
diff
} else {
return None;
};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
let diff1 = self.0 - self.1; let diff1 = self.0 - self.1;
let v = self.0; let v = self.0;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
let n = if let Some(num) = self.0.checked_add(&self.2) { let n = self.0.checked_add(&self.2)?;
num
} else {
return None;
};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
let n = self.0 + self.2; let n = self.0 + self.2;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
let diff2 = if let Some(diff) = n.checked_sub(&self.1) { let diff2 = n.checked_sub(&self.1)?;
diff
} else {
return None;
};
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
let diff2 = n - self.1; let diff2 = n - self.1;
@ -127,6 +104,11 @@ where
} }
} }
impl<T> FusedIterator for StepRange<T> where
T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>
{
}
// Bit-field iterator with step // Bit-field iterator with step
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
struct BitRange(INT, INT, usize); struct BitRange(INT, INT, usize);
@ -190,6 +172,20 @@ impl Iterator for BitRange {
Some(r) Some(r)
} }
} }
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.2, Some(self.2))
}
}
impl FusedIterator for BitRange {}
impl ExactSizeIterator for BitRange {
#[inline(always)]
fn len(&self) -> usize {
self.2
}
} }
// String iterator over characters // String iterator over characters
@ -248,6 +244,21 @@ impl Iterator for CharsStream {
Some(ch) Some(ch)
} }
} }
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.0.len() - self.1;
(remaining, Some(remaining))
}
}
impl FusedIterator for CharsStream {}
impl ExactSizeIterator for CharsStream {
#[inline(always)]
fn len(&self) -> usize {
self.0.len() - self.1
}
} }
macro_rules! reg_range { macro_rules! reg_range {
@ -356,11 +367,11 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
} }
} }
impl std::iter::FusedIterator for StepFloatRange {} impl FusedIterator for StepFloatRange {}
lib.set_iterator::<StepFloatRange>(); lib.set_iterator::<StepFloatRange>();
let _hash = lib.set_native_fn("range", |from, to, step| StepFloatRange::new(from, to, step)); let _hash = lib.set_native_fn("range", StepFloatRange::new);
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"]); lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"]);
} }
@ -418,11 +429,11 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
} }
} }
impl std::iter::FusedIterator for StepDecimalRange {} impl FusedIterator for StepDecimalRange {}
lib.set_iterator::<StepDecimalRange>(); lib.set_iterator::<StepDecimalRange>();
let _hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step)); let _hash = lib.set_native_fn("range", StepDecimalRange::new);
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]); lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
} }
@ -430,7 +441,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
// Register string iterator // Register string iterator
lib.set_iterator::<CharsStream>(); lib.set_iterator::<CharsStream>();
let _hash = lib.set_native_fn("chars", |string, from,len| Ok(CharsStream::new(string, from, len))); let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len)));
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator<Item=char>"]); lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator<Item=char>"]);
@ -445,7 +456,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
// Register bit-field iterator // Register bit-field iterator
lib.set_iterator::<BitRange>(); lib.set_iterator::<BitRange>();
let _hash = lib.set_native_fn("bits", |value, from, len| BitRange::new(value, from, len)); let _hash = lib.set_native_fn("bits", BitRange::new);
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]); lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);

View File

@ -113,7 +113,7 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, {
mod int_functions { mod int_functions {
#[rhai_fn(name = "parse_int", return_raw)] #[rhai_fn(name = "parse_int", return_raw)]
pub fn parse_int_radix(s: &str, radix: INT) -> Result<INT, Box<EvalAltResult>> { pub fn parse_int_radix(s: &str, radix: INT) -> Result<INT, Box<EvalAltResult>> {
if radix < 2 || radix > 36 { if !(2..=36).contains(&radix) {
return EvalAltResult::ErrorArithmetic( return EvalAltResult::ErrorArithmetic(
format!("Invalid radix: '{}'", radix), format!("Invalid radix: '{}'", radix),
Position::NONE, Position::NONE,

View File

@ -79,8 +79,13 @@ macro_rules! def_package {
} }
} }
impl Default for $package {
fn default() -> Self {
Self::new()
}
}
impl $package { impl $package {
#[allow(dead_code)]
pub fn new() -> Self { pub fn new() -> Self {
let mut module = $root::Module::new(); let mut module = $root::Module::new();
<Self as $root::packages::Package>::init(&mut module); <Self as $root::packages::Package>::init(&mut module);

View File

@ -12,8 +12,8 @@ use crate::Array;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use crate::Map; use crate::Map;
pub const FUNC_TO_STRING: &'static str = "to_string"; pub const FUNC_TO_STRING: &str = "to_string";
pub const FUNC_TO_DEBUG: &'static str = "to_debug"; pub const FUNC_TO_DEBUG: &str = "to_debug";
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, { def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
combine_with_exported_module!(lib, "print_debug", print_debug_functions); combine_with_exported_module!(lib, "print_debug", print_debug_functions);
@ -22,7 +22,7 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
// Register print and debug // Register print and debug
#[inline(always)] #[inline]
pub fn print_with_func( pub fn print_with_func(
fn_name: &str, fn_name: &str,
ctx: &NativeCallContext, ctx: &NativeCallContext,
@ -106,7 +106,7 @@ mod print_debug_functions {
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString { pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
let len = array.len(); let len = array.len();
let mut result = String::with_capacity(len * 5 + 2); let mut result = String::with_capacity(len * 5 + 2);
result.push_str("["); result.push('[');
array.iter_mut().enumerate().for_each(|(i, x)| { array.iter_mut().enumerate().for_each(|(i, x)| {
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, x)); result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, x));
@ -115,7 +115,7 @@ mod print_debug_functions {
} }
}); });
result.push_str("]"); result.push(']');
result.into() result.into()
} }
} }
@ -144,7 +144,7 @@ mod print_debug_functions {
)); ));
}); });
result.push_str("}"); result.push('}');
result.into() result.into()
} }
} }

View File

@ -38,12 +38,11 @@ mod string_functions {
) -> ImmutableString { ) -> ImmutableString {
let mut s = print_with_func(FUNC_TO_STRING, &ctx, item); let mut s = print_with_func(FUNC_TO_STRING, &ctx, item);
if string.is_empty() { if !string.is_empty() {
s
} else {
s.make_mut().push_str(string); s.make_mut().push_str(string);
s.into()
} }
s
} }
#[rhai_fn(name = "+", name = "append")] #[rhai_fn(name = "+", name = "append")]
@ -240,7 +239,7 @@ mod string_functions {
let mut chars = StaticVec::with_capacity(string.len()); let mut chars = StaticVec::with_capacity(string.len());
let offset = if string.is_empty() || len <= 0 { let offset = if string.is_empty() || len <= 0 {
return ctx.engine().empty_string.clone().into(); return ctx.engine().empty_string.clone();
} else if start < 0 { } else if start < 0 {
if let Some(n) = start.checked_abs() { if let Some(n) = start.checked_abs() {
chars.extend(string.chars()); chars.extend(string.chars());
@ -253,7 +252,7 @@ mod string_functions {
0 0
} }
} else if start as usize >= string.chars().count() { } else if start as usize >= string.chars().count() {
return ctx.engine().empty_string.clone().into(); return ctx.engine().empty_string.clone();
} else { } else {
start as usize start as usize
}; };

View File

@ -8,7 +8,7 @@ use crate::custom_syntax::{
CustomSyntax, CUSTOM_SYNTAX_MARKER_BLOCK, CUSTOM_SYNTAX_MARKER_BOOL, CUSTOM_SYNTAX_MARKER_EXPR, CustomSyntax, CUSTOM_SYNTAX_MARKER_BLOCK, CUSTOM_SYNTAX_MARKER_BOOL, CUSTOM_SYNTAX_MARKER_EXPR,
CUSTOM_SYNTAX_MARKER_IDENT, CUSTOM_SYNTAX_MARKER_INT, CUSTOM_SYNTAX_MARKER_STRING, CUSTOM_SYNTAX_MARKER_IDENT, CUSTOM_SYNTAX_MARKER_INT, CUSTOM_SYNTAX_MARKER_STRING,
}; };
use crate::dynamic::{AccessMode, Union}; use crate::dynamic::AccessMode;
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
use crate::fn_hash::get_hasher; use crate::fn_hash::get_hasher;
use crate::module::NamespaceRef; use crate::module::NamespaceRef;
@ -17,8 +17,8 @@ use crate::token::{
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl, is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
}; };
use crate::{ use crate::{
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, Identifier, calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, Identifier, LexError,
LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -59,7 +59,7 @@ pub struct IdentifierBuilder(
impl IdentifierBuilder { impl IdentifierBuilder {
/// Get an identifier from a text string. /// Get an identifier from a text string.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier { pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
#[cfg(not(feature = "no_smartstring"))] #[cfg(not(feature = "no_smartstring"))]
@ -141,7 +141,7 @@ impl<'e> ParseState<'e> {
/// i.e. the top element of [`ParseState`]'s variables stack 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]
pub fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> { pub fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
let mut barrier = false; let mut barrier = false;
@ -242,8 +242,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]
#[must_use]
pub fn ensure_level_within_max_limit( pub fn ensure_level_within_max_limit(
&self, &self,
limit: Option<NonZeroUsize>, limit: Option<NonZeroUsize>,
@ -261,7 +260,7 @@ impl Expr {
/// Convert a [`Variable`][Expr::Variable] into a [`Property`][Expr::Property]. /// Convert a [`Variable`][Expr::Variable] into a [`Property`][Expr::Property].
/// All other variants are untouched. /// All other variants are untouched.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline(always)] #[inline]
#[must_use] #[must_use]
fn into_property(self, state: &mut ParseState) -> Self { fn into_property(self, state: &mut ParseState) -> Self {
match self { match self {
@ -412,7 +411,7 @@ fn parse_paren_expr(
// ( xxx ) // ( xxx )
(Token::RightParen, _) => Ok(expr), (Token::RightParen, _) => Ok(expr),
// ( <error> // ( <error>
(Token::LexError(err), pos) => return Err(err.into_err(pos)), (Token::LexError(err), pos) => Err(err.into_err(pos)),
// ( xxx ??? // ( xxx ???
(_, pos) => Err(PERR::MissingToken( (_, pos) => Err(PERR::MissingToken(
Token::RightParen.into(), Token::RightParen.into(),
@ -700,17 +699,19 @@ fn parse_index_chain(
// Indexing binds to right // Indexing binds to right
Ok(Expr::Index( Ok(Expr::Index(
BinaryExpr { lhs, rhs: idx_expr }.into(), BinaryExpr { lhs, rhs: idx_expr }.into(),
false,
prev_pos, prev_pos,
)) ))
} }
// Otherwise terminate the indexing chain // Otherwise terminate the indexing chain
_ => Ok(Expr::Index( _ => Ok(Expr::Index(
BinaryExpr { lhs, rhs: idx_expr }.into(), BinaryExpr { lhs, rhs: idx_expr }.into(),
true,
settings.pos, settings.pos,
)), )),
} }
} }
(Token::LexError(err), pos) => return Err(err.clone().into_err(*pos)), (Token::LexError(err), pos) => Err(err.clone().into_err(*pos)),
(_, pos) => Err(PERR::MissingToken( (_, pos) => Err(PERR::MissingToken(
Token::RightBracket.into(), Token::RightBracket.into(),
"for a matching [ in this index expression".into(), "for a matching [ in this index expression".into(),
@ -806,7 +807,7 @@ fn parse_map_literal(
settings.pos = eat_token(input, Token::MapStart); settings.pos = eat_token(input, Token::MapStart);
let mut map: StaticVec<(Ident, Expr)> = Default::default(); let mut map: StaticVec<(Ident, Expr)> = Default::default();
let mut template: BTreeMap<Identifier, Dynamic> = Default::default(); let mut template: BTreeMap<Identifier, crate::Dynamic> = Default::default();
loop { loop {
const MISSING_RBRACE: &str = "to end this object map literal"; const MISSING_RBRACE: &str = "to end this object map literal";
@ -878,7 +879,7 @@ fn parse_map_literal(
let expr = parse_expr(input, state, lib, settings.level_up())?; let expr = parse_expr(input, state, lib, settings.level_up())?;
let name = state.get_identifier(name); let name = state.get_identifier(name);
template.insert(name.clone().into(), Default::default()); template.insert(name.clone(), Default::default());
map.push((Ident { name, pos }, expr)); map.push((Ident { name, pos }, expr));
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
@ -1085,7 +1086,7 @@ fn parse_primary(
}, },
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Token::FloatConstant(x) => { Token::FloatConstant(x) => {
let x = (*x).into(); let x = *x;
input.next().expect(NEVER_ENDS); input.next().expect(NEVER_ENDS);
Expr::FloatConstant(x, settings.pos) Expr::FloatConstant(x, settings.pos)
} }
@ -1396,7 +1397,7 @@ fn parse_primary(
let rhs = parse_primary(input, state, lib, settings.level_up())?; let rhs = parse_primary(input, state, lib, settings.level_up())?;
make_dot_expr(state, expr, rhs, tail_pos)? make_dot_expr(state, expr, false, rhs, tail_pos)?
} }
// Unknown postfix operator // Unknown postfix operator
(expr, token) => unreachable!( (expr, token) => unreachable!(
@ -1408,23 +1409,26 @@ fn parse_primary(
} }
// Cache the hash key for namespace-qualified variables // Cache the hash key for namespace-qualified variables
match root_expr { let namespaced_variable = match root_expr {
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), 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 x.lhs { Expr::Index(ref mut x, _, _) | Expr::Dot(ref mut x, _, _) => match x.lhs {
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
_ => None, _ => None,
}, },
_ => None, _ => None,
} };
.map(|x| match x {
(_, Some((namespace, hash)), name) => {
*hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name);
#[cfg(not(feature = "no_module"))] if let Some(x) = namespaced_variable {
namespace.set_index(state.find_module(&namespace[0].name)); match x {
(_, Some((namespace, hash)), name) => {
*hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name);
#[cfg(not(feature = "no_module"))]
namespace.set_index(state.find_module(&namespace[0].name));
}
_ => unreachable!("expecting namespace-qualified variable access"),
} }
_ => unreachable!("expecting namespace-qualified variable access"), }
});
// Make sure identifiers are valid // Make sure identifiers are valid
Ok(root_expr) Ok(root_expr)
@ -1529,7 +1533,7 @@ fn parse_unary(
} }
/// Make an assignment statement. /// Make an assignment statement.
fn make_assignment_stmt<'a>( fn make_assignment_stmt(
op: Option<Token>, op: Option<Token>,
state: &mut ParseState, state: &mut ParseState,
lhs: Expr, lhs: Expr,
@ -1539,13 +1543,13 @@ fn make_assignment_stmt<'a>(
#[must_use] #[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 {
Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))),
ref e => Some(e.position()), ref e => Some(e.position()),
}, },
Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs { Expr::Index(x, _, _) | Expr::Dot(x, _, _) => match x.lhs {
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
_ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), _ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))),
}, },
Expr::Property(_) if parent_is_dot => None, Expr::Property(_) if parent_is_dot => None,
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
@ -1554,7 +1558,7 @@ fn make_assignment_stmt<'a>(
} }
} }
let op_info = op.map(|v| OpAssignment::new(v)); let op_info = op.map(OpAssignment::new);
match lhs { match lhs {
// const_expr = rhs // const_expr = rhs
@ -1587,8 +1591,8 @@ fn make_assignment_stmt<'a>(
} }
} }
// xxx[???]... = rhs, xxx.prop... = rhs // xxx[???]... = rhs, xxx.prop... = rhs
Expr::Index(ref x, _) | Expr::Dot(ref 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(_, _, _) => {
@ -1645,41 +1649,38 @@ fn parse_op_assignment_stmt(
fn make_dot_expr( fn make_dot_expr(
state: &mut ParseState, state: &mut ParseState,
lhs: Expr, lhs: Expr,
terminate_chaining: bool,
rhs: Expr, rhs: Expr,
op_pos: Position, op_pos: Position,
) -> Result<Expr, ParseError> { ) -> Result<Expr, ParseError> {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
// idx_lhs[idx_expr].rhs // lhs[???]...[???].rhs
// Attach dot chain to the bottom level of indexing chain (Expr::Index(mut x, false, pos), rhs) if !terminate_chaining => {
(Expr::Index(mut x, pos), rhs) => { // Attach dot chain to the bottom level of indexing chain
x.rhs = make_dot_expr(state, x.rhs, rhs, op_pos)?; x.rhs = make_dot_expr(state, x.rhs, false, rhs, op_pos)?;
Expr::Index(x, pos) Expr::Index(x, false, pos)
}
// lhs[idx_expr].rhs
(Expr::Index(mut x, _, pos), rhs) => {
x.rhs = make_dot_expr(state, x.rhs, true, rhs, op_pos)?;
Expr::Index(x, false, pos)
} }
// lhs.id // lhs.id
(lhs, Expr::Variable(_, var_pos, x)) if x.1.is_none() => { (lhs, var_expr @ Expr::Variable(_, _, _)) if var_expr.is_variable_access(true) => {
let ident = x.2; let rhs = var_expr.into_property(state);
let getter = state.get_identifier(crate::engine::make_getter(&ident)); Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
let hash_get = calc_fn_hash(&getter, 1);
let setter = state.get_identifier(crate::engine::make_setter(&ident));
let hash_set = calc_fn_hash(&setter, 2);
let rhs = Expr::Property(Box::new((
(getter, hash_get),
(setter, hash_set),
(state.get_identifier(ident).into(), var_pos),
)));
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
} }
// lhs.module::id - syntax error // lhs.module::id - syntax error
(_, Expr::Variable(_, _, x)) if x.1.is_some() => { (_, Expr::Variable(_, _, x)) => {
return Err(PERR::PropertyExpected return Err(PERR::PropertyExpected
.into_err(x.1.expect("never fails because the namespace is `Some`").0[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(_)) => Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), op_pos), (lhs, prop @ Expr::Property(_)) => {
Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), false, op_pos)
}
// lhs.dot_lhs.dot_rhs // lhs.dot_lhs.dot_rhs
(lhs, Expr::Dot(x, pos)) => match x.lhs { (lhs, Expr::Dot(x, _, pos)) => match x.lhs {
Expr::Variable(_, _, _) | Expr::Property(_) => { Expr::Variable(_, _, _) | Expr::Property(_) => {
let rhs = Expr::Dot( let rhs = Expr::Dot(
BinaryExpr { BinaryExpr {
@ -1687,9 +1688,10 @@ fn make_dot_expr(
rhs: x.rhs, rhs: x.rhs,
} }
.into(), .into(),
false,
pos, pos,
); );
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
} }
Expr::FnCall(mut func, func_pos) => { Expr::FnCall(mut func, func_pos) => {
// Recalculate hash // Recalculate hash
@ -1704,23 +1706,25 @@ fn make_dot_expr(
rhs: x.rhs, rhs: x.rhs,
} }
.into(), .into(),
false,
pos, pos,
); );
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
} }
_ => unreachable!("invalid dot expression: {:?}", x.lhs), _ => unreachable!("invalid dot expression: {:?}", x.lhs),
}, },
// lhs.idx_lhs[idx_rhs] // lhs.idx_lhs[idx_rhs]
(lhs, Expr::Index(x, pos)) => { (lhs, Expr::Index(x, term, pos)) => {
let rhs = Expr::Index( let rhs = Expr::Index(
BinaryExpr { BinaryExpr {
lhs: x.lhs.into_property(state), lhs: x.lhs.into_property(state),
rhs: x.rhs, rhs: x.rhs,
} }
.into(), .into(),
term,
pos, pos,
); );
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
} }
// lhs.nnn::func(...) // lhs.nnn::func(...)
(_, Expr::FnCall(x, _)) if x.is_qualified() => { (_, Expr::FnCall(x, _)) if x.is_qualified() => {
@ -1756,7 +1760,7 @@ fn make_dot_expr(
calc_fn_hash(&func.name, func.args.len() + 1), calc_fn_hash(&func.name, func.args.len() + 1),
); );
let rhs = Expr::FnCall(func, func_pos); let rhs = Expr::FnCall(func, func_pos);
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos) Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
} }
// lhs.rhs // lhs.rhs
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())), (_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
@ -2715,7 +2719,7 @@ fn parse_stmt(
is_function_scope: true, is_function_scope: true,
is_breakable: false, is_breakable: false,
level: 0, level: 0,
pos: pos, pos,
}; };
let func = parse_fn( let func = parse_fn(
@ -3033,40 +3037,38 @@ fn parse_anon_fn(
let mut params_list: 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 && !match_token(input, Token::Pipe).0 {
if !match_token(input, Token::Pipe).0 { loop {
loop { 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_list.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);
state.stack.push((s.clone(), AccessMode::ReadWrite));
params_list.push(s)
}
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => {
return Err(PERR::MissingToken(
Token::Pipe.into(),
"to close the parameters list of anonymous function".into(),
)
.into_err(pos))
} }
let s = state.get_identifier(s);
state.stack.push((s.clone(), AccessMode::ReadWrite));
params_list.push(s)
} }
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => {
return Err(PERR::MissingToken(
Token::Pipe.into(),
"to close the parameters list of anonymous function".into(),
)
.into_err(pos))
}
}
match input.next().expect(NEVER_ENDS) { match input.next().expect(NEVER_ENDS) {
(Token::Pipe, _) => break, (Token::Pipe, _) => break,
(Token::Comma, _) => (), (Token::Comma, _) => (),
(Token::LexError(err), pos) => return Err(err.into_err(pos)), (Token::LexError(err), pos) => return Err(err.into_err(pos)),
(_, pos) => { (_, pos) => {
return Err(PERR::MissingToken( return Err(PERR::MissingToken(
Token::Comma.into(), Token::Comma.into(),
"to separate the parameters of anonymous function".into(), "to separate the parameters of anonymous function".into(),
) )
.into_err(pos)) .into_err(pos))
}
} }
} }
} }
@ -3119,7 +3121,7 @@ fn parse_anon_fn(
comments: Default::default(), comments: Default::default(),
}; };
let fn_ptr = crate::FnPtr::new_unchecked(fn_name.into(), Default::default()); let fn_ptr = crate::FnPtr::new_unchecked(fn_name, Default::default());
let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos); let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos);
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
@ -3130,7 +3132,6 @@ fn parse_anon_fn(
impl Engine { impl Engine {
/// Parse a global level expression. /// 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,
@ -3172,7 +3173,6 @@ 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,
@ -3234,7 +3234,6 @@ 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,
@ -3250,33 +3249,3 @@ impl Engine {
) )
} }
} }
impl From<Dynamic> for Expr {
fn from(value: Dynamic) -> Self {
match value.0 {
#[cfg(not(feature = "no_float"))]
Union::Float(value, _, _) => Self::FloatConstant(value, Position::NONE),
#[cfg(feature = "decimal")]
Union::Decimal(value, _, _) => {
Self::DynamicConstant(Box::new((*value).into()), Position::NONE)
}
Union::Unit(_, _, _) => Self::Unit(Position::NONE),
Union::Int(value, _, _) => Self::IntegerConstant(value, Position::NONE),
Union::Char(value, _, _) => Self::CharConstant(value, Position::NONE),
Union::Str(value, _, _) => Self::StringConstant(value, Position::NONE),
Union::Bool(value, _, _) => Self::BoolConstant(value, Position::NONE),
#[cfg(not(feature = "no_index"))]
Union::Array(array, _, _) => {
Self::DynamicConstant(Box::new((*array).into()), Position::NONE)
}
#[cfg(not(feature = "no_object"))]
Union::Map(map, _, _) => Self::DynamicConstant(Box::new((*map).into()), Position::NONE),
_ => Self::DynamicConstant(Box::new(value.into()), Position::NONE),
}
}
}

View File

@ -22,7 +22,6 @@ 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?

View File

@ -49,7 +49,7 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether. // look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
// //
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed. // Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
#[derive(Debug, Clone, Hash)] #[derive(Debug, Clone, Hash, Default)]
pub struct Scope<'a> { pub struct Scope<'a> {
/// Current value of the entry. /// Current value of the entry.
values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
@ -59,13 +59,6 @@ pub struct Scope<'a> {
>, >,
} }
impl Default for Scope<'_> {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl<'a> IntoIterator for Scope<'a> { impl<'a> IntoIterator for Scope<'a> {
type Item = (Cow<'a, str>, Dynamic); type Item = (Cow<'a, str>, Dynamic);
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>; type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
@ -97,10 +90,7 @@ impl<'a> Scope<'a> {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn new() -> Self { pub fn new() -> Self {
Self { Default::default()
values: Default::default(),
names: Default::default(),
}
} }
/// Empty the [`Scope`]. /// Empty the [`Scope`].
/// ///
@ -246,7 +236,7 @@ impl<'a> Scope<'a> {
self.push_dynamic_value(name, AccessMode::ReadOnly, value) self.push_dynamic_value(name, AccessMode::ReadOnly, value)
} }
/// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`]. /// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`].
#[inline(always)] #[inline]
pub(crate) fn push_dynamic_value( pub(crate) fn push_dynamic_value(
&mut self, &mut self,
name: impl Into<Cow<'a, str>>, name: impl Into<Cow<'a, str>>,
@ -255,7 +245,7 @@ impl<'a> Scope<'a> {
) -> &mut Self { ) -> &mut Self {
self.names.push((name.into(), Default::default())); self.names.push((name.into(), Default::default()));
value.set_access_mode(access); value.set_access_mode(access);
self.values.push(value.into()); self.values.push(value);
self self
} }
/// Truncate (rewind) the [`Scope`] to a previous size. /// Truncate (rewind) the [`Scope`] to a previous size.
@ -371,7 +361,7 @@ impl<'a> Scope<'a> {
/// my_scope.set_value("x", 0_i64); /// my_scope.set_value("x", 0_i64);
/// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0); /// assert_eq!(my_scope.get_value::<i64>("x").unwrap(), 0);
/// ``` /// ```
#[inline(always)] #[inline]
pub fn set_value(&mut self, name: &'a str, value: impl Variant + Clone) -> &mut Self { pub fn set_value(&mut self, name: &'a str, value: impl Variant + Clone) -> &mut Self {
match self.get_index(name) { match self.get_index(name) {
None => { None => {
@ -408,6 +398,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);
/// ``` /// ```
#[inline(always)]
#[must_use] #[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)
@ -434,7 +425,7 @@ impl<'a> Scope<'a> {
/// ///
/// Panics if the index is out of bounds. /// Panics if the index is out of bounds.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline]
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self { pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
let (_, aliases) = self let (_, aliases) = self
.names .names
@ -456,7 +447,7 @@ impl<'a> Scope<'a> {
#[inline(always)] #[inline(always)]
#[must_use] #[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::new();
self.names self.names
.iter() .iter()
@ -474,7 +465,6 @@ 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>)> {
@ -511,7 +501,6 @@ 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()))
@ -519,7 +508,6 @@ 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

@ -1,6 +1,6 @@
//! Implementations of [`serde::Serialize`]. //! Implementations of [`serde::Serialize`].
use crate::dynamic::{Union, Variant}; use crate::dynamic::Union;
use crate::{Dynamic, ImmutableString}; use crate::{Dynamic, ImmutableString};
use serde::ser::{Serialize, Serializer}; use serde::ser::{Serialize, Serializer};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -9,6 +9,9 @@ use std::prelude::v1::*;
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
#[cfg(not(feature = "no_std"))]
use crate::dynamic::Variant;
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 {

View File

@ -83,53 +83,73 @@ impl Position {
/// Create a new [`Position`]. /// Create a new [`Position`].
/// ///
/// `line` must not be zero. /// `line` must not be zero.
/// If [`Position`] is zero, then it is at the beginning of a line. ///
/// If `position` is zero, then it is at the beginning of a line.
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if `line` is zero. /// Panics if `line` is zero.
#[inline(always)] #[inline(always)]
#[must_use] #[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");
let _pos = position;
Self { Self {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
line, line,
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
pos: _position, pos: _pos,
} }
} }
/// Create a new [`Position`].
///
/// If `line` is zero, then [`None`] is returned.
///
/// If `position` is zero, then it is at the beginning of a line.
#[inline(always)]
#[must_use]
pub const fn new_const(line: u16, position: u16) -> Option<Self> {
if line == 0 {
return None;
}
let _pos = position;
Some(Self {
#[cfg(not(feature = "no_position"))]
line,
#[cfg(not(feature = "no_position"))]
pos: _pos,
})
}
/// 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] #[must_use]
pub fn line(self) -> Option<usize> { pub const fn line(self) -> Option<usize> {
if self.is_none() { #[cfg(not(feature = "no_position"))]
return if self.is_none() {
None None
} else { } else {
#[cfg(not(feature = "no_position"))] Some(self.line as usize)
return Some(self.line as usize); };
#[cfg(feature = "no_position")]
unreachable!("there is no Position"); #[cfg(feature = "no_position")]
} return None;
} }
/// 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] #[must_use]
pub fn position(self) -> Option<usize> { pub const fn position(self) -> Option<usize> {
if self.is_none() { #[cfg(not(feature = "no_position"))]
return if self.is_none() || self.pos == 0 {
None None
} else { } else {
#[cfg(not(feature = "no_position"))] Some(self.pos as usize)
return if self.pos == 0 { };
None
} else {
Some(self.pos as usize)
};
#[cfg(feature = "no_position")] #[cfg(feature = "no_position")]
unreachable!("there is no Position"); return None;
}
} }
/// Advance by one character position. /// Advance by one character position.
#[inline(always)] #[inline(always)]
@ -192,7 +212,6 @@ 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() {
@ -211,7 +230,6 @@ impl Default for Position {
} }
impl fmt::Display for Position { impl fmt::Display for Position {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_none() { if self.is_none() {
write!(f, "none")?; write!(f, "none")?;
@ -227,7 +245,6 @@ impl fmt::Display for Position {
} }
impl fmt::Debug for Position { impl fmt::Debug for Position {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
write!(f, "{}:{}", self.line, self.pos)?; write!(f, "{}:{}", self.line, self.pos)?;
@ -586,20 +603,20 @@ impl Token {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn is_op_assignment(&self) -> bool { pub const fn is_op_assignment(&self) -> bool {
match self { matches!(
self,
Self::PlusAssign Self::PlusAssign
| Self::MinusAssign | Self::MinusAssign
| Self::MultiplyAssign | Self::MultiplyAssign
| Self::DivideAssign | Self::DivideAssign
| Self::LeftShiftAssign | Self::LeftShiftAssign
| Self::RightShiftAssign | Self::RightShiftAssign
| Self::ModuloAssign | Self::ModuloAssign
| Self::PowerOfAssign | Self::PowerOfAssign
| Self::AndAssign | Self::AndAssign
| Self::OrAssign | Self::OrAssign
| Self::XOrAssign => true, | Self::XOrAssign
_ => false, )
}
} }
/// 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.
@ -625,20 +642,20 @@ impl Token {
#[inline] #[inline]
#[must_use] #[must_use]
pub const fn has_op_assignment(&self) -> bool { pub const fn has_op_assignment(&self) -> bool {
match self { matches!(
self,
Self::Plus Self::Plus
| Self::Minus | Self::Minus
| Self::Multiply | Self::Multiply
| Self::Divide | Self::Divide
| Self::LeftShift | Self::LeftShift
| Self::RightShift | Self::RightShift
| Self::Modulo | Self::Modulo
| Self::PowerOf | Self::PowerOf
| Self::Ampersand | Self::Ampersand
| Self::Pipe | Self::Pipe
| Self::XOr => true, | Self::XOr
_ => false, )
}
} }
/// Get the corresponding op-assignment operator of the token. /// Get the corresponding op-assignment operator of the token.
@ -772,12 +789,7 @@ impl Token {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_eof(&self) -> bool { pub const fn is_eof(&self) -> bool {
use Token::*; matches!(self, Self::EOF)
match self {
EOF => true,
_ => false,
}
} }
// If another operator is after these, it's probably an unary operator // If another operator is after these, it's probably an unary operator
@ -917,6 +929,7 @@ impl Token {
} }
/// Is this token an active standard keyword? /// Is this token an active standard keyword?
#[inline]
#[must_use] #[must_use]
pub const fn is_keyword(&self) -> bool { pub const fn is_keyword(&self) -> bool {
use Token::*; use Token::*;
@ -939,15 +952,12 @@ impl Token {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_reserved(&self) -> bool { pub const fn is_reserved(&self) -> bool {
match self { matches!(self, Self::Reserved(_))
Self::Reserved(_) => true,
_ => false,
}
} }
/// 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] #[inline]
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),
@ -959,10 +969,7 @@ impl Token {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_custom(&self) -> bool { pub const fn is_custom(&self) -> bool {
match self { matches!(self, Self::Custom(_))
Self::Custom(_) => true,
_ => false,
}
} }
} }
@ -1045,7 +1052,6 @@ 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,
@ -1269,20 +1275,26 @@ fn scan_block_comment(
while let Some(c) = stream.get_next() { while let Some(c) = stream.get_next() {
pos.advance(); pos.advance();
comment.as_mut().map(|comment| comment.push(c)); if let Some(comment) = comment.as_mut() {
comment.push(c);
}
match c { match c {
'/' => { '/' => {
stream.peek_next().filter(|&c2| c2 == '*').map(|c2| { stream.peek_next().filter(|&c2| c2 == '*').map(|c2| {
eat_next(stream, pos); eat_next(stream, pos);
comment.as_mut().map(|comment| comment.push(c2)); if let Some(comment) = comment.as_mut() {
comment.push(c2);
}
level += 1; level += 1;
}); });
} }
'*' => { '*' => {
stream.peek_next().filter(|&c2| c2 == '/').map(|c2| { stream.peek_next().filter(|&c2| c2 == '/').map(|c2| {
eat_next(stream, pos); eat_next(stream, pos);
comment.as_mut().map(|comment| comment.push(c2)); if let Some(comment) = comment.as_mut() {
comment.push(c2);
}
level -= 1; level -= 1;
}); });
} }
@ -1304,7 +1316,7 @@ fn scan_block_comment(
/// # Volatile API /// # Volatile API
/// ///
/// This function is volatile and may change. /// This function is volatile and may change.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn get_next_token( pub fn get_next_token(
stream: &mut impl InputStream, stream: &mut impl InputStream,
@ -1324,21 +1336,13 @@ pub fn get_next_token(
/// Test if the given character is a hex character. /// Test if the given character is a hex character.
#[inline(always)] #[inline(always)]
fn is_hex_digit(c: char) -> bool { fn is_hex_digit(c: char) -> bool {
match c { matches!(c, 'a'..='f' | 'A'..='F' | '0'..='9')
'a'..='f' => true,
'A'..='F' => true,
'0'..='9' => true,
_ => false,
}
} }
/// Test if the given character is a numeric digit. /// Test if the given character is a numeric digit.
#[inline(always)] #[inline(always)]
fn is_numeric_digit(c: char) -> bool { fn is_numeric_digit(c: char) -> bool {
match c { matches!(c, '0'..='9')
'0'..='9' => true,
_ => false,
}
} }
/// Test if the comment block is a doc-comment. /// Test if the comment block is a doc-comment.
@ -1455,7 +1459,7 @@ fn get_next_token_inner(
break; break;
} }
// symbol after period - probably a float // symbol after period - probably a float
ch @ _ if !is_id_first_alphabetic(ch) => { ch if !is_id_first_alphabetic(ch) => {
result.push(next_char); result.push(next_char);
pos.advance(); pos.advance();
result.push('0'); result.push('0');
@ -1750,7 +1754,9 @@ fn get_next_token_inner(
pos.new_line(); pos.new_line();
break; break;
} }
comment.as_mut().map(|comment| comment.push(c)); if let Some(comment) = comment.as_mut() {
comment.push(c);
}
pos.advance(); pos.advance();
} }
@ -1982,11 +1988,11 @@ fn get_identifier(
)); ));
} }
return Some((Token::Identifier(identifier), start_pos)); Some((Token::Identifier(identifier), start_pos))
} }
/// Is this keyword allowed as a function? /// Is this keyword allowed as a function?
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub fn is_keyword_function(name: &str) -> bool { pub fn is_keyword_function(name: &str) -> bool {
match name { match name {
@ -2062,7 +2068,7 @@ pub struct MultiInputsStream<'a> {
} }
impl InputStream for MultiInputsStream<'_> { impl InputStream for MultiInputsStream<'_> {
#[inline(always)] #[inline]
fn unget(&mut self, ch: char) { fn unget(&mut self, ch: char) {
if self.buf.is_some() { if self.buf.is_some() {
panic!("cannot unget two characters in a row"); panic!("cannot unget two characters in a row");
@ -2252,7 +2258,7 @@ impl Engine {
self.lex_raw(input, Some(map)) self.lex_raw(input, Some(map))
} }
/// Tokenize an input text stream with an optional mapping function. /// Tokenize an input text stream with an optional mapping function.
#[inline(always)] #[inline]
#[must_use] #[must_use]
pub(crate) fn lex_raw<'a>( pub(crate) fn lex_raw<'a>(
&'a self, &'a self,

View File

@ -9,7 +9,6 @@ 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
@ -27,7 +26,6 @@ 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>() {

View File

@ -53,6 +53,18 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(-999, 4); y")?), convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(-999, 4); y")?),
[4, 1, 2, 3] [4, 1, 2, 3]
); );
assert_eq!(
engine.eval::<INT>("let y = [1, 2, 3]; let z = [42]; y[z.len]")?,
2
);
assert_eq!(
engine.eval::<INT>("let y = [1, 2, [3, 4, 5, 6]]; let z = [42]; y[2][z.len]")?,
4
);
assert_eq!(
engine.eval::<INT>("let y = [1, 2, 3]; let z = [2]; y[z[0]]")?,
3
);
assert_eq!( assert_eq!(
convert_to_vec::<INT>(engine.eval( convert_to_vec::<INT>(engine.eval(