diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8687601c..24607668 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,7 @@ jobs: - "--features metadata,serde,internals" - "--features unchecked" - "--features sync" + - "--features no_position" - "--features no_optimize" - "--features no_float" - "--features f32_float,serde,metadata,internals" @@ -34,8 +35,8 @@ jobs: - "--features no_module" - "--features no_closure" - "--features unicode-xid-ident" - - "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,serde,unchecked" - - "--features no_function,no_float,no_index,no_object,no_optimize,no_module,no_closure,unchecked" + - "--features sync,no_function,no_float,no_position,no_optimize,no_module,no_closure,metadata,serde,unchecked" + - "--features no_function,no_float,no_position,no_index,no_object,no_optimize,no_module,no_closure,unchecked" toolchain: [stable] experimental: [false] include: diff --git a/CHANGELOG.md b/CHANGELOG.md index 142f71ce..65c55117 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ New features ------------ * A module called `global` is automatically created to hold global-level constants, which can then be accessed from functions. +* A new feature `no_position` is added to turn off position tracking during parsing to squeeze out the last drop of performance. Version 0.20.0 diff --git a/Cargo.toml b/Cargo.toml index e97fc7f7..7e6d2d03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ rhai_codegen = { version = "0.3.4", path = "codegen", default_features = false } default = ["smartstring/std", "ahash/std", "num-traits/std"] # remove 'smartstring/std' when smartstring is updated to support no-std unchecked = [] # unchecked arithmetic sync = [] # restrict to only types that implement Send + Sync +no_position = [] # do not track position in the parser no_optimize = [] # no script optimizer no_float = [] # no floating-point f32_float = [] # set FLOAT=f32 diff --git a/examples/arrays_and_structs.rs b/examples/arrays_and_structs.rs index 374fc834..8b29d1a8 100644 --- a/examples/arrays_and_structs.rs +++ b/examples/arrays_and_structs.rs @@ -25,7 +25,7 @@ fn main() -> Result<(), Box> { .register_fn("update", TestStruct::update); let result = engine.eval::( - r" + " let x = new_ts(); x.update(); x @@ -35,7 +35,7 @@ fn main() -> Result<(), Box> { println!("{:?}", result); let result = engine.eval::( - r" + " let x = [ new_ts() ]; x[0].update(); x[0] diff --git a/examples/custom_types_and_methods.rs b/examples/custom_types_and_methods.rs index 4e06312c..fe1777f5 100644 --- a/examples/custom_types_and_methods.rs +++ b/examples/custom_types_and_methods.rs @@ -25,7 +25,7 @@ fn main() -> Result<(), Box> { .register_fn("update", TestStruct::update); let result = engine.eval::( - r" + " let x = new_ts(); x.update(); x diff --git a/src/ast.rs b/src/ast.rs index 154d8024..0b56c64b 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,13 +1,11 @@ //! Module defining the AST (abstract syntax tree). -use crate::dynamic::{AccessMode, Union}; use crate::fn_native::shared_make_mut; use crate::module::NamespaceRef; use crate::token::Token; use crate::utils::calc_fn_hash; use crate::{ - Dynamic, FnNamespace, FnPtr, Identifier, ImmutableString, Module, Position, Shared, StaticVec, - INT, + Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -68,7 +66,7 @@ pub struct ScriptFnDef { /// Function doc-comments (if any). #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] - pub comments: StaticVec, + pub comments: StaticVec, } impl fmt::Display for ScriptFnDef { @@ -782,7 +780,12 @@ pub struct Ident { impl fmt::Debug for Ident { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?} @ {:?}", self.name, self.pos) + #[cfg(not(feature = "no_position"))] + write!(f, "{:?} @ {:?}", self.name, self.pos)?; + #[cfg(feature = "no_position")] + write!(f, "{:?}", self.name)?; + + Ok(()) } } @@ -920,6 +923,11 @@ pub enum Stmt { Const(Expr, Box, bool, Position), /// expr op`=` expr Assignment(Box<(Expr, Option, Expr)>, Position), + /// func `(` expr `,` ... `)` + /// + /// Note - this is a duplicate of [`Expr::FnCall`] to cover the very common pattern of a single + /// function call forming one statement. + FnCall(Box, Position), /// `{` stmt`;` ... `}` Block(Vec, Position), /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` @@ -985,6 +993,7 @@ impl Stmt { | Self::Break(pos) | Self::Block(_, pos) | Self::Assignment(_, pos) + | Self::FnCall(_, pos) | Self::If(_, _, pos) | Self::Switch(_, _, pos) | Self::While(_, _, pos) @@ -1014,6 +1023,7 @@ impl Stmt { | Self::Break(pos) | Self::Block(_, pos) | Self::Assignment(_, pos) + | Self::FnCall(_, pos) | Self::If(_, _, pos) | Self::Switch(_, _, pos) | Self::While(_, _, pos) @@ -1042,7 +1052,11 @@ impl Stmt { /// Does this statement return a value? pub fn returns_value(&self) -> bool { match self { - Self::If(_, _, _) | Self::Switch(_, _, _) | Self::Block(_, _) | Self::Expr(_) => true, + Self::If(_, _, _) + | Self::Switch(_, _, _) + | Self::Block(_, _) + | Self::Expr(_) + | Self::FnCall(_, _) => true, Self::Noop(_) | Self::While(_, _, _) @@ -1080,6 +1094,7 @@ impl Stmt { Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) + | Self::FnCall(_, _) | Self::Expr(_) | Self::Do(_, _, _, _) | Self::Continue(_) @@ -1117,7 +1132,10 @@ impl Stmt { condition.is_pure() && block.0.iter().all(Stmt::is_pure) } Self::For(iterable, x, _) => iterable.is_pure() && (x.1).0.iter().all(Stmt::is_pure), - Self::Let(_, _, _, _) | Self::Const(_, _, _, _) | Self::Assignment(_, _) => false, + Self::Let(_, _, _, _) + | Self::Const(_, _, _, _) + | Self::Assignment(_, _) + | Self::FnCall(_, _) => false, Self::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()), Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, Self::TryCatch(x, _, _) => { @@ -1246,6 +1264,13 @@ impl Stmt { return false; } } + Self::FnCall(x, _) => { + for s in &x.args { + if !s.walk(path, on_node) { + return false; + } + } + } Self::Block(x, _) => { for s in x { if !s.walk(path, on_node) { @@ -1370,14 +1395,14 @@ impl OpAssignment { /// /// This type is volatile and may change. #[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] -pub struct FnCallHash { +pub struct FnCallHashes { /// Pre-calculated hash for a script-defined function ([`None`] if native functions only). pub script: Option, /// Pre-calculated hash for a native Rust function with no parameter types. pub native: u64, } -impl fmt::Debug for FnCallHash { +impl fmt::Debug for FnCallHashes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Some(script) = self.script { if script == self.native { @@ -1391,7 +1416,7 @@ impl fmt::Debug for FnCallHash { } } -impl FnCallHash { +impl FnCallHashes { /// Create a [`FnCallHash`] with only the native Rust hash. #[inline(always)] pub fn from_native(hash: u64) -> Self { @@ -1443,23 +1468,28 @@ impl FnCallHash { /// # Volatile Data Structure /// /// This type is volatile and may change. -#[derive(Clone, Default, Hash)] +#[derive(Debug, Clone, Default, Hash)] pub struct FnCallExpr { - /// Pre-calculated hash. - pub hash: FnCallHash, - /// Does this function call capture the parent scope? - pub capture: bool, + /// Namespace of the function, if any. + pub namespace: Option, + /// Pre-calculated hashes. + pub hashes: FnCallHashes, /// List of function call argument expressions. pub args: StaticVec, /// List of function call arguments that are constants. pub constant_args: smallvec::SmallVec<[(Dynamic, Position); 2]>, - /// Namespace of the function, if any. Boxed because it occurs rarely. - pub namespace: Option, /// Function name. pub name: Identifier, + /// Does this function call capture the parent scope? + pub capture: bool, } impl FnCallExpr { + /// Does this function call contain a qualified namespace? + #[inline(always)] + pub fn is_qualified(&self) -> bool { + self.namespace.is_some() + } /// Are there no arguments to this function call? #[inline(always)] pub fn is_args_empty(&self) -> bool { @@ -1467,7 +1497,7 @@ impl FnCallExpr { } /// Get the number of arguments to this function call. #[inline(always)] - pub fn num_args(&self) -> usize { + pub fn args_count(&self) -> usize { self.args.len() + self.constant_args.len() } } @@ -1609,8 +1639,6 @@ pub enum Expr { CharConstant(char, Position), /// [String][ImmutableString] constant. StringConstant(ImmutableString, Position), - /// [`FnPtr`] constant. - FnPointer(ImmutableString, Position), /// An interpolated [string][ImmutableString]. InterpolatedString(Box>), /// [ expr, ... ] @@ -1664,32 +1692,59 @@ impl Default for Expr { impl fmt::Debug for Expr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + #[cfg(not(feature = "no_position"))] Self::DynamicConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), + #[cfg(not(feature = "no_position"))] Self::BoolConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), + #[cfg(not(feature = "no_position"))] Self::IntegerConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), #[cfg(not(feature = "no_float"))] + #[cfg(not(feature = "no_position"))] Self::FloatConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), + #[cfg(not(feature = "no_position"))] Self::CharConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), + #[cfg(not(feature = "no_position"))] Self::StringConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), - Self::FnPointer(value, pos) => write!(f, "Fn({:?}) @ {:?}", value, pos), + #[cfg(not(feature = "no_position"))] Self::Unit(pos) => write!(f, "() @ {:?}", pos), + + #[cfg(feature = "no_position")] + Self::DynamicConstant(value, _) => write!(f, "{:?}", value), + #[cfg(feature = "no_position")] + Self::BoolConstant(value, _) => write!(f, "{:?}", value), + #[cfg(feature = "no_position")] + Self::IntegerConstant(value, _) => write!(f, "{:?}", value), + #[cfg(not(feature = "no_float"))] + #[cfg(feature = "no_position")] + Self::FloatConstant(value, _) => write!(f, "{:?}", value), + #[cfg(feature = "no_position")] + Self::CharConstant(value, _) => write!(f, "{:?}", value), + #[cfg(feature = "no_position")] + Self::StringConstant(value, _) => write!(f, "{:?}", value), + #[cfg(feature = "no_position")] + Self::Unit(_) => f.write_str("()"), + Self::InterpolatedString(x) => { f.write_str("InterpolatedString")?; f.debug_list().entries(x.iter()).finish() } - Self::Array(x, pos) => { + Self::Array(x, _pos) => { f.write_str("Array")?; f.debug_list().entries(x.iter()).finish()?; - write!(f, " @ {:?}", pos) + #[cfg(not(feature = "no_position"))] + write!(f, " @ {:?}", _pos)?; + Ok(()) } - Self::Map(x, pos) => { + Self::Map(x, _pos) => { f.write_str("Map")?; f.debug_map() .entries(x.0.iter().map(|(k, v)| (k, v))) .finish()?; - write!(f, " @ {:?}", pos) + #[cfg(not(feature = "no_position"))] + write!(f, " @ {:?}", _pos)?; + Ok(()) } - Self::Variable(i, pos, x) => { + Self::Variable(i, _pos, x) => { f.write_str("Variable(")?; match x.1 { Some((_, ref namespace)) => write!(f, "{}", namespace)?, @@ -1700,21 +1755,29 @@ impl fmt::Debug for Expr { Some(n) => write!(f, ", {}", n)?, _ => (), } - write!(f, ") @ {:?}", pos) + f.write_str(")")?; + #[cfg(not(feature = "no_position"))] + write!(f, " @ {:?}", _pos)?; + Ok(()) } + #[cfg(not(feature = "no_position"))] Self::Property(x) => write!(f, "Property({:?} @ {:?})", x.2.name, x.2.pos), + #[cfg(feature = "no_position")] + Self::Property(x) => write!(f, "Property({:?})", x.2.name), Self::Stmt(x) => { f.write_str("Stmt")?; f.debug_list().entries(x.0.iter()).finish()?; - write!(f, " @ {:?}", x.1) + #[cfg(not(feature = "no_position"))] + write!(f, " @ {:?}", x.1)?; + Ok(()) } - Self::FnCall(x, pos) => { + Self::FnCall(x, _pos) => { let mut ff = f.debug_struct("FnCall"); if let Some(ref ns) = x.namespace { ff.field("namespace", ns); } ff.field("name", &x.name) - .field("hash", &x.hash) + .field("hash", &x.hashes) .field("args", &x.args); if !x.constant_args.is_empty() { ff.field("constant_args", &x.constant_args); @@ -1723,9 +1786,11 @@ impl fmt::Debug for Expr { ff.field("capture", &x.capture); } ff.finish()?; - write!(f, " @ {:?}", pos) + #[cfg(not(feature = "no_position"))] + write!(f, " @ {:?}", _pos)?; + Ok(()) } - Self::Dot(x, pos) | Self::Index(x, pos) | Self::And(x, pos) | Self::Or(x, pos) => { + Self::Dot(x, _pos) | Self::Index(x, _pos) | Self::And(x, _pos) | Self::Or(x, _pos) => { let op_name = match self { Self::Dot(_, _) => "Dot", Self::Index(_, _) => "Index", @@ -1738,11 +1803,15 @@ impl fmt::Debug for Expr { .field("lhs", &x.lhs) .field("rhs", &x.rhs) .finish()?; - write!(f, " @ {:?}", pos) + #[cfg(not(feature = "no_position"))] + write!(f, " @ {:?}", _pos)?; + Ok(()) } - Self::Custom(x, pos) => { + Self::Custom(x, _pos) => { f.debug_tuple("Custom").field(x).finish()?; - write!(f, " @ {:?}", pos) + #[cfg(not(feature = "no_position"))] + write!(f, " @ {:?}", _pos)?; + Ok(()) } } } @@ -1761,10 +1830,6 @@ impl Expr { Self::FloatConstant(x, _) => (*x).into(), Self::CharConstant(x, _) => (*x).into(), Self::StringConstant(x, _) => x.clone().into(), - Self::FnPointer(x, _) => Dynamic(Union::FnPtr( - Box::new(FnPtr::new_unchecked(x.clone(), Default::default())), - AccessMode::ReadOnly, - )), Self::BoolConstant(x, _) => (*x).into(), Self::Unit(_) => Dynamic::UNIT, @@ -1772,7 +1837,7 @@ impl Expr { Self::Array(x, _) if self.is_constant() => { let mut arr = Array::with_capacity(x.len()); arr.extend(x.iter().map(|v| v.get_constant_value().unwrap())); - arr.into() + Dynamic::from_array(arr) } #[cfg(not(feature = "no_object"))] @@ -1781,7 +1846,7 @@ impl Expr { x.0.iter().for_each(|(k, v)| { *map.get_mut(k.name.as_str()).unwrap() = v.get_constant_value().unwrap() }); - map.into() + Dynamic::from_map(map) } _ => return None, @@ -1816,7 +1881,6 @@ impl Expr { Self::CharConstant(_, pos) => *pos, Self::StringConstant(_, pos) => *pos, Self::InterpolatedString(x) => x.first().unwrap().position(), - Self::FnPointer(_, pos) => *pos, Self::Array(_, pos) => *pos, Self::Map(_, pos) => *pos, Self::Property(x) => (x.2).pos, @@ -1848,7 +1912,6 @@ impl Expr { Self::InterpolatedString(x) => { x.first_mut().unwrap().set_position(new_pos); } - Self::FnPointer(_, pos) => *pos = new_pos, Self::Array(_, pos) => *pos = new_pos, Self::Map(_, pos) => *pos = new_pos, Self::Variable(_, pos, _) => *pos = new_pos, @@ -1904,7 +1967,6 @@ impl Expr { | Self::IntegerConstant(_, _) | Self::CharConstant(_, _) | Self::StringConstant(_, _) - | Self::FnPointer(_, _) | Self::Unit(_) => true, Self::InterpolatedString(x) | Self::Array(x, _) => x.iter().all(Self::is_constant), @@ -1931,7 +1993,6 @@ impl Expr { | Self::BoolConstant(_, _) | Self::IntegerConstant(_, _) | Self::CharConstant(_, _) - | Self::FnPointer(_, _) | Self::And(_, _) | Self::Or(_, _) | Self::Unit(_) => false, @@ -2044,6 +2105,7 @@ mod tests { assert_eq!(size_of::(), 16); assert_eq!(size_of::>(), 16); + #[cfg(not(feature = "no_position"))] assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 16); assert_eq!(size_of::>(), 16); @@ -2052,7 +2114,10 @@ mod tests { assert_eq!(size_of::(), 96); assert_eq!(size_of::(), 288); assert_eq!(size_of::(), 56); - assert_eq!(size_of::(), 16); + assert_eq!( + size_of::(), + if cfg!(feature = "no_position") { 8 } else { 16 } + ); assert_eq!(size_of::(), 72); } } diff --git a/src/bin/README.md b/src/bin/README.md index 1ba7e865..a0afbd8f 100644 --- a/src/bin/README.md +++ b/src/bin/README.md @@ -1,7 +1,7 @@ Rhai Tools ========== -Tools written in Rhai. +Tools for running Rhai scripts. How to Run diff --git a/src/bin/rhai-repl.rs b/src/bin/rhai-repl.rs index 627ab334..7f4f87d0 100644 --- a/src/bin/rhai-repl.rs +++ b/src/bin/rhai-repl.rs @@ -55,8 +55,9 @@ fn print_help() { } fn main() { - println!("Rhai REPL tool"); - println!("=============="); + let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION")); + println!("{}", title); + println!("{0:=<1$}", "", title.len()); print_help(); // Initialize scripting engine diff --git a/src/dynamic.rs b/src/dynamic.rs index cc0ca5c7..19b71823 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -407,6 +407,13 @@ impl Hash for Dynamic { value.hash(state); }) } + Union::FnPtr(f, _) if f.is_curried() => { + unimplemented!( + "{} with curried arguments cannot be hashed", + self.type_name() + ) + } + Union::FnPtr(f, _) => f.fn_name().hash(state), #[cfg(not(feature = "no_closure"))] Union::Shared(cell, _) => { @@ -1717,13 +1724,21 @@ impl From<&ImmutableString> for Dynamic { impl From<&crate::Identifier> for Dynamic { #[inline(always)] fn from(value: &crate::Identifier) -> Self { - std::string::ToString::to_string(value).into() + value.to_string().into() } } #[cfg(not(feature = "no_index"))] -impl From> for Dynamic { +impl Dynamic { + /// Create a [`Dynamc`] from an [`Array`]. #[inline(always)] - fn from(value: std::vec::Vec) -> Self { + pub(crate) fn from_array(array: Array) -> Self { + Self(Union::Array(Box::new(array), AccessMode::ReadWrite)) + } +} +#[cfg(not(feature = "no_index"))] +impl From> for Dynamic { + #[inline(always)] + fn from(value: Vec) -> Self { Self(Union::Array( Box::new(value.into_iter().map(Dynamic::from).collect()), AccessMode::ReadWrite, @@ -1751,6 +1766,14 @@ impl std::iter::FromIterator for Dynamic { } } #[cfg(not(feature = "no_object"))] +impl Dynamic { + /// Create a [`Dynamc`] from a [`Map`]. + #[inline(always)] + pub(crate) fn from_map(map: Map) -> Self { + Self(Union::Map(Box::new(map), AccessMode::ReadWrite)) + } +} +#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_std"))] impl, T: Variant + Clone> From> for Dynamic diff --git a/src/engine.rs b/src/engine.rs index f38cf536..aaaa9300 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -13,8 +13,8 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::syntax::CustomSyntax; use crate::utils::get_hasher; use crate::{ - Dynamic, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, Position, RhaiResult, - Scope, Shared, StaticVec, + Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope, + Shared, StaticVec, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -35,7 +35,7 @@ use crate::{calc_fn_hash, Array}; use crate::Map; #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] -use crate::ast::FnCallHash; +use crate::ast::FnCallHashes; pub type Precedence = NonZeroU8; @@ -574,6 +574,7 @@ impl State { self.scope_level == 0 } /// Get a mutable reference to the current function resolution cache. + #[inline(always)] pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { if self.fn_resolution_caches.0.is_empty() { self.fn_resolution_caches.0.push(BTreeMap::new()); @@ -582,6 +583,7 @@ impl State { } /// Push an empty function resolution cache onto the stack and make it current. #[allow(dead_code)] + #[inline(always)] pub fn push_fn_resolution_cache(&mut self) { self.fn_resolution_caches .0 @@ -592,6 +594,7 @@ impl State { /// # Panics /// /// Panics if there are no more function resolution cache in the stack. + #[inline(always)] pub fn pop_fn_resolution_cache(&mut self) { let mut cache = self.fn_resolution_caches.0.pop().unwrap(); cache.clear(); @@ -836,6 +839,8 @@ fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) { #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))] if let Some(source) = _source { println!("{} @ {:?} | {}", source, _pos, _s); + } else if _pos.is_none() { + println!("{}", _s); } else { println!("{:?} | {}", _pos, _s); } @@ -1186,7 +1191,7 @@ impl Engine { let val_type_name = target.type_name(); let ((_, val_pos), _) = new_val; - let hash_set = FnCallHash::from_native(calc_fn_hash( + let hash_set = FnCallHashes::from_native(calc_fn_hash( std::iter::empty(), FN_IDX_SET, 3, @@ -1227,11 +1232,11 @@ impl Engine { ChainType::Dot => { match rhs { // xxx.fn_name(arg_expr_list) - Expr::FnCall(x, pos) if x.namespace.is_none() && new_val.is_none() => { - let FnCallExpr { name, hash, .. } = x.as_ref(); + Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => { + let FnCallExpr { name, hashes, .. } = x.as_ref(); let mut args = idx_val.as_fn_call_args(); self.make_method_call( - mods, state, lib, name, *hash, target, &mut args, *pos, level, + mods, state, lib, name, *hashes, target, &mut args, *pos, level, ) } // xxx.fn_name(...) = ??? @@ -1272,7 +1277,7 @@ impl Engine { let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap(); if op_info.is_some() { - let hash = FnCallHash::from_native(*hash_get); + let hash = FnCallHashes::from_native(*hash_get); let mut args = [target.as_mut()]; let (mut orig_val, _) = self.exec_fn_call( mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, @@ -1285,7 +1290,7 @@ impl Engine { new_val = orig_val; } - let hash = FnCallHash::from_native(*hash_set); + let hash = FnCallHashes::from_native(*hash_set); let mut args = [target.as_mut(), &mut new_val]; self.exec_fn_call( mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None, @@ -1296,7 +1301,7 @@ impl Engine { // xxx.id Expr::Property(x) => { let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref(); - let hash = FnCallHash::from_native(*hash_get); + let hash = FnCallHashes::from_native(*hash_get); let mut args = [target.as_mut()]; self.exec_fn_call( mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None, @@ -1316,11 +1321,11 @@ impl Engine { )? } // {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr - Expr::FnCall(x, pos) if x.namespace.is_none() => { - let FnCallExpr { name, hash, .. } = x.as_ref(); + Expr::FnCall(x, pos) if !x.is_qualified() => { + let FnCallExpr { name, hashes, .. } = x.as_ref(); let mut args = idx_val.as_fn_call_args(); let (val, _) = self.make_method_call( - mods, state, lib, name, *hash, target, &mut args, *pos, level, + mods, state, lib, name, *hashes, target, &mut args, *pos, level, )?; val.into() } @@ -1345,8 +1350,8 @@ impl Engine { Expr::Property(p) => { let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = p.as_ref(); - let hash_get = FnCallHash::from_native(*hash_get); - let hash_set = FnCallHash::from_native(*hash_set); + let hash_get = FnCallHashes::from_native(*hash_get); + let hash_set = FnCallHashes::from_native(*hash_set); let arg_values = &mut [target.as_mut(), &mut Default::default()]; let args = &mut arg_values[..1]; let (mut val, updated) = self.exec_fn_call( @@ -1394,11 +1399,11 @@ impl Engine { Ok((result, may_be_changed)) } // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr - Expr::FnCall(f, pos) if f.namespace.is_none() => { - let FnCallExpr { name, hash, .. } = f.as_ref(); + Expr::FnCall(f, pos) if !f.is_qualified() => { + let FnCallExpr { name, hashes, .. } = f.as_ref(); let mut args = idx_val.as_fn_call_args(); let (mut val, _) = self.make_method_call( - mods, state, lib, name, *hash, target, &mut args, *pos, level, + mods, state, lib, name, *hashes, target, &mut args, *pos, level, )?; let val = &mut val; let target = &mut val.into(); @@ -1508,7 +1513,7 @@ impl Engine { self.inc_operations(state, expr.position())?; match expr { - Expr::FnCall(x, _) if parent_chain_type == ChainType::Dot && x.namespace.is_none() => { + Expr::FnCall(x, _) if parent_chain_type == ChainType::Dot && !x.is_qualified() => { let mut arg_positions: StaticVec<_> = Default::default(); let mut arg_values = x @@ -1547,7 +1552,7 @@ impl Engine { } Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::FnCall(x, _) - if parent_chain_type == ChainType::Dot && x.namespace.is_none() => + if parent_chain_type == ChainType::Dot && !x.is_qualified() => { let mut arg_positions: StaticVec<_> = Default::default(); @@ -1704,7 +1709,7 @@ impl Engine { let type_name = target.type_name(); let args = &mut [target, &mut _idx]; let hash_get = - FnCallHash::from_native(calc_fn_hash(std::iter::empty(), FN_IDX_GET, 2)); + FnCallHashes::from_native(calc_fn_hash(std::iter::empty(), FN_IDX_GET, 2)); self.exec_fn_call( _mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None, _level, @@ -1749,7 +1754,6 @@ impl Engine { Expr::FloatConstant(x, _) => Ok((*x).into()), Expr::StringConstant(x, _) => Ok(x.clone().into()), Expr::CharConstant(x, _) => Ok((*x).into()), - Expr::FnPointer(x, _) => Ok(FnPtr::new_unchecked(x.clone(), Default::default()).into()), Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr .as_deref() @@ -1828,40 +1832,40 @@ impl Engine { Ok(map.into()) } - // Normal function call - Expr::FnCall(x, pos) if x.namespace.is_none() => { - let FnCallExpr { - name, - capture, - hash, - args, - constant_args: c_args, - .. - } = x.as_ref(); - self.make_function_call( - scope, mods, state, lib, this_ptr, name, args, c_args, *hash, *pos, *capture, - level, - ) - } - // Namespace-qualified function call - Expr::FnCall(x, pos) if x.namespace.is_some() => { + Expr::FnCall(x, pos) if x.is_qualified() => { let FnCallExpr { name, namespace, - hash, + hashes, args, constant_args: c_args, .. } = x.as_ref(); let namespace = namespace.as_ref(); - let hash = hash.native_hash(); + let hash = hashes.native_hash(); self.make_qualified_function_call( scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos, level, ) } + // Normal function call + Expr::FnCall(x, pos) => { + let FnCallExpr { + name, + capture, + hashes, + args, + constant_args: c_args, + .. + } = x.as_ref(); + self.make_function_call( + scope, mods, state, lib, this_ptr, name, args, c_args, *hashes, *pos, *capture, + level, + ) + } + Expr::And(x, _) => { Ok((self .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? @@ -1916,7 +1920,11 @@ impl Engine { _ => unreachable!("expression cannot be evaluated: {:?}", expr), }; - self.check_data_size(result, expr.position()) + #[cfg(not(feature = "unchecked"))] + self.check_data_size(&result) + .map_err(|err| err.fill_position(expr.position()))?; + + result } /// Evaluate a statements block. @@ -2167,29 +2175,26 @@ impl Engine { } // If statement - Stmt::If(expr, x, _) => self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? - .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, expr.position())) - .and_then(|guard_val| { - if guard_val { - if !x.0.is_empty() { - self.eval_stmt_block( - scope, mods, state, lib, this_ptr, &x.0, true, level, - ) - } else { - Ok(Dynamic::UNIT) - } + Stmt::If(expr, x, _) => { + let guard_val = self + .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .as_bool() + .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))?; + + if guard_val { + if !x.0.is_empty() { + self.eval_stmt_block(scope, mods, state, lib, this_ptr, &x.0, true, level) } else { - if !x.1.is_empty() { - self.eval_stmt_block( - scope, mods, state, lib, this_ptr, &x.1, true, level, - ) - } else { - Ok(Dynamic::UNIT) - } + Ok(Dynamic::UNIT) } - }), + } else { + if !x.1.is_empty() { + self.eval_stmt_block(scope, mods, state, lib, this_ptr, &x.1, true, level) + } else { + Ok(Dynamic::UNIT) + } + } + } // Switch statement Stmt::Switch(match_expr, x, _) => { @@ -2259,17 +2264,16 @@ impl Engine { if !condition { return Ok(Dynamic::UNIT); } - if body.is_empty() { - continue; - } - - match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) { - Ok(_) => (), - Err(err) => match *err { - EvalAltResult::LoopBreak(false, _) => (), - EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), - _ => return Err(err), - }, + if !body.is_empty() { + match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) + { + Ok(_) => (), + Err(err) => match *err { + EvalAltResult::LoopBreak(false, _) => (), + EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT), + _ => return Err(err), + }, + } } }, @@ -2287,18 +2291,13 @@ impl Engine { } } - if self + let condition = self .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .as_bool() - .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))? - { - if !*is_while { - return Ok(Dynamic::UNIT); - } - } else { - if *is_while { - return Ok(Dynamic::UNIT); - } + .map_err(|err| self.make_type_mismatch_err::(err, expr.position()))?; + + if condition ^ *is_while { + return Ok(Dynamic::UNIT); } }, @@ -2365,9 +2364,11 @@ impl Engine { continue; } - match self.eval_stmt_block( + let result = self.eval_stmt_block( scope, mods, state, lib, this_ptr, statements, true, level, - ) { + ); + + match result { Ok(_) => (), Err(err) => match *err { EvalAltResult::LoopBreak(false, _) => (), @@ -2391,6 +2392,40 @@ impl Engine { // Break statement Stmt::Break(pos) => EvalAltResult::LoopBreak(true, *pos).into(), + // Namespace-qualified function call + Stmt::FnCall(x, pos) if x.is_qualified() => { + let FnCallExpr { + name, + namespace, + hashes, + args, + constant_args: c_args, + .. + } = x.as_ref(); + let namespace = namespace.as_ref(); + let hash = hashes.native_hash(); + self.make_qualified_function_call( + scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos, + level, + ) + } + + // Normal function call + Stmt::FnCall(x, pos) => { + let FnCallExpr { + name, + capture, + hashes, + args, + constant_args: c_args, + .. + } = x.as_ref(); + self.make_function_call( + scope, mods, state, lib, this_ptr, name, args, c_args, *hashes, *pos, *capture, + level, + ) + } + // Try/Catch statement Stmt::TryCatch(x, _, _) => { let (try_stmt, err_var, catch_stmt) = x.as_ref(); @@ -2478,25 +2513,25 @@ impl Engine { } // Return value - Stmt::Return(ReturnType::Return, Some(expr), pos) => { - let value = self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? - .flatten(); - EvalAltResult::Return(value, *pos).into() - } + Stmt::Return(ReturnType::Return, Some(expr), pos) => EvalAltResult::Return( + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .flatten(), + *pos, + ) + .into(), // Empty return Stmt::Return(ReturnType::Return, None, pos) => { - EvalAltResult::Return(Default::default(), *pos).into() + EvalAltResult::Return(Dynamic::UNIT, *pos).into() } // Throw value - Stmt::Return(ReturnType::Exception, Some(expr), pos) => { - let value = self - .eval_expr(scope, mods, state, lib, this_ptr, expr, level)? - .flatten(); - EvalAltResult::ErrorRuntime(value, *pos).into() - } + Stmt::Return(ReturnType::Exception, Some(expr), pos) => EvalAltResult::ErrorRuntime( + self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? + .flatten(), + *pos, + ) + .into(), // Empty throw Stmt::Return(ReturnType::Exception, None, pos) => { @@ -2641,24 +2676,18 @@ impl Engine { } }; - self.check_data_size(result, stmt.position()) - } + #[cfg(not(feature = "unchecked"))] + self.check_data_size(&result) + .map_err(|err| err.fill_position(stmt.position()))?; - /// Check a result to ensure that the data size is within allowable limit. - /// [`Position`] in [`EvalAltResult`] may be None and should be set afterwards. - #[cfg(feature = "unchecked")] - #[inline(always)] - fn check_data_size(&self, result: RhaiResult, _pos: Position) -> RhaiResult { result } /// Check a result to ensure that the data size is within allowable limit. #[cfg(not(feature = "unchecked"))] - #[inline(always)] - fn check_data_size(&self, result: RhaiResult, pos: Position) -> RhaiResult { - // Simply return all errors + fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box> { if result.is_err() { - return result; + return Ok(()); } // If no data size limits, just return @@ -2673,7 +2702,7 @@ impl Engine { } if !_has_limit { - return result; + return Ok(()); } // Recursively calculate the size of a value (especially `Array` and `Map`) @@ -2735,7 +2764,11 @@ impl Engine { .max_string_size .map_or(usize::MAX, NonZeroUsize::get) { - return EvalAltResult::ErrorDataTooLarge("Length of string".to_string(), pos).into(); + return EvalAltResult::ErrorDataTooLarge( + "Length of string".to_string(), + Position::NONE, + ) + .into(); } #[cfg(not(feature = "no_index"))] @@ -2745,7 +2778,8 @@ impl Engine { .max_array_size .map_or(usize::MAX, NonZeroUsize::get) { - return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), pos).into(); + return EvalAltResult::ErrorDataTooLarge("Size of array".to_string(), Position::NONE) + .into(); } #[cfg(not(feature = "no_object"))] @@ -2755,14 +2789,17 @@ impl Engine { .max_map_size .map_or(usize::MAX, NonZeroUsize::get) { - return EvalAltResult::ErrorDataTooLarge("Size of object map".to_string(), pos).into(); + return EvalAltResult::ErrorDataTooLarge( + "Size of object map".to_string(), + Position::NONE, + ) + .into(); } - result + Ok(()) } /// Check if the number of operations stay within limit. - #[inline] pub(crate) fn inc_operations( &self, state: &mut State, diff --git a/src/engine_api.rs b/src/engine_api.rs index 918263d3..973b1a5c 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -59,7 +59,7 @@ impl Engine { #[cfg(feature = "metadata")] let mut param_type_names: crate::StaticVec<_> = F::param_names() .iter() - .map(|ty| std::format!("_: {}", self.map_type_name(ty))) + .map(|ty| format!("_: {}", self.map_type_name(ty))) .collect(); #[cfg(feature = "metadata")] @@ -119,7 +119,7 @@ impl Engine { #[cfg(feature = "metadata")] let param_type_names: crate::StaticVec<_> = F::param_names() .iter() - .map(|ty| std::format!("_: {}", self.map_type_name(ty))) + .map(|ty| format!("_: {}", self.map_type_name(ty))) .chain(std::iter::once( self.map_type_name(F::return_type_name()).into(), )) @@ -1172,7 +1172,7 @@ impl Engine { let mut f = std::fs::File::open(path.clone()).map_err(|err| { EvalAltResult::ErrorSystem( - std::format!("Cannot open script file '{}'", path.to_string_lossy()), + format!("Cannot open script file '{}'", path.to_string_lossy()), err.into(), ) })?; @@ -1181,7 +1181,7 @@ impl Engine { f.read_to_string(&mut contents).map_err(|err| { EvalAltResult::ErrorSystem( - std::format!("Cannot read script file '{}'", path.to_string_lossy()), + format!("Cannot read script file '{}'", path.to_string_lossy()), err.into(), ) })?; @@ -1802,7 +1802,7 @@ impl Engine { /// /// let engine = Engine::new(); /// - /// let ast = engine.compile(r" + /// let ast = engine.compile(" /// fn add(x, y) { len(x) + y + foo } /// fn add1(x) { len(x) + 1 + foo } /// fn bar() { foo/2 } @@ -1873,7 +1873,7 @@ impl Engine { /// /// let engine = Engine::new(); /// - /// let ast = engine.compile(r" + /// let ast = engine.compile(" /// fn add(x, y) { len(x) + y + foo } /// fn add1(x) { len(x) + 1 + foo } /// fn bar() { foo/2 } @@ -2010,16 +2010,13 @@ impl Engine { /// 2) Functions in registered sub-modules /// 3) Functions in packages (optional) #[cfg(feature = "metadata")] - pub fn gen_fn_signatures(&self, include_packages: bool) -> std::vec::Vec { - let mut signatures: std::vec::Vec<_> = Default::default(); + pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec { + let mut signatures: Vec<_> = Default::default(); signatures.extend(self.global_namespace.gen_fn_signatures()); self.global_sub_modules.iter().for_each(|(name, m)| { - signatures.extend( - m.gen_fn_signatures() - .map(|f| std::format!("{}::{}", name, f)), - ) + signatures.extend(m.gen_fn_signatures().map(|f| format!("{}::{}", name, f))) }); if include_packages { @@ -2169,7 +2166,10 @@ impl Engine { /// ast.set_source("world"); /// engine.consume_ast(&ast)?; /// + /// #[cfg(not(feature = "no_position"))] /// assert_eq!(*result.read().unwrap(), r#"world @ 1:18 > "hello""#); + /// #[cfg(feature = "no_position")] + /// assert_eq!(*result.read().unwrap(), r#"world @ none > "hello""#); /// # Ok(()) /// # } /// ``` diff --git a/src/fn_args.rs b/src/fn_args.rs index c71a5c65..10a4e022 100644 --- a/src/fn_args.rs +++ b/src/fn_args.rs @@ -5,7 +5,8 @@ use crate::dynamic::Variant; use crate::{Dynamic, StaticVec}; -use std::vec::Vec; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; /// Trait that parses arguments to a function call. /// diff --git a/src/fn_call.rs b/src/fn_call.rs index 2443fef1..896e934a 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -1,6 +1,6 @@ //! Implement function-calling mechanism for [`Engine`]. -use crate::ast::FnCallHash; +use crate::ast::FnCallHashes; use crate::engine::{ FnResolutionCacheEntry, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, @@ -463,7 +463,7 @@ impl Engine { ) -> RhaiResult { #[inline(always)] fn make_error( - name: std::string::String, + name: String, fn_def: &crate::ast::ScriptFnDef, state: &State, err: Box, @@ -623,7 +623,7 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - hash: FnCallHash, + hash: FnCallHashes, args: &mut FnCallArgs, is_ref: bool, _is_method: bool, @@ -893,7 +893,7 @@ impl Engine { state: &mut State, lib: &[&Module], fn_name: &str, - mut hash: FnCallHash, + mut hash: FnCallHashes, target: &mut crate::engine::Target, (call_args, call_arg_positions): &mut (StaticVec, StaticVec), pos: Position, @@ -913,7 +913,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hashes - let new_hash = FnCallHash::from_script(calc_fn_hash(empty(), fn_name, args_len)); + let new_hash = FnCallHashes::from_script(calc_fn_hash(empty(), fn_name, args_len)); // Arguments are passed as-is, adding the curried arguments let mut curry = fn_ptr.curry().iter().cloned().collect::>(); let mut args = curry @@ -948,7 +948,7 @@ impl Engine { let fn_name = fn_ptr.fn_name(); let args_len = call_args.len() + fn_ptr.curry().len(); // Recalculate hash - let new_hash = FnCallHash::from_script_and_native( + let new_hash = FnCallHashes::from_script_and_native( calc_fn_hash(empty(), fn_name, args_len), calc_fn_hash(empty(), fn_name, args_len + 1), ); @@ -1022,7 +1022,7 @@ impl Engine { call_arg_positions.insert(i, Position::NONE); }); // Recalculate the hash based on the new function name and new arguments - hash = FnCallHash::from_script_and_native( + hash = FnCallHashes::from_script_and_native( calc_fn_hash(empty(), fn_name, call_args.len()), calc_fn_hash(empty(), fn_name, call_args.len() + 1), ); @@ -1060,7 +1060,7 @@ impl Engine { fn_name: &str, args_expr: &[Expr], constant_args: &[(Dynamic, Position)], - mut hash: FnCallHash, + mut hashes: FnCallHashes, pos: Position, capture_scope: bool, level: usize, @@ -1108,10 +1108,10 @@ impl Engine { // Recalculate hash let args_len = total_args + curry.len(); - hash = if !hash.is_native_only() { - FnCallHash::from_script(calc_fn_hash(empty(), name, args_len)) + hashes = if !hashes.is_native_only() { + FnCallHashes::from_script(calc_fn_hash(empty(), name, args_len)) } else { - FnCallHash::from_native(calc_fn_hash(empty(), name, args_len)) + FnCallHashes::from_native(calc_fn_hash(empty(), name, args_len)) }; } // Handle Fn() @@ -1345,7 +1345,7 @@ impl Engine { } self.exec_fn_call( - mods, state, lib, name, hash, &mut args, is_ref, false, pos, capture, level, + mods, state, lib, name, hashes, &mut args, is_ref, false, pos, capture, level, ) .map(|(v, _)| v) } diff --git a/src/fn_native.rs b/src/fn_native.rs index b84df08c..007ba761 100644 --- a/src/fn_native.rs +++ b/src/fn_native.rs @@ -1,6 +1,6 @@ //! Module defining interfaces to native-Rust functions. -use crate::ast::{FnAccess, FnCallHash}; +use crate::ast::{FnAccess, FnCallHashes}; use crate::engine::Imports; use crate::plugin::PluginFunction; use crate::token::is_valid_identifier; @@ -191,12 +191,12 @@ impl<'a> NativeCallContext<'a> { let fn_name = fn_name.as_ref(); let hash = if is_method { - FnCallHash::from_script_and_native( + FnCallHashes::from_script_and_native( calc_fn_hash(empty(), fn_name, args.len() - 1), calc_fn_hash(empty(), fn_name, args.len()), ) } else { - FnCallHash::from_script(calc_fn_hash(empty(), fn_name, args.len())) + FnCallHashes::from_script(calc_fn_hash(empty(), fn_name, args.len())) }; self.engine() @@ -262,7 +262,7 @@ impl FnPtr { } /// Create a new function pointer without checking its parameters. #[inline(always)] - pub(crate) fn new_unchecked(name: impl Into, curry: StaticVec) -> Self { + pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec) -> Self { Self(name.into(), curry) } /// Get the name of the function. diff --git a/src/lib.rs b/src/lib.rs index 6771162e..6ddb7aab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -220,7 +220,7 @@ pub use token::{ #[cfg(feature = "internals")] #[deprecated = "this type is volatile and may change"] pub use ast::{ - ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHash, Ident, + ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock, }; diff --git a/src/module/mod.rs b/src/module/mod.rs index 50088597..bfefec28 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -72,7 +72,7 @@ impl FuncInfo { let mut sig = format!("{}(", self.name); if !self.param_names.is_empty() { - let mut params: std::vec::Vec = + let mut params: Vec = self.param_names.iter().map(|s| s.as_str().into()).collect(); let return_type = params.pop().unwrap_or_else(|| "()".into()); sig.push_str(¶ms.join(", ")); @@ -200,7 +200,7 @@ impl fmt::Debug for Module { &self .functions .values() - .map(|f| std::string::ToString::to_string(&f.func)) + .map(|f| f.func.to_string()) .collect::>(), ); } diff --git a/src/optimize.rs b/src/optimize.rs index 35ae9148..e9b36d27 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -7,8 +7,8 @@ use crate::fn_builtin::get_builtin_binary_op_fn; use crate::parser::map_dynamic_to_expr; use crate::utils::get_hasher; use crate::{ - calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, ImmutableString, Module, - Position, Scope, StaticVec, AST, + calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString, + Module, Position, Scope, StaticVec, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -628,6 +628,17 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) { let catch_block = mem::take(x.2.statements()).into_vec(); *x.2.statements() = optimize_stmt_block(catch_block, state, false, true, false).into(); } + // func(...) + Stmt::Expr(expr @ Expr::FnCall(_, _)) => { + optimize_expr(expr, state); + match expr { + Expr::FnCall(x, pos) => { + state.set_dirty(); + *stmt = Stmt::FnCall(mem::take(x), *pos); + } + _ => (), + } + } // {} Stmt::Expr(Expr::Stmt(x)) if x.is_empty() => { state.set_dirty(); @@ -862,15 +873,16 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { } // Fn Expr::FnCall(x, pos) - if x.namespace.is_none() // Non-qualified + if !x.is_qualified() // Non-qualified && state.optimization_level == OptimizationLevel::Simple // simple optimizations - && x.num_args() == 1 + && x.args_count() == 1 && x.constant_args.len() == 1 && x.constant_args[0].0.is::() && x.name == KEYWORD_FN_PTR => { state.set_dirty(); - *expr = Expr::FnPointer(mem::take(&mut x.constant_args[0].0).take_immutable_string().unwrap(), *pos); + let fn_ptr = FnPtr::new_unchecked(mem::take(&mut x.constant_args[0].0).as_str_ref().unwrap().into(), Default::default()); + *expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), *pos); } // Do not call some special keywords @@ -880,9 +892,9 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { // Call built-in operators Expr::FnCall(x, pos) - if x.namespace.is_none() // Non-qualified + if !x.is_qualified() // Non-qualified && state.optimization_level == OptimizationLevel::Simple // simple optimizations - && x.num_args() == 2 // binary call + && x.args_count() == 2 // binary call && x.args.iter().all(Expr::is_constant) // all arguments are constants //&& !is_valid_identifier(x.name.chars()) // cannot be scripted => { @@ -893,7 +905,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); // Search for overloaded operators (can override built-in). - if !has_native_fn(state, x.hash.native_hash(), arg_types.as_ref()) { + if !has_native_fn(state, x.hashes.native_hash(), arg_types.as_ref()) { if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) .and_then(|f| { let ctx = (state.engine, x.name.as_ref(), state.lib).into(); @@ -922,13 +934,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { // Eagerly call functions Expr::FnCall(x, pos) - if x.namespace.is_none() // Non-qualified + if !x.is_qualified() // Non-qualified && state.optimization_level == OptimizationLevel::Full // full optimizations && x.args.iter().all(Expr::is_constant) // all arguments are constants => { // First search for script-defined functions (can override built-in) #[cfg(not(feature = "no_function"))] - let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.num_args()).is_some()); + let has_script_fn = state.lib.iter().any(|&m| m.get_script_fn(x.name.as_ref(), x.args_count()).is_some()); #[cfg(feature = "no_function")] let has_script_fn = false; diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index e79e5473..961f45e4 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -4,9 +4,6 @@ use std::ops::Range; #[cfg(feature = "no_std")] use std::prelude::v1::*; -#[cfg(not(feature = "unchecked"))] -use std::string::ToString; - #[cfg(not(feature = "unchecked"))] use num_traits::{CheckedAdd as Add, CheckedSub as Sub}; @@ -209,8 +206,6 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, { pub fn new(from: Decimal, to: Decimal, step: Decimal) -> Result> { #[cfg(not(feature = "unchecked"))] if step.is_zero() { - use std::string::ToString; - return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)), crate::Position::NONE, diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index 8959e868..ea02a027 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -103,7 +103,7 @@ mod print_debug_functions { )] pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString { let len = array.len(); - let mut result = std::string::String::with_capacity(len * 5 + 2); + let mut result = String::with_capacity(len * 5 + 2); result.push_str("["); array.iter_mut().enumerate().for_each(|(i, x)| { @@ -130,7 +130,7 @@ mod print_debug_functions { )] pub fn format_map(ctx: NativeCallContext, map: &mut Map) -> ImmutableString { let len = map.len(); - let mut result = std::string::String::with_capacity(len * 5 + 3); + let mut result = String::with_capacity(len * 5 + 3); result.push_str("#{"); map.iter_mut().enumerate().for_each(|(i, (k, v))| { diff --git a/src/parser.rs b/src/parser.rs index b2849364..87082f7e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,7 +1,7 @@ //! Main module defining the lexer and parser. use crate::ast::{ - BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType, + BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock, }; use crate::dynamic::{AccessMode, Union}; @@ -15,8 +15,8 @@ use crate::token::{ }; use crate::utils::{get_hasher, IdentifierBuilder}; use crate::{ - calc_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError, ParseErrorType, Position, - Scope, Shared, StaticVec, AST, + calc_fn_hash, Dynamic, Engine, FnPtr, Identifier, LexError, ParseError, ParseErrorType, + Position, Scope, Shared, StaticVec, AST, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -342,10 +342,10 @@ fn parse_fn_call( calc_fn_hash(empty(), &id, 0) }; - let hash = if is_valid_identifier(id.chars()) { - FnCallHash::from_script(hash) + let hashes = if is_valid_identifier(id.chars()) { + FnCallHashes::from_script(hash) } else { - FnCallHash::from_native(hash) + FnCallHashes::from_native(hash) }; return Ok(Expr::FnCall( @@ -353,7 +353,7 @@ fn parse_fn_call( name: state.get_identifier(id), capture, namespace, - hash, + hashes, args, ..Default::default() }), @@ -387,10 +387,10 @@ fn parse_fn_call( calc_fn_hash(empty(), &id, args.len()) }; - let hash = if is_valid_identifier(id.chars()) { - FnCallHash::from_script(hash) + let hashes = if is_valid_identifier(id.chars()) { + FnCallHashes::from_script(hash) } else { - FnCallHash::from_native(hash) + FnCallHashes::from_native(hash) }; return Ok(Expr::FnCall( @@ -398,7 +398,7 @@ fn parse_fn_call( name: state.get_identifier(id), capture, namespace, - hash, + hashes, args, ..Default::default() }), @@ -1348,7 +1348,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: state.get_identifier("-"), - hash: FnCallHash::from_native(calc_fn_hash(empty(), "-", 1)), + hashes: FnCallHashes::from_native(calc_fn_hash(empty(), "-", 1)), args, ..Default::default() }), @@ -1374,7 +1374,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: state.get_identifier("+"), - hash: FnCallHash::from_native(calc_fn_hash(empty(), "+", 1)), + hashes: FnCallHashes::from_native(calc_fn_hash(empty(), "+", 1)), args, ..Default::default() }), @@ -1393,7 +1393,7 @@ fn parse_unary( Ok(Expr::FnCall( Box::new(FnCallExpr { name: state.get_identifier("!"), - hash: FnCallHash::from_native(calc_fn_hash(empty(), "!", 1)), + hashes: FnCallHashes::from_native(calc_fn_hash(empty(), "!", 1)), args, ..Default::default() }), @@ -1415,20 +1415,20 @@ fn make_assignment_stmt<'a>( rhs: Expr, op_pos: Position, ) -> Result { - fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Position { + fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option { match expr { Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs { Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), - ref e => e.position(), + ref e => Some(e.position()), }, Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs { Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), _ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), }, - Expr::Property(_) if parent_is_dot => Position::NONE, + Expr::Property(_) if parent_is_dot => None, Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), - e if parent_is_dot => e.position(), - _ => Position::NONE, + e if parent_is_dot => Some(e.position()), + _ => None, } } @@ -1464,7 +1464,7 @@ fn make_assignment_stmt<'a>( // xxx[???]... = rhs, xxx.prop... = rhs Expr::Index(x, _) | Expr::Dot(x, _) => { match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) { - Position::NONE => match &x.lhs { + None => match &x.lhs { // var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs Expr::Variable(None, _, x) if x.0.is_none() => { Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) @@ -1488,7 +1488,7 @@ fn make_assignment_stmt<'a>( Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position())) } }, - pos => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(pos)), + Some(pos) => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(pos)), } } // ??? && ??? = rhs, ??? || ??? = rhs @@ -1595,9 +1595,9 @@ fn make_dot_expr( } Expr::FnCall(mut func, func_pos) => { // Recalculate hash - func.hash = FnCallHash::from_script_and_native( - calc_fn_hash(empty(), &func.name, func.num_args()), - calc_fn_hash(empty(), &func.name, func.num_args() + 1), + func.hashes = FnCallHashes::from_script_and_native( + calc_fn_hash(empty(), &func.name, func.args_count()), + calc_fn_hash(empty(), &func.name, func.args_count() + 1), ); let rhs = Expr::Dot( @@ -1623,7 +1623,7 @@ fn make_dot_expr( Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) } // lhs.nnn::func(...) - (_, Expr::FnCall(x, _)) if x.namespace.is_some() => { + (_, Expr::FnCall(x, _)) if x.is_qualified() => { unreachable!("method call should not be namespace-qualified") } // lhs.Fn() or lhs.eval() @@ -1651,9 +1651,9 @@ fn make_dot_expr( // lhs.func(...) (lhs, Expr::FnCall(mut func, func_pos)) => { // Recalculate hash - func.hash = FnCallHash::from_script_and_native( - calc_fn_hash(empty(), &func.name, func.num_args()), - calc_fn_hash(empty(), &func.name, func.num_args() + 1), + func.hashes = FnCallHashes::from_script_and_native( + calc_fn_hash(empty(), &func.name, func.args_count()), + calc_fn_hash(empty(), &func.name, func.args_count() + 1), ); let rhs = Expr::FnCall(func, func_pos); Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) @@ -1739,7 +1739,7 @@ fn parse_binary_op( let op_base = FnCallExpr { name: state.get_identifier(op.as_ref()), - hash: FnCallHash::from_native(hash), + hashes: FnCallHashes::from_native(hash), capture: false, ..Default::default() }; @@ -1804,7 +1804,7 @@ fn parse_binary_op( let hash = calc_fn_hash(empty(), OP_CONTAINS, 2); Expr::FnCall( Box::new(FnCallExpr { - hash: FnCallHash::from_script(hash), + hashes: FnCallHashes::from_script(hash), args, name: state.get_identifier(OP_CONTAINS), ..op_base @@ -1824,10 +1824,10 @@ fn parse_binary_op( Expr::FnCall( Box::new(FnCallExpr { - hash: if is_valid_identifier(s.chars()) { - FnCallHash::from_script(hash) + hashes: if is_valid_identifier(s.chars()) { + FnCallHashes::from_script(hash) } else { - FnCallHash::from_native(hash) + FnCallHashes::from_native(hash) }, args, ..op_base @@ -2516,7 +2516,7 @@ fn parse_stmt( #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] let comments = { - let mut comments: StaticVec = Default::default(); + let mut comments: StaticVec = Default::default(); let mut comments_pos = Position::NONE; // Handle doc-comments. @@ -2771,7 +2771,7 @@ fn parse_fn( mut settings: ParseSettings, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] - comments: StaticVec, + comments: StaticVec, ) -> Result { #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; @@ -2894,7 +2894,7 @@ fn make_curry_from_externals( let expr = Expr::FnCall( Box::new(FnCallExpr { name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY), - hash: FnCallHash::from_native(calc_fn_hash( + hashes: FnCallHashes::from_native(calc_fn_hash( empty(), crate::engine::KEYWORD_FN_PTR_CURRY, num_externals + 1, @@ -3018,7 +3018,8 @@ fn parse_anon_fn( comments: Default::default(), }; - let expr = Expr::FnPointer(fn_name.into(), settings.pos); + let fn_ptr = FnPtr::new_unchecked(fn_name.into(), Default::default()); + let expr = Expr::DynamicConstant(Box::new(fn_ptr.into()), settings.pos); #[cfg(not(feature = "no_closure"))] let expr = make_curry_from_externals(state, expr, externals, settings.pos); diff --git a/src/token.rs b/src/token.rs index 7b497665..72ee99bc 100644 --- a/src/token.rs +++ b/src/token.rs @@ -57,16 +57,28 @@ pub type TokenStream<'a> = Peekable>; #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] pub struct Position { /// Line number - 0 = none + #[cfg(not(feature = "no_position"))] line: u16, /// Character position - 0 = BOL + #[cfg(not(feature = "no_position"))] pos: u16, } impl Position { /// A [`Position`] representing no position. - pub const NONE: Self = Self { line: 0, pos: 0 }; + pub const NONE: Self = Self { + #[cfg(not(feature = "no_position"))] + line: 0, + #[cfg(not(feature = "no_position"))] + pos: 0, + }; /// A [`Position`] representing the first position. - pub const START: Self = Self { line: 1, pos: 0 }; + pub const START: Self = Self { + #[cfg(not(feature = "no_position"))] + line: 1, + #[cfg(not(feature = "no_position"))] + pos: 0, + }; /// Create a new [`Position`]. /// @@ -77,12 +89,14 @@ impl Position { /// /// Panics if `line` is zero. #[inline(always)] - pub fn new(line: u16, position: u16) -> Self { + pub fn new(line: u16, _position: u16) -> Self { assert!(line != 0, "line cannot be zero"); Self { + #[cfg(not(feature = "no_position"))] line, - pos: position, + #[cfg(not(feature = "no_position"))] + pos: _position, } } /// Get the line number (1-based), or [`None`] if there is no position. @@ -91,26 +105,40 @@ impl Position { if self.is_none() { None } else { - Some(self.line as usize) + #[cfg(not(feature = "no_position"))] + return Some(self.line as usize); + #[cfg(feature = "no_position")] + unreachable!(); } } /// Get the character position (1-based), or [`None`] if at beginning of a line. #[inline(always)] pub fn position(self) -> Option { - if self.is_none() || self.pos == 0 { + if self.is_none() { None } else { - Some(self.pos as usize) + #[cfg(not(feature = "no_position"))] + return if self.pos == 0 { + None + } else { + Some(self.pos as usize) + }; + + #[cfg(feature = "no_position")] + unreachable!(); } } /// Advance by one character position. #[inline(always)] pub(crate) fn advance(&mut self) { - assert!(!self.is_none(), "cannot advance Position::none"); + #[cfg(not(feature = "no_position"))] + { + assert!(!self.is_none(), "cannot advance Position::none"); - // Advance up to maximum position - if self.pos < u16::MAX { - self.pos += 1; + // Advance up to maximum position + if self.pos < u16::MAX { + self.pos += 1; + } } } /// Go backwards by one character position. @@ -120,30 +148,42 @@ impl Position { /// Panics if already at beginning of a line - cannot rewind to a previous line. #[inline(always)] pub(crate) fn rewind(&mut self) { - assert!(!self.is_none(), "cannot rewind Position::none"); - assert!(self.pos > 0, "cannot rewind at position 0"); - self.pos -= 1; + #[cfg(not(feature = "no_position"))] + { + assert!(!self.is_none(), "cannot rewind Position::none"); + assert!(self.pos > 0, "cannot rewind at position 0"); + self.pos -= 1; + } } /// Advance to the next line. #[inline(always)] pub(crate) fn new_line(&mut self) { - assert!(!self.is_none(), "cannot advance Position::none"); + #[cfg(not(feature = "no_position"))] + { + assert!(!self.is_none(), "cannot advance Position::none"); - // Advance up to maximum position - if self.line < u16::MAX { - self.line += 1; - self.pos = 0; + // Advance up to maximum position + if self.line < u16::MAX { + self.line += 1; + self.pos = 0; + } } } /// Is this [`Position`] at the beginning of a line? #[inline(always)] pub fn is_beginning_of_line(self) -> bool { - self.pos == 0 && !self.is_none() + #[cfg(not(feature = "no_position"))] + return self.pos == 0 && !self.is_none(); + #[cfg(feature = "no_position")] + return false; } /// Is there no [`Position`]? #[inline(always)] pub fn is_none(self) -> bool { - self == Self::NONE + #[cfg(not(feature = "no_position"))] + return self == Self::NONE; + #[cfg(feature = "no_position")] + return true; } } @@ -158,17 +198,27 @@ impl fmt::Display for Position { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.is_none() { - write!(f, "none") + write!(f, "none")?; } else { - write!(f, "line {}, position {}", self.line, self.pos) + #[cfg(not(feature = "no_position"))] + write!(f, "line {}, position {}", self.line, self.pos)?; + #[cfg(feature = "no_position")] + unreachable!(); } + + Ok(()) } } impl fmt::Debug for Position { #[inline(always)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}:{}", self.line, self.pos) + #[cfg(not(feature = "no_position"))] + write!(f, "{}:{}", self.line, self.pos)?; + #[cfg(feature = "no_position")] + f.write_str("none")?; + + Ok(()) } } @@ -179,14 +229,17 @@ impl Add for Position { if rhs.is_none() { self } else { - Self { + #[cfg(not(feature = "no_position"))] + return Self { line: self.line + rhs.line - 1, pos: if rhs.is_beginning_of_line() { self.pos } else { self.pos + rhs.pos - 1 }, - } + }; + #[cfg(feature = "no_position")] + unreachable!(); } } } @@ -908,8 +961,9 @@ pub fn parse_string_literal( let mut escape = String::with_capacity(12); let start = *pos; - let mut skip_whitespace_until = 0; let mut interpolated = false; + #[cfg(not(feature = "no_position"))] + let mut skip_whitespace_until = 0; state.is_within_text_terminated_by = Some(termination_char); @@ -1044,7 +1098,11 @@ pub fn parse_string_literal( assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape); escape.clear(); pos.new_line(); - skip_whitespace_until = start.position().unwrap() + 1; + + #[cfg(not(feature = "no_position"))] + { + skip_whitespace_until = start.position().unwrap() + 1; + } } // Unterminated string @@ -1062,13 +1120,18 @@ pub fn parse_string_literal( } // Whitespace to skip + #[cfg(not(feature = "no_position"))] _ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {} // All other characters _ => { escape.clear(); result.push(next_char); - skip_whitespace_until = 0; + + #[cfg(not(feature = "no_position"))] + { + skip_whitespace_until = 0; + } } } } @@ -1241,7 +1304,7 @@ fn get_next_token_inner( ); } - let mut negated_pos = Position::NONE; + let mut negated: Option = None; while let Some(c) = stream.get_next() { pos.advance(); @@ -1350,7 +1413,7 @@ fn get_next_token_inner( } } - let num_pos = if !negated_pos.is_none() { + let num_pos = if let Some(negated_pos) = negated { result.insert(0, '-'); negated_pos } else { @@ -1511,7 +1574,7 @@ fn get_next_token_inner( ('+', _) if !state.non_unary => return Some((Token::UnaryPlus, start_pos)), ('+', _) => return Some((Token::Plus, start_pos)), - ('-', '0'..='9') if !state.non_unary => negated_pos = start_pos, + ('-', '0'..='9') if !state.non_unary => negated = Some(start_pos), ('-', '0'..='9') => return Some((Token::Minus, start_pos)), ('-', '=') => { eat_next(stream, pos); diff --git a/tests/arrays.rs b/tests/arrays.rs index 3760305d..74cea10b 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -56,7 +56,7 @@ fn test_arrays() -> Result<(), Box> { assert_eq!( convert_to_vec::(engine.eval( - r" + " let x = [2, 9]; x.insert(-1, 1); x.insert(999, 3); @@ -76,7 +76,7 @@ fn test_arrays() -> Result<(), Box> { assert_eq!( convert_to_vec::(engine.eval( - r" + " let x = [1, 2, 3]; x += [4, 5]; x @@ -86,7 +86,7 @@ fn test_arrays() -> Result<(), Box> { ); assert_eq!( convert_to_vec::(engine.eval( - r" + " let x = [1, 2, 3]; let y = [4, 5]; x + y @@ -136,7 +136,7 @@ fn test_array_with_structs() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let a = [new_ts()]; a[0].x = 100; a[0].update(); @@ -158,7 +158,7 @@ fn test_arrays_map_reduce() -> Result<(), Box> { assert_eq!( convert_to_vec::(engine.eval( - r" + " let x = [1, 2, 3]; x.filter(|v| v > 2) " @@ -168,7 +168,7 @@ fn test_arrays_map_reduce() -> Result<(), Box> { assert_eq!( convert_to_vec::(engine.eval( - r" + " let x = [1, 2, 3]; x.filter(|v, i| v > i) " @@ -178,7 +178,7 @@ fn test_arrays_map_reduce() -> Result<(), Box> { assert_eq!( convert_to_vec::(engine.eval( - r" + " let x = [1, 2, 3]; x.map(|v| v * 2) " @@ -188,7 +188,7 @@ fn test_arrays_map_reduce() -> Result<(), Box> { assert_eq!( convert_to_vec::(engine.eval( - r" + " let x = [1, 2, 3]; x.map(|v, i| v * i) " diff --git a/tests/assignments.rs b/tests/assignments.rs index d3936223..03df4be8 100644 --- a/tests/assignments.rs +++ b/tests/assignments.rs @@ -21,41 +21,38 @@ fn test_assignments_bad_lhs() -> Result<(), Box> { let engine = Engine::new(); assert_eq!( - *engine.compile(r"(x+y) = 42;").expect_err("should error").0, + *engine.compile("(x+y) = 42;").expect_err("should error").0, ParseErrorType::AssignmentToInvalidLHS("".to_string()) ); assert_eq!( - *engine.compile(r"foo(x) = 42;").expect_err("should error").0, + *engine.compile("foo(x) = 42;").expect_err("should error").0, ParseErrorType::AssignmentToInvalidLHS("".to_string()) ); assert_eq!( - *engine.compile(r"true = 42;").expect_err("should error").0, + *engine.compile("true = 42;").expect_err("should error").0, ParseErrorType::AssignmentToConstant("".to_string()) ); assert_eq!( - *engine.compile(r"123 = 42;").expect_err("should error").0, + *engine.compile("123 = 42;").expect_err("should error").0, ParseErrorType::AssignmentToConstant("".to_string()) ); #[cfg(not(feature = "no_object"))] { + assert_eq!( + *engine.compile("x.foo() = 42;").expect_err("should error").0, + ParseErrorType::AssignmentToInvalidLHS("".to_string()) + ); assert_eq!( *engine - .compile(r"x.foo() = 42;") + .compile("x.foo().x.y = 42;") .expect_err("should error") .0, ParseErrorType::AssignmentToInvalidLHS("".to_string()) ); assert_eq!( *engine - .compile(r"x.foo().x.y = 42;") - .expect_err("should error") - .0, - ParseErrorType::AssignmentToInvalidLHS("".to_string()) - ); - assert_eq!( - *engine - .compile(r"x.y.z.foo() = 42;") + .compile("x.y.z.foo() = 42;") .expect_err("should error") .0, ParseErrorType::AssignmentToInvalidLHS("".to_string()) @@ -63,7 +60,7 @@ fn test_assignments_bad_lhs() -> Result<(), Box> { #[cfg(not(feature = "no_index"))] assert_eq!( *engine - .compile(r"x.foo()[0] = 42;") + .compile("x.foo()[0] = 42;") .expect_err("should error") .0, ParseErrorType::AssignmentToInvalidLHS("".to_string()) @@ -71,7 +68,7 @@ fn test_assignments_bad_lhs() -> Result<(), Box> { #[cfg(not(feature = "no_index"))] assert_eq!( *engine - .compile(r"x[y].z.foo() = 42;") + .compile("x[y].z.foo() = 42;") .expect_err("should error") .0, ParseErrorType::AssignmentToInvalidLHS("".to_string()) diff --git a/tests/bool_op.rs b/tests/bool_op.rs index 0520736d..18dc4ea6 100644 --- a/tests/bool_op.rs +++ b/tests/bool_op.rs @@ -38,7 +38,7 @@ fn test_bool_op_short_circuit() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let x = true; x || { throw; }; " @@ -48,7 +48,7 @@ fn test_bool_op_short_circuit() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let x = false; x && { throw; }; " @@ -65,7 +65,7 @@ fn test_bool_op_no_short_circuit1() { assert!(engine .eval::( - r" + " let x = true; x | { throw; } " @@ -79,7 +79,7 @@ fn test_bool_op_no_short_circuit2() { assert!(engine .eval::( - r" + " let x = false; x & { throw; } " diff --git a/tests/call_fn.rs b/tests/call_fn.rs index 07fdefda..cb9aa559 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -10,7 +10,7 @@ fn test_call_fn() -> Result<(), Box> { scope.push("foo", 42 as INT); let ast = engine.compile( - r" + " fn hello(x, y) { x + y } diff --git a/tests/chars.rs b/tests/chars.rs index 6882633a..1e6d0dbc 100644 --- a/tests/chars.rs +++ b/tests/chars.rs @@ -7,7 +7,7 @@ fn test_chars() -> Result<(), Box> { assert_eq!(engine.eval::("'y'")?, 'y'); assert_eq!(engine.eval::(r"'\''")?, '\''); assert_eq!(engine.eval::(r#"'"'"#)?, '"'); - assert_eq!(engine.eval::("'\\u2764'")?, '❤'); + assert_eq!(engine.eval::(r"'\u2764'")?, '❤'); #[cfg(not(feature = "no_index"))] { diff --git a/tests/closures.rs b/tests/closures.rs index 4b35e2d7..67ad11a9 100644 --- a/tests/closures.rs +++ b/tests/closures.rs @@ -54,7 +54,7 @@ fn test_closures() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let foo = #{ x: 42 }; let f = || { this.x }; foo.call(f) diff --git a/tests/comments.rs b/tests/comments.rs index 4c3452f7..91d7b200 100644 --- a/tests/comments.rs +++ b/tests/comments.rs @@ -33,7 +33,7 @@ fn test_comments_doc() -> Result<(), Box> { let mut engine = Engine::new(); let ast = engine.compile( - r" + " /// Hello world @@ -48,7 +48,7 @@ fn test_comments_doc() -> Result<(), Box> { assert!(engine .compile( - r" + " /// Hello world let x = 42; " @@ -56,7 +56,7 @@ fn test_comments_doc() -> Result<(), Box> { .is_err()); engine.compile( - r" + " /////////////// let x = 42; @@ -66,7 +66,7 @@ fn test_comments_doc() -> Result<(), Box> { )?; let ast = engine.compile( - r" + " /** Hello world ** how are you? **/ @@ -82,7 +82,7 @@ fn test_comments_doc() -> Result<(), Box> { assert!(engine .compile( - r" + " /** Hello world */ let x = 42; " @@ -92,7 +92,7 @@ fn test_comments_doc() -> Result<(), Box> { engine.enable_doc_comments(false); engine.compile( - r" + " /// Hello world! let x = 42; diff --git a/tests/constants.rs b/tests/constants.rs index b3432449..8c3c95af 100644 --- a/tests/constants.rs +++ b/tests/constants.rs @@ -59,7 +59,7 @@ fn test_constant_mut() -> Result<(), Box> { assert_eq!( engine.eval_with_scope::( &mut scope, - r" + " MY_NUMBER.update_value(42); MY_NUMBER.value ", diff --git a/tests/data_size.rs b/tests/data_size.rs index 3c67b4bc..f3ff3cd6 100644 --- a/tests/data_size.rs +++ b/tests/data_size.rs @@ -91,7 +91,7 @@ fn test_max_array_size() -> Result<(), Box> { assert!(matches!( *engine .eval::( - r" + " let x = [1,2,3,4,5,6]; let y = [7,8,9,10,11,12]; x + y @@ -105,7 +105,7 @@ fn test_max_array_size() -> Result<(), Box> { assert!(matches!( *engine .eval::( - r" + " let x = [1,2,3,4,5,6]; x.pad(100, 42); x @@ -118,7 +118,7 @@ fn test_max_array_size() -> Result<(), Box> { assert!(matches!( *engine .eval::( - r" + " let x = [1,2,3]; [x, x, x, x] " @@ -131,7 +131,7 @@ fn test_max_array_size() -> Result<(), Box> { assert!(matches!( *engine .eval::( - r" + " let x = #{a:1, b:2, c:3}; [x, x, x, x] " @@ -143,7 +143,7 @@ fn test_max_array_size() -> Result<(), Box> { assert!(matches!( *engine .eval::( - r" + " let x = [1]; let y = [x, x]; let z = [y, y]; @@ -159,7 +159,7 @@ fn test_max_array_size() -> Result<(), Box> { assert_eq!( engine .eval::( - r" + " let x = [1,2,3,4,5,6]; let y = [7,8,9,10,11,12]; x + y @@ -172,7 +172,7 @@ fn test_max_array_size() -> Result<(), Box> { assert_eq!( engine .eval::( - r" + " let x = [1,2,3]; [x, x, x, x] " @@ -209,7 +209,7 @@ fn test_max_map_size() -> Result<(), Box> { assert!(matches!( *engine .eval::( - r" + " let x = #{a:1,b:2,c:3,d:4,e:5,f:6}; let y = #{g:7,h:8,i:9,j:10,k:11,l:12}; x + y @@ -222,7 +222,7 @@ fn test_max_map_size() -> Result<(), Box> { assert!(matches!( *engine .eval::( - r" + " let x = #{a:1,b:2,c:3}; #{u:x, v:x, w:x, z:x} " @@ -235,7 +235,7 @@ fn test_max_map_size() -> Result<(), Box> { assert!(matches!( *engine .eval::( - r" + " let x = [1, 2, 3]; #{u:x, v:x, w:x, z:x} " @@ -249,7 +249,7 @@ fn test_max_map_size() -> Result<(), Box> { assert_eq!( engine .eval::( - r" + " let x = #{a:1,b:2,c:3,d:4,e:5,f:6}; let y = #{g:7,h:8,i:9,j:10,k:11,l:12}; x + y @@ -262,7 +262,7 @@ fn test_max_map_size() -> Result<(), Box> { assert_eq!( engine .eval::( - r" + " let x = #{a:1,b:2,c:3}; #{u:x, v:x, w:x, z:x} " diff --git a/tests/for.rs b/tests/for.rs index 65bca46f..3118b8a7 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -5,7 +5,7 @@ use rhai::{Engine, EvalAltResult, Module, INT}; fn test_for() -> Result<(), Box> { let engine = Engine::new(); - let script = r" + let script = " let sum1 = 0; let sum2 = 0; let inputs = [1, 2, 3, 4, 5]; @@ -29,7 +29,7 @@ fn test_for() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let sum = 0; for x in range(1, 10, 2) { sum += x; } sum @@ -40,7 +40,7 @@ fn test_for() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let sum = 0; for x in range(10, 1, 2) { sum += x; } sum @@ -51,7 +51,7 @@ fn test_for() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let sum = 0; for x in range(1, 10, -2) { sum += x; } sum @@ -62,7 +62,7 @@ fn test_for() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let sum = 0; for x in range(10, 1, -2) { sum += x; } sum @@ -80,7 +80,7 @@ fn test_for_overflow() -> Result<(), Box> { let engine = Engine::new(); #[cfg(not(feature = "only_i32"))] - let script = r" + let script = " let sum = 0; for x in range(9223372036854775807, 0, 9223372036854775807) { @@ -90,7 +90,7 @@ fn test_for_overflow() -> Result<(), Box> { sum "; #[cfg(feature = "only_i32")] - let script = r" + let script = " let sum = 0; for x in range(2147483647 , 0, 2147483647 ) { diff --git a/tests/functions.rs b/tests/functions.rs index 1387f3eb..cd40c8ee 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -67,7 +67,7 @@ fn test_functions_namespaces() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " const ANSWER = 42; fn foo() { global::ANSWER } diff --git a/tests/if_block.rs b/tests/if_block.rs index eeb345a1..ca9eaa2d 100644 --- a/tests/if_block.rs +++ b/tests/if_block.rs @@ -13,7 +13,7 @@ fn test_if() -> Result<(), Box> { ); assert_eq!( engine.eval::( - r" + " if false { 55 } else if false { 33 } else if false { 66 } @@ -34,7 +34,7 @@ fn test_if_expr() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let x = 42; let y = 1 + if x > 40 { 100 } else { 0 } / x; y diff --git a/tests/internal_fn.rs b/tests/internal_fn.rs index b7bee8b6..eaa3364b 100644 --- a/tests/internal_fn.rs +++ b/tests/internal_fn.rs @@ -70,7 +70,7 @@ fn test_internal_fn_big() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " fn math_me(a, b, c, d, e, f) { a - b * c + d * e - f } @@ -89,7 +89,7 @@ fn test_internal_fn_overloading() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " fn abc(x,y,z) { 2*x + 3*y + 4*z + 888 } fn abc(x,y) { x + 2*y + 88 } fn abc() { 42 } @@ -104,7 +104,7 @@ fn test_internal_fn_overloading() -> Result<(), Box> { assert_eq!( *engine .compile( - r" + " fn abc(x) { x + 42 } fn abc(x) { x - 42 } " diff --git a/tests/looping.rs b/tests/looping.rs index 097297f7..8b99e1bd 100644 --- a/tests/looping.rs +++ b/tests/looping.rs @@ -6,7 +6,7 @@ fn test_loop() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let x = 0; let i = 0; diff --git a/tests/maps.rs b/tests/maps.rs index 170aed90..4efea70f 100644 --- a/tests/maps.rs +++ b/tests/maps.rs @@ -72,7 +72,7 @@ b`: 1}; y["a\nb"] ); assert_eq!( engine.eval::( - r" + " let x = #{a: 1, b: 2, c: 3}; let y = #{b: 42, d: 9}; x.mixin(y); @@ -83,7 +83,7 @@ b`: 1}; y["a\nb"] ); assert_eq!( engine.eval::( - r" + " let x = #{a: 1, b: 2, c: 3}; x += #{b: 42, d: 9}; x.len() + x.b @@ -94,7 +94,7 @@ b`: 1}; y["a\nb"] assert_eq!( engine .eval::( - r" + " let x = #{a: 1, b: 2, c: 3}; let y = #{b: 42, d: 9}; x + y @@ -206,7 +206,7 @@ fn test_map_json() -> Result<(), Box> { assert!(matches!( *engine.parse_json(" 123", true).expect_err("should error"), EvalAltResult::ErrorParsing(ParseErrorType::MissingToken(token, _), pos) - if token == "{" && pos.position() == Some(4) + if token == "{" )); Ok(()) diff --git a/tests/mismatched_op.rs b/tests/mismatched_op.rs index 1f259484..afbf9f40 100644 --- a/tests/mismatched_op.rs +++ b/tests/mismatched_op.rs @@ -30,7 +30,7 @@ fn test_mismatched_op_custom_type() -> Result<(), Box> { .register_type_with_name::("TestStruct") .register_fn("new_ts", TestStruct::new); - assert!(matches!(*engine.eval::(r" + assert!(matches!(*engine.eval::(" let x = new_ts(); let y = new_ts(); x == y diff --git a/tests/modules.rs b/tests/modules.rs index 03a2b477..b55e275d 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -381,13 +381,13 @@ fn test_module_export() -> Result<(), Box> { let engine = Engine::new(); assert!(matches!( - engine.compile(r"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 )); #[cfg(not(feature = "no_function"))] assert!(matches!( - engine.compile(r"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 )); diff --git a/tests/operations.rs b/tests/operations.rs index 9ebf5fcf..d35813c2 100644 --- a/tests/operations.rs +++ b/tests/operations.rs @@ -72,7 +72,7 @@ fn test_max_operations_functions() -> Result<(), Box> { fn inc(x) { x + 1 } let x = 0; - while x < 31 { + while x < 36 { print(x); x = inc(x); } diff --git a/tests/optimizer.rs b/tests/optimizer.rs index cd3baf58..91b29a5c 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -5,9 +5,9 @@ use rhai::{Engine, EvalAltResult, OptimizationLevel, INT}; #[test] fn test_optimizer_run() -> Result<(), Box> { fn run_test(engine: &mut Engine) -> Result<(), Box> { - assert_eq!(engine.eval::(r"if true { 42 } else { 123 }")?, 42); + assert_eq!(engine.eval::("if true { 42 } else { 123 }")?, 42); assert_eq!( - engine.eval::(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, + engine.eval::("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, 42 ); assert_eq!( @@ -34,14 +34,14 @@ fn test_optimizer_run() -> Result<(), Box> { engine.set_optimization_level(OptimizationLevel::Simple); assert_eq!( - engine.eval::(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, + engine.eval::("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, 123 ); engine.set_optimization_level(OptimizationLevel::Full); assert_eq!( - engine.eval::(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, + engine.eval::("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, 123 ); @@ -49,6 +49,7 @@ fn test_optimizer_run() -> Result<(), Box> { } #[cfg(not(feature = "no_module"))] +#[cfg(not(feature = "no_position"))] #[test] fn test_optimizer_parse() -> Result<(), Box> { let mut engine = Engine::new(); diff --git a/tests/print.rs b/tests/print.rs index d574d5fe..0bfcefcf 100644 --- a/tests/print.rs +++ b/tests/print.rs @@ -60,7 +60,11 @@ fn test_print_debug() -> Result<(), Box> { assert_eq!(logbook.read().unwrap()[0], "entry: 42"); assert_eq!( logbook.read().unwrap()[1], - r#"DEBUG of world at 1:19: "hello!""# + if cfg!(not(feature = "no_position")) { + r#"DEBUG of world at 1:19: "hello!""# + } else { + r#"DEBUG of world at none: "hello!""# + } ); for entry in logbook.read().unwrap().iter() { diff --git a/tests/side_effects.rs b/tests/side_effects.rs index 52caabd2..6f729ded 100644 --- a/tests/side_effects.rs +++ b/tests/side_effects.rs @@ -49,7 +49,7 @@ fn test_side_effects_command() -> Result<(), Box> { assert_eq!( engine.eval_with_scope::( &mut scope, - r" + " // Drive the command object via the wrapper Command.action(30); Command.value diff --git a/tests/stack.rs b/tests/stack.rs index 281c2d87..527522bf 100644 --- a/tests/stack.rs +++ b/tests/stack.rs @@ -8,7 +8,7 @@ fn test_stack_overflow_fn_calls() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " fn foo(n) { if n <= 1 { 0 } else { n + foo(n-1) } } foo(6) ", @@ -22,7 +22,7 @@ fn test_stack_overflow_fn_calls() -> Result<(), Box> { assert!(matches!( *engine .eval::<()>(&format!( - r" + " fn foo(n) {{ if n == 0 {{ 0 }} else {{ n + foo(n-1) }} }} foo({}) ", @@ -40,7 +40,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box> { let mut engine = Engine::new(); assert_eq!( - *engine.compile(r" + *engine.compile(" let a = (1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+(1+1)))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))) ").expect_err("should error").0, ParseErrorType::ExprTooDeep @@ -54,7 +54,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box> { engine.compile("1 + 2")?; engine.compile( - r" + " 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + @@ -71,7 +71,7 @@ fn test_stack_overflow_parsing() -> Result<(), Box> { assert_eq!( *engine .compile( - r" + " 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + diff --git a/tests/string.rs b/tests/string.rs index 99e51606..d60be0dc 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -14,7 +14,11 @@ fn test_string() -> Result<(), Box> { ); assert_eq!( engine.eval::(" \"Test string: \\u2764\\\n hello, world!\"")?, - "Test string: ❤ hello, world!" + if cfg!(not(feature = "no_position")) { + "Test string: ❤ hello, world!" + } else { + "Test string: ❤ hello, world!" + } ); assert_eq!( engine.eval::(" `Test string: \\u2764\nhello,\\nworld!`")?, @@ -319,7 +323,7 @@ fn test_string_interpolated() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let x = 40; `hello ${x+2} worlds!` " @@ -339,7 +343,7 @@ fn test_string_interpolated() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " const x = 42; `hello ${x} worlds!` " @@ -351,7 +355,7 @@ fn test_string_interpolated() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " const x = 42; `${x} worlds!` " @@ -361,7 +365,7 @@ fn test_string_interpolated() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " const x = 42; `hello ${x}` " @@ -371,7 +375,7 @@ fn test_string_interpolated() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " const x = 20; `hello ${let y = x + 1; `${y * 2}`} worlds!` " diff --git a/tests/switch.rs b/tests/switch.rs index db0d8e17..614d0876 100644 --- a/tests/switch.rs +++ b/tests/switch.rs @@ -33,7 +33,7 @@ fn test_switch() -> Result<(), Box> { assert_eq!( engine.eval_with_scope::( &mut scope, - r" + " let y = [1, 2, 3]; switch y { @@ -50,7 +50,7 @@ fn test_switch() -> Result<(), Box> { assert_eq!( engine.eval_with_scope::( &mut scope, - r" + " let y = #{a:1, b:true, c:'x'}; switch y { @@ -98,7 +98,7 @@ fn test_switch_condition() -> Result<(), Box> { assert_eq!( engine.eval_with_scope::( &mut scope, - r" + " switch x / 2 { 21 if x > 40 => 1, 0 if x < 100 => 2, @@ -113,7 +113,7 @@ fn test_switch_condition() -> Result<(), Box> { assert_eq!( engine.eval_with_scope::( &mut scope, - r" + " switch x / 2 { 21 if x < 40 => 1, 0 if x < 100 => 2, @@ -128,7 +128,7 @@ fn test_switch_condition() -> Result<(), Box> { assert!(matches!( *engine .compile( - r" + " switch x { 21 if x < 40 => 1, 21 if x == 10 => 10, diff --git a/tests/syntax.rs b/tests/syntax.rs index 55272f54..5dd03d9a 100644 --- a/tests/syntax.rs +++ b/tests/syntax.rs @@ -57,7 +57,7 @@ fn test_custom_syntax() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let x = 0; let foo = (exec |x| -> { x += 2 } while x < 42) * 10; foo @@ -67,7 +67,7 @@ fn test_custom_syntax() -> Result<(), Box> { ); assert_eq!( engine.eval::( - r" + " let x = 0; exec |x| -> { x += 1 } while x < 42; x @@ -77,7 +77,7 @@ fn test_custom_syntax() -> Result<(), Box> { ); assert_eq!( engine.eval::( - r" + " exec |x| -> { x += 1 } while x < 42; x " diff --git a/tests/time.rs b/tests/time.rs index 94de7933..43778b38 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -40,7 +40,7 @@ fn test_timestamp() -> Result<(), Box> { ); assert!(engine.eval::( - r" + " let time1 = timestamp(); for x in range(0, 10000) {} let time2 = timestamp(); diff --git a/tests/tokens.rs b/tests/tokens.rs index b8343a69..3da33e39 100644 --- a/tests/tokens.rs +++ b/tests/tokens.rs @@ -49,7 +49,7 @@ fn test_tokens_custom_operator_identifiers() -> Result<(), Box> { #[cfg(not(feature = "no_function"))] assert_eq!( engine.eval::( - r" + " fn foo(x, y) { y - x } 1 + 2 * 3 foo 4 - 5 / 6 " @@ -87,7 +87,7 @@ fn test_tokens_custom_operator_symbol() -> Result<(), Box> { fn test_tokens_unicode_xid_ident() -> Result<(), Box> { let engine = Engine::new(); let result = engine.eval::( - r" + " fn すべての答え() { 42 } すべての答え() ", @@ -99,7 +99,7 @@ fn test_tokens_unicode_xid_ident() -> Result<(), Box> { assert!(result.is_err()); let result = engine.eval::( - r" + " fn _1() { 1 } _1() ", diff --git a/tests/while_loop.rs b/tests/while_loop.rs index 35a71dae..222ce7b1 100644 --- a/tests/while_loop.rs +++ b/tests/while_loop.rs @@ -6,7 +6,7 @@ fn test_while() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let x = 0; while x < 10 { @@ -31,7 +31,7 @@ fn test_do() -> Result<(), Box> { assert_eq!( engine.eval::( - r" + " let x = 0; do {