Merge pull request #519 from schungx/master

Consolidate most unsafe casts.
This commit is contained in:
Stephen Chung 2022-02-08 22:53:09 +08:00 committed by GitHub
commit ea9b6b0e22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 1547 additions and 1693 deletions

View File

@ -14,6 +14,7 @@ Bug fixes
* Off-by-one error in character positions after a comment line is now fixed. * Off-by-one error in character positions after a comment line is now fixed.
* Globally-defined constants are now encapsulated correctly inside a loaded module and no longer spill across call boundaries. * Globally-defined constants are now encapsulated correctly inside a loaded module and no longer spill across call boundaries.
* Type names display is fixed. * Type names display is fixed.
* Exceptions thrown inside function calls now unwrap correctly when `catch`-ed.
Script-breaking changes Script-breaking changes
----------------------- -----------------------
@ -40,6 +41,7 @@ Enhancements
* `AST` position display is improved: * `AST` position display is improved:
* `Expr::start_position` is added to give the beginning of the expression (not the operator's position). * `Expr::start_position` is added to give the beginning of the expression (not the operator's position).
* `StmtBlock` and `Stmt::Block` now keep the position of the closing `}` as well. * `StmtBlock` and `Stmt::Block` now keep the position of the closing `}` as well.
* `EvalAltResult::unwrap_inner` is added to access the base error inside multiple layers of wrappings (e.g. `EvalAltResult::ErrorInFunction`).
REPL tool changes REPL tool changes
----------------- -----------------
@ -53,15 +55,16 @@ The REPL bin tool, `rhai-rpl`, has been enhanced.
### Line editor ### Line editor
* `rhai-repl` now uses [`rustyline`](https://crates.io/crates/rustyline) as a line editor with history. * `rhai-repl` now uses a modified version of [`rustyline`](https://crates.io/crates/rustyline) as a line editor with history.
* Shift-Enter can now be used to enter multiple lines without having to attach the `\` continuation character the end of each line. * Ctrl-Enter can now be used to enter multiple lines without having to attach the `\` continuation character the end of each line.
* Bracketed paste is supported, even on Windows (version 10 or above), so pasting code directly into `rhai-repl` is made much more convenient.
### New commands ### New commands
* `strict` to turn on/off _Strict Variables Mode_. * `strict` to turn on/off _Strict Variables Mode_.
* `optimize` to turn on/off script optimization. * `optimize` to turn on/off script optimization.
* `history` to print lines history. * `history` to print lines history.
* `!!`, `!`_num_ and `!`_text_ to recall a history line. * `!!`, `!`_num_, `!`_text_ and `!?`_text_ to recall a history line.
* `keys` to print all key bindings. * `keys` to print all key bindings.

View File

@ -29,7 +29,9 @@ serde = { version = "1.0", default-features = false, features = ["derive", "allo
serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true } serde_json = { version = "1.0", default-features = false, features = ["alloc"], optional = true }
unicode-xid = { version = "0.2", default-features = false, optional = true } unicode-xid = { version = "0.2", default-features = false, optional = true }
rust_decimal = { version = "1.16", default-features = false, features = ["maths"], optional = true } rust_decimal = { version = "1.16", default-features = false, features = ["maths"], optional = true }
rustyline = { version = "9", optional = true } # notice that a custom modified version of `rustyline` is used which supports bracketed paste on Windows
# this can be moved to the official version when bracketed paste is added
rustyline = { version = "9", optional = true, git = "https://github.com/schungx/rustyline", branch = "bracketed_paste" }
[dev-dependencies] [dev-dependencies]
serde_bytes = "0.11" serde_bytes = "0.11"
@ -50,9 +52,9 @@ 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)
no_closure = [] # no automatic sharing and capture of anonymous functions to external variables no_closure = [] # no automatic sharing and capture of anonymous functions to external variables
no_module = [] # no modules no_module = [] # no modules
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", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata
internals = [] # expose internal data structures
debugging = ["internals"] # enable debugging debugging = ["internals"] # enable debugging
# compiling for no-std # compiling for no-std

View File

@ -252,7 +252,7 @@ impl ExportedParams for ExportedFnParams {
} }
} }
(attr, _) => { (attr, ..) => {
return Err(syn::Error::new( return Err(syn::Error::new(
key.span(), key.span(),
format!("unknown attribute '{}'", attr), format!("unknown attribute '{}'", attr),
@ -366,7 +366,7 @@ impl Parse for ExportedFn {
}) => { }) => {
matches!(flatten_type_groups(elem.as_ref()), syn::Type::Path(ref p) if p.path == str_type_path) matches!(flatten_type_groups(elem.as_ref()), syn::Type::Path(ref p) if p.path == str_type_path)
} }
syn::Type::Verbatim(_) => false, syn::Type::Verbatim(..) => false,
_ => true, _ => true,
}; };
if !is_ok { if !is_ok {
@ -379,15 +379,15 @@ impl Parse for ExportedFn {
// Check return type. // Check return type.
match fn_all.sig.output { match fn_all.sig.output {
syn::ReturnType::Type(_, ref ret_type) => { syn::ReturnType::Type(.., ref ret_type) => {
match flatten_type_groups(ret_type.as_ref()) { match flatten_type_groups(ret_type.as_ref()) {
syn::Type::Ptr(_) => { syn::Type::Ptr(..) => {
return Err(syn::Error::new( return Err(syn::Error::new(
fn_all.sig.output.span(), fn_all.sig.output.span(),
"Rhai functions cannot return pointers", "Rhai functions cannot return pointers",
)) ))
} }
syn::Type::Reference(_) => { syn::Type::Reference(..) => {
return Err(syn::Error::new( return Err(syn::Error::new(
fn_all.sig.output.span(), fn_all.sig.output.span(),
"Rhai functions cannot return references", "Rhai functions cannot return references",
@ -425,10 +425,10 @@ impl ExportedFn {
pub fn update_scope(&mut self, parent_scope: &ExportScope) { pub fn update_scope(&mut self, parent_scope: &ExportScope) {
let keep = match (self.params.skip, parent_scope) { let keep = match (self.params.skip, parent_scope) {
(true, _) => false, (true, ..) => false,
(_, ExportScope::PubOnly) => self.is_public(), (.., ExportScope::PubOnly) => self.is_public(),
(_, ExportScope::Prefix(s)) => self.name().to_string().starts_with(s), (.., ExportScope::Prefix(s)) => self.name().to_string().starts_with(s),
(_, ExportScope::All) => true, (.., ExportScope::All) => true,
}; };
self.params.skip = !keep; self.params.skip = !keep;
} }
@ -502,7 +502,7 @@ impl ExportedFn {
pub fn return_type(&self) -> Option<&syn::Type> { pub fn return_type(&self) -> Option<&syn::Type> {
match self.signature.output { match self.signature.output {
syn::ReturnType::Type(_, ref ret_type) => Some(flatten_type_groups(ret_type)), syn::ReturnType::Type(.., ref ret_type) => Some(flatten_type_groups(ret_type)),
_ => None, _ => None,
} }
} }
@ -544,28 +544,28 @@ impl ExportedFn {
match params.special { match params.special {
// 2a. Property getters must take only the subject as an argument. // 2a. Property getters must take only the subject as an argument.
FnSpecialAccess::Property(Property::Get(_)) if self.arg_count() != 1 => { FnSpecialAccess::Property(Property::Get(..)) if self.arg_count() != 1 => {
return Err(syn::Error::new( return Err(syn::Error::new(
self.signature.inputs.span(), self.signature.inputs.span(),
"property getter requires exactly 1 parameter", "property getter requires exactly 1 parameter",
)) ))
} }
// 2b. Property getters must return a value. // 2b. Property getters must return a value.
FnSpecialAccess::Property(Property::Get(_)) if self.return_type().is_none() => { FnSpecialAccess::Property(Property::Get(..)) if self.return_type().is_none() => {
return Err(syn::Error::new( return Err(syn::Error::new(
self.signature.span(), self.signature.span(),
"property getter must return a value", "property getter must return a value",
)) ))
} }
// 3a. Property setters must take the subject and a new value as arguments. // 3a. Property setters must take the subject and a new value as arguments.
FnSpecialAccess::Property(Property::Set(_)) if self.arg_count() != 2 => { FnSpecialAccess::Property(Property::Set(..)) if self.arg_count() != 2 => {
return Err(syn::Error::new( return Err(syn::Error::new(
self.signature.inputs.span(), self.signature.inputs.span(),
"property setter requires exactly 2 parameters", "property setter requires exactly 2 parameters",
)) ))
} }
// 3b. Non-raw property setters must return nothing. // 3b. Non-raw property setters must return nothing.
FnSpecialAccess::Property(Property::Set(_)) FnSpecialAccess::Property(Property::Set(..))
if params.return_raw.is_none() && self.return_type().is_some() => if params.return_raw.is_none() && self.return_type().is_some() =>
{ {
return Err(syn::Error::new( return Err(syn::Error::new(
@ -734,7 +734,7 @@ impl ExportedFn {
.unwrap(), .unwrap(),
); );
} }
syn::FnArg::Receiver(_) => todo!("true self parameters not implemented yet"), syn::FnArg::Receiver(..) => todo!("true self parameters not implemented yet"),
} }
unpack_exprs.push(syn::parse2::<syn::Expr>(quote! { #var }).unwrap()); unpack_exprs.push(syn::parse2::<syn::Expr>(quote! { #var }).unwrap());
} else { } else {
@ -811,7 +811,7 @@ impl ExportedFn {
); );
} }
} }
syn::FnArg::Receiver(_) => panic!("internal error: how did this happen!?"), syn::FnArg::Receiver(..) => panic!("internal error: how did this happen!?"),
} }
if !is_ref { if !is_ref {
unpack_exprs.push(syn::parse2::<syn::Expr>(quote! { #var }).unwrap()); unpack_exprs.push(syn::parse2::<syn::Expr>(quote! { #var }).unwrap());

View File

@ -69,7 +69,7 @@ impl ExportedParams for ExportedModParams {
("export_all", Some(s)) => { ("export_all", Some(s)) => {
return Err(syn::Error::new(s.span(), "extraneous value")) return Err(syn::Error::new(s.span(), "extraneous value"))
} }
(attr, _) => { (attr, ..) => {
return Err(syn::Error::new( return Err(syn::Error::new(
key.span(), key.span(),
format!("unknown attribute '{}'", attr), format!("unknown attribute '{}'", attr),
@ -109,7 +109,7 @@ impl Parse for Module {
let mut consts = Vec::new(); let mut consts = Vec::new();
let mut sub_modules = Vec::new(); let mut sub_modules = Vec::new();
if let Some((_, ref mut content)) = mod_all.content { if let Some((.., ref mut content)) = mod_all.content {
// Gather and parse functions. // Gather and parse functions.
fns = content fns = content
.iter_mut() .iter_mut()
@ -144,12 +144,14 @@ impl Parse for Module {
attrs, attrs,
ty, ty,
.. ..
}) if matches!(vis, syn::Visibility::Public(_)) => consts.push(ExportedConst { }) if matches!(vis, syn::Visibility::Public(..)) => {
name: ident.to_string(), consts.push(ExportedConst {
typ: ty.clone(), name: ident.to_string(),
expr: expr.as_ref().clone(), typ: ty.clone(),
cfg_attrs: crate::attrs::collect_cfg_attr(&attrs), expr: expr.as_ref().clone(),
}), cfg_attrs: crate::attrs::collect_cfg_attr(&attrs),
})
}
_ => {} _ => {}
} }
} }
@ -161,7 +163,7 @@ impl Parse for Module {
let mut i = 0; let mut i = 0;
while i < content.len() { while i < content.len() {
match content[i] { match content[i] {
syn::Item::Mod(_) => { syn::Item::Mod(..) => {
let mut item_mod = match content.remove(i) { let mut item_mod = match content.remove(i) {
syn::Item::Mod(m) => m, syn::Item::Mod(m) => m,
_ => unreachable!(), _ => unreachable!(),
@ -211,10 +213,10 @@ impl Module {
pub fn update_scope(&mut self, parent_scope: &ExportScope) { pub fn update_scope(&mut self, parent_scope: &ExportScope) {
let keep = match (self.params.skip, parent_scope) { let keep = match (self.params.skip, parent_scope) {
(true, _) => false, (true, ..) => false,
(_, ExportScope::PubOnly) => matches!(self.mod_all.vis, syn::Visibility::Public(_)), (.., ExportScope::PubOnly) => matches!(self.mod_all.vis, syn::Visibility::Public(..)),
(_, ExportScope::Prefix(s)) => self.mod_all.ident.to_string().starts_with(s), (.., ExportScope::Prefix(s)) => self.mod_all.ident.to_string().starts_with(s),
(_, ExportScope::All) => true, (.., ExportScope::All) => true,
}; };
self.params.skip = !keep; self.params.skip = !keep;
} }
@ -245,7 +247,7 @@ impl Module {
} = self; } = self;
let mod_vis = mod_all.vis; let mod_vis = mod_all.vis;
let mod_name = mod_all.ident.clone(); let mod_name = mod_all.ident.clone();
let (_, orig_content) = mod_all.content.take().unwrap(); let (.., orig_content) = mod_all.content.take().unwrap();
let mod_attrs = mem::take(&mut mod_all.attrs); let mod_attrs = mem::take(&mut mod_all.attrs);
if !params.skip { if !params.skip {
@ -312,7 +314,7 @@ impl Module {
pub fn content(&self) -> Option<&[syn::Item]> { pub fn content(&self) -> Option<&[syn::Item]> {
match self.mod_all { match self.mod_all {
syn::ItemMod { syn::ItemMod {
content: Some((_, ref vec)), content: Some((.., ref vec)),
.. ..
} => Some(vec), } => Some(vec),
_ => None, _ => None,

View File

@ -99,7 +99,7 @@ pub fn generate_body(
let fn_input_types: Vec<_> = function let fn_input_types: Vec<_> = function
.arg_list() .arg_list()
.map(|fn_arg| match fn_arg { .map(|fn_arg| match fn_arg {
syn::FnArg::Receiver(_) => panic!("internal error: receiver fn outside impl!?"), syn::FnArg::Receiver(..) => panic!("internal error: receiver fn outside impl!?"),
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => { syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
let arg_type = match flatten_type_groups(ty.as_ref()) { let arg_type = match flatten_type_groups(ty.as_ref()) {
syn::Type::Reference(syn::TypeReference { syn::Type::Reference(syn::TypeReference {
@ -150,7 +150,7 @@ pub fn generate_body(
match function.params().special { match function.params().special {
FnSpecialAccess::None => (), FnSpecialAccess::None => (),
FnSpecialAccess::Index(_) | FnSpecialAccess::Property(_) => { FnSpecialAccess::Index(..) | FnSpecialAccess::Property(..) => {
let reg_name = fn_literal.value(); let reg_name = fn_literal.value();
if reg_name.starts_with(FN_GET) if reg_name.starts_with(FN_GET)
|| reg_name.starts_with(FN_SET) || reg_name.starts_with(FN_SET)
@ -240,7 +240,7 @@ pub fn generate_body(
}) })
.unwrap(); .unwrap();
let (_, generate_call_content) = generate_fn_call.content.take().unwrap(); let (.., generate_call_content) = generate_fn_call.content.take().unwrap();
quote! { quote! {
#(#generate_call_content)* #(#generate_call_content)*
@ -254,7 +254,7 @@ pub fn check_rename_collisions(fns: &[ExportedFn]) -> Result<(), syn::Error> {
.arg_list() .arg_list()
.fold(name.to_string(), |mut arg_str, fn_arg| { .fold(name.to_string(), |mut arg_str, fn_arg| {
let type_string: String = match fn_arg { let type_string: String = match fn_arg {
syn::FnArg::Receiver(_) => unimplemented!("receiver rhai_fns not implemented"), syn::FnArg::Receiver(..) => unimplemented!("receiver rhai_fns not implemented"),
syn::FnArg::Typed(syn::PatType { ref ty, .. }) => print_type(ty), syn::FnArg::Typed(syn::PatType { ref ty, .. }) => print_type(ty),
}; };
arg_str.push('.'); arg_str.push('.');
@ -275,7 +275,7 @@ pub fn check_rename_collisions(fns: &[ExportedFn]) -> Result<(), syn::Error> {
.map(|n| (n.clone(), n.clone())) .map(|n| (n.clone(), n.clone()))
.collect(); .collect();
if let Some((s, n, _)) = item_fn.params().special.get_fn_name() { if let Some((s, n, ..)) = item_fn.params().special.get_fn_name() {
names.push((s, n)); names.push((s, n));
} }

View File

@ -11,7 +11,7 @@ Root Sources
| `tokenizer.rs` | Script tokenizer/lexer | | `tokenizer.rs` | Script tokenizer/lexer |
| `parser.rs` | Script parser | | `parser.rs` | Script parser |
| `optimizer.rs` | Script optimizer | | `optimizer.rs` | Script optimizer |
| `unsafe.rs` | `unsafe` functions | | `reify.rs` | Utilities for making generic types concrete |
| `tests.rs` | Unit tests (not integration tests, which are in the `rhai/tests` sub-directory) | | `tests.rs` | Unit tests (not integration tests, which are in the `rhai/tests` sub-directory) |

View File

@ -1,7 +1,7 @@
//! Module that defines the public compilation API of [`Engine`]. //! Module that defines the public compilation API of [`Engine`].
use crate::parser::{ParseResult, ParseState}; use crate::parser::{ParseResult, ParseState};
use crate::{Engine, Scope, AST}; use crate::{Engine, OptimizationLevel, Scope, AST};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -99,7 +99,7 @@ impl Engine {
) { ) {
ast.walk(&mut |path| match path.last().unwrap() { ast.walk(&mut |path| match path.last().unwrap() {
// Collect all `import` statements with a string constant path // Collect all `import` statements with a string constant path
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _)) ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, ..), ..))
if !resolver.contains_path(s) && !imports.contains(s.as_str()) => if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>
{ {
imports.insert(s.clone().into()); imports.insert(s.clone().into());
@ -196,12 +196,7 @@ impl Engine {
scope: &Scope, scope: &Scope,
scripts: impl AsRef<[S]>, scripts: impl AsRef<[S]>,
) -> ParseResult<AST> { ) -> ParseResult<AST> {
self.compile_with_scope_and_optimization_level( self.compile_with_scope_and_optimization_level(scope, scripts, self.optimization_level)
scope,
scripts,
#[cfg(not(feature = "no_optimize"))]
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.
/// ///
@ -215,7 +210,7 @@ impl Engine {
&self, &self,
scope: &Scope, scope: &Scope,
scripts: impl AsRef<[S]>, scripts: impl AsRef<[S]>,
#[cfg(not(feature = "no_optimize"))] optimization_level: crate::OptimizationLevel, optimization_level: OptimizationLevel,
) -> ParseResult<AST> { ) -> ParseResult<AST> {
let (stream, tokenizer_control) = self.lex_raw( let (stream, tokenizer_control) = self.lex_raw(
scripts.as_ref(), scripts.as_ref(),
@ -226,7 +221,6 @@ impl Engine {
&mut stream.peekable(), &mut stream.peekable(),
&mut state, &mut state,
scope, scope,
#[cfg(not(feature = "no_optimize"))]
optimization_level, optimization_level,
) )
} }
@ -298,13 +292,7 @@ impl Engine {
let mut peekable = stream.peekable(); let mut peekable = stream.peekable();
let mut state = ParseState::new(self, tokenizer_control); let mut state = ParseState::new(self, tokenizer_control);
self.parse_global_expr( self.parse_global_expr(&mut peekable, &mut state, scope, self.optimization_level)
&mut peekable,
&mut state,
scope,
#[cfg(not(feature = "no_optimize"))]
self.optimization_level,
)
} }
/// Parse a JSON string into an [object map][crate::Map]. /// Parse a JSON string into an [object map][crate::Map].
/// This is a light-weight alternative to using, say, /// This is a light-weight alternative to using, say,
@ -400,7 +388,9 @@ impl Engine {
&mut state, &mut state,
&scope, &scope,
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
crate::OptimizationLevel::None, OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
OptimizationLevel::default(),
)?; )?;
if has_null { if has_null {
scope.push_constant("null", ()); scope.push_constant("null", ());

View File

@ -3,16 +3,15 @@
use crate::ast::Expr; use crate::ast::Expr;
use crate::func::native::SendSync; use crate::func::native::SendSync;
use crate::parser::ParseResult; use crate::parser::ParseResult;
use crate::r#unsafe::unsafe_try_cast;
use crate::tokenizer::{is_valid_identifier, Token}; use crate::tokenizer::{is_valid_identifier, Token};
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult, Shared, reify, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult,
StaticVec, INT, Shared, StaticVec,
}; };
use std::ops::Deref;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{any::TypeId, ops::Deref};
/// Collection of special markers for custom syntax definition. /// Collection of special markers for custom syntax definition.
pub mod markers { pub mod markers {
@ -73,9 +72,9 @@ impl Expression<'_> {
pub fn get_string_value(&self) -> Option<&str> { pub fn get_string_value(&self) -> Option<&str> {
match self.0 { match self.0 {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Expr::Variable(_, _, x) if x.1.is_some() => None, Expr::Variable(.., x) if x.1.is_some() => None,
Expr::Variable(_, _, x) => Some(x.2.as_str()), Expr::Variable(.., x) => Some(x.2.as_str()),
Expr::StringConstant(x, _) => Some(x.as_str()), Expr::StringConstant(x, ..) => Some(x.as_str()),
_ => None, _ => None,
} }
} }
@ -94,46 +93,23 @@ impl Expression<'_> {
#[must_use] #[must_use]
pub fn get_literal_value<T: Variant>(&self) -> Option<T> { pub fn get_literal_value<T: Variant>(&self) -> Option<T> {
// Coded this way in order to maximally leverage potentials for dead-code removal. // Coded this way in order to maximally leverage potentials for dead-code removal.
match self.0 {
Expr::IntegerConstant(x, ..) => reify!(*x => Option<T>),
if TypeId::of::<T>() == TypeId::of::<INT>() { #[cfg(not(feature = "no_float"))]
return match self.0 { Expr::FloatConstant(x, ..) => reify!(*x => Option<T>),
Expr::IntegerConstant(x, _) => unsafe_try_cast(*x),
_ => None, Expr::CharConstant(x, ..) => reify!(*x => Option<T>),
}; Expr::StringConstant(x, ..) => reify!(x.clone() => Option<T>),
Expr::Variable(.., x) => {
let x: ImmutableString = x.2.clone().into();
reify!(x => Option<T>)
}
Expr::BoolConstant(x, ..) => reify!(*x => Option<T>),
Expr::Unit(..) => reify!(() => Option<T>),
_ => None,
} }
#[cfg(not(feature = "no_float"))]
if TypeId::of::<T>() == TypeId::of::<crate::FLOAT>() {
return match self.0 {
Expr::FloatConstant(x, _) => unsafe_try_cast(*x),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<char>() {
return match self.0 {
Expr::CharConstant(x, _) => unsafe_try_cast(*x),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
return match self.0 {
Expr::StringConstant(x, _) => unsafe_try_cast(x.clone()),
Expr::Variable(_, _, x) => unsafe_try_cast(Into::<ImmutableString>::into(&x.2)),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<bool>() {
return match self.0 {
Expr::BoolConstant(x, _) => unsafe_try_cast(*x),
_ => None,
};
}
if TypeId::of::<T>() == TypeId::of::<()>() {
return match self.0 {
Expr::Unit(_) => unsafe_try_cast(()),
_ => None,
};
}
None
} }
} }

View File

@ -3,7 +3,9 @@
use crate::eval::{EvalState, GlobalRuntimeState}; use crate::eval::{EvalState, GlobalRuntimeState};
use crate::parser::ParseState; use crate::parser::ParseState;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR}; use crate::{
Dynamic, Engine, Module, OptimizationLevel, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR,
};
use std::any::type_name; use std::any::type_name;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -64,7 +66,6 @@ impl Engine {
let ast = self.compile_with_scope_and_optimization_level( let ast = self.compile_with_scope_and_optimization_level(
scope, scope,
&[script], &[script],
#[cfg(not(feature = "no_optimize"))]
self.optimization_level, self.optimization_level,
)?; )?;
self.eval_ast_with_scope(scope, &ast) self.eval_ast_with_scope(scope, &ast)
@ -122,7 +123,9 @@ impl Engine {
&mut state, &mut state,
scope, scope,
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
crate::OptimizationLevel::None, OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
OptimizationLevel::default(),
)?; )?;
self.eval_ast_with_scope(scope, &ast) self.eval_ast_with_scope(scope, &ast)

View File

@ -226,7 +226,7 @@ impl Engine {
match Token::lookup_from_syntax(keyword.as_ref()) { match Token::lookup_from_syntax(keyword.as_ref()) {
// Standard identifiers, reserved keywords and custom keywords are OK // Standard identifiers, reserved keywords and custom keywords are OK
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (), None | Some(Token::Reserved(..)) | Some(Token::Custom(..)) => (),
// Active standard keywords cannot be made custom // Active standard keywords cannot be made custom
// Disabled keywords are OK // Disabled keywords are OK
Some(token) if token.is_standard_keyword() => { Some(token) if token.is_standard_keyword() => {

View File

@ -30,7 +30,6 @@ impl Engine {
&mut stream.peekable(), &mut stream.peekable(),
&mut state, &mut state,
scope, scope,
#[cfg(not(feature = "no_optimize"))]
self.optimization_level, self.optimization_level,
)?; )?;

View File

@ -53,7 +53,7 @@ impl fmt::Debug for AST {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
if !self.lib.is_empty() { if !self.lib.is_empty() {
for (_, _, _, _, ref fn_def) in self.lib.iter_script_fn() { for (.., ref fn_def) in self.lib.iter_script_fn() {
let sig = fn_def.to_string(); let sig = fn_def.to_string();
fp.field(&sig, &fn_def.body.as_slice()); fp.field(&sig, &fn_def.body.as_slice());
} }
@ -449,7 +449,7 @@ impl AST {
/// foo("!") /// foo("!")
/// "#)?; /// "#)?;
/// ///
/// // Merge 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' /// // Merge 'ast2', picking only 'error()' but not 'foo(..)', into 'ast1'
/// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params| /// let ast = ast1.merge_filtered(&ast2, |_, _, script, name, params|
/// script && name == "error" && params == 0); /// script && name == "error" && params == 0);
/// ///
@ -551,7 +551,7 @@ impl AST {
/// foo("!") /// foo("!")
/// "#)?; /// "#)?;
/// ///
/// // Combine 'ast2', picking only 'error()' but not 'foo(_)', into 'ast1' /// // Combine 'ast2', picking only 'error()' but not 'foo(..)', into 'ast1'
/// ast1.combine_filtered(ast2, |_, _, script, name, params| /// ast1.combine_filtered(ast2, |_, _, script, name, params|
/// script && name == "error" && params == 0); /// script && name == "error" && params == 0);
/// ///
@ -613,7 +613,7 @@ impl AST {
/// fn bar() { print("hello"); } /// fn bar() { print("hello"); }
/// "#)?; /// "#)?;
/// ///
/// // Remove all functions except 'foo(_)' /// // Remove all functions except 'foo(..)'
/// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1); /// ast.retain_functions(|_, _, name, params| name == "foo" && params == 1);
/// # } /// # }
/// # Ok(()) /// # Ok(())
@ -640,7 +640,7 @@ impl AST {
pub fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> { pub fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
self.lib self.lib
.iter_script_fn() .iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def.as_ref()) .map(|(.., fn_def)| fn_def.as_ref())
} }
/// Iterate through all function definitions. /// Iterate through all function definitions.
/// ///
@ -652,7 +652,7 @@ impl AST {
pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> { pub(crate) fn iter_fn_def(&self) -> impl Iterator<Item = &super::ScriptFnDef> {
self.lib self.lib
.iter_script_fn() .iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def.as_ref()) .map(|(.., fn_def)| fn_def.as_ref())
} }
/// Iterate through all function definitions. /// Iterate through all function definitions.
/// ///
@ -662,7 +662,7 @@ impl AST {
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = super::ScriptFnMetadata> + 'a { pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = super::ScriptFnMetadata> + 'a {
self.lib self.lib
.iter_script_fn() .iter_script_fn()
.map(|(_, _, _, _, fn_def)| fn_def.as_ref().into()) .map(|(.., fn_def)| fn_def.as_ref().into())
} }
/// Clear all function definitions in the [`AST`]. /// Clear all function definitions in the [`AST`].
/// ///
@ -744,7 +744,7 @@ impl AST {
include_variables: bool, include_variables: bool,
) -> impl Iterator<Item = (&str, bool, Dynamic)> { ) -> impl Iterator<Item = (&str, bool, Dynamic)> {
self.statements().iter().filter_map(move |stmt| match stmt { self.statements().iter().filter_map(move |stmt| match stmt {
Stmt::Var(expr, name, options, _) Stmt::Var(expr, name, options, ..)
if options.contains(AST_OPTION_CONSTANT) && include_constants if options.contains(AST_OPTION_CONSTANT) && include_constants
|| !options.contains(AST_OPTION_CONSTANT) && include_variables => || !options.contains(AST_OPTION_CONSTANT) && include_variables =>
{ {

View File

@ -443,24 +443,24 @@ impl fmt::Debug for Expr {
let mut display_pos = self.start_position(); let mut display_pos = self.start_position();
match self { match self {
Self::DynamicConstant(value, _) => write!(f, "{:?}", value), Self::DynamicConstant(value, ..) => write!(f, "{:?}", value),
Self::BoolConstant(value, _) => write!(f, "{:?}", value), Self::BoolConstant(value, ..) => write!(f, "{:?}", value),
Self::IntegerConstant(value, _) => write!(f, "{:?}", value), Self::IntegerConstant(value, ..) => write!(f, "{:?}", value),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(value, _) => write!(f, "{:?}", value), Self::FloatConstant(value, ..) => write!(f, "{:?}", value),
Self::CharConstant(value, _) => write!(f, "{:?}", value), Self::CharConstant(value, ..) => write!(f, "{:?}", value),
Self::StringConstant(value, _) => write!(f, "{:?}", value), Self::StringConstant(value, ..) => write!(f, "{:?}", value),
Self::Unit(_) => f.write_str("()"), Self::Unit(..) => f.write_str("()"),
Self::InterpolatedString(x, _) => { Self::InterpolatedString(x, ..) => {
f.write_str("InterpolatedString")?; f.write_str("InterpolatedString")?;
return f.debug_list().entries(x.iter()).finish(); return f.debug_list().entries(x.iter()).finish();
} }
Self::Array(x, _) => { Self::Array(x, ..) => {
f.write_str("Array")?; f.write_str("Array")?;
f.debug_list().entries(x.iter()).finish() f.debug_list().entries(x.iter()).finish()
} }
Self::Map(x, _) => { Self::Map(x, ..) => {
f.write_str("Map")?; f.write_str("Map")?;
f.debug_map() f.debug_map()
.entries(x.0.iter().map(|(k, v)| (k, v))) .entries(x.0.iter().map(|(k, v)| (k, v)))
@ -470,7 +470,7 @@ impl fmt::Debug for Expr {
f.write_str("Variable(")?; f.write_str("Variable(")?;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if let Some((_, ref namespace)) = x.1 { if let Some((.., ref namespace)) = x.1 {
write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())? write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())?
} }
f.write_str(&x.2)?; f.write_str(&x.2)?;
@ -479,13 +479,13 @@ impl fmt::Debug for Expr {
} }
f.write_str(")") f.write_str(")")
} }
Self::Property(x, _) => write!(f, "Property({})", x.2), Self::Property(x, ..) => write!(f, "Property({})", x.2),
Self::Stack(x, _) => write!(f, "ConstantArg#{}", x), Self::Stack(x, ..) => write!(f, "ConstantArg#{}", x),
Self::Stmt(x) => { Self::Stmt(x) => {
f.write_str("ExprStmtBlock")?; f.write_str("ExprStmtBlock")?;
f.debug_list().entries(x.iter()).finish() f.debug_list().entries(x.iter()).finish()
} }
Self::FnCall(x, _) => fmt::Debug::fmt(x, f), Self::FnCall(x, ..) => fmt::Debug::fmt(x, f),
Self::Index(x, term, pos) => { Self::Index(x, term, pos) => {
display_pos = *pos; display_pos = *pos;
@ -497,9 +497,9 @@ impl fmt::Debug for Expr {
} }
Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => { 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::And(_, _) => "And", Self::And(..) => "And",
Self::Or(_, _) => "Or", Self::Or(..) => "Or",
expr => unreachable!( expr => unreachable!(
"Self::Dot or Self::And or Self::Or expected but gets {:?}", "Self::Dot or Self::And or Self::Or expected but gets {:?}",
expr expr
@ -513,7 +513,7 @@ impl fmt::Debug for Expr {
.field("rhs", &x.rhs) .field("rhs", &x.rhs)
.finish() .finish()
} }
Self::Custom(x, _) => f.debug_tuple("Custom").field(x).finish(), Self::Custom(x, ..) => f.debug_tuple("Custom").field(x).finish(),
}?; }?;
display_pos.debug_print(f) display_pos.debug_print(f)
@ -528,24 +528,24 @@ impl Expr {
#[must_use] #[must_use]
pub fn get_literal_value(&self) -> Option<Dynamic> { pub fn get_literal_value(&self) -> Option<Dynamic> {
Some(match self { Some(match self {
Self::DynamicConstant(x, _) => x.as_ref().clone(), Self::DynamicConstant(x, ..) => x.as_ref().clone(),
Self::IntegerConstant(x, _) => (*x).into(), Self::IntegerConstant(x, ..) => (*x).into(),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(x, _) => (*x).into(), Self::FloatConstant(x, ..) => (*x).into(),
Self::CharConstant(x, _) => (*x).into(), Self::CharConstant(x, ..) => (*x).into(),
Self::StringConstant(x, _) => x.clone().into(), Self::StringConstant(x, ..) => x.clone().into(),
Self::BoolConstant(x, _) => (*x).into(), Self::BoolConstant(x, ..) => (*x).into(),
Self::Unit(_) => Dynamic::UNIT, Self::Unit(..) => Dynamic::UNIT,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::Array(x, _) if self.is_constant() => { Self::Array(x, ..) if self.is_constant() => {
let mut arr = crate::Array::with_capacity(x.len()); let mut arr = crate::Array::with_capacity(x.len());
arr.extend(x.iter().map(|v| v.get_literal_value().unwrap())); arr.extend(x.iter().map(|v| v.get_literal_value().unwrap()));
Dynamic::from_array(arr) Dynamic::from_array(arr)
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Self::Map(x, _) if self.is_constant() => { Self::Map(x, ..) if self.is_constant() => {
Dynamic::from_map(x.0.iter().fold(x.1.clone(), |mut map, (k, v)| { Dynamic::from_map(x.0.iter().fold(x.1.clone(), |mut map, (k, v)| {
let value_ref = map.get_mut(k.name.as_str()).unwrap(); let value_ref = map.get_mut(k.name.as_str()).unwrap();
*value_ref = v.get_literal_value().unwrap(); *value_ref = v.get_literal_value().unwrap();
@ -554,10 +554,10 @@ impl Expr {
} }
// Fn // Fn
Self::FnCall(ref x, _) Self::FnCall(ref x, ..)
if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR => if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR =>
{ {
if let Expr::StringConstant(ref s, _) = x.args[0] { if let Expr::StringConstant(ref s, ..) = x.args[0] {
if let Ok(fn_ptr) = FnPtr::new(s) { if let Ok(fn_ptr) = FnPtr::new(s) {
fn_ptr.into() fn_ptr.into()
} else { } else {
@ -569,33 +569,35 @@ impl Expr {
} }
// Binary operators // Binary operators
Self::FnCall(x, _) if !x.is_qualified() && x.args.len() == 2 => match x.name.as_str() { Self::FnCall(x, ..) if !x.is_qualified() && x.args.len() == 2 => {
// x..y match x.name.as_str() {
OP_EXCLUSIVE_RANGE => { // x..y
if let Expr::IntegerConstant(ref start, _) = x.args[0] { OP_EXCLUSIVE_RANGE => {
if let Expr::IntegerConstant(ref end, _) = x.args[1] { if let Expr::IntegerConstant(ref start, ..) = x.args[0] {
(*start..*end).into() if let Expr::IntegerConstant(ref end, ..) = x.args[1] {
(*start..*end).into()
} else {
return None;
}
} else { } else {
return None; return None;
} }
} else {
return None;
} }
} // x..=y
// x..=y OP_INCLUSIVE_RANGE => {
OP_INCLUSIVE_RANGE => { if let Expr::IntegerConstant(ref start, ..) = x.args[0] {
if let Expr::IntegerConstant(ref start, _) = x.args[0] { if let Expr::IntegerConstant(ref end, ..) = x.args[1] {
if let Expr::IntegerConstant(ref end, _) = x.args[1] { (*start..=*end).into()
(*start..=*end).into() } else {
return None;
}
} else { } else {
return None; return None;
} }
} else {
return None;
} }
_ => return None,
} }
_ => return None, }
},
_ => return None, _ => return None,
}) })
@ -605,25 +607,25 @@ impl Expr {
#[must_use] #[must_use]
pub fn from_dynamic(value: Dynamic, pos: Position) -> Self { pub fn from_dynamic(value: Dynamic, pos: Position) -> Self {
match value.0 { match value.0 {
Union::Unit(_, _, _) => Self::Unit(pos), Union::Unit(..) => Self::Unit(pos),
Union::Bool(b, _, _) => Self::BoolConstant(b, pos), Union::Bool(b, ..) => Self::BoolConstant(b, pos),
Union::Str(s, _, _) => Self::StringConstant(s, pos), Union::Str(s, ..) => Self::StringConstant(s, pos),
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")] #[cfg(feature = "decimal")]
Union::Decimal(value, _, _) => Self::DynamicConstant(Box::new((*value).into()), pos), 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"))] #[cfg(not(feature = "no_index"))]
Union::Array(a, _, _) => Self::DynamicConstant(Box::new((*a).into()), pos), Union::Array(a, ..) => Self::DynamicConstant(Box::new((*a).into()), pos),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(m, _, _) => Self::DynamicConstant(Box::new((*m).into()), pos), Union::Map(m, ..) => Self::DynamicConstant(Box::new((*m).into()), pos),
Union::FnPtr(f, _, _) if !f.is_curried() => Self::FnCall( Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
FnCallExpr { FnCallExpr {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace: None, namespace: None,
@ -651,8 +653,8 @@ impl Expr {
match self { match self {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Variable(_, _, x) if _non_qualified && x.1.is_some() => false, Self::Variable(.., x) if _non_qualified && x.1.is_some() => false,
Self::Variable(_, _, _) => true, Self::Variable(..) => true,
_ => false, _ => false,
} }
} }
@ -666,8 +668,8 @@ impl Expr {
match self { match self {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Variable(_, _, x) if _non_qualified && x.1.is_some() => None, Self::Variable(.., x) if _non_qualified && x.1.is_some() => None,
Self::Variable(_, _, x) => Some(x.2.as_str()), Self::Variable(.., x) => Some(x.2.as_str()),
_ => None, _ => None,
} }
} }
@ -677,27 +679,27 @@ impl Expr {
pub const fn position(&self) -> Position { pub const fn position(&self) -> Position {
match self { match self {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos, Self::FloatConstant(.., pos) => *pos,
Self::DynamicConstant(_, pos) Self::DynamicConstant(.., pos)
| Self::BoolConstant(_, pos) | Self::BoolConstant(.., pos)
| Self::IntegerConstant(_, pos) | Self::IntegerConstant(.., pos)
| Self::CharConstant(_, pos) | Self::CharConstant(.., pos)
| Self::Unit(pos) | Self::Unit(pos)
| Self::StringConstant(_, pos) | Self::StringConstant(.., pos)
| Self::Array(_, pos) | Self::Array(.., pos)
| Self::Map(_, pos) | Self::Map(.., pos)
| Self::Variable(_, pos, _) | Self::Variable(.., pos, _)
| Self::Stack(_, pos) | Self::Stack(.., pos)
| Self::And(_, pos) | Self::And(.., pos)
| Self::Or(_, pos) | Self::Or(.., pos)
| Self::Index(_, _, pos) | Self::Index(.., pos)
| Self::Dot(_, _, pos) | Self::Dot(.., pos)
| Self::Custom(_, pos) | Self::Custom(.., pos)
| Self::InterpolatedString(_, pos) | Self::InterpolatedString(.., pos)
| Self::Property(_, pos) => *pos, | Self::Property(.., pos) => *pos,
Self::FnCall(x, _) => x.pos, Self::FnCall(x, ..) => x.pos,
Self::Stmt(x) => x.position(), Self::Stmt(x) => x.position(),
} }
@ -708,10 +710,10 @@ impl Expr {
#[must_use] #[must_use]
pub const fn start_position(&self) -> Position { pub const fn start_position(&self) -> Position {
match self { match self {
Self::And(x, _) | Self::Or(x, _) | Self::Index(x, _, _) | Self::Dot(x, _, _) => { Self::And(x, ..) | Self::Or(x, ..) | Self::Index(x, ..) | Self::Dot(x, ..) => {
x.lhs.start_position() x.lhs.start_position()
} }
Self::FnCall(_, pos) => *pos, Self::FnCall(.., pos) => *pos,
_ => self.position(), _ => self.position(),
} }
} }
@ -720,26 +722,26 @@ impl Expr {
pub fn set_position(&mut self, new_pos: Position) -> &mut Self { pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self { match self {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, pos) => *pos = new_pos, Self::FloatConstant(.., pos) => *pos = new_pos,
Self::DynamicConstant(_, pos) Self::DynamicConstant(.., pos)
| Self::BoolConstant(_, pos) | Self::BoolConstant(.., pos)
| Self::IntegerConstant(_, pos) | Self::IntegerConstant(.., pos)
| Self::CharConstant(_, pos) | Self::CharConstant(.., pos)
| Self::Unit(pos) | Self::Unit(pos)
| Self::StringConstant(_, pos) | Self::StringConstant(.., pos)
| Self::Array(_, pos) | Self::Array(.., pos)
| 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)
| Self::Custom(_, pos) | Self::Custom(.., pos)
| Self::InterpolatedString(_, pos) | Self::InterpolatedString(.., pos)
| Self::Property(_, pos) => *pos = new_pos, | Self::Property(.., pos) => *pos = new_pos,
Self::Stmt(x) => x.set_position(new_pos, Position::NONE), Self::Stmt(x) => x.set_position(new_pos, Position::NONE),
} }
@ -753,15 +755,15 @@ impl Expr {
#[must_use] #[must_use]
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { match self {
Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_pure), Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_pure),
Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure), Self::Map(x, ..) => x.0.iter().map(|(.., v)| v).all(Self::is_pure),
Self::And(x, _) | Self::Or(x, _) => x.lhs.is_pure() && x.rhs.is_pure(), Self::And(x, ..) | Self::Or(x, ..) => x.lhs.is_pure() && x.rhs.is_pure(),
Self::Stmt(x) => x.iter().all(Stmt::is_pure), Self::Stmt(x) => x.iter().all(Stmt::is_pure),
Self::Variable(_, _, _) | Self::Stack(_, _) => true, Self::Variable(..) | Self::Stack(..) => true,
_ => self.is_constant(), _ => self.is_constant(),
} }
@ -770,7 +772,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 {
matches!(self, Self::Unit(_)) matches!(self, Self::Unit(..))
} }
/// Is the expression a constant? /// Is the expression a constant?
#[inline] #[inline]
@ -778,19 +780,19 @@ impl Expr {
pub fn is_constant(&self) -> bool { pub fn is_constant(&self) -> bool {
match self { match self {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => true, Self::FloatConstant(..) => true,
Self::DynamicConstant(_, _) Self::DynamicConstant(..)
| Self::BoolConstant(_, _) | Self::BoolConstant(..)
| Self::IntegerConstant(_, _) | Self::IntegerConstant(..)
| Self::CharConstant(_, _) | Self::CharConstant(..)
| Self::StringConstant(_, _) | Self::StringConstant(..)
| Self::Unit(_) | Self::Unit(..)
| Self::Stack(_, _) => true, | Self::Stack(..) => true,
Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_constant), Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_constant),
Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant), Self::Map(x, ..) => x.0.iter().map(|(.., expr)| expr).all(Self::is_constant),
_ => false, _ => false,
} }
@ -807,31 +809,31 @@ impl Expr {
match self { match self {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Self::FloatConstant(_, _) => false, Self::FloatConstant(..) => false,
Self::DynamicConstant(_, _) Self::DynamicConstant(..)
| Self::BoolConstant(_, _) | Self::BoolConstant(..)
| Self::CharConstant(_, _) | Self::CharConstant(..)
| Self::And(_, _) | Self::And(..)
| Self::Or(_, _) | Self::Or(..)
| Self::Unit(_) => false, | Self::Unit(..) => false,
Self::IntegerConstant(_, _) Self::IntegerConstant(..)
| Self::StringConstant(_, _) | Self::StringConstant(..)
| 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(_, _) | Self::Map(..)
| Self::Custom(_, _) => match token { | Self::Custom(..) => match token {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::LeftBracket => true, Token::LeftBracket => true,
_ => false, _ => false,
}, },
Self::Variable(_, _, _) => match token { Self::Variable(..) => match token {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::LeftBracket => true, Token::LeftBracket => true,
Token::LeftParen => true, Token::LeftParen => true,
@ -840,14 +842,14 @@ impl Expr {
_ => false, _ => false,
}, },
Self::Property(_, _) => match token { Self::Property(..) => match token {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::LeftBracket => true, Token::LeftBracket => true,
Token::LeftParen => true, Token::LeftParen => true,
_ => false, _ => false,
}, },
Self::Stack(_, _) => false, Self::Stack(..) => false,
} }
} }
/// Recursively walk this expression. /// Recursively walk this expression.
@ -872,21 +874,21 @@ impl Expr {
} }
} }
} }
Self::InterpolatedString(x, _) | Self::Array(x, _) => { Self::InterpolatedString(x, ..) | Self::Array(x, ..) => {
for e in x.as_ref() { for e in x.as_ref() {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
} }
} }
Self::Map(x, _) => { Self::Map(x, ..) => {
for (_, e) in &x.0 { for (.., e) in &x.0 {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
} }
} }
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;
} }
@ -894,14 +896,14 @@ impl Expr {
return false; return false;
} }
} }
Self::FnCall(x, _) => { Self::FnCall(x, ..) => {
for e in &x.args { for e in &x.args {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
} }
} }
Self::Custom(x, _) => { Self::Custom(x, ..) => {
for e in &x.inputs { for e in &x.inputs {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;

View File

@ -404,65 +404,65 @@ impl Stmt {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_noop(&self) -> bool { pub const fn is_noop(&self) -> bool {
matches!(self, Self::Noop(_)) matches!(self, Self::Noop(..))
} }
/// Get the [position][Position] of this statement. /// Get the [position][Position] of this statement.
#[must_use] #[must_use]
pub const fn position(&self) -> Position { pub const fn position(&self) -> Position {
match self { match self {
Self::Noop(pos) Self::Noop(pos)
| Self::BreakLoop(_, pos) | Self::BreakLoop(.., pos)
| Self::Block(_, (pos, _)) | Self::Block(.., (pos, ..))
| Self::Assignment(_, pos) | Self::Assignment(.., pos)
| Self::FnCall(_, pos) | Self::FnCall(.., pos)
| Self::If(_, _, pos) | Self::If(.., pos)
| Self::Switch(_, _, pos) | Self::Switch(.., pos)
| Self::While(_, _, pos) | Self::While(.., pos)
| Self::Do(_, _, _, pos) | Self::Do(.., pos)
| Self::For(_, _, pos) | Self::For(.., pos)
| Self::Return(_, _, pos) | Self::Return(.., pos)
| Self::Var(_, _, _, pos) | Self::Var(.., pos)
| Self::TryCatch(_, pos) => *pos, | Self::TryCatch(.., pos) => *pos,
Self::Expr(x) => x.start_position(), Self::Expr(x) => x.start_position(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_, _, pos) => *pos, Self::Import(.., pos) => *pos,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Export(_, pos) => *pos, Self::Export(.., pos) => *pos,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(_) => Position::NONE, Self::Share(..) => Position::NONE,
} }
} }
/// Override the [position][Position] of this statement. /// Override the [position][Position] of this statement.
pub fn set_position(&mut self, new_pos: Position) -> &mut Self { pub fn set_position(&mut self, new_pos: Position) -> &mut Self {
match self { match self {
Self::Noop(pos) Self::Noop(pos)
| Self::BreakLoop(_, pos) | Self::BreakLoop(.., pos)
| Self::Block(_, (pos, _)) | Self::Block(.., (pos, ..))
| Self::Assignment(_, pos) | Self::Assignment(.., pos)
| Self::FnCall(_, pos) | Self::FnCall(.., pos)
| Self::If(_, _, pos) | Self::If(.., pos)
| Self::Switch(_, _, pos) | Self::Switch(.., pos)
| Self::While(_, _, pos) | Self::While(.., pos)
| Self::Do(_, _, _, pos) | Self::Do(.., pos)
| Self::For(_, _, pos) | Self::For(.., pos)
| Self::Return(_, _, pos) | Self::Return(.., pos)
| Self::Var(_, _, _, pos) | Self::Var(.., pos)
| Self::TryCatch(_, pos) => *pos = new_pos, | Self::TryCatch(.., pos) => *pos = new_pos,
Self::Expr(x) => { Self::Expr(x) => {
x.set_position(new_pos); x.set_position(new_pos);
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_, _, pos) => *pos = new_pos, Self::Import(.., pos) => *pos = new_pos,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Export(_, pos) => *pos = new_pos, Self::Export(.., pos) => *pos = new_pos,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(_) => (), Self::Share(..) => (),
} }
self self
@ -471,59 +471,56 @@ impl Stmt {
#[must_use] #[must_use]
pub const fn returns_value(&self) -> bool { pub const fn returns_value(&self) -> bool {
match self { match self {
Self::If(_, _, _) Self::If(..)
| Self::Switch(_, _, _) | Self::Switch(..)
| Self::Block(_, _) | Self::Block(..)
| Self::Expr(_) | Self::Expr(..)
| Self::FnCall(_, _) => true, | Self::FnCall(..) => true,
Self::Noop(_) Self::Noop(..)
| Self::While(_, _, _) | Self::While(..)
| Self::Do(_, _, _, _) | Self::Do(..)
| Self::For(_, _, _) | Self::For(..)
| Self::TryCatch(_, _) => false, | Self::TryCatch(..) => false,
Self::Var(_, _, _, _) Self::Var(..) | Self::Assignment(..) | Self::BreakLoop(..) | Self::Return(..) => false,
| Self::Assignment(_, _)
| Self::BreakLoop(_, _)
| Self::Return(_, _, _) => false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) | Self::Export(_, _) => false, Self::Import(..) | Self::Export(..) => false,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(_) => false, Self::Share(..) => false,
} }
} }
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)? /// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
#[must_use] #[must_use]
pub const fn is_self_terminated(&self) -> bool { pub const fn is_self_terminated(&self) -> bool {
match self { match self {
Self::If(_, _, _) Self::If(..)
| Self::Switch(_, _, _) | Self::Switch(..)
| Self::While(_, _, _) | Self::While(..)
| Self::For(_, _, _) | Self::For(..)
| Self::Block(_, _) | Self::Block(..)
| Self::TryCatch(_, _) => true, | Self::TryCatch(..) => true,
// A No-op requires a semicolon in order to know it is an empty statement! // A No-op requires a semicolon in order to know it is an empty statement!
Self::Noop(_) => false, Self::Noop(..) => false,
Self::Expr(Expr::Custom(x, _)) if x.is_self_terminated() => true, Self::Expr(Expr::Custom(x, ..)) if x.is_self_terminated() => true,
Self::Var(_, _, _, _) Self::Var(..)
| Self::Assignment(_, _) | Self::Assignment(..)
| Self::Expr(_) | Self::Expr(..)
| Self::FnCall(_, _) | Self::FnCall(..)
| Self::Do(_, _, _, _) | Self::Do(..)
| Self::BreakLoop(_, _) | Self::BreakLoop(..)
| Self::Return(_, _, _) => false, | Self::Return(..) => false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) | Self::Export(_, _) => false, Self::Import(..) | Self::Export(..) => false,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(_) => false, Self::Share(..) => false,
} }
} }
/// Is this statement _pure_? /// Is this statement _pure_?
@ -532,20 +529,20 @@ impl Stmt {
#[must_use] #[must_use]
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { match self {
Self::Noop(_) => true, Self::Noop(..) => true,
Self::Expr(expr) => expr.is_pure(), Self::Expr(expr) => expr.is_pure(),
Self::If(condition, x, _) => { Self::If(condition, x, ..) => {
condition.is_pure() condition.is_pure()
&& x.0.iter().all(Stmt::is_pure) && x.0.iter().all(Stmt::is_pure)
&& x.1.iter().all(Stmt::is_pure) && x.1.iter().all(Stmt::is_pure)
} }
Self::Switch(expr, x, _) => { Self::Switch(expr, x, ..) => {
expr.is_pure() expr.is_pure()
&& x.cases.values().all(|block| { && x.cases.values().all(|block| {
block.condition.as_ref().map(Expr::is_pure).unwrap_or(true) block.condition.as_ref().map(Expr::is_pure).unwrap_or(true)
&& block.statements.iter().all(Stmt::is_pure) && block.statements.iter().all(Stmt::is_pure)
}) })
&& x.ranges.iter().all(|(_, _, _, block)| { && x.ranges.iter().all(|(.., block)| {
block.condition.as_ref().map(Expr::is_pure).unwrap_or(true) block.condition.as_ref().map(Expr::is_pure).unwrap_or(true)
&& block.statements.iter().all(Stmt::is_pure) && block.statements.iter().all(Stmt::is_pure)
}) })
@ -553,34 +550,34 @@ impl Stmt {
} }
// Loops that exit can be pure because it can never be infinite. // Loops that exit can be pure because it can never be infinite.
Self::While(Expr::BoolConstant(false, _), _, _) => true, Self::While(Expr::BoolConstant(false, ..), ..) => true,
Self::Do(body, Expr::BoolConstant(x, _), options, _) Self::Do(body, Expr::BoolConstant(x, ..), options, ..)
if *x == options.contains(AST_OPTION_NEGATED) => if *x == options.contains(AST_OPTION_NEGATED) =>
{ {
body.iter().all(Stmt::is_pure) body.iter().all(Stmt::is_pure)
} }
// Loops are never pure since they can be infinite - and that's a side effect. // Loops are never pure since they can be infinite - and that's a side effect.
Self::While(_, _, _) | Self::Do(_, _, _, _) => false, Self::While(..) | Self::Do(..) => false,
// For loops can be pure because if the iterable is pure, it is finite, // For loops can be pure because if the iterable is pure, it is finite,
// so infinite loops can never occur. // so infinite loops can never occur.
Self::For(iterable, x, _) => iterable.is_pure() && x.2.iter().all(Stmt::is_pure), Self::For(iterable, x, ..) => iterable.is_pure() && x.2.iter().all(Stmt::is_pure),
Self::Var(_, _, _, _) | Self::Assignment(_, _) | Self::FnCall(_, _) => false, Self::Var(..) | Self::Assignment(..) | Self::FnCall(..) => false,
Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Block(block, ..) => block.iter().all(|stmt| stmt.is_pure()),
Self::BreakLoop(_, _) | Self::Return(_, _, _) => false, Self::BreakLoop(..) | Self::Return(..) => false,
Self::TryCatch(x, _) => { Self::TryCatch(x, ..) => {
x.try_block.iter().all(Stmt::is_pure) && x.catch_block.iter().all(Stmt::is_pure) x.try_block.iter().all(Stmt::is_pure) && x.catch_block.iter().all(Stmt::is_pure)
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) => false, Self::Import(..) => false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Export(_, _) => false, Self::Export(..) => false,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::Share(_) => false, Self::Share(..) => false,
} }
} }
/// Does this statement's behavior depend on its containing block? /// Does this statement's behavior depend on its containing block?
@ -594,16 +591,16 @@ impl Stmt {
#[must_use] #[must_use]
pub fn is_block_dependent(&self) -> bool { pub fn is_block_dependent(&self) -> bool {
match self { match self {
Self::Var(_, _, _, _) => true, Self::Var(..) => true,
Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_block_dependent), Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_block_dependent),
Self::FnCall(x, _) | Self::Expr(Expr::FnCall(x, _)) => { Self::FnCall(x, ..) | Self::Expr(Expr::FnCall(x, ..)) => {
!x.is_qualified() && x.name == KEYWORD_EVAL !x.is_qualified() && x.name == KEYWORD_EVAL
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(_, _, _) | Self::Export(_, _) => true, Self::Import(..) | Self::Export(..) => true,
_ => false, _ => false,
} }
@ -618,14 +615,14 @@ impl Stmt {
#[must_use] #[must_use]
pub fn is_internally_pure(&self) -> bool { pub fn is_internally_pure(&self) -> bool {
match self { match self {
Self::Var(expr, _, _, _) => expr.is_pure(), Self::Var(expr, _, ..) => expr.is_pure(),
Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_internally_pure), Self::Expr(Expr::Stmt(s)) => s.iter().all(Stmt::is_internally_pure),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(expr, _, _) => expr.is_pure(), Self::Import(expr, ..) => expr.is_pure(),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Export(_, _) => true, Self::Export(..) => true,
_ => self.is_pure(), _ => self.is_pure(),
} }
@ -639,7 +636,7 @@ impl Stmt {
#[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 {
Self::Return(_, _, _) | Self::BreakLoop(_, _) => true, Self::Return(..) | Self::BreakLoop(..) => true,
_ => false, _ => false,
} }
} }
@ -658,12 +655,12 @@ impl Stmt {
} }
match self { match self {
Self::Var(e, _, _, _) => { Self::Var(e, _, ..) => {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
} }
Self::If(e, x, _) => { Self::If(e, x, ..) => {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
@ -678,7 +675,7 @@ impl Stmt {
} }
} }
} }
Self::Switch(e, x, _) => { Self::Switch(e, x, ..) => {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
@ -697,7 +694,7 @@ impl Stmt {
} }
} }
} }
for (_, _, _, b) in &x.ranges { for (.., b) in &x.ranges {
if !b if !b
.condition .condition
.as_ref() .as_ref()
@ -718,7 +715,7 @@ impl Stmt {
} }
} }
} }
Self::While(e, s, _) | Self::Do(s, e, _, _) => { Self::While(e, s, ..) | Self::Do(s, e, ..) => {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
@ -728,7 +725,7 @@ impl Stmt {
} }
} }
} }
Self::For(e, x, _) => { Self::For(e, x, ..) => {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }
@ -738,7 +735,7 @@ impl Stmt {
} }
} }
} }
Self::Assignment(x, _) => { Self::Assignment(x, ..) => {
if !x.1.lhs.walk(path, on_node) { if !x.1.lhs.walk(path, on_node) {
return false; return false;
} }
@ -746,21 +743,21 @@ impl Stmt {
return false; return false;
} }
} }
Self::FnCall(x, _) => { Self::FnCall(x, ..) => {
for s in &x.args { for s in &x.args {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }
} }
} }
Self::Block(x, _) => { Self::Block(x, ..) => {
for s in x.iter() { for s in x.iter() {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
} }
} }
} }
Self::TryCatch(x, _) => { Self::TryCatch(x, ..) => {
for s in x.try_block.iter() { for s in x.try_block.iter() {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
return false; return false;
@ -778,7 +775,7 @@ impl Stmt {
} }
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Self::Import(e, _, _) => { Self::Import(e, ..) => {
if !e.walk(path, on_node) { if !e.walk(path, on_node) {
return false; return false;
} }

View File

@ -1,5 +1,5 @@
use rhai::debugger::{BreakPoint, DebuggerCommand, DebuggerEvent}; use rhai::debugger::{BreakPoint, DebuggerCommand, DebuggerEvent};
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString, Position, Scope}; use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString, Position, Scope, INT};
use std::{ use std::{
env, env,
@ -271,7 +271,7 @@ fn main() {
} }
DebuggerEvent::FunctionExitWithValue(r) => { DebuggerEvent::FunctionExitWithValue(r) => {
println!( println!(
"! Return from function call '{}' => {}", "! Return from function call '{}' => {:?}",
context context
.global_runtime_state() .global_runtime_state()
.debugger .debugger
@ -553,7 +553,16 @@ fn main() {
["throw"] => { ["throw"] => {
break Err(EvalAltResult::ErrorRuntime(Dynamic::UNIT, pos).into()) break Err(EvalAltResult::ErrorRuntime(Dynamic::UNIT, pos).into())
} }
["throw", _msg, ..] => { ["throw", num] if num.trim().parse::<INT>().is_ok() => {
let value = num.trim().parse::<INT>().unwrap().into();
break Err(EvalAltResult::ErrorRuntime(value, pos).into());
}
#[cfg(not(feature = "no_float"))]
["throw", num] if num.trim().parse::<rhai::FLOAT>().is_ok() => {
let value = num.trim().parse::<rhai::FLOAT>().unwrap().into();
break Err(EvalAltResult::ErrorRuntime(value, pos).into());
}
["throw", ..] => {
let msg = input.trim().splitn(2, ' ').skip(1).next().unwrap_or(""); let msg = input.trim().splitn(2, ' ').skip(1).next().unwrap_or("");
break Err(EvalAltResult::ErrorRuntime(msg.trim().into(), pos).into()); break Err(EvalAltResult::ErrorRuntime(msg.trim().into(), pos).into());
} }
@ -584,7 +593,7 @@ fn main() {
while let Err(err) = engine.run_ast_with_scope(&mut Scope::new(), &main_ast) { while let Err(err) = engine.run_ast_with_scope(&mut Scope::new(), &main_ast) {
match *err { match *err {
// Loop back to restart // Loop back to restart
EvalAltResult::ErrorTerminated(_, _) => (), EvalAltResult::ErrorTerminated(..) => (),
// Break evaluation // Break evaluation
_ => { _ => {
print_error(&script, *err); print_error(&script, *err);

View File

@ -62,8 +62,8 @@ fn print_help() {
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!(); println!();
println!("press Shift-Enter to continue to the next line,"); println!("press Ctrl-Enter or end a line with `\\`");
println!(r"or end a line with '\' (e.g. when pasting code)."); println!("to continue to the next line.");
println!(); println!();
} }
@ -204,6 +204,7 @@ fn load_script_files(engine: &mut Engine) {
// Setup the Rustyline editor. // Setup the Rustyline editor.
fn setup_editor() -> Editor<()> { fn setup_editor() -> Editor<()> {
//env_logger::init();
let config = Builder::new() let config = Builder::new()
.tab_stop(4) .tab_stop(4)
.indent_size(4) .indent_size(4)
@ -225,23 +226,13 @@ fn setup_editor() -> Editor<()> {
Event::KeySeq(smallvec![KeyEvent::ctrl('z')]), Event::KeySeq(smallvec![KeyEvent::ctrl('z')]),
EventHandler::Simple(Cmd::Undo(1)), EventHandler::Simple(Cmd::Undo(1)),
); );
// Map Shift-Return to insert a new line - bypass need for `\` continuation // Map Ctrl-Enter to insert a new line - bypass need for `\` continuation
rl.bind_sequence( rl.bind_sequence(
Event::KeySeq(smallvec![KeyEvent( Event::KeySeq(smallvec![KeyEvent(KeyCode::Char('J'), Modifiers::CTRL)]),
KeyCode::Char('m'),
Modifiers::CTRL_SHIFT
)]),
EventHandler::Simple(Cmd::Newline), EventHandler::Simple(Cmd::Newline),
); );
rl.bind_sequence( rl.bind_sequence(
Event::KeySeq(smallvec![KeyEvent( Event::KeySeq(smallvec![KeyEvent(KeyCode::Enter, Modifiers::CTRL)]),
KeyCode::Char('j'),
Modifiers::CTRL_SHIFT
)]),
EventHandler::Simple(Cmd::Newline),
);
rl.bind_sequence(
Event::KeySeq(smallvec![KeyEvent(KeyCode::Enter, Modifiers::SHIFT)]),
EventHandler::Simple(Cmd::Newline), EventHandler::Simple(Cmd::Newline),
); );
// Map Ctrl-Home and Ctrl-End for beginning/end of input // Map Ctrl-Home and Ctrl-End for beginning/end of input
@ -489,7 +480,7 @@ fn main() {
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|&(_, h)| h.contains(text)) .find(|&(.., h)| h.contains(text))
{ {
replacement = Some(line.clone()); replacement = Some(line.clone());
replacement_index = history_offset + (rl.history().len() - 1 - n); replacement_index = history_offset + (rl.history().len() - 1 - n);
@ -514,7 +505,7 @@ fn main() {
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|&(_, h)| h.trim_start().starts_with(prefix)) .find(|&(.., h)| h.trim_start().starts_with(prefix))
{ {
replacement = Some(line.clone()); replacement = Some(line.clone());
replacement_index = history_offset + (rl.history().len() - 1 - n); replacement_index = history_offset + (rl.history().len() - 1 - n);

View File

@ -1,8 +1,5 @@
use rhai::{Engine, EvalAltResult, Position}; use rhai::{Engine, EvalAltResult, Position};
#[cfg(not(feature = "no_optimize"))]
use rhai::OptimizationLevel;
use std::{env, fs::File, io::Read, path::Path, process::exit}; use std::{env, fs::File, io::Read, path::Path, process::exit};
fn eprint_error(input: &str, mut err: EvalAltResult) { fn eprint_error(input: &str, mut err: EvalAltResult) {
@ -53,7 +50,7 @@ fn main() {
let mut engine = Engine::new(); let mut engine = Engine::new();
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
engine.set_optimization_level(OptimizationLevel::Full); engine.set_optimization_level(rhai::OptimizationLevel::Full);
let mut f = match File::open(&filename) { let mut f = match File::open(&filename) {
Err(err) => { Err(err) => {

View File

@ -8,7 +8,8 @@ use crate::packages::{Package, StandardPackage};
use crate::tokenizer::Token; use crate::tokenizer::Token;
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
use crate::{ use crate::{
Dynamic, Identifier, ImmutableString, Module, Position, RhaiResult, Shared, StaticVec, Dynamic, Identifier, ImmutableString, Module, OptimizationLevel, Position, RhaiResult, Shared,
StaticVec,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -131,8 +132,7 @@ pub struct Engine {
pub(crate) progress: Option<Box<crate::func::native::OnProgressCallback>>, pub(crate) progress: Option<Box<crate::func::native::OnProgressCallback>>,
/// Optimize the [`AST`][crate::AST] after compilation. /// Optimize the [`AST`][crate::AST] after compilation.
#[cfg(not(feature = "no_optimize"))] pub(crate) optimization_level: OptimizationLevel,
pub(crate) optimization_level: crate::OptimizationLevel,
/// Language options. /// Language options.
pub(crate) options: crate::api::options::LanguageOptions, pub(crate) options: crate::api::options::LanguageOptions,
@ -287,8 +287,7 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
progress: None, progress: None,
#[cfg(not(feature = "no_optimize"))] optimization_level: OptimizationLevel::default(),
optimization_level: crate::OptimizationLevel::default(),
options: crate::api::options::LanguageOptions::new(), options: crate::api::options::LanguageOptions::new(),
@ -324,7 +323,7 @@ impl Engine {
match result { match result {
Ok(ref mut r) => { Ok(ref mut r) => {
// Concentrate all empty strings into one instance to save memory // Concentrate all empty strings into one instance to save memory
if let Dynamic(Union::Str(s, _, _)) = r { if let Dynamic(Union::Str(s, ..)) = r {
if s.is_empty() { if s.is_empty() {
if !s.ptr_eq(&self.empty_string) { if !s.ptr_eq(&self.empty_string) {
*s = self.const_empty_string(); *s = self.const_empty_string();

View File

@ -4,7 +4,9 @@
use super::{EvalState, GlobalRuntimeState, Target}; use super::{EvalState, GlobalRuntimeState, Target};
use crate::ast::{Expr, OpAssignment}; use crate::ast::{Expr, OpAssignment};
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR}; use crate::{
Dynamic, Engine, Module, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR,
};
use std::hash::Hash; use std::hash::Hash;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -25,9 +27,9 @@ impl From<&Expr> for ChainType {
fn from(expr: &Expr) -> Self { fn from(expr: &Expr) -> Self {
match expr { match expr {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(_, _, _) => Self::Indexing, Expr::Index(..) => Self::Indexing,
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(_, _, _) => Self::Dotting, Expr::Dot(..) => Self::Dotting,
expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr), expr => unreachable!("Expr::Index or Expr::Dot expected but gets {:?}", expr),
} }
} }
@ -58,7 +60,7 @@ impl ChainArgument {
#[must_use] #[must_use]
pub fn into_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"))] #[cfg(not(feature = "no_object"))]
_ => None, _ => None,
} }
@ -89,9 +91,9 @@ impl ChainArgument {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
ChainArgument::Property(pos) => *pos, ChainArgument::Property(pos) => *pos,
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
ChainArgument::MethodCallArgs(_, pos) => *pos, ChainArgument::MethodCallArgs(.., pos) => *pos,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
ChainArgument::IndexValue(_, pos) => *pos, ChainArgument::IndexValue(.., pos) => *pos,
} }
} }
/// Create n [`MethodCallArgs`][ChainArgument::MethodCallArgs]. /// Create n [`MethodCallArgs`][ChainArgument::MethodCallArgs].
@ -193,7 +195,7 @@ impl Engine {
true, root_pos, level, true, root_pos, level,
) { ) {
// Just ignore if there is no index setter // Just ignore if there is no index setter
if !matches!(*err, ERR::ErrorFunctionNotFound(_, _)) { if !matches!(*err, ERR::ErrorFunctionNotFound(..)) {
return Err(err); return Err(err);
} }
} }
@ -225,9 +227,7 @@ impl Engine {
} }
// Can't index - try to call an index setter // Can't index - try to call an index setter
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Err(err) if matches!(*err, ERR::ErrorIndexingType(_, _)) => { Err(err) if matches!(*err, ERR::ErrorIndexingType(..)) => Some(new_val),
Some(new_val)
}
// Any other error // Any other error
Err(err) => return Err(err), Err(err) => return Err(err),
}; };
@ -283,11 +283,11 @@ impl Engine {
result result
} }
// xxx.fn_name(...) = ??? // xxx.fn_name(...) = ???
Expr::FnCall(_, _) if new_val.is_some() => { Expr::FnCall(..) if new_val.is_some() => {
unreachable!("method call cannot be assigned to") unreachable!("method call cannot be assigned to")
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_, _) => { Expr::FnCall(..) => {
unreachable!("function call in dot chain should not be namespace-qualified") unreachable!("function call in dot chain should not be namespace-qualified")
} }
// {xxx:map}.id op= ??? // {xxx:map}.id op= ???
@ -333,14 +333,14 @@ impl Engine {
if op_info.is_some() { if op_info.is_some() {
let hash = crate::ast::FnCallHashes::from_native(*hash_get); let hash = crate::ast::FnCallHashes::from_native(*hash_get);
let args = &mut [target.as_mut()]; let args = &mut [target.as_mut()];
let (mut orig_val, _) = self let (mut orig_val, ..) = self
.exec_fn_call( .exec_fn_call(
None, global, state, lib, getter, hash, args, is_ref_mut, true, None, global, state, lib, getter, hash, args, is_ref_mut, true,
*pos, level, *pos, level,
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// Try an indexer if property does not exist // Try an indexer if property does not exist
ERR::ErrorDotExpr(_, _) => { ERR::ErrorDotExpr(..) => {
let prop = name.into(); let prop = name.into();
self.get_indexed_mut( self.get_indexed_mut(
global, state, lib, target, prop, *pos, false, true, global, state, lib, target, prop, *pos, false, true,
@ -349,7 +349,7 @@ impl Engine {
.map(|v| (v.take_or_clone(), false)) .map(|v| (v.take_or_clone(), false))
.map_err( .map_err(
|idx_err| match *idx_err { |idx_err| match *idx_err {
ERR::ErrorIndexingType(_, _) => err, ERR::ErrorIndexingType(..) => err,
_ => idx_err, _ => idx_err,
}, },
) )
@ -381,7 +381,7 @@ impl Engine {
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// Try an indexer if property does not exist // Try an indexer if property does not exist
ERR::ErrorDotExpr(_, _) => { ERR::ErrorDotExpr(..) => {
let args = &mut [target, &mut name.into(), &mut new_val]; let args = &mut [target, &mut name.into(), &mut new_val];
let fn_name = crate::engine::FN_IDX_SET; let fn_name = crate::engine::FN_IDX_SET;
let hash_set = let hash_set =
@ -394,7 +394,7 @@ impl Engine {
) )
.map_err( .map_err(
|idx_err| match *idx_err { |idx_err| match *idx_err {
ERR::ErrorIndexingType(_, _) => err, ERR::ErrorIndexingType(..) => err,
_ => idx_err, _ => idx_err,
}, },
) )
@ -417,7 +417,7 @@ impl Engine {
.map_or_else( .map_or_else(
|err| match *err { |err| match *err {
// Try an indexer if property does not exist // Try an indexer if property does not exist
ERR::ErrorDotExpr(_, _) => { ERR::ErrorDotExpr(..) => {
let prop = name.into(); let prop = name.into();
self.get_indexed_mut( self.get_indexed_mut(
global, state, lib, target, prop, *pos, false, true, level, global, state, lib, target, prop, *pos, false, true, level,
@ -425,7 +425,7 @@ impl Engine {
.map(|v| (v.take_or_clone(), false)) .map(|v| (v.take_or_clone(), false))
.map_err(|idx_err| { .map_err(|idx_err| {
match *idx_err { match *idx_err {
ERR::ErrorIndexingType(_, _) => err, ERR::ErrorIndexingType(..) => err,
_ => idx_err, _ => idx_err,
} }
}) })
@ -433,7 +433,7 @@ impl Engine {
_ => Err(err), _ => Err(err),
}, },
// Assume getters are always pure // Assume getters are always pure
|(v, _)| Ok((v, false)), |(v, ..)| Ok((v, false)),
) )
} }
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr // {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
@ -475,7 +475,7 @@ impl Engine {
result?.0.into() result?.0.into()
} }
// {xxx:map}.module::fn_name(...) - syntax error // {xxx:map}.module::fn_name(...) - syntax error
Expr::FnCall(_, _) => unreachable!( Expr::FnCall(..) => unreachable!(
"function call in dot chain should not be namespace-qualified" "function call in dot chain should not be namespace-qualified"
), ),
// Others - syntax error // Others - syntax error
@ -509,14 +509,14 @@ impl Engine {
let args = &mut arg_values[..1]; let args = &mut arg_values[..1];
// Assume getters are always pure // Assume getters are always pure
let (mut val, _) = self let (mut val, ..) = self
.exec_fn_call( .exec_fn_call(
None, global, state, lib, getter, hash_get, args, None, global, state, lib, getter, hash_get, args,
is_ref_mut, true, pos, level, is_ref_mut, true, pos, level,
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// Try an indexer if property does not exist // Try an indexer if property does not exist
ERR::ErrorDotExpr(_, _) => { ERR::ErrorDotExpr(..) => {
let prop = name.into(); let prop = name.into();
self.get_indexed_mut( self.get_indexed_mut(
global, state, lib, target, prop, pos, false, true, global, state, lib, target, prop, pos, false, true,
@ -525,7 +525,7 @@ impl Engine {
.map(|v| (v.take_or_clone(), false)) .map(|v| (v.take_or_clone(), false))
.map_err( .map_err(
|idx_err| match *idx_err { |idx_err| match *idx_err {
ERR::ErrorIndexingType(_, _) => err, ERR::ErrorIndexingType(..) => err,
_ => idx_err, _ => idx_err,
}, },
) )
@ -565,7 +565,7 @@ impl Engine {
.or_else( .or_else(
|err| match *err { |err| match *err {
// Try an indexer if property does not exist // Try an indexer if property does not exist
ERR::ErrorDotExpr(_, _) => { ERR::ErrorDotExpr(..) => {
let args = let args =
&mut [target.as_mut(), &mut name.into(), val]; &mut [target.as_mut(), &mut name.into(), val];
let fn_name = crate::engine::FN_IDX_SET; let fn_name = crate::engine::FN_IDX_SET;
@ -578,7 +578,7 @@ impl Engine {
args, is_ref_mut, true, pos, level, args, is_ref_mut, true, pos, level,
) )
.or_else(|idx_err| match *idx_err { .or_else(|idx_err| match *idx_err {
ERR::ErrorIndexingType(_, _) => { ERR::ErrorIndexingType(..) => {
// If there is no setter, no need to feed it back because // If there is no setter, no need to feed it back because
// the property is read-only // the property is read-only
Ok((Dynamic::UNIT, false)) Ok((Dynamic::UNIT, false))
@ -621,7 +621,7 @@ impl Engine {
.map_err(|err| err.fill_position(pos)) .map_err(|err| err.fill_position(pos))
} }
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_, _) => unreachable!( Expr::FnCall(..) => unreachable!(
"function call in dot chain should not be namespace-qualified" "function call in dot chain should not be namespace-qualified"
), ),
// Others - syntax error // Others - syntax error
@ -665,14 +665,14 @@ impl Engine {
match lhs { match lhs {
// id.??? or id[???] // id.??? or id[???]
Expr::Variable(_, var_pos, x) => { Expr::Variable(.., var_pos, x) => {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?; self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, *var_pos)?; self.inc_operations(&mut global.num_operations, *var_pos)?;
let (mut target, _) = let (mut target, ..) =
self.search_namespace(scope, global, state, lib, this_ptr, lhs, level)?; self.search_namespace(scope, global, state, lib, this_ptr, lhs, level)?;
let obj_ptr = &mut target; let obj_ptr = &mut target;
@ -682,7 +682,7 @@ impl Engine {
global, state, lib, &mut None, obj_ptr, root, expr, rhs, term, idx_values, global, state, lib, &mut None, obj_ptr, root, expr, rhs, term, idx_values,
chain_type, level, new_val, chain_type, 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))
} }
// {expr}.??? = ??? or {expr}[???] = ??? // {expr}.??? = ??? or {expr}[???] = ???
@ -696,7 +696,7 @@ impl Engine {
global, state, lib, this_ptr, obj_ptr, root, expr, rhs, term, idx_values, global, state, lib, this_ptr, obj_ptr, root, expr, rhs, term, idx_values,
chain_type, level, new_val, chain_type, level, new_val,
) )
.map(|(v, _)| if is_assignment { Dynamic::UNIT } else { v }) .map(|(v, ..)| if is_assignment { Dynamic::UNIT } else { v })
.map_err(|err| err.fill_position(op_pos)) .map_err(|err| err.fill_position(op_pos))
} }
} }
@ -726,14 +726,16 @@ impl Engine {
match expr { match expr {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dotting && !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();
let (values, pos) = args.iter().try_fold( let (values, pos) = args.iter().try_fold(
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE), (crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|(mut values, mut pos), expr| -> RhaiResultOf<_> { |(mut values, mut pos), expr| {
let (value, arg_pos) = self.get_arg_value( let (value, arg_pos) = self.get_arg_value(
scope, global, state, lib, this_ptr, expr, constants, level, scope, global, state, lib, this_ptr, expr, constants, level,
)?; )?;
@ -741,36 +743,36 @@ impl Engine {
pos = arg_pos; pos = arg_pos;
} }
values.push(value.flatten()); values.push(value.flatten());
Ok((values, pos)) Ok::<_, RhaiError>((values, pos))
}, },
)?; )?;
idx_values.push(super::ChainArgument::from_fn_call_args(values, pos)); idx_values.push(super::ChainArgument::from_fn_call_args(values, pos));
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { 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(_, pos) if _parent_chain_type == ChainType::Dotting => { Expr::Property(.., pos) if _parent_chain_type == ChainType::Dotting => {
idx_values.push(super::ChainArgument::Property(*pos)) idx_values.push(super::ChainArgument::Property(*pos))
} }
Expr::Property(_, _) => unreachable!("unexpected Expr::Property for indexing"), Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
Expr::Index(x, term, _) | Expr::Dot(x, term, _) if !terminate_chaining => { 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_arg_val = match lhs { let lhs_arg_val = match lhs {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Property(_, pos) if _parent_chain_type == ChainType::Dotting => { Expr::Property(.., pos) if _parent_chain_type == ChainType::Dotting => {
super::ChainArgument::Property(*pos) super::ChainArgument::Property(*pos)
} }
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::Dotting && !x.is_qualified() => if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
{ {
let crate::ast::FnCallExpr { let crate::ast::FnCallExpr {
@ -779,7 +781,7 @@ impl Engine {
let (values, pos) = args.iter().try_fold( let (values, pos) = args.iter().try_fold(
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE), (crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|(mut values, mut pos), expr| -> RhaiResultOf<_> { |(mut values, mut pos), expr| {
let (value, arg_pos) = self.get_arg_value( let (value, arg_pos) = self.get_arg_value(
scope, global, state, lib, this_ptr, expr, constants, level, scope, global, state, lib, this_ptr, expr, constants, level,
)?; )?;
@ -787,13 +789,13 @@ impl Engine {
pos = arg_pos pos = arg_pos
} }
values.push(value.flatten()); values.push(value.flatten());
Ok((values, pos)) Ok::<_, RhaiError>((values, pos))
}, },
)?; )?;
super::ChainArgument::from_fn_call_args(values, pos) super::ChainArgument::from_fn_call_args(values, pos)
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => { 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"))]
@ -862,7 +864,7 @@ impl Engine {
match target { match target {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Array(arr, _, _)) => { Dynamic(Union::Array(arr, ..)) => {
// val_array[idx] // val_array[idx]
let index = idx let index = idx
.as_int() .as_int()
@ -876,7 +878,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Blob(arr, _, _)) => { Dynamic(Union::Blob(arr, ..)) => {
// val_blob[idx] // val_blob[idx]
let index = idx let index = idx
.as_int() .as_int()
@ -896,7 +898,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Dynamic(Union::Map(map, _, _)) => { Dynamic(Union::Map(map, ..)) => {
// val_map[idx] // val_map[idx]
let index = idx.read_lock::<crate::ImmutableString>().ok_or_else(|| { let index = idx.read_lock::<crate::ImmutableString>().ok_or_else(|| {
self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), pos) self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), pos)
@ -913,7 +915,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Int(value, _, _)) Dynamic(Union::Int(value, ..))
if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() => if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() =>
{ {
// val_int[range] // val_int[range]
@ -984,7 +986,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Int(value, _, _)) => { Dynamic(Union::Int(value, ..)) => {
// val_int[idx] // val_int[idx]
let index = idx let index = idx
.as_int() .as_int()
@ -1006,7 +1008,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Dynamic(Union::Str(s, _, _)) => { Dynamic(Union::Str(s, ..)) => {
// val_string[idx] // val_string[idx]
let index = idx let index = idx
.as_int() .as_int()
@ -1052,7 +1054,7 @@ impl Engine {
self.exec_fn_call( self.exec_fn_call(
None, global, state, lib, fn_name, hash_get, args, true, true, pos, level, None, global, state, lib, fn_name, hash_get, args, true, true, pos, level,
) )
.map(|(v, _)| v.into()) .map(|(v, ..)| v.into())
} }
_ => Err(ERR::ErrorIndexingType( _ => Err(ERR::ErrorIndexingType(

View File

@ -10,7 +10,7 @@ use std::prelude::v1::*;
impl Engine { impl Engine {
/// Recursively calculate the sizes of a value. /// Recursively calculate the sizes of a value.
/// ///
/// Sizes returned are `(`[`Array`][crate::Array], [`Map`][crate::Map] and `String)`. /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`.
/// ///
/// # Panics /// # Panics
/// ///
@ -21,51 +21,51 @@ impl Engine {
match value.0 { match value.0 {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref arr, _, _) => { Union::Array(ref arr, ..) => {
arr.iter() arr.iter()
.fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 {
Union::Array(_, _, _) => { Union::Array(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = Self::calc_data_sizes(value, false);
(arrays + a + 1, maps + m, strings + s) (arrays + a + 1, maps + m, strings + s)
} }
Union::Blob(ref a, _, _) => (arrays + 1 + a.len(), maps, strings), Union::Blob(ref a, ..) => (arrays + 1 + a.len(), maps, strings),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, _, _) => { Union::Map(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = Self::calc_data_sizes(value, false);
(arrays + a + 1, maps + m, strings + s) (arrays + a + 1, maps + m, strings + s)
} }
Union::Str(ref s, _, _) => (arrays + 1, maps, strings + s.len()), Union::Str(ref s, ..) => (arrays + 1, maps, strings + s.len()),
_ => (arrays + 1, maps, strings), _ => (arrays + 1, maps, strings),
}) })
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(ref arr, _, _) => (arr.len(), 0, 0), Union::Blob(ref arr, ..) => (arr.len(), 0, 0),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref map, _, _) => { Union::Map(ref map, ..) => {
map.values() map.values()
.fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 { .fold((0, 0, 0), |(arrays, maps, strings), value| match value.0 {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(_, _, _) => { Union::Array(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = Self::calc_data_sizes(value, false);
(arrays + a, maps + m + 1, strings + s) (arrays + a, maps + m + 1, strings + s)
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(ref a, _, _) => (arrays + a.len(), maps, strings), Union::Blob(ref a, ..) => (arrays + a.len(), maps, strings),
Union::Map(_, _, _) => { Union::Map(..) => {
let (a, m, s) = Self::calc_data_sizes(value, false); let (a, m, s) = Self::calc_data_sizes(value, false);
(arrays + a, maps + m + 1, strings + s) (arrays + a, maps + m + 1, strings + s)
} }
Union::Str(ref s, _, _) => (arrays, maps + 1, strings + s.len()), Union::Str(ref s, ..) => (arrays, maps + 1, strings + s.len()),
_ => (arrays, maps + 1, strings), _ => (arrays, maps + 1, strings),
}) })
} }
Union::Str(ref s, _, _) => (0, 0, s.len()), Union::Str(ref s, ..) => (0, 0, s.len()),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(_, _, _) if _top => { Union::Shared(..) if _top => {
Self::calc_data_sizes(&*value.read_lock::<Dynamic>().unwrap(), true) Self::calc_data_sizes(&*value.read_lock::<Dynamic>().unwrap(), true)
} }
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(_, _, _) => { Union::Shared(..) => {
unreachable!("shared values discovered within data: {}", value) unreachable!("shared values discovered within data: {}", value)
} }
_ => (0, 0, 0), _ => (0, 0, 0),

View File

@ -271,7 +271,7 @@ impl Debugger {
} else { } else {
DebuggerStatus::CONTINUE DebuggerStatus::CONTINUE
}, },
state: if let Some((ref init, _)) = engine.debugger { state: if let Some((ref init, ..)) = engine.debugger {
init() init()
} else { } else {
Dynamic::UNIT Dynamic::UNIT
@ -348,8 +348,8 @@ impl Debugger {
self.break_points() self.break_points()
.iter() .iter()
.enumerate() .enumerate()
.filter(|&(_, bp)| bp.is_enabled()) .filter(|&(.., bp)| bp.is_enabled())
.find(|&(_, bp)| match bp { .find(|&(.., bp)| match bp {
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
BreakPoint::AtPosition { pos, .. } if pos.is_none() => false, BreakPoint::AtPosition { pos, .. } if pos.is_none() => false,
#[cfg(not(feature = "no_position"))] #[cfg(not(feature = "no_position"))]
@ -361,26 +361,26 @@ impl Debugger {
node.position() == *pos && _src == source node.position() == *pos && _src == source
} }
BreakPoint::AtFunctionName { name, .. } => match node { BreakPoint::AtFunctionName { name, .. } => match node {
ASTNode::Expr(Expr::FnCall(x, _)) ASTNode::Expr(Expr::FnCall(x, ..))
| ASTNode::Stmt(Stmt::FnCall(x, _)) | ASTNode::Stmt(Stmt::FnCall(x, ..))
| ASTNode::Stmt(Stmt::Expr(Expr::FnCall(x, _))) => x.name == *name, | ASTNode::Stmt(Stmt::Expr(Expr::FnCall(x, ..))) => x.name == *name,
_ => false, _ => false,
}, },
BreakPoint::AtFunctionCall { name, args, .. } => match node { BreakPoint::AtFunctionCall { name, args, .. } => match node {
ASTNode::Expr(Expr::FnCall(x, _)) ASTNode::Expr(Expr::FnCall(x, ..))
| ASTNode::Stmt(Stmt::FnCall(x, _)) | ASTNode::Stmt(Stmt::FnCall(x, ..))
| ASTNode::Stmt(Stmt::Expr(Expr::FnCall(x, _))) => { | ASTNode::Stmt(Stmt::Expr(Expr::FnCall(x, ..))) => {
x.args.len() == *args && x.name == *name x.args.len() == *args && x.name == *name
} }
_ => false, _ => false,
}, },
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
BreakPoint::AtProperty { name, .. } => match node { BreakPoint::AtProperty { name, .. } => match node {
ASTNode::Expr(Expr::Property(x, _)) => x.2 == *name, ASTNode::Expr(Expr::Property(x, ..)) => x.2 == *name,
_ => false, _ => false,
}, },
}) })
.map(|(i, _)| i) .map(|(i, ..)| i)
} }
/// Get a slice of all [`BreakPoint`]'s. /// Get a slice of all [`BreakPoint`]'s.
#[inline(always)] #[inline(always)]
@ -465,16 +465,16 @@ impl Engine {
// Skip transitive nodes // Skip transitive nodes
match node { match node {
ASTNode::Expr(Expr::Stmt(_)) | ASTNode::Stmt(Stmt::Expr(_)) => return Ok(None), ASTNode::Expr(Expr::Stmt(..)) | ASTNode::Stmt(Stmt::Expr(..)) => return Ok(None),
_ => (), _ => (),
} }
let stop = match global.debugger.status { let stop = match global.debugger.status {
DebuggerStatus::Next(false, false) => false, DebuggerStatus::Next(false, false) => false,
DebuggerStatus::Next(true, false) => matches!(node, ASTNode::Stmt(_)), DebuggerStatus::Next(true, false) => matches!(node, ASTNode::Stmt(..)),
DebuggerStatus::Next(false, true) => matches!(node, ASTNode::Expr(_)), DebuggerStatus::Next(false, true) => matches!(node, ASTNode::Expr(..)),
DebuggerStatus::Next(true, true) => true, DebuggerStatus::Next(true, true) => true,
DebuggerStatus::FunctionExit(_) => false, DebuggerStatus::FunctionExit(..) => false,
}; };
let event = if stop { let event = if stop {
@ -525,7 +525,7 @@ impl Engine {
level, level,
}; };
if let Some((_, ref on_debugger)) = self.debugger { if let Some((.., ref on_debugger)) = self.debugger {
let command = on_debugger(&mut context, event, node, source, node.position())?; let command = on_debugger(&mut context, event, node, source, node.position())?;
match command { match command {
@ -548,9 +548,9 @@ impl Engine {
DebuggerCommand::FunctionExit => { DebuggerCommand::FunctionExit => {
// Bump a level if it is a function call // Bump a level if it is a function call
let level = match node { let level = match node {
ASTNode::Expr(Expr::FnCall(_, _)) ASTNode::Expr(Expr::FnCall(..))
| ASTNode::Stmt(Stmt::FnCall(_, _)) | ASTNode::Stmt(Stmt::FnCall(..))
| ASTNode::Stmt(Stmt::Expr(Expr::FnCall(_, _))) => context.call_level() + 1, | ASTNode::Stmt(Stmt::Expr(Expr::FnCall(..))) => context.call_level() + 1,
_ => context.call_level(), _ => context.call_level(),
}; };
global.debugger.status = DebuggerStatus::FunctionExit(level); global.debugger.status = DebuggerStatus::FunctionExit(level);

View File

@ -53,7 +53,7 @@ impl Engine {
level: usize, level: usize,
) -> RhaiResultOf<(Target<'s>, Position)> { ) -> RhaiResultOf<(Target<'s>, Position)> {
match expr { match expr {
Expr::Variable(Some(_), _, _) => { Expr::Variable(Some(_), ..) => {
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
} }
Expr::Variable(None, _var_pos, v) => match v.as_ref() { Expr::Variable(None, _var_pos, v) => match v.as_ref() {
@ -80,7 +80,7 @@ impl Engine {
Ok((target.into(), *_var_pos)) Ok((target.into(), *_var_pos))
} }
Err(err) => Err(match *err { Err(err) => Err(match *err {
ERR::ErrorVariableNotFound(_, _) => ERR::ErrorVariableNotFound( ERR::ErrorVariableNotFound(..) => ERR::ErrorVariableNotFound(
format!( format!(
"{}{}{}", "{}{}{}",
namespace, namespace,
@ -155,7 +155,7 @@ impl Engine {
} }
} }
_ if state.always_search_scope => (0, expr.start_position()), _ if state.always_search_scope => (0, expr.start_position()),
Expr::Variable(Some(i), pos, _) => (i.get() as usize, *pos), Expr::Variable(Some(i), pos, ..) => (i.get() as usize, *pos),
Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
_ => unreachable!("Expr::Variable expected but gets {:?}", expr), _ => unreachable!("Expr::Variable expected but gets {:?}", expr),
}; };
@ -270,7 +270,7 @@ impl Engine {
// Function calls should account for a relatively larger portion of expressions because // Function calls should account for a relatively larger portion of expressions because
// binary operators are also function calls. // binary operators are also function calls.
if let Expr::FnCall(x, pos) = expr { if let Expr::FnCall(x, ..) = expr {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let reset_debugger = let reset_debugger =
self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)?; self.run_debugger_with_reset(scope, global, state, lib, this_ptr, expr, level)?;
@ -279,7 +279,7 @@ impl Engine {
self.inc_operations(&mut global.num_operations, expr.position())?; self.inc_operations(&mut global.num_operations, expr.position())?;
let result = let result =
self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level); self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, x.pos, level);
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
global.debugger.reset_status(reset_debugger); global.debugger.reset_status(reset_debugger);
@ -304,7 +304,7 @@ impl Engine {
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into()) .ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into())
} else { } else {
self.search_namespace(scope, global, state, lib, this_ptr, expr, level) self.search_namespace(scope, global, state, lib, this_ptr, expr, level)
.map(|(val, _)| val.take_or_clone()) .map(|(val, ..)| val.take_or_clone())
}; };
} }
@ -317,14 +317,14 @@ impl Engine {
let result = match expr { let result = match expr {
// Constants // Constants
Expr::DynamicConstant(x, _) => Ok(x.as_ref().clone()), Expr::DynamicConstant(x, ..) => Ok(x.as_ref().clone()),
Expr::IntegerConstant(x, _) => Ok((*x).into()), Expr::IntegerConstant(x, ..) => Ok((*x).into()),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
Expr::FloatConstant(x, _) => Ok((*x).into()), Expr::FloatConstant(x, ..) => Ok((*x).into()),
Expr::StringConstant(x, _) => Ok(x.clone().into()), Expr::StringConstant(x, ..) => Ok(x.clone().into()),
Expr::CharConstant(x, _) => Ok((*x).into()), Expr::CharConstant(x, ..) => Ok((*x).into()),
Expr::BoolConstant(x, _) => Ok((*x).into()), Expr::BoolConstant(x, ..) => Ok((*x).into()),
Expr::Unit(_) => Ok(Dynamic::UNIT), Expr::Unit(..) => Ok(Dynamic::UNIT),
// `... ${...} ...` // `... ${...} ...`
Expr::InterpolatedString(x, pos) => { Expr::InterpolatedString(x, pos) => {
@ -364,7 +364,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(x, _) => { Expr::Array(x, ..) => {
let mut arr = crate::Array::with_capacity(x.len()); let mut arr = crate::Array::with_capacity(x.len());
let mut result = Ok(Dynamic::UNIT); let mut result = Ok(Dynamic::UNIT);
@ -402,7 +402,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(x, _) => { Expr::Map(x, ..) => {
let mut map = x.1.clone(); let mut map = x.1.clone();
let mut result = Ok(Dynamic::UNIT); let mut result = Ok(Dynamic::UNIT);
@ -440,7 +440,7 @@ impl Engine {
result.map(|_| map.into()) result.map(|_| map.into())
} }
Expr::And(x, _) => { Expr::And(x, ..) => {
let lhs = self let lhs = self
.eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level) .eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)
.and_then(|v| { .and_then(|v| {
@ -463,7 +463,7 @@ impl Engine {
} }
} }
Expr::Or(x, _) => { Expr::Or(x, ..) => {
let lhs = self let lhs = self
.eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level) .eval_expr(scope, global, state, lib, this_ptr, &x.lhs, level)
.and_then(|v| { .and_then(|v| {
@ -519,12 +519,12 @@ impl Engine {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(_, _, _) => { Expr::Index(..) => {
self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None)
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(_, _, _) => { Expr::Dot(..) => {
self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None) self.eval_dot_index_chain(scope, global, state, lib, this_ptr, expr, level, None)
} }

View File

@ -67,13 +67,13 @@ impl Engine {
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if matches!(stmt, Stmt::Import(_, _, _)) { if matches!(stmt, Stmt::Import(..)) {
// Get the extra modules - see if any functions are marked global. // Get the extra modules - see if any functions are marked global.
// Without global functions, the extra modules never affect function resolution. // Without global functions, the extra modules never affect function resolution.
if global if global
.scan_imports_raw() .scan_imports_raw()
.skip(imports_len) .skip(imports_len)
.any(|(_, m)| m.contains_indexed_global_functions()) .any(|(.., m)| m.contains_indexed_global_functions())
{ {
if state.fn_resolution_caches_len() > orig_fn_resolution_caches_len { if state.fn_resolution_caches_len() > orig_fn_resolution_caches_len {
// When new module is imported with global functions and there is already // When new module is imported with global functions and there is already
@ -162,10 +162,10 @@ impl Engine {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.check_data_size(&args[0], root.1)?; self.check_data_size(&args[0], root.1)?;
} }
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, _) if f.starts_with(op_assign)) => Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
{ {
// Expand to `var = var op rhs` // Expand to `var = var op rhs`
let (value, _) = self.call_native_fn( let (value, ..) = self.call_native_fn(
global, state, lib, op, hash_op, args, true, false, op_pos, level, global, state, lib, op, hash_op, args, true, false, op_pos, level,
)?; )?;
@ -211,12 +211,12 @@ impl Engine {
// Popular branches are lifted out of the `match` statement into their own branches. // Popular branches are lifted out of the `match` statement into their own branches.
// Function calls should account for a relatively larger portion of statements. // Function calls should account for a relatively larger portion of statements.
if let Stmt::FnCall(x, pos) = stmt { if let Stmt::FnCall(x, ..) = stmt {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
self.inc_operations(&mut global.num_operations, stmt.position())?; self.inc_operations(&mut global.num_operations, stmt.position())?;
let result = let result =
self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, *pos, level); self.eval_fn_call_expr(scope, global, state, lib, this_ptr, x, x.pos, level);
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
global.debugger.reset_status(reset_debugger); global.debugger.reset_status(reset_debugger);
@ -288,19 +288,19 @@ impl Engine {
// 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 { match lhs {
// name op= rhs (handled above) // name op= rhs (handled above)
Expr::Variable(_, _, _) => { Expr::Variable(..) => {
unreachable!("Expr::Variable case is already handled") unreachable!("Expr::Variable case is already handled")
} }
// idx_lhs[idx_expr] op= rhs // idx_lhs[idx_expr] op= rhs
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Index(_, _, _) => self Expr::Index(..) => self
.eval_dot_index_chain( .eval_dot_index_chain(
scope, global, state, lib, this_ptr, lhs, level, _new_val, scope, global, state, lib, this_ptr, lhs, level, _new_val,
) )
.map(|_| Dynamic::UNIT), .map(|_| Dynamic::UNIT),
// dot_lhs.dot_rhs op= rhs // dot_lhs.dot_rhs op= rhs
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Dot(_, _, _) => self Expr::Dot(..) => self
.eval_dot_index_chain( .eval_dot_index_chain(
scope, global, state, lib, this_ptr, lhs, level, _new_val, scope, global, state, lib, this_ptr, lhs, level, _new_val,
) )
@ -323,7 +323,7 @@ impl Engine {
let result = match stmt { let result = match stmt {
// No-op // No-op
Stmt::Noop(_) => Ok(Dynamic::UNIT), Stmt::Noop(..) => Ok(Dynamic::UNIT),
// Expression as statement // Expression as statement
Stmt::Expr(expr) => self Stmt::Expr(expr) => self
@ -331,13 +331,13 @@ impl Engine {
.map(Dynamic::flatten), .map(Dynamic::flatten),
// Block scope // Block scope
Stmt::Block(statements, _) if statements.is_empty() => Ok(Dynamic::UNIT), Stmt::Block(statements, ..) if statements.is_empty() => Ok(Dynamic::UNIT),
Stmt::Block(statements, _) => { Stmt::Block(statements, ..) => {
self.eval_stmt_block(scope, global, state, lib, this_ptr, statements, true, level) self.eval_stmt_block(scope, global, state, lib, this_ptr, statements, true, level)
} }
// If statement // If statement
Stmt::If(expr, x, _) => { Stmt::If(expr, x, ..) => {
let guard_val = self let guard_val = self
.eval_expr(scope, global, state, lib, this_ptr, expr, level) .eval_expr(scope, global, state, lib, this_ptr, expr, level)
.and_then(|v| { .and_then(|v| {
@ -370,7 +370,7 @@ impl Engine {
} }
// Switch statement // Switch statement
Stmt::Switch(match_expr, x, _) => { Stmt::Switch(match_expr, x, ..) => {
let SwitchCases { let SwitchCases {
cases, cases,
def_case, def_case,
@ -414,8 +414,8 @@ impl Engine {
let value = value.as_int().expect("`INT`"); let value = value.as_int().expect("`INT`");
let mut result = Ok(None); let mut result = Ok(None);
for (_, _, _, block) in for (.., block) in
ranges.iter().filter(|&&(start, end, inclusive, _)| { ranges.iter().filter(|&&(start, end, inclusive, ..)| {
(!inclusive && (start..end).contains(&value)) (!inclusive && (start..end).contains(&value))
|| (inclusive && (start..=end).contains(&value)) || (inclusive && (start..=end).contains(&value))
}) })
@ -483,15 +483,15 @@ impl Engine {
} }
// Loop // Loop
Stmt::While(Expr::Unit(_), body, _) => loop { Stmt::While(Expr::Unit(..), body, ..) => loop {
if !body.is_empty() { if !body.is_empty() {
match self match self
.eval_stmt_block(scope, global, state, lib, this_ptr, body, true, level) .eval_stmt_block(scope, global, state, lib, this_ptr, body, true, level)
{ {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
ERR::LoopBreak(false, _) => (), ERR::LoopBreak(false, ..) => (),
ERR::LoopBreak(true, _) => break Ok(Dynamic::UNIT), ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT),
_ => break Err(err), _ => break Err(err),
}, },
} }
@ -502,7 +502,7 @@ impl Engine {
}, },
// While loop // While loop
Stmt::While(expr, body, _) => loop { Stmt::While(expr, body, ..) => loop {
let condition = self let condition = self
.eval_expr(scope, global, state, lib, this_ptr, expr, level) .eval_expr(scope, global, state, lib, this_ptr, expr, level)
.and_then(|v| { .and_then(|v| {
@ -520,8 +520,8 @@ impl Engine {
{ {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
ERR::LoopBreak(false, _) => (), ERR::LoopBreak(false, ..) => (),
ERR::LoopBreak(true, _) => break Ok(Dynamic::UNIT), ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT),
_ => break Err(err), _ => break Err(err),
}, },
} }
@ -531,7 +531,7 @@ impl Engine {
}, },
// Do loop // Do loop
Stmt::Do(body, expr, options, _) => loop { Stmt::Do(body, expr, options, ..) => loop {
let is_while = !options.contains(AST_OPTION_NEGATED); let is_while = !options.contains(AST_OPTION_NEGATED);
if !body.is_empty() { if !body.is_empty() {
@ -540,8 +540,8 @@ impl Engine {
{ {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
ERR::LoopBreak(false, _) => continue, ERR::LoopBreak(false, ..) => continue,
ERR::LoopBreak(true, _) => break Ok(Dynamic::UNIT), ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT),
_ => break Err(err), _ => break Err(err),
}, },
} }
@ -563,7 +563,7 @@ impl Engine {
}, },
// For loop // For loop
Stmt::For(expr, x, _) => { Stmt::For(expr, x, ..) => {
let (Ident { name: var_name, .. }, counter, statements) = x.as_ref(); let (Ident { name: var_name, .. }, counter, statements) = x.as_ref();
let iter_result = self let iter_result = self
@ -672,8 +672,8 @@ impl Engine {
match result { match result {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
ERR::LoopBreak(false, _) => (), ERR::LoopBreak(false, ..) => (),
ERR::LoopBreak(true, _) => break, ERR::LoopBreak(true, ..) => break,
_ => { _ => {
loop_result = Err(err); loop_result = Err(err);
break; break;
@ -699,7 +699,7 @@ impl Engine {
} }
// Try/Catch statement // Try/Catch statement
Stmt::TryCatch(x, _) => { Stmt::TryCatch(x, ..) => {
let TryCatchBlock { let TryCatchBlock {
try_block, try_block,
catch_var, catch_var,
@ -715,8 +715,8 @@ impl Engine {
Err(err) if err.is_pseudo_error() => Err(err), Err(err) if err.is_pseudo_error() => Err(err),
Err(err) if !err.is_catchable() => Err(err), Err(err) if !err.is_catchable() => Err(err),
Err(mut err) => { Err(mut err) => {
let err_value = match *err { let err_value = match err.unwrap_inner() {
ERR::ErrorRuntime(ref x, _) => x.clone(), ERR::ErrorRuntime(x, ..) => x.clone(),
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
_ => { _ => {
@ -773,7 +773,7 @@ impl Engine {
Ok(_) => Ok(Dynamic::UNIT), Ok(_) => Ok(Dynamic::UNIT),
Err(result_err) => match *result_err { Err(result_err) => match *result_err {
// Re-throw exception // Re-throw exception
ERR::ErrorRuntime(Dynamic(Union::Unit(_, _, _)), pos) => { ERR::ErrorRuntime(Dynamic(Union::Unit(..)), pos) => {
err.set_position(pos); err.set_position(pos);
Err(err) Err(err)
} }
@ -795,15 +795,15 @@ impl Engine {
} }
// Return value // Return value
Stmt::Return(_, Some(expr), pos) => self Stmt::Return(.., Some(expr), pos) => self
.eval_expr(scope, global, state, lib, this_ptr, expr, level) .eval_expr(scope, global, state, lib, this_ptr, expr, level)
.and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())), .and_then(|v| Err(ERR::Return(v.flatten(), *pos).into())),
// Empty return // Empty return
Stmt::Return(_, None, pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()), Stmt::Return(.., None, pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()),
// Let/const statement - shadowing disallowed // Let/const statement - shadowing disallowed
Stmt::Var(_, x, _, pos) if !self.allow_shadowing() && scope.contains(&x.name) => { Stmt::Var(.., x, _, pos) if !self.allow_shadowing() && scope.contains(&x.name) => {
Err(ERR::ErrorVariableExists(x.name.to_string(), *pos).into()) Err(ERR::ErrorVariableExists(x.name.to_string(), *pos).into())
} }
// Let/const statement // Let/const statement
@ -924,7 +924,7 @@ impl Engine {
let module_result = resolver let module_result = resolver
.as_ref() .as_ref()
.and_then(|r| match r.resolve_raw(self, global, &path, path_pos) { .and_then(|r| match r.resolve_raw(self, global, &path, path_pos) {
Err(err) if matches!(*err, ERR::ErrorModuleNotFound(_, _)) => None, Err(err) if matches!(*err, ERR::ErrorModuleNotFound(..)) => None,
result => Some(result), result => Some(result),
}) })
.or_else(|| { .or_else(|| {
@ -961,10 +961,10 @@ impl Engine {
// Export statement // Export statement
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Export(x, _) => { Stmt::Export(x, ..) => {
let (Ident { name, pos, .. }, Ident { name: alias, .. }) = x.as_ref(); let (Ident { name, pos, .. }, Ident { name: alias, .. }) = x.as_ref();
// Mark scope variables as public // Mark scope variables as public
if let Some((index, _)) = scope.get_index(name) { if let Some((index, ..)) = scope.get_index(name) {
scope.add_entry_alias( scope.add_entry_alias(
index, index,
if alias.is_empty() { name } else { alias }.clone(), if alias.is_empty() { name } else { alias }.clone(),
@ -978,7 +978,7 @@ impl Engine {
// Share statement // Share statement
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Stmt::Share(name) => { Stmt::Share(name) => {
if let Some((index, _)) = scope.get_index(name) { 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() {

View File

@ -144,10 +144,10 @@ impl<'a> Target<'a> {
#[must_use] #[must_use]
pub const fn is_ref(&self) -> bool { pub const fn is_ref(&self) -> bool {
match self { match self {
Self::RefMut(_) => true, Self::RefMut(..) => true,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::SharedValue { .. } => true, Self::SharedValue { .. } => true,
Self::TempValue(_) => false, Self::TempValue(..) => false,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::Bit { .. } Self::Bit { .. }
| Self::BitField { .. } | Self::BitField { .. }
@ -160,10 +160,10 @@ impl<'a> Target<'a> {
#[must_use] #[must_use]
pub const fn is_temp_value(&self) -> bool { pub const fn is_temp_value(&self) -> bool {
match self { match self {
Self::RefMut(_) => false, Self::RefMut(..) => false,
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::SharedValue { .. } => false, Self::SharedValue { .. } => false,
Self::TempValue(_) => true, Self::TempValue(..) => true,
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Self::Bit { .. } Self::Bit { .. }
| Self::BitField { .. } | Self::BitField { .. }
@ -275,7 +275,7 @@ impl<'a> Target<'a> {
#[inline] #[inline]
pub fn propagate_changed_value(&mut self) -> RhaiResultOf<()> { pub fn propagate_changed_value(&mut self) -> RhaiResultOf<()> {
match self { match self {
Self::RefMut(_) | Self::TempValue(_) => (), Self::RefMut(..) | Self::TempValue(..) => (),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Self::SharedValue { .. } => (), Self::SharedValue { .. } => (),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]

View File

@ -12,7 +12,8 @@ use crate::engine::{
use crate::eval::{EvalState, GlobalRuntimeState}; use crate::eval::{EvalState, GlobalRuntimeState};
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr,
Identifier, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, Identifier, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult,
RhaiResultOf, Scope, ERR,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -45,11 +46,13 @@ impl<'a> ArgBackup<'a> {
/// This function replaces the first argument of a method call with a clone copy. /// This function replaces the first argument of a method call with a clone copy.
/// This is to prevent a pure function unintentionally consuming the first argument. /// This is to prevent a pure function unintentionally consuming the first argument.
/// ///
/// `restore_first_arg` must be called before the end of the scope to prevent the shorter lifetime from leaking. /// `restore_first_arg` must be called before the end of the scope to prevent the shorter
/// lifetime from leaking.
/// ///
/// # Safety /// # Safety
/// ///
/// This method blindly casts a reference to another lifetime, which saves allocation and string cloning. /// This method blindly casts a reference to another lifetime, which saves allocation and
/// string cloning.
/// ///
/// As long as `restore_first_arg` is called before the end of the scope, the shorter lifetime /// As long as `restore_first_arg` is called before the end of the scope, the shorter lifetime
/// will not leak. /// will not leak.
@ -70,8 +73,8 @@ impl<'a> ArgBackup<'a> {
// Blindly casting a reference to another lifetime saves allocation and string cloning, // Blindly casting a reference to another lifetime saves allocation and string cloning,
// but must be used with the utmost care. // but must be used with the utmost care.
// //
// We can do this here because, before the end of this scope, we'd restore the original reference // We can do this here because, before the end of this scope, we'd restore the original
// via `restore_first_arg`. Therefore this shorter lifetime does not leak. // reference via `restore_first_arg`. Therefore this shorter lifetime does not leak.
self.orig_mut = Some(mem::replace(&mut args[0], unsafe { self.orig_mut = Some(mem::replace(&mut args[0], unsafe {
mem::transmute(&mut self.value_copy) mem::transmute(&mut self.value_copy)
})); }));
@ -80,8 +83,8 @@ impl<'a> ArgBackup<'a> {
/// ///
/// # Safety /// # Safety
/// ///
/// If `change_first_arg_to_copy` has been called, this function **MUST** be called _BEFORE_ exiting /// If `change_first_arg_to_copy` has been called, this function **MUST** be called _BEFORE_
/// the current scope. Otherwise it is undefined behavior as the shorter lifetime will leak. /// exiting 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>) {
if let Some(p) = self.orig_mut.take() { if let Some(p) = self.orig_mut.take() {
@ -108,11 +111,11 @@ pub fn ensure_no_data_race(
args: &FnCallArgs, args: &FnCallArgs,
is_method_call: bool, is_method_call: bool,
) -> RhaiResultOf<()> { ) -> RhaiResultOf<()> {
if let Some((n, _)) = args if let Some((n, ..)) = args
.iter() .iter()
.enumerate() .enumerate()
.skip(if is_method_call { 1 } else { 0 }) .skip(if is_method_call { 1 } else { 0 })
.find(|(_, a)| a.is_locked()) .find(|(.., a)| a.is_locked())
{ {
return Err(ERR::ErrorDataRace( return Err(ERR::ErrorDataRace(
format!("argument #{} of function '{}'", n + 1, fn_name), format!("argument #{} of function '{}'", n + 1, fn_name),
@ -391,7 +394,7 @@ impl Engine {
let source = match (source.as_str(), parent_source.as_str()) { let source = match (source.as_str(), parent_source.as_str()) {
("", "") => None, ("", "") => None,
("", s) | (s, _) => Some(s), ("", s) | (s, ..) => Some(s),
}; };
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
@ -429,7 +432,7 @@ impl Engine {
{ {
let trigger = match global.debugger.status { let trigger = match global.debugger.status {
crate::eval::DebuggerStatus::FunctionExit(n) => n >= level, crate::eval::DebuggerStatus::FunctionExit(n) => n >= level,
crate::eval::DebuggerStatus::Next(_, true) => true, crate::eval::DebuggerStatus::Next(.., true) => true,
_ => false, _ => false,
}; };
if trigger { if trigger {
@ -773,8 +776,8 @@ impl Engine {
scope, global, state, lib, &mut None, statements, false, level, scope, global, state, lib, &mut None, statements, false, level,
) )
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::Return(out, _) => Ok(out), ERR::Return(out, ..) => Ok(out),
ERR::LoopBreak(_, _) => { ERR::LoopBreak(..) => {
unreachable!("no outer loop scope to break out of") unreachable!("no outer loop scope to break out of")
} }
_ => Err(err), _ => Err(err),
@ -957,7 +960,7 @@ impl Engine {
level: usize, level: usize,
) -> RhaiResultOf<(Dynamic, Position)> { ) -> RhaiResultOf<(Dynamic, Position)> {
Ok(( Ok((
if let Expr::Stack(slot, _) = arg_expr { if let Expr::Stack(slot, ..) = arg_expr {
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?;
constants[*slot].clone() constants[*slot].clone()
@ -969,7 +972,7 @@ impl Engine {
// Do not match function exit for arguments // Do not match function exit for arguments
#[cfg(feature = "debugging")] #[cfg(feature = "debugging")]
let reset_debugger = global.debugger.clear_status_if(|status| { let reset_debugger = global.debugger.clear_status_if(|status| {
matches!(status, crate::eval::DebuggerStatus::FunctionExit(_)) matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
}); });
let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level); let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level);
@ -1076,16 +1079,13 @@ impl Engine {
let (name, fn_curry) = arg_value.cast::<FnPtr>().take_data(); let (name, fn_curry) = arg_value.cast::<FnPtr>().take_data();
// Append the new curried arguments to the existing list. // Append the new curried arguments to the existing list.
let fn_curry = let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| {
a_expr let (value, ..) = self.get_arg_value(
.iter() scope, global, state, lib, this_ptr, expr, constants, level,
.try_fold(fn_curry, |mut curried, expr| -> RhaiResultOf<_> { )?;
let (value, _) = self.get_arg_value( curried.push(value);
scope, global, state, lib, this_ptr, expr, constants, level, Ok::<_, RhaiError>(curried)
)?; })?;
curried.push(value);
Ok(curried)
})?;
return Ok(FnPtr::new_unchecked(name, fn_curry).into()); return Ok(FnPtr::new_unchecked(name, fn_curry).into());
} }
@ -1094,7 +1094,7 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
let arg = first_arg.unwrap(); let arg = first_arg.unwrap();
let (arg_value, _) = let (arg_value, ..) =
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
return Ok(arg_value.is_shared().into()); return Ok(arg_value.is_shared().into());
} }
@ -1194,7 +1194,7 @@ impl Engine {
.chain(a_expr.iter()) .chain(a_expr.iter())
.try_for_each(|expr| { .try_for_each(|expr| {
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
.map(|(value, _)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
args.extend(curry.iter_mut()); args.extend(curry.iter_mut());
args.extend(arg_values.iter_mut()); args.extend(arg_values.iter_mut());
@ -1207,7 +1207,7 @@ impl Engine {
scope, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, scope, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos,
level, level,
) )
.map(|(v, _)| v); .map(|(v, ..)| v);
} }
// Call with blank scope // Call with blank scope
@ -1226,7 +1226,7 @@ impl Engine {
// func(x, ...) -> x.func(...) // func(x, ...) -> x.func(...)
a_expr.iter().try_for_each(|expr| { a_expr.iter().try_for_each(|expr| {
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
.map(|(value, _)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
let (mut target, _pos) = let (mut target, _pos) =
@ -1263,7 +1263,7 @@ impl Engine {
self.get_arg_value( self.get_arg_value(
scope, global, state, lib, this_ptr, expr, constants, level, scope, global, state, lib, this_ptr, expr, constants, level,
) )
.map(|(value, _)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
args.extend(curry.iter_mut()); args.extend(curry.iter_mut());
args.extend(arg_values.iter_mut()); args.extend(arg_values.iter_mut());
@ -1273,7 +1273,7 @@ impl Engine {
self.exec_fn_call( self.exec_fn_call(
None, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, level, None, global, state, lib, name, hashes, &mut args, is_ref_mut, false, pos, level,
) )
.map(|(v, _)| v) .map(|(v, ..)| v)
} }
/// Call a namespace-qualified function in normal function-call style. /// Call a namespace-qualified function in normal function-call style.
@ -1312,7 +1312,7 @@ impl Engine {
args_expr.iter().skip(1).try_for_each(|expr| { args_expr.iter().skip(1).try_for_each(|expr| {
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
.map(|(value, _)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
// Get target reference to first argument // Get target reference to first argument
@ -1343,7 +1343,7 @@ impl Engine {
// func(..., ...) or func(mod::x, ...) // func(..., ...) or func(mod::x, ...)
args_expr.iter().try_for_each(|expr| { args_expr.iter().try_for_each(|expr| {
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
.map(|(value, _)| arg_values.push(value.flatten())) .map(|(value, ..)| arg_values.push(value.flatten()))
})?; })?;
args.extend(arg_values.iter_mut()); args.extend(arg_values.iter_mut());
} }
@ -1450,7 +1450,9 @@ impl Engine {
&Scope::new(), &Scope::new(),
&[script], &[script],
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
crate::OptimizationLevel::None, OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
OptimizationLevel::default(),
)?; )?;
// If new functions are defined within the eval string, it is an error // If new functions are defined within the eval string, it is an error

View File

@ -30,10 +30,10 @@ pub enum CallableFunction {
impl fmt::Debug for CallableFunction { impl fmt::Debug for CallableFunction {
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"),
Self::Method(_) => write!(f, "NativeMethod"), Self::Method(..) => write!(f, "NativeMethod"),
Self::Iterator(_) => write!(f, "NativeIterator"), Self::Iterator(..) => write!(f, "NativeIterator"),
Self::Plugin(_) => write!(f, "PluginFunction"), Self::Plugin(..) => write!(f, "PluginFunction"),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f), Self::Script(fn_def) => fmt::Debug::fmt(fn_def, f),
@ -44,10 +44,10 @@ impl fmt::Debug for CallableFunction {
impl fmt::Display for CallableFunction { impl fmt::Display for CallableFunction {
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"),
Self::Method(_) => write!(f, "NativeMethod"), Self::Method(..) => write!(f, "NativeMethod"),
Self::Iterator(_) => write!(f, "NativeIterator"), Self::Iterator(..) => write!(f, "NativeIterator"),
Self::Plugin(_) => write!(f, "PluginFunction"), Self::Plugin(..) => write!(f, "PluginFunction"),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(s) => fmt::Display::fmt(s, f), Self::Script(s) => fmt::Display::fmt(s, f),
@ -61,13 +61,13 @@ impl CallableFunction {
#[must_use] #[must_use]
pub fn is_pure(&self) -> bool { pub fn is_pure(&self) -> bool {
match self { match self {
Self::Pure(_) => true, Self::Pure(..) => true,
Self::Method(_) | Self::Iterator(_) => false, Self::Method(..) | Self::Iterator(..) => false,
Self::Plugin(p) => !p.is_method_call(), Self::Plugin(p) => !p.is_method_call(),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(_) => false, Self::Script(..) => false,
} }
} }
/// Is this a native Rust method function? /// Is this a native Rust method function?
@ -75,13 +75,13 @@ impl CallableFunction {
#[must_use] #[must_use]
pub fn is_method(&self) -> bool { pub fn is_method(&self) -> bool {
match self { match self {
Self::Method(_) => true, Self::Method(..) => true,
Self::Pure(_) | Self::Iterator(_) => false, Self::Pure(..) | Self::Iterator(..) => false,
Self::Plugin(p) => p.is_method_call(), Self::Plugin(p) => p.is_method_call(),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(_) => false, Self::Script(..) => false,
} }
} }
/// Is this an iterator function? /// Is this an iterator function?
@ -89,11 +89,11 @@ impl CallableFunction {
#[must_use] #[must_use]
pub const fn is_iter(&self) -> bool { pub const fn is_iter(&self) -> bool {
match self { match self {
Self::Iterator(_) => true, Self::Iterator(..) => true,
Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => false, Self::Pure(..) | Self::Method(..) | Self::Plugin(..) => false,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(_) => false, Self::Script(..) => false,
} }
} }
/// Is this a script-defined function? /// Is this a script-defined function?
@ -105,8 +105,8 @@ impl CallableFunction {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
match self { match self {
Self::Script(_) => true, Self::Script(..) => true,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false, Self::Pure(..) | Self::Method(..) | Self::Iterator(..) | Self::Plugin(..) => false,
} }
} }
/// Is this a plugin function? /// Is this a plugin function?
@ -114,11 +114,11 @@ impl CallableFunction {
#[must_use] #[must_use]
pub const fn is_plugin_fn(&self) -> bool { pub const fn is_plugin_fn(&self) -> bool {
match self { match self {
Self::Plugin(_) => true, Self::Plugin(..) => true,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => false, Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => false,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(_) => false, Self::Script(..) => false,
} }
} }
/// Is this a native Rust function? /// Is this a native Rust function?
@ -130,10 +130,10 @@ impl CallableFunction {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
match self { match self {
Self::Pure(_) | Self::Method(_) => true, Self::Pure(..) | Self::Method(..) => true,
Self::Plugin(_) => true, Self::Plugin(..) => true,
Self::Iterator(_) => true, Self::Iterator(..) => true,
Self::Script(_) => false, Self::Script(..) => false,
} }
} }
/// Get the access mode. /// Get the access mode.
@ -145,8 +145,8 @@ impl CallableFunction {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
match self { match self {
Self::Plugin(_) => FnAccess::Public, Self::Plugin(..) => FnAccess::Public,
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => FnAccess::Public, Self::Pure(..) | Self::Method(..) | Self::Iterator(..) => FnAccess::Public,
Self::Script(f) => f.access, Self::Script(f) => f.access,
} }
} }
@ -156,10 +156,10 @@ impl CallableFunction {
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> { pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
match self { match self {
Self::Pure(f) | Self::Method(f) => Some(f), Self::Pure(f) | Self::Method(f) => Some(f),
Self::Iterator(_) | Self::Plugin(_) => None, Self::Iterator(..) | Self::Plugin(..) => None,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(_) => None, Self::Script(..) => None,
} }
} }
/// Get a shared reference to a script-defined function definition. /// Get a shared reference to a script-defined function definition.
@ -170,7 +170,7 @@ impl CallableFunction {
#[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 {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => None, Self::Pure(..) | Self::Method(..) | Self::Iterator(..) | Self::Plugin(..) => None,
Self::Script(f) => Some(f), Self::Script(f) => Some(f),
} }
} }
@ -180,10 +180,10 @@ impl CallableFunction {
pub fn get_iter_fn(&self) -> Option<&IteratorFn> { pub fn get_iter_fn(&self) -> Option<&IteratorFn> {
match self { match self {
Self::Iterator(f) => Some(f.as_ref()), Self::Iterator(f) => Some(f.as_ref()),
Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => None, Self::Pure(..) | Self::Method(..) | Self::Plugin(..) => None,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(_) => None, Self::Script(..) => None,
} }
} }
/// Get a shared reference to a plugin function. /// Get a shared reference to a plugin function.
@ -192,10 +192,10 @@ impl CallableFunction {
pub fn get_plugin_fn(&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,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::Script(_) => None, Self::Script(..) => None,
} }
} }
/// Create a new [`CallableFunction::Pure`]. /// Create a new [`CallableFunction::Pure`].

View File

@ -333,7 +333,7 @@ impl<'a> NativeCallContext<'a> {
Position::NONE, Position::NONE,
self.level + 1, self.level + 1,
) )
.map(|(r, _)| r) .map(|(r, ..)| r)
} }
} }

View File

@ -5,10 +5,9 @@
use super::call::FnCallArgs; use super::call::FnCallArgs;
use super::callable_function::CallableFunction; use super::callable_function::CallableFunction;
use super::native::{FnAny, SendSync}; use super::native::{FnAny, SendSync};
use crate::r#unsafe::unsafe_cast;
use crate::tokenizer::Position; use crate::tokenizer::Position;
use crate::types::dynamic::{DynamicWriteLock, Variant}; use crate::types::dynamic::{DynamicWriteLock, Variant};
use crate::{Dynamic, NativeCallContext, RhaiResultOf, ERR}; use crate::{reify, Dynamic, NativeCallContext, RhaiResultOf, ERR};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{any::TypeId, mem}; use std::{any::TypeId, mem};
@ -39,23 +38,26 @@ pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
} }
/// Dereference into value. /// Dereference into value.
#[inline] #[inline(always)]
#[must_use] #[must_use]
pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T { pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
if TypeId::of::<T>() == TypeId::of::<&str>() { if TypeId::of::<T>() == TypeId::of::<&str>() {
// If T is `&str`, data must be `ImmutableString`, so map directly to it // If T is `&str`, data must be `ImmutableString`, so map directly to it
data.flatten_in_place(); data.flatten_in_place();
let ref_str = data.as_str_ref().expect("&str"); let ref_str = data.as_str_ref().expect("&str");
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) }; // # Safety
ref_t.clone() //
} else if TypeId::of::<T>() == TypeId::of::<String>() { // We already checked that `T` is `&str`, so it is safe to cast here.
// If T is `String`, data must be `ImmutableString`, so map directly to it return unsafe { std::mem::transmute_copy::<_, T>(&ref_str) };
unsafe_cast(mem::take(data).into_string().expect("`ImmutableString`"))
} else {
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
mem::take(data).cast::<T>()
} }
if TypeId::of::<T>() == TypeId::of::<String>() {
// If T is `String`, data must be `ImmutableString`, so map directly to it
return reify!(mem::take(data).into_string().expect("`ImmutableString`") => T);
}
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
return mem::take(data).cast::<T>();
} }
/// Trait to register custom Rust functions. /// Trait to register custom Rust functions.

View File

@ -103,7 +103,7 @@ impl Engine {
scope scope
.iter() .iter()
.skip(orig_scope_len) .skip(orig_scope_len)
.map(|(_, _, v)| v.clone()) .map(|(.., v)| v.clone())
.collect(), .collect(),
global.source.clone(), global.source.clone(),
pos, pos,
@ -161,9 +161,9 @@ impl Engine {
) )
.or_else(|err| match *err { .or_else(|err| match *err {
// Convert return statement to return value // Convert return statement to return value
ERR::Return(x, _) => Ok(x), ERR::Return(x, ..) => Ok(x),
// Error in sub function call // Error in sub function call
ERR::ErrorInFunctionCall(name, src, err, _) => { ERR::ErrorInFunctionCall(name, src, err, ..) => {
let fn_name = if src.is_empty() { let fn_name = if src.is_empty() {
format!("{} < {}", name, fn_def.name) format!("{} < {}", name, fn_def.name)
} else { } else {
@ -185,7 +185,7 @@ impl Engine {
{ {
let trigger = match global.debugger.status { let trigger = match global.debugger.status {
crate::eval::DebuggerStatus::FunctionExit(n) => n >= level, crate::eval::DebuggerStatus::FunctionExit(n) => n >= level,
crate::eval::DebuggerStatus::Next(_, true) => true, crate::eval::DebuggerStatus::Next(.., true) => true,
_ => false, _ => false,
}; };
if trigger { if trigger {

View File

@ -68,7 +68,6 @@ extern crate no_std_compat as std;
use std::prelude::v1::*; use std::prelude::v1::*;
// Internal modules // Internal modules
mod api; mod api;
mod ast; mod ast;
mod engine; mod engine;
@ -78,10 +77,10 @@ mod module;
mod optimizer; mod optimizer;
pub mod packages; pub mod packages;
mod parser; mod parser;
mod reify;
mod tests; mod tests;
mod tokenizer; mod tokenizer;
mod types; mod types;
mod r#unsafe;
/// Error encountered when parsing a script. /// Error encountered when parsing a script.
type PERR = ParseErrorType; type PERR = ParseErrorType;
@ -234,6 +233,10 @@ pub mod serde;
#[cfg(not(feature = "no_optimize"))] #[cfg(not(feature = "no_optimize"))]
pub use optimizer::OptimizationLevel; pub use optimizer::OptimizationLevel;
/// Placeholder for the optimization level.
#[cfg(feature = "no_optimize")]
pub type OptimizationLevel = ();
// Expose internal data structures. // Expose internal data structures.
#[cfg(feature = "internals")] #[cfg(feature = "internals")]

View File

@ -1402,7 +1402,7 @@ impl Module {
/// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level. /// Sub-modules are flattened onto the root [`Module`], with higher level overriding lower level.
#[inline] #[inline]
pub fn combine_flatten(&mut self, other: Self) -> &mut Self { pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
for (_, m) in other.modules.into_iter() { for (.., m) in other.modules.into_iter() {
self.combine_flatten(shared_take_or_clone(m)); self.combine_flatten(shared_take_or_clone(m));
} }
self.variables.extend(other.variables.into_iter()); self.variables.extend(other.variables.into_iter());
@ -1471,7 +1471,7 @@ impl Module {
other other
.functions .functions
.iter() .iter()
.filter(|&(_, f)| { .filter(|&(.., f)| {
_filter( _filter(
f.metadata.namespace, f.metadata.namespace,
f.metadata.access, f.metadata.access,
@ -1502,7 +1502,7 @@ impl Module {
) -> &mut Self { ) -> &mut Self {
self.functions = std::mem::take(&mut self.functions) self.functions = std::mem::take(&mut self.functions)
.into_iter() .into_iter()
.filter(|(_, f)| { .filter(|(.., f)| {
if f.func.is_script() { if f.func.is_script() {
filter( filter(
f.metadata.namespace, f.metadata.namespace,
@ -1717,7 +1717,7 @@ impl Module {
result?; result?;
// Variables with an alias left in the scope become module variables // Variables with an alias left in the scope become module variables
for (_, value, mut aliases) in scope { for (.., value, mut aliases) in scope {
match aliases.len() { match aliases.len() {
0 => (), 0 => (),
1 => { 1 => {

View File

@ -128,7 +128,7 @@ impl ModuleResolver for ModuleResolversCollection {
match resolver.resolve(engine, source_path, path, pos) { match resolver.resolve(engine, source_path, path, pos) {
Ok(module) => return Ok(module), Ok(module) => return Ok(module),
Err(err) => match *err { Err(err) => match *err {
ERR::ErrorModuleNotFound(_, _) => continue, ERR::ErrorModuleNotFound(..) => continue,
ERR::ErrorInModule(_, err, _) => return Err(err), ERR::ErrorInModule(_, err, _) => return Err(err),
_ => panic!("ModuleResolver::resolve returns error that is not ErrorModuleNotFound or ErrorInModule"), _ => panic!("ModuleResolver::resolve returns error that is not ErrorModuleNotFound or ErrorInModule"),
}, },

View File

@ -230,7 +230,7 @@ impl FileModuleResolver {
locked_write(&self.cache) locked_write(&self.cache)
.remove_entry(&file_path) .remove_entry(&file_path)
.map(|(_, v)| v) .map(|(.., v)| v)
} }
/// Construct a full file path. /// Construct a full file path.
#[must_use] #[must_use]
@ -288,7 +288,7 @@ impl FileModuleResolver {
let mut ast = engine let mut ast = engine
.compile_file(file_path.clone()) .compile_file(file_path.clone())
.map_err(|err| match *err { .map_err(|err| match *err {
ERR::ErrorSystem(_, err) if err.is::<IoError>() => { ERR::ErrorSystem(.., err) if err.is::<IoError>() => {
Box::new(ERR::ErrorModuleNotFound(path.to_string(), pos)) Box::new(ERR::ErrorModuleNotFound(path.to_string(), pos))
} }
_ => Box::new(ERR::ErrorInModule(path.to_string(), err, pos)), _ => Box::new(ERR::ErrorInModule(path.to_string(), err, pos)),
@ -356,7 +356,7 @@ impl ModuleResolver for FileModuleResolver {
ast ast
}) })
.map_err(|err| match *err { .map_err(|err| match *err {
ERR::ErrorSystem(_, err) if err.is::<IoError>() => { ERR::ErrorSystem(.., err) if err.is::<IoError>() => {
ERR::ErrorModuleNotFound(path.to_string(), pos).into() ERR::ErrorModuleNotFound(path.to_string(), pos).into()
} }
_ => ERR::ErrorInModule(path.to_string(), err, pos).into(), _ => ERR::ErrorInModule(path.to_string(), err, pos).into(),

View File

@ -150,7 +150,7 @@ impl<'a> OptimizerState<'a> {
0, 0,
) )
.ok() .ok()
.map(|(v, _)| v) .map(|(v, ..)| v)
} }
} }
@ -205,13 +205,13 @@ fn optimize_stmt_block(
// Flatten blocks // Flatten blocks
loop { loop {
if let Some(n) = statements.iter().enumerate().find_map(|(i, s)| match s { if let Some(n) = statements.iter().enumerate().find_map(|(i, s)| match s {
Stmt::Block(block, _) if !block.iter().any(Stmt::is_block_dependent) => Some(i), Stmt::Block(block, ..) if !block.iter().any(Stmt::is_block_dependent) => Some(i),
_ => None, _ => None,
}) { }) {
let (first, second) = statements.split_at_mut(n); let (first, second) = statements.split_at_mut(n);
let stmt = mem::take(&mut second[0]); let stmt = mem::take(&mut second[0]);
let mut stmts = match stmt { let mut stmts = match stmt {
Stmt::Block(block, _) => block, Stmt::Block(block, ..) => block,
stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt), stmt => unreachable!("Stmt::Block expected but gets {:?}", stmt),
}; };
statements = first statements = first
@ -252,7 +252,7 @@ fn optimize_stmt_block(
// Optimize each statement in the block // Optimize each statement in the block
for stmt in statements.iter_mut() { for stmt in statements.iter_mut() {
match stmt { match stmt {
Stmt::Var(value_expr, x, options, _) => { Stmt::Var(value_expr, x, options, ..) => {
if options.contains(AST_OPTION_CONSTANT) { if options.contains(AST_OPTION_CONSTANT) {
// Add constant literals into the state // Add constant literals into the state
optimize_expr(value_expr, state, false); optimize_expr(value_expr, state, false);
@ -284,10 +284,10 @@ fn optimize_stmt_block(
.find_map(|(i, stmt)| match stmt { .find_map(|(i, stmt)| match stmt {
stmt if !is_pure(stmt) => Some(i), stmt if !is_pure(stmt) => Some(i),
Stmt::Var(e, _, _, _) | Stmt::Expr(e) if !e.is_constant() => Some(i), Stmt::Var(e, _, ..) | Stmt::Expr(e) if !e.is_constant() => Some(i),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(e, _, _) if !e.is_constant() => Some(i), Stmt::Import(e, ..) if !e.is_constant() => Some(i),
_ => None, _ => None,
}) })
@ -320,7 +320,7 @@ fn optimize_stmt_block(
loop { loop {
match statements[..] { match statements[..] {
// { return; } -> {} // { return; } -> {}
[Stmt::Return(options, None, _)] [Stmt::Return(options, None, ..)]
if reduce_return && !options.contains(AST_OPTION_BREAK) => if reduce_return && !options.contains(AST_OPTION_BREAK) =>
{ {
state.set_dirty(); state.set_dirty();
@ -331,7 +331,7 @@ fn optimize_stmt_block(
statements.clear(); statements.clear();
} }
// { ...; return; } -> { ... } // { ...; return; } -> { ... }
[.., ref last_stmt, Stmt::Return(options, None, _)] [.., ref last_stmt, Stmt::Return(options, None, ..)]
if reduce_return if reduce_return
&& !options.contains(AST_OPTION_BREAK) && !options.contains(AST_OPTION_BREAK)
&& !last_stmt.returns_value() => && !last_stmt.returns_value() =>
@ -349,7 +349,7 @@ fn optimize_stmt_block(
.map_or_else(|| Stmt::Noop(pos), |e| Stmt::Expr(mem::take(e))); .map_or_else(|| Stmt::Noop(pos), |e| Stmt::Expr(mem::take(e)));
} }
// { ...; stmt; noop } -> done // { ...; stmt; noop } -> done
[.., ref second_last_stmt, Stmt::Noop(_)] [.., ref second_last_stmt, Stmt::Noop(..)]
if second_last_stmt.returns_value() => if second_last_stmt.returns_value() =>
{ {
break break
@ -377,14 +377,14 @@ fn optimize_stmt_block(
statements.clear(); statements.clear();
} }
// { ...; return; } -> { ... } // { ...; return; } -> { ... }
[.., Stmt::Return(options, None, _)] [.., Stmt::Return(options, None, ..)]
if reduce_return && !options.contains(AST_OPTION_BREAK) => if reduce_return && !options.contains(AST_OPTION_BREAK) =>
{ {
state.set_dirty(); state.set_dirty();
statements.pop().unwrap(); statements.pop().unwrap();
} }
// { ...; return pure_val; } -> { ... } // { ...; return pure_val; } -> { ... }
[.., Stmt::Return(options, Some(ref expr), _)] [.., Stmt::Return(options, Some(ref expr), ..)]
if reduce_return if reduce_return
&& !options.contains(AST_OPTION_BREAK) && !options.contains(AST_OPTION_BREAK)
&& expr.is_pure() => && expr.is_pure() =>
@ -424,17 +424,17 @@ fn optimize_stmt_block(
fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: bool) { fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: bool) {
match stmt { match stmt {
// var = var op expr => var op= expr // var = var op expr => var op= expr
Stmt::Assignment(x, _) Stmt::Assignment(x, ..)
if x.0.is_none() if x.0.is_none()
&& x.1.lhs.is_variable_access(true) && x.1.lhs.is_variable_access(true)
&& matches!(&x.1.rhs, Expr::FnCall(x2, _) && matches!(&x.1.rhs, Expr::FnCall(x2, ..)
if Token::lookup_from_syntax(&x2.name).map(|t| t.has_op_assignment()).unwrap_or(false) if Token::lookup_from_syntax(&x2.name).map(|t| t.has_op_assignment()).unwrap_or(false)
&& x2.args.len() == 2 && x2.args.len() == 2
&& x2.args[0].get_variable_name(true) == x.1.lhs.get_variable_name(true) && x2.args[0].get_variable_name(true) == x.1.lhs.get_variable_name(true)
) => ) =>
{ {
match x.1.rhs { match x.1.rhs {
Expr::FnCall(ref mut x2, _) => { Expr::FnCall(ref mut x2, ..) => {
state.set_dirty(); state.set_dirty();
x.0 = Some(OpAssignment::new_from_base(&x2.name)); x.0 = Some(OpAssignment::new_from_base(&x2.name));
@ -452,7 +452,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
// expr op= expr // expr op= expr
Stmt::Assignment(x, _) => { Stmt::Assignment(x, ..) => {
if !x.1.lhs.is_variable_access(false) { if !x.1.lhs.is_variable_access(false) {
optimize_expr(&mut x.1.lhs, state, false); optimize_expr(&mut x.1.lhs, state, false);
} }
@ -460,7 +460,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
// if expr {} // if expr {}
Stmt::If(condition, x, _) if x.0.is_empty() && x.1.is_empty() => { Stmt::If(condition, x, ..) if x.0.is_empty() && x.1.is_empty() => {
state.set_dirty(); state.set_dirty();
let pos = condition.start_position(); let pos = condition.start_position();
@ -479,12 +479,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
}; };
} }
// if false { if_block } -> Noop // if false { if_block } -> Noop
Stmt::If(Expr::BoolConstant(false, pos), x, _) if x.1.is_empty() => { Stmt::If(Expr::BoolConstant(false, pos), x, ..) if x.1.is_empty() => {
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Noop(*pos); *stmt = Stmt::Noop(*pos);
} }
// if false { if_block } else { else_block } -> else_block // if false { if_block } else { else_block } -> else_block
Stmt::If(Expr::BoolConstant(false, _), x, _) => { Stmt::If(Expr::BoolConstant(false, ..), x, ..) => {
state.set_dirty(); state.set_dirty();
*stmt = *stmt =
match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false) match optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false)
@ -494,7 +494,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
} }
// if true { if_block } else { else_block } -> if_block // if true { if_block } else { else_block } -> if_block
Stmt::If(Expr::BoolConstant(true, _), x, _) => { Stmt::If(Expr::BoolConstant(true, ..), x, ..) => {
state.set_dirty(); state.set_dirty();
*stmt = *stmt =
match optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false) match optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false)
@ -504,7 +504,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
} }
// if expr { if_block } else { else_block } // if expr { if_block } else { else_block }
Stmt::If(condition, x, _) => { Stmt::If(condition, x, ..) => {
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
*x.0 = optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false); *x.0 = optimize_stmt_block(mem::take(&mut *x.0), state, preserve_result, true, false);
*x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false); *x.1 = optimize_stmt_block(mem::take(&mut *x.1), state, preserve_result, true, false);
@ -564,11 +564,11 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
let value = value.as_int().expect("`INT`"); let value = value.as_int().expect("`INT`");
// Only one range or all ranges without conditions // Only one range or all ranges without conditions
if ranges.len() == 1 || ranges.iter().all(|(_, _, _, c)| !c.has_condition()) { if ranges.len() == 1 || ranges.iter().all(|(.., c)| !c.has_condition()) {
for (_, _, _, block) in for (.., block) in
ranges ranges
.iter_mut() .iter_mut()
.filter(|&&mut (start, end, inclusive, _)| { .filter(|&&mut (start, end, inclusive, ..)| {
(!inclusive && (start..end).contains(&value)) (!inclusive && (start..end).contains(&value))
|| (inclusive && (start..=end).contains(&value)) || (inclusive && (start..=end).contains(&value))
}) })
@ -619,7 +619,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
let old_ranges_len = ranges.len(); let old_ranges_len = ranges.len();
ranges.retain(|&mut (start, end, inclusive, _)| { ranges.retain(|&mut (start, end, inclusive, ..)| {
(!inclusive && (start..end).contains(&value)) (!inclusive && (start..end).contains(&value))
|| (inclusive && (start..=end).contains(&value)) || (inclusive && (start..=end).contains(&value))
}); });
@ -628,7 +628,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
state.set_dirty(); state.set_dirty();
} }
for (_, _, _, block) in ranges.iter_mut() { for (.., block) in ranges.iter_mut() {
let statements = mem::take(&mut *block.statements); let statements = mem::take(&mut *block.statements);
*block.statements = *block.statements =
optimize_stmt_block(statements, state, preserve_result, true, false); optimize_stmt_block(statements, state, preserve_result, true, false);
@ -636,7 +636,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if let Some(mut condition) = mem::take(&mut block.condition) { if let Some(mut condition) = mem::take(&mut block.condition) {
optimize_expr(&mut condition, state, false); optimize_expr(&mut condition, state, false);
match condition { match condition {
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(), Expr::Unit(..) | Expr::BoolConstant(true, ..) => state.set_dirty(),
_ => block.condition = Some(condition), _ => block.condition = Some(condition),
} }
} }
@ -655,7 +655,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
); );
} }
// switch // switch
Stmt::Switch(match_expr, x, _) => { Stmt::Switch(match_expr, x, ..) => {
optimize_expr(match_expr, state, false); optimize_expr(match_expr, state, false);
for block in x.cases.values_mut() { for block in x.cases.values_mut() {
let statements = mem::take(&mut *block.statements); let statements = mem::take(&mut *block.statements);
@ -665,15 +665,15 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
if let Some(mut condition) = mem::take(&mut block.condition) { if let Some(mut condition) = mem::take(&mut block.condition) {
optimize_expr(&mut condition, state, false); optimize_expr(&mut condition, state, false);
match condition { match condition {
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(), Expr::Unit(..) | Expr::BoolConstant(true, ..) => state.set_dirty(),
_ => block.condition = Some(condition), _ => block.condition = Some(condition),
} }
} }
} }
// Remove false cases // Remove false cases
while let Some((&key, _)) = x.cases.iter().find(|(_, block)| match block.condition { while let Some((&key, ..)) = x.cases.iter().find(|(.., block)| match block.condition {
Some(Expr::BoolConstant(false, _)) => true, Some(Expr::BoolConstant(false, ..)) => true,
_ => false, _ => false,
}) { }) {
state.set_dirty(); state.set_dirty();
@ -685,12 +685,12 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
// while false { block } -> Noop // while false { block } -> Noop
Stmt::While(Expr::BoolConstant(false, pos), _, _) => { Stmt::While(Expr::BoolConstant(false, pos), ..) => {
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Noop(*pos) *stmt = Stmt::Noop(*pos)
} }
// while expr { block } // while expr { block }
Stmt::While(condition, body, _) => { Stmt::While(condition, body, ..) => {
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
if let Expr::BoolConstant(true, pos) = condition { if let Expr::BoolConstant(true, pos) = condition {
*condition = Expr::Unit(*pos); *condition = Expr::Unit(*pos);
@ -719,7 +719,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
} }
// do { block } while false | do { block } until true -> { block } // do { block } while false | do { block } until true -> { block }
Stmt::Do(body, Expr::BoolConstant(x, _), options, _) Stmt::Do(body, Expr::BoolConstant(x, ..), options, ..)
if *x == options.contains(AST_OPTION_NEGATED) => if *x == options.contains(AST_OPTION_NEGATED) =>
{ {
state.set_dirty(); state.set_dirty();
@ -730,22 +730,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
); );
} }
// do { block } while|until expr // do { block } while|until expr
Stmt::Do(body, condition, _, _) => { Stmt::Do(body, condition, ..) => {
optimize_expr(condition, state, false); optimize_expr(condition, state, false);
***body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false); ***body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
} }
// for id in expr { block } // for id in expr { block }
Stmt::For(iterable, x, _) => { Stmt::For(iterable, x, ..) => {
optimize_expr(iterable, state, false); optimize_expr(iterable, state, false);
*x.2 = optimize_stmt_block(mem::take(&mut *x.2), state, false, true, false); *x.2 = optimize_stmt_block(mem::take(&mut *x.2), state, false, true, false);
} }
// let id = expr; // let id = expr;
Stmt::Var(expr, _, options, _) if !options.contains(AST_OPTION_CONSTANT) => { Stmt::Var(expr, _, options, ..) if !options.contains(AST_OPTION_CONSTANT) => {
optimize_expr(expr, state, false) optimize_expr(expr, state, false)
} }
// import expr as var; // import expr as var;
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Stmt::Import(expr, _, _) => optimize_expr(expr, state, false), Stmt::Import(expr, ..) => optimize_expr(expr, state, false),
// { block } // { block }
Stmt::Block(statements, pos) => { Stmt::Block(statements, pos) => {
let statements = mem::take(statements).into_vec().into(); let statements = mem::take(statements).into_vec().into();
@ -765,7 +765,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
} }
} }
// try { pure try_block } catch ( var ) { catch_block } -> try_block // try { pure try_block } catch ( var ) { catch_block } -> try_block
Stmt::TryCatch(x, _) if x.try_block.iter().all(Stmt::is_pure) => { Stmt::TryCatch(x, ..) if x.try_block.iter().all(Stmt::is_pure) => {
// If try block is pure, there will never be any exceptions // If try block is pure, there will never be any exceptions
state.set_dirty(); state.set_dirty();
*stmt = Stmt::Block( *stmt = Stmt::Block(
@ -775,14 +775,14 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
); );
} }
// try { try_block } catch ( var ) { catch_block } // try { try_block } catch ( var ) { catch_block }
Stmt::TryCatch(x, _) => { Stmt::TryCatch(x, ..) => {
*x.try_block = *x.try_block =
optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false); optimize_stmt_block(mem::take(&mut *x.try_block), state, false, true, false);
*x.catch_block = *x.catch_block =
optimize_stmt_block(mem::take(&mut *x.catch_block), state, false, true, false); optimize_stmt_block(mem::take(&mut *x.catch_block), state, false, true, false);
} }
// func(...) // func(...)
Stmt::Expr(expr @ Expr::FnCall(_, _)) => { Stmt::Expr(expr @ Expr::FnCall(..)) => {
optimize_expr(expr, state, false); optimize_expr(expr, state, false);
match expr { match expr {
Expr::FnCall(x, pos) => { Expr::FnCall(x, pos) => {
@ -838,31 +838,31 @@ 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.as_str(); let prop = p.2.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));
} }
// var.rhs // var.rhs
(Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state, true), (Expr::Variable(..), rhs) => optimize_expr(rhs, state, true),
// lhs.rhs // lhs.rhs
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); } (lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); }
} }
// ....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, _)) if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) => { (Expr::Array(a, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) => {
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
@ -871,7 +871,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
*expr = result; *expr = result;
} }
// array[-int] // array[-int]
(Expr::Array(a, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= a.len()).unwrap_or(false) && a.iter().all(Expr::is_pure) => { (Expr::Array(a, pos), Expr::IntegerConstant(i, ..)) if *i < 0 && i.checked_abs().map(|n| n as usize <= a.len()).unwrap_or(false) && a.iter().all(Expr::is_pure) => {
// Array literal where everything is pure - promote the indexed item. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); state.set_dirty();
@ -881,58 +881,58 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
*expr = result; *expr = result;
} }
// map[string] // map[string]
(Expr::Map(m, pos), Expr::StringConstant(s, _)) if m.0.iter().all(|(_, x)| x.is_pure()) => { (Expr::Map(m, pos), Expr::StringConstant(s, ..)) if m.0.iter().all(|(.., x)| x.is_pure()) => {
// 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.as_str() == s.as_str()) *expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name.as_str() == s.as_str())
.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));
} }
// int[int] // int[int]
(Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < (std::mem::size_of_val(n) * 8) => { (Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < (std::mem::size_of_val(n) * 8) => {
// Bit-field literal indexing - get the bit // Bit-field literal indexing - get the bit
state.set_dirty(); state.set_dirty();
*expr = Expr::BoolConstant((*n & (1 << (*i as usize))) != 0, *pos); *expr = Expr::BoolConstant((*n & (1 << (*i as usize))) != 0, *pos);
} }
// int[-int] // int[-int]
(Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|i| i as usize <= (std::mem::size_of_val(n) * 8)).unwrap_or(false) => { (Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i < 0 && i.checked_abs().map(|i| i as usize <= (std::mem::size_of_val(n) * 8)).unwrap_or(false) => {
// Bit-field literal indexing - get the bit // Bit-field literal indexing - get the bit
state.set_dirty(); state.set_dirty();
*expr = Expr::BoolConstant((*n & (1 << (std::mem::size_of_val(n) * 8 - i.abs() as usize))) != 0, *pos); *expr = Expr::BoolConstant((*n & (1 << (std::mem::size_of_val(n) * 8 - i.abs() as usize))) != 0, *pos);
} }
// string[int] // string[int]
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i >= 0 && (*i as usize) < s.chars().count() => { (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < s.chars().count() => {
// String literal indexing - get the character // String literal indexing - get the character
state.set_dirty(); state.set_dirty();
*expr = Expr::CharConstant(s.chars().nth(*i as usize).unwrap(), *pos); *expr = Expr::CharConstant(s.chars().nth(*i as usize).unwrap(), *pos);
} }
// string[-int] // string[-int]
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, _)) if *i < 0 && i.checked_abs().map(|n| n as usize <= s.chars().count()).unwrap_or(false) => { (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, ..)) if *i < 0 && i.checked_abs().map(|n| n as usize <= s.chars().count()).unwrap_or(false) => {
// String literal indexing - get the character // String literal indexing - get the character
state.set_dirty(); state.set_dirty();
*expr = Expr::CharConstant(s.chars().rev().nth(i.abs() as usize - 1).unwrap(), *pos); *expr = Expr::CharConstant(s.chars().rev().nth(i.abs() as usize - 1).unwrap(), *pos);
} }
// var[rhs] // var[rhs]
(Expr::Variable(_, _, _), rhs) => optimize_expr(rhs, state, true), (Expr::Variable(..), rhs) => optimize_expr(rhs, state, true),
// lhs[rhs] // lhs[rhs]
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); } (lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, true); }
}, },
// ...[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();
*expr = Expr::StringConstant(state.engine.const_empty_string(), *pos); *expr = Expr::StringConstant(state.engine.const_empty_string(), *pos);
} }
// `...` // `...`
Expr::InterpolatedString(x, _) if x.len() == 1 && matches!(x[0], Expr::StringConstant(_, _)) => { Expr::InterpolatedString(x, ..) if x.len() == 1 && matches!(x[0], Expr::StringConstant(..)) => {
state.set_dirty(); state.set_dirty();
*expr = mem::take(&mut x[0]); *expr = mem::take(&mut x[0]);
} }
// `... ${ ... } ...` // `... ${ ... } ...`
Expr::InterpolatedString(x, _) => { Expr::InterpolatedString(x, ..) => {
x.iter_mut().for_each(|expr| optimize_expr(expr, state, false)); x.iter_mut().for_each(|expr| optimize_expr(expr, state, false));
let mut n = 0; let mut n = 0;
@ -940,11 +940,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
// Merge consecutive strings // Merge consecutive strings
while n < x.len() - 1 { while n < x.len() - 1 {
match (mem::take(&mut x[n]), mem::take(&mut x[n+1])) { match (mem::take(&mut x[n]), mem::take(&mut x[n+1])) {
(Expr::StringConstant(mut s1, pos), Expr::StringConstant(s2, _)) => { s1 += s2; x[n] = Expr::StringConstant(s1, pos); x.remove(n+1); state.set_dirty(); } (Expr::StringConstant(mut s1, pos), Expr::StringConstant(s2, ..)) => { s1 += s2; x[n] = Expr::StringConstant(s1, pos); x.remove(n+1); state.set_dirty(); }
(expr1, Expr::Unit(_)) => { x[n] = expr1; x.remove(n+1); state.set_dirty(); } (expr1, Expr::Unit(..)) => { x[n] = expr1; x.remove(n+1); state.set_dirty(); }
(Expr::Unit(_), expr2) => { x[n+1] = expr2; x.remove(n); state.set_dirty(); } (Expr::Unit(..), expr2) => { x[n+1] = expr2; x.remove(n); state.set_dirty(); }
(expr1, Expr::StringConstant(s, _)) if s.is_empty() => { x[n] = expr1; x.remove(n+1); state.set_dirty(); } (expr1, Expr::StringConstant(s, ..)) if s.is_empty() => { x[n] = expr1; x.remove(n+1); state.set_dirty(); }
(Expr::StringConstant(s, _), expr2) if s.is_empty()=> { x[n+1] = expr2; x.remove(n); state.set_dirty(); } (Expr::StringConstant(s, ..), expr2) if s.is_empty()=> { x[n+1] = expr2; x.remove(n); state.set_dirty(); }
(expr1, expr2) => { x[n] = expr1; x[n+1] = expr2; n += 1; } (expr1, expr2) => { x[n] = expr1; x[n+1] = expr2; n += 1; }
} }
} }
@ -953,47 +953,47 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
} }
// [ constant .. ] // [ constant .. ]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(_, _) if expr.is_constant() => { Expr::Array(..) if expr.is_constant() => {
state.set_dirty(); state.set_dirty();
*expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position()); *expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position());
} }
// [ items .. ] // [ items .. ]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Expr::Array(x, _) => x.iter_mut().for_each(|expr| optimize_expr(expr, state, false)), Expr::Array(x, ..) => x.iter_mut().for_each(|expr| optimize_expr(expr, state, false)),
// #{ key:constant, .. } // #{ key:constant, .. }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(_, _) if expr.is_constant() => { Expr::Map(..) if expr.is_constant() => {
state.set_dirty(); state.set_dirty();
*expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position()); *expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position());
} }
// #{ key:value, .. } // #{ key:value, .. }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Expr::Map(x, _) => x.0.iter_mut().for_each(|(_, expr)| optimize_expr(expr, state, false)), Expr::Map(x, ..) => x.0.iter_mut().for_each(|(.., expr)| optimize_expr(expr, state, false)),
// lhs && rhs // lhs && rhs
Expr::And(x, _) => match (&mut x.lhs, &mut x.rhs) { Expr::And(x, ..) => match (&mut x.lhs, &mut x.rhs) {
// true && rhs -> rhs // true && rhs -> rhs
(Expr::BoolConstant(true, _), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); } (Expr::BoolConstant(true, ..), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); }
// false && rhs -> false // false && rhs -> false
(Expr::BoolConstant(false, pos), _) => { state.set_dirty(); *expr = Expr::BoolConstant(false, *pos); } (Expr::BoolConstant(false, pos), ..) => { state.set_dirty(); *expr = Expr::BoolConstant(false, *pos); }
// lhs && true -> lhs // lhs && true -> lhs
(lhs, Expr::BoolConstant(true, _)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); } (lhs, Expr::BoolConstant(true, ..)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); }
// lhs && rhs // lhs && rhs
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); } (lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); }
}, },
// lhs || rhs // lhs || rhs
Expr::Or(ref mut x, _) => match (&mut x.lhs, &mut x.rhs) { Expr::Or(ref mut x, ..) => match (&mut x.lhs, &mut x.rhs) {
// false || rhs -> rhs // false || rhs -> rhs
(Expr::BoolConstant(false, _), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); } (Expr::BoolConstant(false, ..), rhs) => { state.set_dirty(); optimize_expr(rhs, state, false); *expr = mem::take(rhs); }
// true || rhs -> true // true || rhs -> true
(Expr::BoolConstant(true, pos), _) => { state.set_dirty(); *expr = Expr::BoolConstant(true, *pos); } (Expr::BoolConstant(true, pos), ..) => { state.set_dirty(); *expr = Expr::BoolConstant(true, *pos); }
// lhs || false // lhs || false
(lhs, Expr::BoolConstant(false, _)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); } (lhs, Expr::BoolConstant(false, ..)) => { state.set_dirty(); optimize_expr(lhs, state, false); *expr = mem::take(lhs); }
// lhs || rhs // lhs || rhs
(lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); } (lhs, rhs) => { optimize_expr(lhs, state, false); optimize_expr(rhs, state, false); }
}, },
// eval! // eval!
Expr::FnCall(x, _) if x.name == KEYWORD_EVAL => { Expr::FnCall(x, ..) if x.name == KEYWORD_EVAL => {
state.propagate_constants = false; state.propagate_constants = false;
} }
// Fn // Fn
@ -1005,8 +1005,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
&& x.args[0].is_constant() && x.args[0].is_constant()
=> { => {
let fn_name = match x.args[0] { let fn_name = match x.args[0] {
Expr::Stack(slot, _) => x.constants[slot].clone(), Expr::Stack(slot, ..) => x.constants[slot].clone(),
Expr::StringConstant(ref s, _) => s.clone().into(), Expr::StringConstant(ref s, ..) => s.clone().into(),
_ => Dynamic::UNIT _ => Dynamic::UNIT
}; };
@ -1019,7 +1019,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
} }
// Do not call some special keywords // Do not call some special keywords
Expr::FnCall(x, _) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => { Expr::FnCall(x, ..) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => {
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
} }
@ -1031,7 +1031,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted //&& !is_valid_identifier(x.name.chars()) // cannot be scripted
=> { => {
let arg_values = &mut 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::<StaticVec<_>>(); }).collect::<StaticVec<_>>();
@ -1097,7 +1097,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
if !has_script_fn { if !has_script_fn {
let arg_values = &mut 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::<StaticVec<_>>(); }).collect::<StaticVec<_>>();
@ -1119,7 +1119,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
} }
// id(args ..) -> optimize function call arguments // id(args ..) -> optimize function call arguments
Expr::FnCall(x, _) => for arg in x.args.iter_mut() { Expr::FnCall(x, ..) => for arg in x.args.iter_mut() {
optimize_expr(arg, state, false); optimize_expr(arg, state, false);
// Move constant arguments // Move constant arguments
@ -1132,15 +1132,15 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
// constant-name // constant-name
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
Expr::Variable(_, _, x) if x.1.is_some() => (), Expr::Variable(.., x) if x.1.is_some() => (),
Expr::Variable(_, pos, x) if state.find_constant(&x.2).is_some() => { Expr::Variable(.., pos, x) if state.find_constant(&x.2).is_some() => {
// Replace constant with value // Replace constant with value
*expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos); *expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos);
state.set_dirty(); state.set_dirty();
} }
// Custom syntax // Custom syntax
Expr::Custom(x, _) => { Expr::Custom(x, ..) => {
if x.scope_may_be_changed { if x.scope_may_be_changed {
state.propagate_constants = false; state.propagate_constants = false;
} }

View File

@ -52,7 +52,7 @@ pub mod array_functions {
return Dynamic::UNIT; return Dynamic::UNIT;
} }
let (index, _) = calc_offset_len(array.len(), index, 0); let (index, ..) = calc_offset_len(array.len(), index, 0);
if index >= array.len() { if index >= array.len() {
Dynamic::UNIT Dynamic::UNIT
@ -88,7 +88,7 @@ pub mod array_functions {
return; return;
} }
let (index, _) = calc_offset_len(array.len(), index, 0); let (index, ..) = calc_offset_len(array.len(), index, 0);
if index < array.len() { if index < array.len() {
array[index] = value; array[index] = value;
@ -182,7 +182,7 @@ pub mod array_functions {
return; return;
} }
let (index, _) = calc_offset_len(array.len(), index, 0); let (index, ..) = calc_offset_len(array.len(), index, 0);
if index >= array.len() { if index >= array.len() {
array.push(item); array.push(item);
@ -231,10 +231,11 @@ pub mod array_functions {
} }
let check_sizes = match item.0 { let check_sizes = match item.0 {
crate::types::dynamic::Union::Array(_, _, _) crate::types::dynamic::Union::Array(..) | crate::types::dynamic::Union::Str(..) => {
| crate::types::dynamic::Union::Str(_, _, _) => true, true
}
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
crate::types::dynamic::Union::Map(_, _, _) => true, crate::types::dynamic::Union::Map(..) => true,
_ => false, _ => false,
}; };
@ -651,7 +652,7 @@ pub mod array_functions {
mapper mapper
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(mapper.fn_name()) => if fn_sig.starts_with(mapper.fn_name()) =>
{ {
mapper.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) mapper.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -740,7 +741,7 @@ pub mod array_functions {
if filter if filter
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -828,7 +829,7 @@ pub mod array_functions {
if ctx if ctx
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => { ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
if item.type_id() == value.type_id() { if item.type_id() == value.type_id() {
// No default when comparing same type // No default when comparing same type
Err(err) Err(err)
@ -914,13 +915,13 @@ pub mod array_functions {
return Ok(-1); return Ok(-1);
} }
let (start, _) = calc_offset_len(array.len(), start, 0); let (start, ..) = calc_offset_len(array.len(), start, 0);
for (i, item) in array.iter_mut().enumerate().skip(start) { for (i, item) in array.iter_mut().enumerate().skip(start) {
if ctx if ctx
.call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()]) .call_fn_raw(OP_EQUALS, true, false, &mut [item, &mut value.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => { ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
if item.type_id() == value.type_id() { if item.type_id() == value.type_id() {
// No default when comparing same type // No default when comparing same type
Err(err) Err(err)
@ -1044,13 +1045,13 @@ pub mod array_functions {
return Ok(-1); return Ok(-1);
} }
let (start, _) = calc_offset_len(array.len(), start, 0); let (start, ..) = calc_offset_len(array.len(), start, 0);
for (i, item) in array.iter().enumerate().skip(start) { for (i, item) in array.iter().enumerate().skip(start) {
if filter if filter
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -1149,7 +1150,7 @@ pub mod array_functions {
if filter if filter
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -1236,7 +1237,7 @@ pub mod array_functions {
if !filter if !filter
.call_raw(&ctx, None, [item.clone()]) .call_raw(&ctx, None, [item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [item.clone(), (i as INT).into()])
@ -1486,7 +1487,7 @@ pub mod array_functions {
result = reducer result = reducer
.call_raw(&ctx, None, [result.clone(), item.clone()]) .call_raw(&ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_raw(&ctx, None, [result, item, (i as INT).into()]) reducer.call_raw(&ctx, None, [result, item, (i as INT).into()])
@ -1648,7 +1649,7 @@ pub mod array_functions {
result = reducer result = reducer
.call_raw(&ctx, None, [result.clone(), item.clone()]) .call_raw(&ctx, None, [result.clone(), item.clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(reducer.fn_name()) => if fn_sig.starts_with(reducer.fn_name()) =>
{ {
reducer.call_raw(&ctx, None, [result, item, ((len - 1 - i) as INT).into()]) reducer.call_raw(&ctx, None, [result, item, ((len - 1 - i) as INT).into()])
@ -1925,7 +1926,7 @@ pub mod array_functions {
if filter if filter
.call_raw(&ctx, None, [array[x].clone()]) .call_raw(&ctx, None, [array[x].clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()])
@ -2121,7 +2122,7 @@ pub mod array_functions {
if !filter if !filter
.call_raw(&ctx, None, [array[x].clone()]) .call_raw(&ctx, None, [array[x].clone()])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(fn_sig, _) ERR::ErrorFunctionNotFound(fn_sig, ..)
if fn_sig.starts_with(filter.fn_name()) => if fn_sig.starts_with(filter.fn_name()) =>
{ {
filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()]) filter.call_raw(&ctx, None, [array[x].clone(), (i as INT).into()])
@ -2311,7 +2312,7 @@ pub mod array_functions {
if !ctx if !ctx
.call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2]) .call_fn_raw(OP_EQUALS, true, false, &mut [a1, a2])
.or_else(|err| match *err { .or_else(|err| match *err {
ERR::ErrorFunctionNotFound(ref fn_sig, _) if fn_sig.starts_with(OP_EQUALS) => { ERR::ErrorFunctionNotFound(ref fn_sig, ..) if fn_sig.starts_with(OP_EQUALS) => {
if a1.type_id() == a2.type_id() { if a1.type_id() == a2.type_id() {
// No default when comparing same type // No default when comparing same type
Err(err) Err(err)

View File

@ -124,7 +124,7 @@ pub mod blob_functions {
return 0; return 0;
} }
let (index, _) = calc_offset_len(blob.len(), index, 0); let (index, ..) = calc_offset_len(blob.len(), index, 0);
if index >= blob.len() { if index >= blob.len() {
0 0
@ -162,7 +162,7 @@ pub mod blob_functions {
return; return;
} }
let (index, _) = calc_offset_len(blob.len(), index, 0); let (index, ..) = calc_offset_len(blob.len(), index, 0);
if index < blob.len() { if index < blob.len() {
blob[index] = (value & 0x000000ff) as u8; blob[index] = (value & 0x000000ff) as u8;
@ -256,7 +256,7 @@ pub mod blob_functions {
return; return;
} }
let (index, _) = calc_offset_len(blob.len(), index, 0); let (index, ..) = calc_offset_len(blob.len(), index, 0);
if index >= blob.len() { if index >= blob.len() {
blob.push(value); blob.push(value);

View File

@ -152,7 +152,7 @@ fn collect_fn_metadata(
ctx.iter_namespaces() ctx.iter_namespaces()
.flat_map(Module::iter_script_fn) .flat_map(Module::iter_script_fn)
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f)) .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
.for_each(|(_, _, _, _, f)| { .for_each(|(.., f)| {
list.push( list.push(
make_metadata( make_metadata(
&dict, &dict,
@ -169,7 +169,7 @@ fn collect_fn_metadata(
.iter() .iter()
.flat_map(|m| m.iter_script_fn()) .flat_map(|m| m.iter_script_fn())
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f)) .filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
.for_each(|(_, _, _, _, f)| { .for_each(|(.., f)| {
list.push( list.push(
make_metadata( make_metadata(
&dict, &dict,
@ -187,7 +187,7 @@ fn collect_fn_metadata(
.values() .values()
.flat_map(|m| m.iter_script_fn()) .flat_map(|m| m.iter_script_fn())
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f)) .filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
.for_each(|(_, _, _, _, f)| { .for_each(|(.., f)| {
list.push( list.push(
make_metadata( make_metadata(
&dict, &dict,
@ -219,7 +219,7 @@ fn collect_fn_metadata(
module module
.iter_script_fn() .iter_script_fn()
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f)) .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
.for_each(|(_, _, _, _, f)| { .for_each(|(.., f)| {
list.push(make_metadata(dict, Some(namespace.clone()), f).into()) list.push(make_metadata(dict, Some(namespace.clone()), f).into())
}); });
for (ns, m) in module.iter_sub_modules() { for (ns, m) in module.iter_sub_modules() {

File diff suppressed because it is too large Load Diff

40
src/reify.rs Normal file
View File

@ -0,0 +1,40 @@
/// Runs `$code` if `$old` is of type `$t`.
///
/// This macro is primarily used for type casting between known types.
#[macro_export]
macro_rules! reify {
($old:ident, |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
if std::any::TypeId::of::<$t>() == std::any::Any::type_id(&$old) {
// SAFETY: This is safe because we check to make sure the two types are
// actually the same type.
let $new: $t = unsafe { std::mem::transmute_copy(&std::mem::ManuallyDrop::new($old)) };
$code
} else {
$fallback
}
}};
($old:expr, |$new:ident : $t:ty| $code:expr, || $fallback:expr) => {{
let old = $old;
reify!(old, |$new: $t| $code, || $fallback)
}};
($old:ident, |$new:ident : $t:ty| $code:expr) => {
reify!($old, |$new: $t| $code, || ())
};
($old:expr, |$new:ident : $t:ty| $code:expr) => {
reify!($old, |$new: $t| $code, || ())
};
($old:ident => Option<$t:ty>) => {
reify!($old, |v: $t| Some(v), || None)
};
($old:expr => Option<$t:ty>) => {
reify!($old, |v: $t| Some(v), || None)
};
($old:ident => $t:ty) => {
reify!($old, |v: $t| v, || unreachable!())
};
($old:expr => $t:ty) => {
reify!($old, |v: $t| v, || unreachable!())
};
}

View File

@ -118,55 +118,55 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
match self.value.0 { match self.value.0 {
Union::Unit(_, _, _) => self.deserialize_unit(visitor), Union::Unit(..) => self.deserialize_unit(visitor),
Union::Bool(_, _, _) => self.deserialize_bool(visitor), Union::Bool(..) => self.deserialize_bool(visitor),
Union::Str(_, _, _) => self.deserialize_str(visitor), Union::Str(..) => self.deserialize_str(visitor),
Union::Char(_, _, _) => self.deserialize_char(visitor), Union::Char(..) => self.deserialize_char(visitor),
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
Union::Int(_, _, _) => self.deserialize_i64(visitor), Union::Int(..) => self.deserialize_i64(visitor),
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
Union::Int(_, _, _) => self.deserialize_i32(visitor), Union::Int(..) => self.deserialize_i32(visitor),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
Union::Float(_, _, _) => self.deserialize_f64(visitor), Union::Float(..) => self.deserialize_f64(visitor),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(feature = "f32_float")] #[cfg(feature = "f32_float")]
Union::Float(_, _, _) => self.deserialize_f32(visitor), Union::Float(..) => self.deserialize_f32(visitor),
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
Union::Decimal(_, _, _) => self.deserialize_f64(visitor), Union::Decimal(..) => self.deserialize_f64(visitor),
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[cfg(feature = "f32_float")] #[cfg(feature = "f32_float")]
Union::Decimal(_, _, _) => self.deserialize_f32(visitor), Union::Decimal(..) => self.deserialize_f32(visitor),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(_, _, _) => self.deserialize_seq(visitor), Union::Array(..) => self.deserialize_seq(visitor),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(_, _, _) => self.deserialize_bytes(visitor), Union::Blob(..) => self.deserialize_bytes(visitor),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(_, _, _) => self.deserialize_map(visitor), Union::Map(..) => self.deserialize_map(visitor),
Union::FnPtr(_, _, _) => self.type_error(), Union::FnPtr(..) => self.type_error(),
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
Union::TimeStamp(_, _, _) => self.type_error(), Union::TimeStamp(..) => self.type_error(),
Union::Variant(ref value, _, _) if value.is::<i8>() => self.deserialize_i8(visitor), Union::Variant(ref value, ..) if value.is::<i8>() => self.deserialize_i8(visitor),
Union::Variant(ref value, _, _) if value.is::<i16>() => self.deserialize_i16(visitor), Union::Variant(ref value, ..) if value.is::<i16>() => self.deserialize_i16(visitor),
Union::Variant(ref value, _, _) if value.is::<i32>() => self.deserialize_i32(visitor), Union::Variant(ref value, ..) if value.is::<i32>() => self.deserialize_i32(visitor),
Union::Variant(ref value, _, _) if value.is::<i64>() => self.deserialize_i64(visitor), Union::Variant(ref value, ..) if value.is::<i64>() => self.deserialize_i64(visitor),
Union::Variant(ref value, _, _) if value.is::<i128>() => self.deserialize_i128(visitor), Union::Variant(ref value, ..) if value.is::<i128>() => self.deserialize_i128(visitor),
Union::Variant(ref value, _, _) if value.is::<u8>() => self.deserialize_u8(visitor), Union::Variant(ref value, ..) if value.is::<u8>() => self.deserialize_u8(visitor),
Union::Variant(ref value, _, _) if value.is::<u16>() => self.deserialize_u16(visitor), Union::Variant(ref value, ..) if value.is::<u16>() => self.deserialize_u16(visitor),
Union::Variant(ref value, _, _) if value.is::<u32>() => self.deserialize_u32(visitor), Union::Variant(ref value, ..) if value.is::<u32>() => self.deserialize_u32(visitor),
Union::Variant(ref value, _, _) if value.is::<u64>() => self.deserialize_u64(visitor), Union::Variant(ref value, ..) if value.is::<u64>() => self.deserialize_u64(visitor),
Union::Variant(ref value, _, _) if value.is::<u128>() => self.deserialize_u128(visitor), Union::Variant(ref value, ..) if value.is::<u128>() => self.deserialize_u128(visitor),
Union::Variant(_, _, _) => self.type_error(), Union::Variant(..) => self.type_error(),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(_, _, _) => self.type_error(), Union::Shared(..) => self.type_error(),
} }
} }

View File

@ -15,26 +15,26 @@ use crate::types::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 {
Union::Unit(_, _, _) => ser.serialize_unit(), Union::Unit(..) => ser.serialize_unit(),
Union::Bool(x, _, _) => ser.serialize_bool(x), Union::Bool(x, ..) => ser.serialize_bool(x),
Union::Str(ref s, _, _) => ser.serialize_str(s.as_str()), Union::Str(ref s, ..) => ser.serialize_str(s.as_str()),
Union::Char(c, _, _) => ser.serialize_str(&c.to_string()), Union::Char(c, ..) => ser.serialize_str(&c.to_string()),
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
Union::Int(x, _, _) => ser.serialize_i64(x), Union::Int(x, ..) => ser.serialize_i64(x),
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
Union::Int(x, _, _) => ser.serialize_i32(x), Union::Int(x, ..) => ser.serialize_i32(x),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
Union::Float(x, _, _) => ser.serialize_f64(*x), Union::Float(x, ..) => ser.serialize_f64(*x),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(feature = "f32_float")] #[cfg(feature = "f32_float")]
Union::Float(x, _, _) => ser.serialize_f32(*x), Union::Float(x, ..) => ser.serialize_f32(*x),
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
Union::Decimal(ref x, _, _) => { Union::Decimal(ref x, ..) => {
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
if let Some(v) = x.to_f64() { if let Some(v) = x.to_f64() {
@ -45,7 +45,7 @@ impl Serialize for Dynamic {
} }
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[cfg(feature = "f32_float")] #[cfg(feature = "f32_float")]
Union::Decimal(ref x, _, _) => { Union::Decimal(ref x, ..) => {
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
if let Some(v) = x.to_f32() { if let Some(v) = x.to_f32() {
@ -56,28 +56,28 @@ impl Serialize for Dynamic {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Array(ref a, _, _) => (**a).serialize(ser), Union::Array(ref a, ..) => (**a).serialize(ser),
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Union::Blob(ref a, _, _) => (**a).serialize(ser), Union::Blob(ref a, ..) => (**a).serialize(ser),
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Union::Map(ref m, _, _) => { Union::Map(ref m, ..) => {
let mut map = ser.serialize_map(Some(m.len()))?; let mut map = ser.serialize_map(Some(m.len()))?;
m.iter() m.iter()
.try_for_each(|(k, v)| map.serialize_entry(k.as_str(), v))?; .try_for_each(|(k, v)| map.serialize_entry(k.as_str(), v))?;
map.end() map.end()
} }
Union::FnPtr(ref f, _, _) => ser.serialize_str(f.fn_name()), Union::FnPtr(ref f, ..) => ser.serialize_str(f.fn_name()),
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
Union::TimeStamp(ref x, _, _) => ser.serialize_str(x.as_ref().type_name()), Union::TimeStamp(ref x, ..) => ser.serialize_str(x.as_ref().type_name()),
Union::Variant(ref v, _, _) => ser.serialize_str((***v).type_name()), Union::Variant(ref v, ..) => ser.serialize_str((***v).type_name()),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
Union::Shared(ref cell, _, _) => cell.borrow().serialize(ser), Union::Shared(ref cell, ..) => cell.borrow().serialize(ser),
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
#[cfg(feature = "sync")] #[cfg(feature = "sync")]
Union::Shared(ref cell, _, _) => cell.read().unwrap().serialize(ser), Union::Shared(ref cell, ..) => cell.read().unwrap().serialize(ser),
} }
} }
} }

View File

@ -603,8 +603,8 @@ impl Token {
FloatConstant(f) => f.to_string().into(), FloatConstant(f) => f.to_string().into(),
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
DecimalConstant(d) => d.to_string().into(), DecimalConstant(d) => d.to_string().into(),
StringConstant(_) => "string".into(), StringConstant(..) => "string".into(),
InterpolatedString(_) => "string".into(), InterpolatedString(..) => "string".into(),
CharConstant(c) => c.to_string().into(), CharConstant(c) => c.to_string().into(),
Identifier(s) => s.to_string().into(), Identifier(s) => s.to_string().into(),
Reserved(s) => s.to_string().into(), Reserved(s) => s.to_string().into(),
@ -825,7 +825,7 @@ impl Token {
use Token::*; use Token::*;
match self { match self {
LexError(_) | LexError(..) |
SemiColon | // ; - is unary SemiColon | // ; - is unary
Colon | // #{ foo: - is unary Colon | // #{ foo: - is unary
Comma | // ( ... , -expr ) - is unary Comma | // ( ... , -expr ) - is unary
@ -970,7 +970,7 @@ impl Token {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_reserved(&self) -> bool { pub const fn is_reserved(&self) -> bool {
matches!(self, Self::Reserved(_)) matches!(self, Self::Reserved(..))
} }
/// Convert a token into a function name, if possible. /// Convert a token into a function name, if possible.
@ -987,7 +987,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 {
matches!(self, Self::Custom(_)) matches!(self, Self::Custom(..))
} }
} }
@ -1332,7 +1332,7 @@ pub fn get_next_token(
let result = get_next_token_inner(stream, state, pos); let result = get_next_token_inner(stream, state, pos);
// Save the last token's state // Save the last token's state
if let Some((ref token, _)) = result { if let Some((ref token, ..)) = result {
state.next_token_cannot_be_unary = !token.is_next_unary(); state.next_token_cannot_be_unary = !token.is_next_unary();
} }
@ -1420,10 +1420,10 @@ fn get_next_token_inner(
match (c, stream.peek_next().unwrap_or('\0')) { match (c, stream.peek_next().unwrap_or('\0')) {
// \n // \n
('\n', _) => pos.new_line(), ('\n', ..) => pos.new_line(),
// digit ... // digit ...
('0'..='9', _) => { ('0'..='9', ..) => {
let mut result = smallvec::SmallVec::<[char; 16]>::new(); let mut result = smallvec::SmallVec::<[char; 16]>::new();
let mut radix_base: Option<u32> = None; let mut radix_base: Option<u32> = None;
let mut valid: fn(char) -> bool = is_numeric_digit; let mut valid: fn(char) -> bool = is_numeric_digit;
@ -1573,24 +1573,24 @@ fn get_next_token_inner(
// letter or underscore ... // letter or underscore ...
#[cfg(not(feature = "unicode-xid-ident"))] #[cfg(not(feature = "unicode-xid-ident"))]
('a'..='z', _) | ('_', _) | ('A'..='Z', _) => { ('a'..='z', ..) | ('_', ..) | ('A'..='Z', ..) => {
return get_identifier(stream, pos, start_pos, c); return get_identifier(stream, pos, start_pos, c);
} }
#[cfg(feature = "unicode-xid-ident")] #[cfg(feature = "unicode-xid-ident")]
(ch, _) if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' => { (ch, ..) if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' => {
return get_identifier(stream, pos, start_pos, c); return get_identifier(stream, pos, start_pos, c);
} }
// " - string literal // " - string literal
('"', _) => { ('"', ..) => {
return parse_string_literal(stream, state, pos, c, false, true, false) return parse_string_literal(stream, state, pos, c, false, true, false)
.map_or_else( .map_or_else(
|(err, err_pos)| Some((Token::LexError(err), err_pos)), |(err, err_pos)| Some((Token::LexError(err), err_pos)),
|(result, _)| Some((Token::StringConstant(result), start_pos)), |(result, ..)| Some((Token::StringConstant(result), start_pos)),
); );
} }
// ` - string literal // ` - string literal
('`', _) => { ('`', ..) => {
// Start from the next line if at the end of line // Start from the next line if at the end of line
match stream.peek_next() { match stream.peek_next() {
// `\r - start from next line // `\r - start from next line
@ -1629,11 +1629,11 @@ fn get_next_token_inner(
start_pos, start_pos,
)) ))
} }
('\'', _) => { ('\'', ..) => {
return Some( return Some(
parse_string_literal(stream, state, pos, c, false, false, false).map_or_else( parse_string_literal(stream, state, pos, c, false, false, false).map_or_else(
|(err, err_pos)| (Token::LexError(err), err_pos), |(err, err_pos)| (Token::LexError(err), err_pos),
|(result, _)| { |(result, ..)| {
let mut chars = result.chars(); let mut chars = result.chars();
let first = chars.next().unwrap(); let first = chars.next().unwrap();
@ -1651,20 +1651,20 @@ fn get_next_token_inner(
} }
// Braces // Braces
('{', _) => return Some((Token::LeftBrace, start_pos)), ('{', ..) => return Some((Token::LeftBrace, start_pos)),
('}', _) => return Some((Token::RightBrace, start_pos)), ('}', ..) => return Some((Token::RightBrace, start_pos)),
// Parentheses // Parentheses
('(', '*') => { ('(', '*') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("(*".into()), start_pos)); return Some((Token::Reserved("(*".into()), start_pos));
} }
('(', _) => return Some((Token::LeftParen, start_pos)), ('(', ..) => return Some((Token::LeftParen, start_pos)),
(')', _) => return Some((Token::RightParen, start_pos)), (')', ..) => return Some((Token::RightParen, start_pos)),
// Indexing // Indexing
('[', _) => return Some((Token::LeftBracket, start_pos)), ('[', ..) => return Some((Token::LeftBracket, start_pos)),
(']', _) => return Some((Token::RightBracket, start_pos)), (']', ..) => return Some((Token::RightBracket, start_pos)),
// Map literal // Map literal
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -1686,7 +1686,7 @@ fn get_next_token_inner(
return Some((Token::Reserved(token.into()), start_pos)); return Some((Token::Reserved(token.into()), start_pos));
} }
('#', _) => return Some((Token::Reserved("#".into()), start_pos)), ('#', ..) => return Some((Token::Reserved("#".into()), start_pos)),
// Operators // Operators
('+', '=') => { ('+', '=') => {
@ -1697,10 +1697,10 @@ fn get_next_token_inner(
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("++".into()), start_pos)); return Some((Token::Reserved("++".into()), start_pos));
} }
('+', _) if !state.next_token_cannot_be_unary => { ('+', ..) if !state.next_token_cannot_be_unary => {
return Some((Token::UnaryPlus, start_pos)) return Some((Token::UnaryPlus, start_pos))
} }
('+', _) => return Some((Token::Plus, start_pos)), ('+', ..) => return Some((Token::Plus, start_pos)),
('-', '0'..='9') if !state.next_token_cannot_be_unary => negated = Some(start_pos), ('-', '0'..='9') if !state.next_token_cannot_be_unary => negated = Some(start_pos),
('-', '0'..='9') => return Some((Token::Minus, start_pos)), ('-', '0'..='9') => return Some((Token::Minus, start_pos)),
@ -1716,10 +1716,10 @@ fn get_next_token_inner(
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("--".into()), start_pos)); return Some((Token::Reserved("--".into()), start_pos));
} }
('-', _) if !state.next_token_cannot_be_unary => { ('-', ..) if !state.next_token_cannot_be_unary => {
return Some((Token::UnaryMinus, start_pos)) return Some((Token::UnaryMinus, start_pos))
} }
('-', _) => return Some((Token::Minus, start_pos)), ('-', ..) => return Some((Token::Minus, start_pos)),
('*', ')') => { ('*', ')') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1742,7 +1742,7 @@ fn get_next_token_inner(
start_pos, start_pos,
)); ));
} }
('*', _) => return Some((Token::Multiply, start_pos)), ('*', ..) => return Some((Token::Multiply, start_pos)),
// Comments // Comments
('/', '/') => { ('/', '/') => {
@ -1819,10 +1819,10 @@ fn get_next_token_inner(
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::DivideAssign, start_pos)); return Some((Token::DivideAssign, start_pos));
} }
('/', _) => return Some((Token::Divide, start_pos)), ('/', ..) => return Some((Token::Divide, start_pos)),
(';', _) => return Some((Token::SemiColon, start_pos)), (';', ..) => return Some((Token::SemiColon, start_pos)),
(',', _) => return Some((Token::Comma, start_pos)), (',', ..) => return Some((Token::Comma, start_pos)),
('.', '.') => { ('.', '.') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1841,7 +1841,7 @@ fn get_next_token_inner(
start_pos, start_pos,
)); ));
} }
('.', _) => return Some((Token::Period, start_pos)), ('.', ..) => return Some((Token::Period, start_pos)),
('=', '=') => { ('=', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1857,7 +1857,7 @@ fn get_next_token_inner(
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::DoubleArrow, start_pos)); return Some((Token::DoubleArrow, start_pos));
} }
('=', _) => return Some((Token::Equals, start_pos)), ('=', ..) => return Some((Token::Equals, start_pos)),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
(':', ':') => { (':', ':') => {
@ -1878,7 +1878,7 @@ fn get_next_token_inner(
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved(":;".into()), start_pos)); return Some((Token::Reserved(":;".into()), start_pos));
} }
(':', _) => return Some((Token::Colon, start_pos)), (':', ..) => return Some((Token::Colon, start_pos)),
('<', '=') => { ('<', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1901,7 +1901,7 @@ fn get_next_token_inner(
start_pos, start_pos,
)); ));
} }
('<', _) => return Some((Token::LessThan, start_pos)), ('<', ..) => return Some((Token::LessThan, start_pos)),
('>', '=') => { ('>', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1920,7 +1920,7 @@ fn get_next_token_inner(
start_pos, start_pos,
)); ));
} }
('>', _) => return Some((Token::GreaterThan, start_pos)), ('>', ..) => return Some((Token::GreaterThan, start_pos)),
('!', '=') => { ('!', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1932,7 +1932,7 @@ fn get_next_token_inner(
return Some((Token::NotEqualsTo, start_pos)); return Some((Token::NotEqualsTo, start_pos));
} }
('!', _) => return Some((Token::Bang, start_pos)), ('!', ..) => return Some((Token::Bang, start_pos)),
('|', '|') => { ('|', '|') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1942,7 +1942,7 @@ fn get_next_token_inner(
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::OrAssign, start_pos)); return Some((Token::OrAssign, start_pos));
} }
('|', _) => return Some((Token::Pipe, start_pos)), ('|', ..) => return Some((Token::Pipe, start_pos)),
('&', '&') => { ('&', '&') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1952,29 +1952,29 @@ fn get_next_token_inner(
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::AndAssign, start_pos)); return Some((Token::AndAssign, start_pos));
} }
('&', _) => return Some((Token::Ampersand, start_pos)), ('&', ..) => return Some((Token::Ampersand, start_pos)),
('^', '=') => { ('^', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::XOrAssign, start_pos)); return Some((Token::XOrAssign, start_pos));
} }
('^', _) => return Some((Token::XOr, start_pos)), ('^', ..) => return Some((Token::XOr, start_pos)),
('~', _) => return Some((Token::Reserved("~".into()), start_pos)), ('~', ..) => return Some((Token::Reserved("~".into()), start_pos)),
('%', '=') => { ('%', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::ModuloAssign, start_pos)); return Some((Token::ModuloAssign, start_pos));
} }
('%', _) => return Some((Token::Modulo, start_pos)), ('%', ..) => return Some((Token::Modulo, start_pos)),
('@', _) => return Some((Token::Reserved("@".into()), start_pos)), ('@', ..) => return Some((Token::Reserved("@".into()), start_pos)),
('$', _) => return Some((Token::Reserved("$".into()), start_pos)), ('$', ..) => return Some((Token::Reserved("$".into()), start_pos)),
(ch, _) if ch.is_whitespace() => (), (ch, ..) if ch.is_whitespace() => (),
(ch, _) => { (ch, ..) => {
return Some(( return Some((
Token::LexError(LERR::UnexpectedInput(ch.to_string())), Token::LexError(LERR::UnexpectedInput(ch.to_string())),
start_pos, start_pos,
@ -2197,7 +2197,7 @@ impl<'a> Iterator for TokenIterator<'a> {
// a verbatim string or a string with continuation encounters {EOF}. // a verbatim string or a string with continuation encounters {EOF}.
// This is necessary to handle such cases for line-by-line parsing, but for an entire // This is necessary to handle such cases for line-by-line parsing, but for an entire
// script it is a syntax error. // script it is a syntax error.
Some((Token::StringConstant(_), pos)) if self.state.is_within_text_terminated_by.is_some() => { Some((Token::StringConstant(..), pos)) if self.state.is_within_text_terminated_by.is_some() => {
self.state.is_within_text_terminated_by = None; self.state.is_within_text_terminated_by = None;
return Some((Token::LexError(LERR::UnterminatedString), pos)); return Some((Token::LexError(LERR::UnterminatedString), pos));
} }
@ -2232,14 +2232,14 @@ impl<'a> Iterator for TokenIterator<'a> {
"'#' is not a valid symbol. Should it be '#{'?".to_string(), "'#' is not a valid symbol. Should it be '#{'?".to_string(),
)), )),
// Reserved keyword/operator that is custom. // Reserved keyword/operator that is custom.
(_, true) => Token::Custom(s), (.., true) => Token::Custom(s),
// Reserved keyword that is not custom and disabled. // Reserved keyword that is not custom and disabled.
(token, false) if self.engine.disabled_symbols.contains(token) => { (token, false) if self.engine.disabled_symbols.contains(token) => {
let msg = format!("reserved {} '{}' is disabled", if is_valid_identifier(token.chars()) { "keyword"} else {"symbol"}, token); let msg = format!("reserved {} '{}' is disabled", if is_valid_identifier(token.chars()) { "keyword"} else {"symbol"}, token);
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg)) Token::LexError(LERR::ImproperSymbol(s.to_string(), msg))
}, },
// Reserved keyword/operator that is not custom. // Reserved keyword/operator that is not custom.
(_, false) => Token::Reserved(s), (.., false) => Token::Reserved(s),
}, pos), }, pos),
// Custom keyword // Custom keyword
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => { Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => {

File diff suppressed because it is too large Load Diff

View File

@ -120,77 +120,77 @@ impl fmt::Display for EvalAltResult {
s => write!(f, "{}: {}", s, err), s => write!(f, "{}: {}", s, err),
}?, }?,
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?, Self::ErrorParsing(p, ..) => write!(f, "Syntax error: {}", p)?,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
Self::ErrorInFunctionCall(s, src, err, _) if crate::parser::is_anonymous_fn(s) => { Self::ErrorInFunctionCall(s, src, err, ..) if crate::parser::is_anonymous_fn(s) => {
write!(f, "{} in call to closure", err)?; write!(f, "{} in call to closure", err)?;
if !src.is_empty() { if !src.is_empty() {
write!(f, " @ '{}'", src)?; write!(f, " @ '{}'", src)?;
} }
} }
Self::ErrorInFunctionCall(s, src, err, _) => { Self::ErrorInFunctionCall(s, src, err, ..) => {
write!(f, "{} in call to function {}", err, s)?; write!(f, "{} in call to function {}", err, s)?;
if !src.is_empty() { if !src.is_empty() {
write!(f, " @ '{}'", src)?; write!(f, " @ '{}'", src)?;
} }
} }
Self::ErrorInModule(s, err, _) if s.is_empty() => { Self::ErrorInModule(s, err, ..) if s.is_empty() => {
write!(f, "Error in module: {}", err)? write!(f, "Error in module: {}", err)?
} }
Self::ErrorInModule(s, err, _) => write!(f, "Error in module {}: {}", s, err)?, Self::ErrorInModule(s, err, ..) => write!(f, "Error in module {}: {}", s, err)?,
Self::ErrorVariableExists(s, _) => write!(f, "Variable is already defined: {}", s)?, Self::ErrorVariableExists(s, ..) => write!(f, "Variable is already defined: {}", s)?,
Self::ErrorVariableNotFound(s, _) => write!(f, "Variable not found: {}", s)?, Self::ErrorVariableNotFound(s, ..) => write!(f, "Variable not found: {}", s)?,
Self::ErrorFunctionNotFound(s, _) => write!(f, "Function not found: {}", s)?, Self::ErrorFunctionNotFound(s, ..) => write!(f, "Function not found: {}", s)?,
Self::ErrorModuleNotFound(s, _) => write!(f, "Module not found: {}", s)?, Self::ErrorModuleNotFound(s, ..) => write!(f, "Module not found: {}", s)?,
Self::ErrorDataRace(s, _) => { Self::ErrorDataRace(s, ..) => {
write!(f, "Data race detected when accessing variable: {}", s)? write!(f, "Data race detected when accessing variable: {}", s)?
} }
Self::ErrorDotExpr(s, _) => match s.as_str() { Self::ErrorDotExpr(s, ..) => match s.as_str() {
"" => f.write_str("Malformed dot expression"), "" => f.write_str("Malformed dot expression"),
s => f.write_str(s), s => f.write_str(s),
}?, }?,
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered: {}", s)?, Self::ErrorIndexingType(s, ..) => write!(f, "Indexer not registered: {}", s)?,
Self::ErrorUnboundThis(_) => f.write_str("'this' is not bound")?, Self::ErrorUnboundThis(..) => f.write_str("'this' is not bound")?,
Self::ErrorFor(_) => f.write_str("For loop expects a type that is iterable")?, Self::ErrorFor(..) => f.write_str("For loop expects a type that is iterable")?,
Self::ErrorTooManyOperations(_) => f.write_str("Too many operations")?, Self::ErrorTooManyOperations(..) => f.write_str("Too many operations")?,
Self::ErrorTooManyModules(_) => f.write_str("Too many modules imported")?, Self::ErrorTooManyModules(..) => f.write_str("Too many modules imported")?,
Self::ErrorStackOverflow(_) => f.write_str("Stack overflow")?, Self::ErrorStackOverflow(..) => f.write_str("Stack overflow")?,
Self::ErrorTerminated(_, _) => f.write_str("Script terminated")?, Self::ErrorTerminated(..) => f.write_str("Script terminated")?,
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str("Runtime error")?, Self::ErrorRuntime(d, ..) if d.is::<()>() => f.write_str("Runtime error")?,
Self::ErrorRuntime(d, _) Self::ErrorRuntime(d, ..)
if d.read_lock::<ImmutableString>() if d.read_lock::<ImmutableString>()
.map_or(false, |v| v.is_empty()) => .map_or(false, |v| v.is_empty()) =>
{ {
write!(f, "Runtime error")? write!(f, "Runtime error")?
} }
Self::ErrorRuntime(d, _) => write!(f, "Runtime error: {}", d)?, Self::ErrorRuntime(d, ..) => write!(f, "Runtime error: {}", d)?,
Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot modify constant: {}", s)?, Self::ErrorAssignmentToConstant(s, ..) => write!(f, "Cannot modify constant: {}", s)?,
Self::ErrorMismatchOutputType(s, r, _) => match (r.as_str(), s.as_str()) { Self::ErrorMismatchOutputType(s, r, ..) => match (r.as_str(), s.as_str()) {
("", s) => write!(f, "Output type is incorrect, expecting {}", s), ("", s) => write!(f, "Output type is incorrect, expecting {}", s),
(r, "") => write!(f, "Output type is incorrect: {}", r), (r, "") => write!(f, "Output type is incorrect: {}", r),
(r, s) => write!(f, "Output type is incorrect: {} (expecting {})", r, s), (r, s) => write!(f, "Output type is incorrect: {} (expecting {})", r, s),
}?, }?,
Self::ErrorMismatchDataType(s, r, _) => match (r.as_str(), s.as_str()) { Self::ErrorMismatchDataType(s, r, ..) => match (r.as_str(), s.as_str()) {
("", s) => write!(f, "Data type is incorrect, expecting {}", s), ("", s) => write!(f, "Data type is incorrect, expecting {}", s),
(r, "") => write!(f, "Data type is incorrect: {}", r), (r, "") => write!(f, "Data type is incorrect: {}", r),
(r, s) => write!(f, "Data type is incorrect: {} (expecting {})", r, s), (r, s) => write!(f, "Data type is incorrect: {} (expecting {})", r, s),
}?, }?,
Self::ErrorArithmetic(s, _) => match s.as_str() { Self::ErrorArithmetic(s, ..) => match s.as_str() {
"" => f.write_str("Arithmetic error"), "" => f.write_str("Arithmetic error"),
s => f.write_str(s), s => f.write_str(s),
}?, }?,
Self::LoopBreak(true, _) => f.write_str("'break' not inside a loop")?, Self::LoopBreak(true, ..) => f.write_str("'break' not inside a loop")?,
Self::LoopBreak(false, _) => f.write_str("'continue' not inside a loop")?, Self::LoopBreak(false, ..) => f.write_str("'continue' not inside a loop")?,
Self::Return(_, _) => f.write_str("NOT AN ERROR - function returns value")?, Self::Return(..) => f.write_str("NOT AN ERROR - function returns value")?,
Self::ErrorArrayBounds(max, index, _) => match max { Self::ErrorArrayBounds(max, index, ..) => match max {
0 => write!(f, "Array index {} out of bounds: array is empty", index), 0 => write!(f, "Array index {} out of bounds: array is empty", index),
1 => write!( 1 => write!(
f, f,
@ -203,7 +203,7 @@ impl fmt::Display for EvalAltResult {
index, max index, max
), ),
}?, }?,
Self::ErrorStringBounds(max, index, _) => match max { Self::ErrorStringBounds(max, index, ..) => match max {
0 => write!(f, "String index {} out of bounds: string is empty", index), 0 => write!(f, "String index {} out of bounds: string is empty", index),
1 => write!( 1 => write!(
f, f,
@ -216,14 +216,14 @@ impl fmt::Display for EvalAltResult {
index, max index, max
), ),
}?, }?,
Self::ErrorBitFieldBounds(max, index, _) => write!( Self::ErrorBitFieldBounds(max, index, ..) => write!(
f, f,
"Bit-field index {} out of bounds: only {} bits in the bit-field", "Bit-field index {} out of bounds: only {} bits in the bit-field",
index, max index, max
)?, )?,
Self::ErrorDataTooLarge(typ, _) => write!(f, "{} exceeds maximum limit", typ)?, Self::ErrorDataTooLarge(typ, ..) => write!(f, "{} exceeds maximum limit", typ)?,
Self::ErrorCustomSyntax(s, tokens, _) => write!(f, "{}: {}", s, tokens.join(" "))?, Self::ErrorCustomSyntax(s, tokens, ..) => write!(f, "{}: {}", s, tokens.join(" "))?,
} }
// Do not write any position if None // Do not write any position if None
@ -256,7 +256,7 @@ impl EvalAltResult {
#[must_use] #[must_use]
pub const fn is_pseudo_error(&self) -> bool { pub const fn is_pseudo_error(&self) -> bool {
match self { match self {
Self::LoopBreak(_, _) | Self::Return(_, _) => true, Self::LoopBreak(..) | Self::Return(..) => true,
_ => false, _ => false,
} }
} }
@ -264,58 +264,58 @@ impl EvalAltResult {
#[must_use] #[must_use]
pub const fn is_catchable(&self) -> bool { pub const fn is_catchable(&self) -> bool {
match self { match self {
Self::ErrorSystem(_, _) => false, Self::ErrorSystem(..) => false,
Self::ErrorParsing(_, _) => false, Self::ErrorParsing(..) => false,
Self::ErrorFunctionNotFound(_, _) Self::ErrorFunctionNotFound(..)
| Self::ErrorInFunctionCall(_, _, _, _) | Self::ErrorInFunctionCall(..)
| Self::ErrorInModule(_, _, _) | Self::ErrorInModule(..)
| Self::ErrorUnboundThis(_) | Self::ErrorUnboundThis(..)
| Self::ErrorMismatchDataType(_, _, _) | Self::ErrorMismatchDataType(..)
| Self::ErrorArrayBounds(_, _, _) | Self::ErrorArrayBounds(..)
| Self::ErrorStringBounds(_, _, _) | Self::ErrorStringBounds(..)
| Self::ErrorBitFieldBounds(_, _, _) | Self::ErrorBitFieldBounds(..)
| Self::ErrorIndexingType(_, _) | Self::ErrorIndexingType(..)
| Self::ErrorFor(_) | Self::ErrorFor(..)
| Self::ErrorVariableExists(_, _) | Self::ErrorVariableExists(..)
| Self::ErrorVariableNotFound(_, _) | Self::ErrorVariableNotFound(..)
| Self::ErrorModuleNotFound(_, _) | Self::ErrorModuleNotFound(..)
| Self::ErrorDataRace(_, _) | Self::ErrorDataRace(..)
| Self::ErrorAssignmentToConstant(_, _) | Self::ErrorAssignmentToConstant(..)
| Self::ErrorMismatchOutputType(_, _, _) | Self::ErrorMismatchOutputType(..)
| Self::ErrorDotExpr(_, _) | Self::ErrorDotExpr(..)
| Self::ErrorArithmetic(_, _) | Self::ErrorArithmetic(..)
| Self::ErrorRuntime(_, _) => true, | Self::ErrorRuntime(..) => true,
// Custom syntax raises errors only when they are compiled by one // Custom syntax raises errors only when they are compiled by one
// [`Engine`][crate::Engine] and run by another, causing a mismatch. // [`Engine`][crate::Engine] and run by another, causing a mismatch.
// //
// Therefore, this error should not be catchable. // Therefore, this error should not be catchable.
Self::ErrorCustomSyntax(_, _, _) => false, Self::ErrorCustomSyntax(..) => false,
Self::ErrorTooManyOperations(_) Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(_) | Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(_) | Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(_, _) | Self::ErrorDataTooLarge(..)
| Self::ErrorTerminated(_, _) => false, | Self::ErrorTerminated(..) => false,
Self::LoopBreak(_, _) | Self::Return(_, _) => false, Self::LoopBreak(..) | Self::Return(..) => false,
} }
} }
/// Is this error a system exception? /// Is this error a system exception?
#[must_use] #[must_use]
pub const fn is_system_exception(&self) -> bool { pub const fn is_system_exception(&self) -> bool {
match self { match self {
Self::ErrorSystem(_, _) => true, Self::ErrorSystem(..) => true,
Self::ErrorParsing(_, _) => true, Self::ErrorParsing(..) => true,
Self::ErrorCustomSyntax(_, _, _) Self::ErrorCustomSyntax(..)
| Self::ErrorTooManyOperations(_) | Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(_) | Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(_) | Self::ErrorStackOverflow(..)
| Self::ErrorDataTooLarge(_, _) => true, | Self::ErrorDataTooLarge(..) => true,
Self::ErrorTerminated(_, _) => true, Self::ErrorTerminated(..) => true,
_ => false, _ => false,
} }
@ -333,58 +333,58 @@ impl EvalAltResult {
); );
match self { match self {
Self::LoopBreak(_, _) | Self::Return(_, _) => (), Self::LoopBreak(..) | Self::Return(..) => (),
Self::ErrorSystem(_, _) Self::ErrorSystem(..)
| Self::ErrorParsing(_, _) | Self::ErrorParsing(..)
| Self::ErrorUnboundThis(_) | Self::ErrorUnboundThis(..)
| Self::ErrorFor(_) | Self::ErrorFor(..)
| Self::ErrorArithmetic(_, _) | Self::ErrorArithmetic(..)
| Self::ErrorTooManyOperations(_) | Self::ErrorTooManyOperations(..)
| Self::ErrorTooManyModules(_) | Self::ErrorTooManyModules(..)
| Self::ErrorStackOverflow(_) | Self::ErrorStackOverflow(..)
| Self::ErrorRuntime(_, _) => (), | Self::ErrorRuntime(..) => (),
Self::ErrorFunctionNotFound(f, _) => { Self::ErrorFunctionNotFound(f, ..) => {
map.insert("function".into(), f.into()); map.insert("function".into(), f.into());
} }
Self::ErrorInFunctionCall(f, s, _, _) => { Self::ErrorInFunctionCall(f, s, ..) => {
map.insert("function".into(), f.into()); map.insert("function".into(), f.into());
map.insert("source".into(), s.into()); map.insert("source".into(), s.into());
} }
Self::ErrorInModule(m, _, _) => { Self::ErrorInModule(m, ..) => {
map.insert("module".into(), m.into()); map.insert("module".into(), m.into());
} }
Self::ErrorMismatchDataType(r, a, _) | Self::ErrorMismatchOutputType(r, a, _) => { Self::ErrorMismatchDataType(r, a, ..) | Self::ErrorMismatchOutputType(r, a, ..) => {
map.insert("requested".into(), r.into()); map.insert("requested".into(), r.into());
map.insert("actual".into(), a.into()); map.insert("actual".into(), a.into());
} }
Self::ErrorArrayBounds(n, i, _) Self::ErrorArrayBounds(n, i, ..)
| Self::ErrorStringBounds(n, i, _) | Self::ErrorStringBounds(n, i, ..)
| Self::ErrorBitFieldBounds(n, i, _) => { | Self::ErrorBitFieldBounds(n, i, ..) => {
map.insert("length".into(), (*n as INT).into()); map.insert("length".into(), (*n as INT).into());
map.insert("index".into(), (*i as INT).into()); map.insert("index".into(), (*i as INT).into());
} }
Self::ErrorIndexingType(t, _) => { Self::ErrorIndexingType(t, ..) => {
map.insert("type".into(), t.into()); map.insert("type".into(), t.into());
} }
Self::ErrorVariableExists(v, _) Self::ErrorVariableExists(v, ..)
| Self::ErrorVariableNotFound(v, _) | Self::ErrorVariableNotFound(v, ..)
| Self::ErrorDataRace(v, _) | Self::ErrorDataRace(v, ..)
| Self::ErrorAssignmentToConstant(v, _) => { | Self::ErrorAssignmentToConstant(v, ..) => {
map.insert("variable".into(), v.into()); map.insert("variable".into(), v.into());
} }
Self::ErrorModuleNotFound(m, _) => { Self::ErrorModuleNotFound(m, ..) => {
map.insert("module".into(), m.into()); map.insert("module".into(), m.into());
} }
Self::ErrorDotExpr(p, _) => { Self::ErrorDotExpr(p, ..) => {
map.insert("property".into(), p.into()); map.insert("property".into(), p.into());
} }
Self::ErrorDataTooLarge(t, _) => { Self::ErrorDataTooLarge(t, ..) => {
map.insert("type".into(), t.into()); map.insert("type".into(), t.into());
} }
Self::ErrorTerminated(t, _) => { Self::ErrorTerminated(t, ..) => {
map.insert("token".into(), t.clone()); map.insert("token".into(), t.clone());
} }
Self::ErrorCustomSyntax(_, tokens, _) => { Self::ErrorCustomSyntax(_, tokens, _) => {
@ -403,40 +403,50 @@ impl EvalAltResult {
} }
}; };
} }
/// Unwrap this error and get the very base error.
#[must_use]
pub fn unwrap_inner(&self) -> &Self {
match self {
Self::ErrorInFunctionCall(.., err, _) | Self::ErrorInModule(.., err, _) => {
err.unwrap_inner()
}
_ => self,
}
}
/// Get the [position][Position] of this error. /// Get the [position][Position] of this error.
#[must_use] #[must_use]
pub const fn position(&self) -> Position { pub const fn position(&self) -> Position {
match self { match self {
Self::ErrorSystem(_, _) => Position::NONE, Self::ErrorSystem(..) => Position::NONE,
Self::ErrorParsing(_, pos) Self::ErrorParsing(.., pos)
| Self::ErrorFunctionNotFound(_, pos) | Self::ErrorFunctionNotFound(.., pos)
| Self::ErrorInFunctionCall(_, _, _, pos) | Self::ErrorInFunctionCall(.., pos)
| Self::ErrorInModule(_, _, pos) | Self::ErrorInModule(.., pos)
| Self::ErrorUnboundThis(pos) | Self::ErrorUnboundThis(pos)
| Self::ErrorMismatchDataType(_, _, pos) | Self::ErrorMismatchDataType(.., pos)
| Self::ErrorArrayBounds(_, _, pos) | Self::ErrorArrayBounds(.., pos)
| Self::ErrorStringBounds(_, _, pos) | Self::ErrorStringBounds(.., pos)
| Self::ErrorBitFieldBounds(_, _, pos) | Self::ErrorBitFieldBounds(.., pos)
| Self::ErrorIndexingType(_, pos) | Self::ErrorIndexingType(.., pos)
| Self::ErrorFor(pos) | Self::ErrorFor(pos)
| Self::ErrorVariableExists(_, pos) | Self::ErrorVariableExists(.., pos)
| Self::ErrorVariableNotFound(_, pos) | Self::ErrorVariableNotFound(.., pos)
| Self::ErrorModuleNotFound(_, pos) | Self::ErrorModuleNotFound(.., pos)
| Self::ErrorDataRace(_, pos) | Self::ErrorDataRace(.., pos)
| Self::ErrorAssignmentToConstant(_, pos) | Self::ErrorAssignmentToConstant(.., pos)
| Self::ErrorMismatchOutputType(_, _, pos) | Self::ErrorMismatchOutputType(.., pos)
| Self::ErrorDotExpr(_, pos) | Self::ErrorDotExpr(.., pos)
| Self::ErrorArithmetic(_, pos) | Self::ErrorArithmetic(.., pos)
| Self::ErrorTooManyOperations(pos) | Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyModules(pos) | Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos) | Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(_, pos) | Self::ErrorDataTooLarge(.., pos)
| Self::ErrorTerminated(_, pos) | Self::ErrorTerminated(.., pos)
| Self::ErrorCustomSyntax(_, _, pos) | Self::ErrorCustomSyntax(.., pos)
| Self::ErrorRuntime(_, pos) | Self::ErrorRuntime(.., pos)
| Self::LoopBreak(_, pos) | Self::LoopBreak(.., pos)
| Self::Return(_, pos) => *pos, | Self::Return(.., pos) => *pos,
} }
} }
/// Remove the [position][Position] information from this error. /// Remove the [position][Position] information from this error.
@ -456,36 +466,36 @@ impl EvalAltResult {
/// Override the [position][Position] of this error. /// Override the [position][Position] of this error.
pub fn set_position(&mut self, new_position: Position) -> &mut Self { pub fn set_position(&mut self, new_position: Position) -> &mut Self {
match self { match self {
Self::ErrorSystem(_, _) => (), Self::ErrorSystem(..) => (),
Self::ErrorParsing(_, pos) Self::ErrorParsing(.., pos)
| Self::ErrorFunctionNotFound(_, pos) | Self::ErrorFunctionNotFound(.., pos)
| Self::ErrorInFunctionCall(_, _, _, pos) | Self::ErrorInFunctionCall(.., pos)
| Self::ErrorInModule(_, _, pos) | Self::ErrorInModule(.., pos)
| Self::ErrorUnboundThis(pos) | Self::ErrorUnboundThis(pos)
| Self::ErrorMismatchDataType(_, _, pos) | Self::ErrorMismatchDataType(.., pos)
| Self::ErrorArrayBounds(_, _, pos) | Self::ErrorArrayBounds(.., pos)
| Self::ErrorStringBounds(_, _, pos) | Self::ErrorStringBounds(.., pos)
| Self::ErrorBitFieldBounds(_, _, pos) | Self::ErrorBitFieldBounds(.., pos)
| Self::ErrorIndexingType(_, pos) | Self::ErrorIndexingType(.., pos)
| Self::ErrorFor(pos) | Self::ErrorFor(pos)
| Self::ErrorVariableExists(_, pos) | Self::ErrorVariableExists(.., pos)
| Self::ErrorVariableNotFound(_, pos) | Self::ErrorVariableNotFound(.., pos)
| Self::ErrorModuleNotFound(_, pos) | Self::ErrorModuleNotFound(.., pos)
| Self::ErrorDataRace(_, pos) | Self::ErrorDataRace(.., pos)
| Self::ErrorAssignmentToConstant(_, pos) | Self::ErrorAssignmentToConstant(.., pos)
| Self::ErrorMismatchOutputType(_, _, pos) | Self::ErrorMismatchOutputType(.., pos)
| Self::ErrorDotExpr(_, pos) | Self::ErrorDotExpr(.., pos)
| Self::ErrorArithmetic(_, pos) | Self::ErrorArithmetic(.., pos)
| Self::ErrorTooManyOperations(pos) | Self::ErrorTooManyOperations(pos)
| Self::ErrorTooManyModules(pos) | Self::ErrorTooManyModules(pos)
| Self::ErrorStackOverflow(pos) | Self::ErrorStackOverflow(pos)
| Self::ErrorDataTooLarge(_, pos) | Self::ErrorDataTooLarge(.., pos)
| Self::ErrorTerminated(_, pos) | Self::ErrorTerminated(.., pos)
| Self::ErrorCustomSyntax(_, _, pos) | Self::ErrorCustomSyntax(.., pos)
| Self::ErrorRuntime(_, pos) | Self::ErrorRuntime(.., pos)
| Self::LoopBreak(_, pos) | Self::LoopBreak(.., pos)
| Self::Return(_, pos) => *pos = new_position, | Self::Return(.., pos) => *pos = new_position,
} }
self self
} }

View File

@ -52,7 +52,7 @@ impl fmt::Display for LexError {
Self::ImproperSymbol(s, d) if d.is_empty() => { Self::ImproperSymbol(s, d) if d.is_empty() => {
write!(f, "Invalid symbol encountered: '{}'", s) write!(f, "Invalid symbol encountered: '{}'", s)
} }
Self::ImproperSymbol(_, d) => f.write_str(d), Self::ImproperSymbol(.., d) => f.write_str(d),
} }
} }
} }

View File

@ -310,7 +310,7 @@ impl Scope<'_> {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn contains(&self, name: &str) -> bool { pub fn contains(&self, name: &str) -> bool {
self.names.iter().any(|(key, _)| name == key) self.names.iter().any(|(key, ..)| name == key)
} }
/// Find an entry in the [`Scope`], starting from the last. /// Find an entry in the [`Scope`], starting from the last.
#[inline] #[inline]
@ -322,7 +322,7 @@ impl Scope<'_> {
.iter() .iter()
.rev() // Always search a Scope in reverse order .rev() // Always search a Scope in reverse order
.enumerate() .enumerate()
.find_map(|(i, (key, _))| { .find_map(|(i, (key, ..))| {
if name == key { if name == key {
let index = len - 1 - i; let index = len - 1 - i;
Some((index, self.values[index].access_mode())) Some((index, self.values[index].access_mode()))
@ -352,8 +352,8 @@ impl Scope<'_> {
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|(_, (key, _))| name == key) .find(|(.., (key, ..))| name == key)
.and_then(|(index, _)| self.values[len - 1 - index].flatten_clone().try_cast()) .and_then(|(index, ..)| self.values[len - 1 - index].flatten_clone().try_cast())
} }
/// Check if the named entry in the [`Scope`] is constant. /// Check if the named entry in the [`Scope`] is constant.
/// ///
@ -374,7 +374,7 @@ impl Scope<'_> {
/// ``` /// ```
#[inline] #[inline]
pub fn is_constant(&self, name: &str) -> Option<bool> { pub fn is_constant(&self, name: &str) -> Option<bool> {
self.get_index(name).and_then(|(_, access)| match access { self.get_index(name).and_then(|(.., access)| match access {
AccessMode::ReadWrite => None, AccessMode::ReadWrite => None,
AccessMode::ReadOnly => Some(true), AccessMode::ReadOnly => Some(true),
}) })
@ -411,7 +411,7 @@ impl Scope<'_> {
value: impl Variant + Clone, value: impl Variant + Clone,
) -> &mut Self { ) -> &mut Self {
match self.get_index(name.as_ref()) { match self.get_index(name.as_ref()) {
None | Some((_, AccessMode::ReadOnly)) => { None | Some((.., AccessMode::ReadOnly)) => {
self.push(name, value); self.push(name, value);
} }
Some((index, AccessMode::ReadWrite)) => { Some((index, AccessMode::ReadWrite)) => {
@ -453,7 +453,7 @@ impl Scope<'_> {
None => { None => {
self.push(name, value); self.push(name, value);
} }
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()), Some((.., AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()),
Some((index, AccessMode::ReadWrite)) => { Some((index, AccessMode::ReadWrite)) => {
let value_ref = self.values.get_mut(index).unwrap(); let value_ref = self.values.get_mut(index).unwrap();
*value_ref = Dynamic::from(value); *value_ref = Dynamic::from(value);
@ -511,7 +511,7 @@ impl Scope<'_> {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline] #[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.names.get_mut(index).unwrap(); let (.., aliases) = self.names.get_mut(index).unwrap();
match aliases { match aliases {
None => { None => {
let mut list = StaticVec::new_const(); let mut list = StaticVec::new_const();
@ -533,7 +533,7 @@ impl Scope<'_> {
self.names.iter().rev().enumerate().fold( self.names.iter().rev().enumerate().fold(
Self::new(), Self::new(),
|mut entries, (index, (name, alias))| { |mut entries, (index, (name, alias))| {
if !entries.names.iter().any(|(key, _)| key == name) { if !entries.names.iter().any(|(key, ..)| key == name) {
let orig_value = &self.values[len - 1 - index]; let orig_value = &self.values[len - 1 - index];
let mut value = orig_value.clone(); let mut value = orig_value.clone();
value.set_access_mode(orig_value.access_mode()); value.set_access_mode(orig_value.access_mode());
@ -593,7 +593,7 @@ impl Scope<'_> {
self.names self.names
.iter() .iter()
.zip(self.values.iter()) .zip(self.values.iter())
.map(|((name, _), value)| (name.as_ref(), value.is_read_only(), value)) .map(|((name, ..), value)| (name.as_ref(), value.is_read_only(), value))
} }
/// Remove a range of entries within the [`Scope`]. /// Remove a range of entries within the [`Scope`].
/// ///

View File

@ -1,54 +0,0 @@
//! A helper module containing unsafe utility functions.
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
any::{Any, TypeId},
mem, ptr,
};
/// Cast a type into another type.
///
/// # Undefined Behavior
///
/// It is UB if the types are not compatible.
#[inline(always)]
#[must_use]
pub fn unsafe_cast<A: Any, B: Any>(a: A) -> B {
unsafe {
let ret: B = ptr::read(&a as *const _ as *const B);
// We explicitly forget the value immediately after moving out,
// removing any chance of a destructor running or value otherwise
// being used again.
mem::forget(a);
ret
}
}
/// Cast a type into another type.
#[inline(always)]
#[must_use]
pub fn unsafe_try_cast<A: Any, B: Any>(a: A) -> Option<B> {
if TypeId::of::<B>() == a.type_id() {
// SAFETY: Just checked we have the right type.
Some(unsafe_cast(a))
} else {
None
}
}
/// Cast a Boxed type into another type.
#[inline(always)]
#[must_use]
pub fn unsafe_cast_box<X: Any, T: Any>(item: Box<X>) -> Option<T> {
// Only allow casting to the exact same type
if TypeId::of::<X>() == TypeId::of::<T>() {
// SAFETY: just checked whether we are pointing to the correct type
unsafe {
let raw: *mut dyn Any = Box::into_raw(item as Box<dyn Any>);
Some(*Box::from_raw(raw as *mut T))
}
} else {
None
}
}

View File

@ -330,7 +330,7 @@ fn test_call_fn_events() -> Result<(), Box<EvalAltResult>> {
"update" => engine "update" => engine
.call_fn(scope, ast, "update", (event_data,)) .call_fn(scope, ast, "update", (event_data,))
.or_else(|err| match *err { .or_else(|err| match *err {
EvalAltResult::ErrorFunctionNotFound(fn_name, _) EvalAltResult::ErrorFunctionNotFound(fn_name, ..)
if fn_name.starts_with("update") => if fn_name.starts_with("update") =>
{ {
// Default implementation of 'update' event handler // Default implementation of 'update' event handler

View File

@ -48,7 +48,7 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
.compile_expression("let f = |x| {};") .compile_expression("let f = |x| {};")
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::BadInput(_) ParseErrorType::BadInput(..)
)); ));
assert_eq!( assert_eq!(
@ -251,7 +251,7 @@ fn test_closures_data_race() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataRace(_, _) EvalAltResult::ErrorDataRace(..)
)); ));
Ok(()) Ok(())

View File

@ -10,13 +10,13 @@ fn test_constant() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.eval::<INT>("const x = 123; x = 42;") .eval::<INT>("const x = 123; x = 42;")
.expect_err("expects error"), .expect_err("expects error"),
EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), _) if x == "x" EvalAltResult::ErrorParsing(ParseErrorType::AssignmentToConstant(x), ..) if x == "x"
)); ));
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
assert!(matches!( assert!(matches!(
*engine.run("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"), *engine.run("const x = [1, 2, 3, 4, 5]; x[2] = 42;").expect_err("expects error"),
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "x" EvalAltResult::ErrorAssignmentToConstant(x, ..) if x == "x"
)); ));
Ok(()) Ok(())
@ -31,7 +31,7 @@ fn test_constant_scope() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine.run_with_scope(&mut scope, "x = 1").expect_err("expects error"), *engine.run_with_scope(&mut scope, "x = 1").expect_err("expects error"),
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "x" EvalAltResult::ErrorAssignmentToConstant(x, ..) if x == "x"
)); ));
Ok(()) Ok(())
@ -87,7 +87,7 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorAssignmentToConstant(_, _) EvalAltResult::ErrorAssignmentToConstant(..)
)); ));
let mut scope = Scope::new(); let mut scope = Scope::new();
@ -120,7 +120,7 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.run_with_scope(&mut scope, "MY_NUMBER.value = 42;") .run_with_scope(&mut scope, "MY_NUMBER.value = 42;")
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorAssignmentToConstant(_, _) EvalAltResult::ErrorAssignmentToConstant(..)
)); ));
Ok(()) Ok(())

View File

@ -81,7 +81,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.run("let foo = (exec [x<<15] -> { x += 2 } while x < 42) * 10;") .run("let foo = (exec [x<<15] -> { x += 2 } while x < 42) * 10;")
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorRuntime(_, _) EvalAltResult::ErrorRuntime(..)
)); ));
assert_eq!( assert_eq!(

View File

@ -38,7 +38,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -52,7 +52,7 @@ fn test_max_string_size() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
engine.set_max_string_size(0); engine.set_max_string_size(0);
@ -98,7 +98,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
@ -131,7 +131,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
assert!(matches!( assert!(matches!(
@ -143,7 +143,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -169,7 +169,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
assert_eq!( assert_eq!(
@ -191,7 +191,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -204,7 +204,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
assert!(matches!( assert!(matches!(
@ -218,7 +218,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
engine.set_max_array_size(0); engine.set_max_array_size(0);
@ -282,7 +282,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
assert!(matches!( assert!(matches!(
@ -295,7 +295,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
assert!(matches!( assert!(matches!(
@ -307,7 +307,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -320,7 +320,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
" "
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorDataTooLarge(_, _) EvalAltResult::ErrorDataTooLarge(..)
)); ));
engine.set_max_map_size(0); engine.set_max_map_size(0);

View File

@ -171,7 +171,7 @@ fn test_eval_disabled() -> Result<(), Box<EvalAltResult>> {
.compile(r#"eval("40 + 2")"#) .compile(r#"eval("40 + 2")"#)
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::BadInput(LexError::ImproperSymbol(err, _)) if err == "eval" ParseErrorType::BadInput(LexError::ImproperSymbol(err, ..)) if err == "eval"
)); ));
Ok(()) Ok(())

View File

@ -73,8 +73,8 @@ fn test_fn_ptr() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorInFunctionCall(fn_name, _, err, _) EvalAltResult::ErrorInFunctionCall(fn_name, _, err, ..)
if fn_name == "foo" && matches!(*err, EvalAltResult::ErrorUnboundThis(_)) if fn_name == "foo" && matches!(*err, EvalAltResult::ErrorUnboundThis(..))
)); ));
Ok(()) Ok(())

View File

@ -92,8 +92,8 @@ fn test_functions_global_module() -> Result<(), Box<EvalAltResult>> {
foo() foo()
} }
").expect_err("should error"), ").expect_err("should error"),
EvalAltResult::ErrorInFunctionCall(_, _, err, _) EvalAltResult::ErrorInFunctionCall(.., err, _)
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, _) if v == "global::ANSWER") if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, ..) if v == "global::ANSWER")
)); ));
engine.register_result_fn( engine.register_result_fn(
@ -110,8 +110,8 @@ fn test_functions_global_module() -> Result<(), Box<EvalAltResult>> {
global::LOCAL_VALUE global::LOCAL_VALUE
}); });
").expect_err("should error"), ").expect_err("should error"),
EvalAltResult::ErrorInFunctionCall(_, _, err, _) EvalAltResult::ErrorInFunctionCall(.., err, _)
if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, _) if v == "global::LOCAL_VALUE") if matches!(&*err, EvalAltResult::ErrorVariableNotFound(v, ..) if v == "global::LOCAL_VALUE")
)); ));
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]

View File

@ -170,7 +170,7 @@ fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
assert!(matches!( assert!(matches!(
*engine.eval::<INT>(r#"let f = Fn("abc"); f.call(0)"#).expect_err("should error"), *engine.eval::<INT>(r#"let f = Fn("abc"); f.call(0)"#).expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with("abc (") EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("abc (")
)); ));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -249,7 +249,7 @@ fn test_internal_fn_bang() -> Result<(), Box<EvalAltResult>> {
) )
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::MalformedCapture(_) ParseErrorType::MalformedCapture(..)
)); ));
Ok(()) Ok(())

View File

@ -53,7 +53,7 @@ b`: 1}; y["a\nb"]
*engine *engine
.eval::<INT>("let y = #{`a${1}`: 1}; y.a1") .eval::<INT>("let y = #{`a${1}`: 1}; y.a1")
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorParsing(ParseErrorType::PropertyExpected, _) EvalAltResult::ErrorParsing(ParseErrorType::PropertyExpected, ..)
)); ));
assert!(engine.eval::<bool>(r#"let y = #{a: 1, b: 2, c: 3}; "c" in y"#)?); assert!(engine.eval::<bool>(r#"let y = #{a: 1, b: 2, c: 3}; "c" in y"#)?);
@ -205,7 +205,7 @@ fn test_map_json() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine.parse_json(" 123", true).expect_err("should error"), *engine.parse_json(" 123", true).expect_err("should error"),
EvalAltResult::ErrorParsing(ParseErrorType::MissingToken(token, _), _) EvalAltResult::ErrorParsing(ParseErrorType::MissingToken(token, ..), ..)
if token == "{" if token == "{"
)); ));

View File

@ -37,37 +37,37 @@ fn test_math() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.eval::<INT>("abs(-9223372036854775808)") .eval::<INT>("abs(-9223372036854775808)")
.expect_err("expects negation overflow"), .expect_err("expects negation overflow"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("9223372036854775807 + 1") .eval::<INT>("9223372036854775807 + 1")
.expect_err("expects overflow"), .expect_err("expects overflow"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("-9223372036854775808 - 1") .eval::<INT>("-9223372036854775808 - 1")
.expect_err("expects underflow"), .expect_err("expects underflow"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("9223372036854775807 * 9223372036854775807") .eval::<INT>("9223372036854775807 * 9223372036854775807")
.expect_err("expects overflow"), .expect_err("expects overflow"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("9223372036854775807 / 0") .eval::<INT>("9223372036854775807 / 0")
.expect_err("expects division by zero"), .expect_err("expects division by zero"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("9223372036854775807 % 0") .eval::<INT>("9223372036854775807 % 0")
.expect_err("expects division by zero"), .expect_err("expects division by zero"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
} }
@ -77,31 +77,31 @@ fn test_math() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.eval::<INT>("2147483647 + 1") .eval::<INT>("2147483647 + 1")
.expect_err("expects overflow"), .expect_err("expects overflow"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("-2147483648 - 1") .eval::<INT>("-2147483648 - 1")
.expect_err("expects underflow"), .expect_err("expects underflow"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("2147483647 * 2147483647") .eval::<INT>("2147483647 * 2147483647")
.expect_err("expects overflow"), .expect_err("expects overflow"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("2147483647 / 0") .eval::<INT>("2147483647 / 0")
.expect_err("expects division by zero"), .expect_err("expects division by zero"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<INT>("2147483647 % 0") .eval::<INT>("2147483647 % 0")
.expect_err("expects division by zero"), .expect_err("expects division by zero"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
} }
} }

View File

@ -6,7 +6,7 @@ fn test_mismatched_op() {
assert!(matches!( assert!(matches!(
*engine.eval::<INT>(r#""hello, " + "world!""#).expect_err("expects error"), *engine.eval::<INT>(r#""hello, " + "world!""#).expect_err("expects error"),
EvalAltResult::ErrorMismatchOutputType(need, actual, _) if need == std::any::type_name::<INT>() && actual == "string" EvalAltResult::ErrorMismatchOutputType(need, actual, ..) if need == std::any::type_name::<INT>() && actual == "string"
)); ));
} }
@ -35,18 +35,18 @@ fn test_mismatched_op_custom_type() -> Result<(), Box<EvalAltResult>> {
let y = new_ts(); let y = new_ts();
x == y x == y
").expect_err("should error"), ").expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(f, _) if f == "== (TestStruct, TestStruct)")); EvalAltResult::ErrorFunctionNotFound(f, ..) if f == "== (TestStruct, TestStruct)"));
assert!(!engine.eval::<bool>("new_ts() == 42")?); assert!(!engine.eval::<bool>("new_ts() == 42")?);
assert!(matches!( assert!(matches!(
*engine.eval::<INT>("60 + new_ts()").expect_err("should error"), *engine.eval::<INT>("60 + new_ts()").expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(f, _) if f == format!("+ ({}, TestStruct)", std::any::type_name::<INT>()) EvalAltResult::ErrorFunctionNotFound(f, ..) if f == format!("+ ({}, TestStruct)", std::any::type_name::<INT>())
)); ));
assert!(matches!( assert!(matches!(
*engine.eval::<TestStruct>("42").expect_err("should error"), *engine.eval::<TestStruct>("42").expect_err("should error"),
EvalAltResult::ErrorMismatchOutputType(need, actual, _) EvalAltResult::ErrorMismatchOutputType(need, actual, ..)
if need == "TestStruct" && actual == std::any::type_name::<INT>() if need == "TestStruct" && actual == std::any::type_name::<INT>()
)); ));

View File

@ -214,7 +214,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorTooManyModules(_) EvalAltResult::ErrorTooManyModules(..)
)); ));
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
@ -237,7 +237,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorInFunctionCall(fn_name, _, _, _) if fn_name == "foo" EvalAltResult::ErrorInFunctionCall(fn_name, _, ..) if fn_name == "foo"
)); ));
engine.set_max_modules(1000); engine.set_max_modules(1000);
@ -369,7 +369,7 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.run(r#"import "testing" as ttt; ttt::hidden()"#) .run(r#"import "testing" as ttt; ttt::hidden()"#)
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(fn_name, _) if fn_name == "ttt::hidden ()" EvalAltResult::ErrorFunctionNotFound(fn_name, ..) if fn_name == "ttt::hidden ()"
)); ));
Ok(()) Ok(())
@ -381,13 +381,13 @@ fn test_module_export() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
engine.compile("let x = 10; { export x; }").expect_err("should error"), engine.compile("let x = 10; { export x; }").expect_err("should error"),
ParseError(x, _) if *x == ParseErrorType::WrongExport ParseError(x, ..) if *x == ParseErrorType::WrongExport
)); ));
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
assert!(matches!( assert!(matches!(
engine.compile("fn abc(x) { export x; }").expect_err("should error"), engine.compile("fn abc(x) { export x; }").expect_err("should error"),
ParseError(x, _) if *x == ParseErrorType::WrongExport ParseError(x, ..) if *x == ParseErrorType::WrongExport
)); ));
Ok(()) Ok(())

View File

@ -19,7 +19,7 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine.run("for x in 0..500 {}").expect_err("should error"), *engine.run("for x in 0..500 {}").expect_err("should error"),
EvalAltResult::ErrorTooManyOperations(_) EvalAltResult::ErrorTooManyOperations(..)
)); ));
engine.set_max_operations(0); engine.set_max_operations(0);
@ -44,7 +44,7 @@ fn test_max_operations_literal() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.run("[1, 2, 3, 4, 5, 6, 7, 8, 9]") .run("[1, 2, 3, 4, 5, 6, 7, 8, 9]")
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorTooManyOperations(_) EvalAltResult::ErrorTooManyOperations(..)
)); ));
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -55,7 +55,7 @@ fn test_max_operations_literal() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.run("#{a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9}") .run("#{a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9}")
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorTooManyOperations(_) EvalAltResult::ErrorTooManyOperations(..)
)); ));
Ok(()) Ok(())
@ -111,7 +111,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
"#, "#,
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorTooManyOperations(_) EvalAltResult::ErrorTooManyOperations(..)
)); ));
Ok(()) Ok(())
@ -138,7 +138,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
"# "#
) )
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorInFunctionCall(_, _, err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(_)) EvalAltResult::ErrorInFunctionCall(.., err, _) if matches!(*err, EvalAltResult::ErrorTooManyOperations(..))
)); ));
Ok(()) Ok(())
@ -163,7 +163,7 @@ fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.run("for x in 0..500 {}") .run("for x in 0..500 {}")
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorTerminated(x, _) if x.as_int()? == 42 EvalAltResult::ErrorTerminated(x, ..) if x.as_int()? == 42
)); ));
Ok(()) Ok(())

View File

@ -27,12 +27,12 @@ fn test_ops_other_number_types() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine.eval_with_scope::<bool>(&mut scope, "x == 42").expect_err("should error"), *engine.eval_with_scope::<bool>(&mut scope, "x == 42").expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with("== (u16,") EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (u16,")
)); ));
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
assert!(matches!( assert!(matches!(
*engine.eval_with_scope::<bool>(&mut scope, "x == 42.0").expect_err("should error"), *engine.eval_with_scope::<bool>(&mut scope, "x == 42.0").expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with("== (u16,") EvalAltResult::ErrorFunctionNotFound(f, ..) if f.starts_with("== (u16,")
)); ));
assert!(!engine.eval_with_scope::<bool>(&mut scope, r#"x == "hello""#)?); assert!(!engine.eval_with_scope::<bool>(&mut scope, r#"x == "hello""#)?);

View File

@ -93,7 +93,7 @@ fn test_plugins_package() -> Result<(), Box<EvalAltResult>> {
assert!( assert!(
matches!(*engine.run("const A = [1, 2, 3]; A.test(42);").expect_err("should error"), matches!(*engine.run("const A = [1, 2, 3]; A.test(42);").expect_err("should error"),
EvalAltResult::ErrorAssignmentToConstant(x, _) if x == "array") EvalAltResult::ErrorAssignmentToConstant(x, ..) if x == "array")
) )
} }

View File

@ -29,7 +29,7 @@ fn test_stack_overflow_fn_calls() -> Result<(), Box<EvalAltResult>> {
max + 1 max + 1
)) ))
.expect_err("should error"), .expect_err("should error"),
EvalAltResult::ErrorStackOverflow(_) EvalAltResult::ErrorStackOverflow(..)
)); ));
Ok(()) Ok(())

View File

@ -117,7 +117,7 @@ fn test_string_mut() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<INT>(r#"bar("hello")"#)?, 5); assert_eq!(engine.eval::<INT>(r#"bar("hello")"#)?, 5);
assert!( assert!(
matches!(*engine.eval::<INT>(r#"baz("hello")"#).expect_err("should error"), matches!(*engine.eval::<INT>(r#"baz("hello")"#).expect_err("should error"),
EvalAltResult::ErrorFunctionNotFound(f, _) if f == "baz (&str | ImmutableString | String)" EvalAltResult::ErrorFunctionNotFound(f, ..) if f == "baz (&str | ImmutableString | String)"
) )
); );

View File

@ -6,12 +6,12 @@ fn test_throw() {
assert!(matches!( assert!(matches!(
*engine.run("if true { throw 42 }").expect_err("expects error"), *engine.run("if true { throw 42 }").expect_err("expects error"),
EvalAltResult::ErrorRuntime(s, _) if s.as_int().unwrap() == 42 EvalAltResult::ErrorRuntime(s, ..) if s.as_int().unwrap() == 42
)); ));
assert!(matches!( assert!(matches!(
*engine.run(r#"throw"#).expect_err("expects error"), *engine.run(r#"throw"#).expect_err("expects error"),
EvalAltResult::ErrorRuntime(s, _) if s.is::<()>() EvalAltResult::ErrorRuntime(s, ..) if s.is::<()>()
)); ));
} }
@ -96,7 +96,7 @@ fn test_try_catch() -> Result<(), Box<EvalAltResult>> {
*engine *engine
.run("try { 42/0; } catch { throw; }") .run("try { 42/0; } catch { throw; }")
.expect_err("expects error"), .expect_err("expects error"),
EvalAltResult::ErrorArithmetic(_, _) EvalAltResult::ErrorArithmetic(..)
)); ));
Ok(()) Ok(())

View File

@ -115,7 +115,7 @@ fn test_var_resolver() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "chameleon")?, 1); assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "chameleon")?, 1);
assert!( assert!(
matches!(*engine.eval_with_scope::<INT>(&mut scope, "DO_NOT_USE").expect_err("should error"), matches!(*engine.eval_with_scope::<INT>(&mut scope, "DO_NOT_USE").expect_err("should error"),
EvalAltResult::ErrorVariableNotFound(n, _) if n == "DO_NOT_USE") EvalAltResult::ErrorVariableNotFound(n, ..) if n == "DO_NOT_USE")
); );
Ok(()) Ok(())