Merge pull request #407 from schungx/master

Add no_position feature.
This commit is contained in:
Stephen Chung 2021-04-22 23:21:51 +08:00 committed by GitHub
commit d579c1b208
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 600 additions and 393 deletions

View File

@ -21,6 +21,7 @@ jobs:
- "--features metadata,serde,internals" - "--features metadata,serde,internals"
- "--features unchecked" - "--features unchecked"
- "--features sync" - "--features sync"
- "--features no_position"
- "--features no_optimize" - "--features no_optimize"
- "--features no_float" - "--features no_float"
- "--features f32_float,serde,metadata,internals" - "--features f32_float,serde,metadata,internals"
@ -34,8 +35,8 @@ jobs:
- "--features no_module" - "--features no_module"
- "--features no_closure" - "--features no_closure"
- "--features unicode-xid-ident" - "--features unicode-xid-ident"
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,serde,unchecked" - "--features sync,no_function,no_float,no_position,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 no_function,no_float,no_position,no_index,no_object,no_optimize,no_module,no_closure,unchecked"
toolchain: [stable] toolchain: [stable]
experimental: [false] experimental: [false]
include: include:

View File

@ -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 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 Version 0.20.0

View File

@ -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 default = ["smartstring/std", "ahash/std", "num-traits/std"] # remove 'smartstring/std' when smartstring is updated to support no-std
unchecked = [] # unchecked arithmetic unchecked = [] # unchecked arithmetic
sync = [] # restrict to only types that implement Send + Sync sync = [] # restrict to only types that implement Send + Sync
no_position = [] # do not track position in the parser
no_optimize = [] # no script optimizer no_optimize = [] # no script optimizer
no_float = [] # no floating-point no_float = [] # no floating-point
f32_float = [] # set FLOAT=f32 f32_float = [] # set FLOAT=f32

View File

@ -25,7 +25,7 @@ fn main() -> Result<(), Box<EvalAltResult>> {
.register_fn("update", TestStruct::update); .register_fn("update", TestStruct::update);
let result = engine.eval::<TestStruct>( let result = engine.eval::<TestStruct>(
r" "
let x = new_ts(); let x = new_ts();
x.update(); x.update();
x x
@ -35,7 +35,7 @@ fn main() -> Result<(), Box<EvalAltResult>> {
println!("{:?}", result); println!("{:?}", result);
let result = engine.eval::<TestStruct>( let result = engine.eval::<TestStruct>(
r" "
let x = [ new_ts() ]; let x = [ new_ts() ];
x[0].update(); x[0].update();
x[0] x[0]

View File

@ -25,7 +25,7 @@ fn main() -> Result<(), Box<EvalAltResult>> {
.register_fn("update", TestStruct::update); .register_fn("update", TestStruct::update);
let result = engine.eval::<TestStruct>( let result = engine.eval::<TestStruct>(
r" "
let x = new_ts(); let x = new_ts();
x.update(); x.update();
x x

View File

@ -1,13 +1,11 @@
//! Module defining the AST (abstract syntax tree). //! Module defining the AST (abstract syntax tree).
use crate::dynamic::{AccessMode, Union};
use crate::fn_native::shared_make_mut; use crate::fn_native::shared_make_mut;
use crate::module::NamespaceRef; use crate::module::NamespaceRef;
use crate::token::Token; use crate::token::Token;
use crate::utils::calc_fn_hash; use crate::utils::calc_fn_hash;
use crate::{ use crate::{
Dynamic, FnNamespace, FnPtr, Identifier, ImmutableString, Module, Position, Shared, StaticVec, Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT,
INT,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -68,7 +66,7 @@ pub struct ScriptFnDef {
/// Function doc-comments (if any). /// Function doc-comments (if any).
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
pub comments: StaticVec<std::string::String>, pub comments: StaticVec<String>,
} }
impl fmt::Display for ScriptFnDef { impl fmt::Display for ScriptFnDef {
@ -782,7 +780,12 @@ pub struct Ident {
impl fmt::Debug for Ident { impl fmt::Debug for Ident {
#[inline(always)] #[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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<Ident>, bool, Position), Const(Expr, Box<Ident>, bool, Position),
/// expr op`=` expr /// expr op`=` expr
Assignment(Box<(Expr, Option<OpAssignment>, Expr)>, Position), Assignment(Box<(Expr, Option<OpAssignment>, 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<FnCallExpr>, Position),
/// `{` stmt`;` ... `}` /// `{` stmt`;` ... `}`
Block(Vec<Stmt>, Position), Block(Vec<Stmt>, Position),
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}` /// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
@ -985,6 +993,7 @@ impl Stmt {
| Self::Break(pos) | Self::Break(pos)
| Self::Block(_, pos) | Self::Block(_, pos)
| Self::Assignment(_, pos) | Self::Assignment(_, pos)
| Self::FnCall(_, pos)
| Self::If(_, _, pos) | Self::If(_, _, pos)
| Self::Switch(_, _, pos) | Self::Switch(_, _, pos)
| Self::While(_, _, pos) | Self::While(_, _, pos)
@ -1014,6 +1023,7 @@ impl Stmt {
| Self::Break(pos) | Self::Break(pos)
| Self::Block(_, pos) | Self::Block(_, pos)
| Self::Assignment(_, pos) | Self::Assignment(_, pos)
| Self::FnCall(_, pos)
| Self::If(_, _, pos) | Self::If(_, _, pos)
| Self::Switch(_, _, pos) | Self::Switch(_, _, pos)
| Self::While(_, _, pos) | Self::While(_, _, pos)
@ -1042,7 +1052,11 @@ impl Stmt {
/// Does this statement return a value? /// Does this statement return a value?
pub fn returns_value(&self) -> bool { pub fn returns_value(&self) -> bool {
match self { 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::Noop(_)
| Self::While(_, _, _) | Self::While(_, _, _)
@ -1080,6 +1094,7 @@ impl Stmt {
Self::Let(_, _, _, _) Self::Let(_, _, _, _)
| Self::Const(_, _, _, _) | Self::Const(_, _, _, _)
| Self::Assignment(_, _) | Self::Assignment(_, _)
| Self::FnCall(_, _)
| Self::Expr(_) | Self::Expr(_)
| Self::Do(_, _, _, _) | Self::Do(_, _, _, _)
| Self::Continue(_) | Self::Continue(_)
@ -1117,7 +1132,10 @@ impl Stmt {
condition.is_pure() && block.0.iter().all(Stmt::is_pure) 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::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::Block(block, _) => block.iter().all(|stmt| stmt.is_pure()),
Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false, Self::Continue(_) | Self::Break(_) | Self::Return(_, _, _) => false,
Self::TryCatch(x, _, _) => { Self::TryCatch(x, _, _) => {
@ -1246,6 +1264,13 @@ impl Stmt {
return false; return false;
} }
} }
Self::FnCall(x, _) => {
for s in &x.args {
if !s.walk(path, on_node) {
return false;
}
}
}
Self::Block(x, _) => { Self::Block(x, _) => {
for s in x { for s in x {
if !s.walk(path, on_node) { if !s.walk(path, on_node) {
@ -1370,14 +1395,14 @@ impl OpAssignment {
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
#[derive(Clone, Copy, Eq, PartialEq, Hash, Default)] #[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). /// Pre-calculated hash for a script-defined function ([`None`] if native functions only).
pub script: Option<u64>, pub script: Option<u64>,
/// Pre-calculated hash for a native Rust function with no parameter types. /// Pre-calculated hash for a native Rust function with no parameter types.
pub native: u64, pub native: u64,
} }
impl fmt::Debug for FnCallHash { impl fmt::Debug for FnCallHashes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(script) = self.script { if let Some(script) = self.script {
if script == self.native { 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. /// Create a [`FnCallHash`] with only the native Rust hash.
#[inline(always)] #[inline(always)]
pub fn from_native(hash: u64) -> Self { pub fn from_native(hash: u64) -> Self {
@ -1443,23 +1468,28 @@ impl FnCallHash {
/// # Volatile Data Structure /// # Volatile Data Structure
/// ///
/// This type is volatile and may change. /// This type is volatile and may change.
#[derive(Clone, Default, Hash)] #[derive(Debug, Clone, Default, Hash)]
pub struct FnCallExpr { pub struct FnCallExpr {
/// Pre-calculated hash. /// Namespace of the function, if any.
pub hash: FnCallHash, pub namespace: Option<NamespaceRef>,
/// Does this function call capture the parent scope? /// Pre-calculated hashes.
pub capture: bool, pub hashes: FnCallHashes,
/// List of function call argument expressions. /// List of function call argument expressions.
pub args: StaticVec<Expr>, pub args: StaticVec<Expr>,
/// List of function call arguments that are constants. /// List of function call arguments that are constants.
pub constant_args: smallvec::SmallVec<[(Dynamic, Position); 2]>, pub constant_args: smallvec::SmallVec<[(Dynamic, Position); 2]>,
/// Namespace of the function, if any. Boxed because it occurs rarely.
pub namespace: Option<NamespaceRef>,
/// Function name. /// Function name.
pub name: Identifier, pub name: Identifier,
/// Does this function call capture the parent scope?
pub capture: bool,
} }
impl FnCallExpr { 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? /// Are there no arguments to this function call?
#[inline(always)] #[inline(always)]
pub fn is_args_empty(&self) -> bool { pub fn is_args_empty(&self) -> bool {
@ -1467,7 +1497,7 @@ impl FnCallExpr {
} }
/// Get the number of arguments to this function call. /// Get the number of arguments to this function call.
#[inline(always)] #[inline(always)]
pub fn num_args(&self) -> usize { pub fn args_count(&self) -> usize {
self.args.len() + self.constant_args.len() self.args.len() + self.constant_args.len()
} }
} }
@ -1609,8 +1639,6 @@ pub enum Expr {
CharConstant(char, Position), CharConstant(char, Position),
/// [String][ImmutableString] constant. /// [String][ImmutableString] constant.
StringConstant(ImmutableString, Position), StringConstant(ImmutableString, Position),
/// [`FnPtr`] constant.
FnPointer(ImmutableString, Position),
/// An interpolated [string][ImmutableString]. /// An interpolated [string][ImmutableString].
InterpolatedString(Box<StaticVec<Expr>>), InterpolatedString(Box<StaticVec<Expr>>),
/// [ expr, ... ] /// [ expr, ... ]
@ -1664,32 +1692,59 @@ impl Default for Expr {
impl fmt::Debug for Expr { impl fmt::Debug for Expr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
#[cfg(not(feature = "no_position"))]
Self::DynamicConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::DynamicConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::BoolConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::BoolConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::IntegerConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::IntegerConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[cfg(not(feature = "no_position"))]
Self::FloatConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::FloatConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::CharConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), Self::CharConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
#[cfg(not(feature = "no_position"))]
Self::StringConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos), 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), 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) => { Self::InterpolatedString(x) => {
f.write_str("InterpolatedString")?; f.write_str("InterpolatedString")?;
f.debug_list().entries(x.iter()).finish() f.debug_list().entries(x.iter()).finish()
} }
Self::Array(x, pos) => { Self::Array(x, _pos) => {
f.write_str("Array")?; f.write_str("Array")?;
f.debug_list().entries(x.iter()).finish()?; 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.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)))
.finish()?; .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(")?; f.write_str("Variable(")?;
match x.1 { match x.1 {
Some((_, ref namespace)) => write!(f, "{}", namespace)?, Some((_, ref namespace)) => write!(f, "{}", namespace)?,
@ -1700,21 +1755,29 @@ impl fmt::Debug for Expr {
Some(n) => write!(f, ", {}", n)?, 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), 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) => { Self::Stmt(x) => {
f.write_str("Stmt")?; f.write_str("Stmt")?;
f.debug_list().entries(x.0.iter()).finish()?; 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"); let mut ff = f.debug_struct("FnCall");
if let Some(ref ns) = x.namespace { if let Some(ref ns) = x.namespace {
ff.field("namespace", ns); ff.field("namespace", ns);
} }
ff.field("name", &x.name) ff.field("name", &x.name)
.field("hash", &x.hash) .field("hash", &x.hashes)
.field("args", &x.args); .field("args", &x.args);
if !x.constant_args.is_empty() { if !x.constant_args.is_empty() {
ff.field("constant_args", &x.constant_args); ff.field("constant_args", &x.constant_args);
@ -1723,9 +1786,11 @@ impl fmt::Debug for Expr {
ff.field("capture", &x.capture); ff.field("capture", &x.capture);
} }
ff.finish()?; 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 { let op_name = match self {
Self::Dot(_, _) => "Dot", Self::Dot(_, _) => "Dot",
Self::Index(_, _) => "Index", Self::Index(_, _) => "Index",
@ -1738,11 +1803,15 @@ impl fmt::Debug for Expr {
.field("lhs", &x.lhs) .field("lhs", &x.lhs)
.field("rhs", &x.rhs) .field("rhs", &x.rhs)
.finish()?; .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()?; 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::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::FnPointer(x, _) => Dynamic(Union::FnPtr(
Box::new(FnPtr::new_unchecked(x.clone(), Default::default())),
AccessMode::ReadOnly,
)),
Self::BoolConstant(x, _) => (*x).into(), Self::BoolConstant(x, _) => (*x).into(),
Self::Unit(_) => Dynamic::UNIT, Self::Unit(_) => Dynamic::UNIT,
@ -1772,7 +1837,7 @@ impl Expr {
Self::Array(x, _) if self.is_constant() => { Self::Array(x, _) if self.is_constant() => {
let mut arr = Array::with_capacity(x.len()); let mut arr = Array::with_capacity(x.len());
arr.extend(x.iter().map(|v| v.get_constant_value().unwrap())); arr.extend(x.iter().map(|v| v.get_constant_value().unwrap()));
arr.into() Dynamic::from_array(arr)
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
@ -1781,7 +1846,7 @@ impl Expr {
x.0.iter().for_each(|(k, v)| { x.0.iter().for_each(|(k, v)| {
*map.get_mut(k.name.as_str()).unwrap() = v.get_constant_value().unwrap() *map.get_mut(k.name.as_str()).unwrap() = v.get_constant_value().unwrap()
}); });
map.into() Dynamic::from_map(map)
} }
_ => return None, _ => return None,
@ -1816,7 +1881,6 @@ impl Expr {
Self::CharConstant(_, pos) => *pos, Self::CharConstant(_, pos) => *pos,
Self::StringConstant(_, pos) => *pos, Self::StringConstant(_, pos) => *pos,
Self::InterpolatedString(x) => x.first().unwrap().position(), Self::InterpolatedString(x) => x.first().unwrap().position(),
Self::FnPointer(_, pos) => *pos,
Self::Array(_, pos) => *pos, Self::Array(_, pos) => *pos,
Self::Map(_, pos) => *pos, Self::Map(_, pos) => *pos,
Self::Property(x) => (x.2).pos, Self::Property(x) => (x.2).pos,
@ -1848,7 +1912,6 @@ impl Expr {
Self::InterpolatedString(x) => { Self::InterpolatedString(x) => {
x.first_mut().unwrap().set_position(new_pos); x.first_mut().unwrap().set_position(new_pos);
} }
Self::FnPointer(_, pos) => *pos = new_pos,
Self::Array(_, pos) => *pos = new_pos, Self::Array(_, pos) => *pos = new_pos,
Self::Map(_, pos) => *pos = new_pos, Self::Map(_, pos) => *pos = new_pos,
Self::Variable(_, pos, _) => *pos = new_pos, Self::Variable(_, pos, _) => *pos = new_pos,
@ -1904,7 +1967,6 @@ impl Expr {
| Self::IntegerConstant(_, _) | Self::IntegerConstant(_, _)
| Self::CharConstant(_, _) | Self::CharConstant(_, _)
| Self::StringConstant(_, _) | Self::StringConstant(_, _)
| Self::FnPointer(_, _)
| Self::Unit(_) => true, | Self::Unit(_) => 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),
@ -1931,7 +1993,6 @@ impl Expr {
| Self::BoolConstant(_, _) | Self::BoolConstant(_, _)
| Self::IntegerConstant(_, _) | Self::IntegerConstant(_, _)
| Self::CharConstant(_, _) | Self::CharConstant(_, _)
| Self::FnPointer(_, _)
| Self::And(_, _) | Self::And(_, _)
| Self::Or(_, _) | Self::Or(_, _)
| Self::Unit(_) => false, | Self::Unit(_) => false,
@ -2044,6 +2105,7 @@ mod tests {
assert_eq!(size_of::<Dynamic>(), 16); assert_eq!(size_of::<Dynamic>(), 16);
assert_eq!(size_of::<Option<Dynamic>>(), 16); assert_eq!(size_of::<Option<Dynamic>>(), 16);
#[cfg(not(feature = "no_position"))]
assert_eq!(size_of::<Position>(), 4); assert_eq!(size_of::<Position>(), 4);
assert_eq!(size_of::<ast::Expr>(), 16); assert_eq!(size_of::<ast::Expr>(), 16);
assert_eq!(size_of::<Option<ast::Expr>>(), 16); assert_eq!(size_of::<Option<ast::Expr>>(), 16);
@ -2052,7 +2114,10 @@ mod tests {
assert_eq!(size_of::<FnPtr>(), 96); assert_eq!(size_of::<FnPtr>(), 96);
assert_eq!(size_of::<Scope>(), 288); assert_eq!(size_of::<Scope>(), 288);
assert_eq!(size_of::<LexError>(), 56); assert_eq!(size_of::<LexError>(), 56);
assert_eq!(size_of::<ParseError>(), 16); assert_eq!(
size_of::<ParseError>(),
if cfg!(feature = "no_position") { 8 } else { 16 }
);
assert_eq!(size_of::<EvalAltResult>(), 72); assert_eq!(size_of::<EvalAltResult>(), 72);
} }
} }

View File

@ -1,7 +1,7 @@
Rhai Tools Rhai Tools
========== ==========
Tools written in Rhai. Tools for running Rhai scripts.
How to Run How to Run

View File

@ -55,8 +55,9 @@ fn print_help() {
} }
fn main() { fn main() {
println!("Rhai REPL tool"); let title = format!("Rhai REPL tool (version {})", env!("CARGO_PKG_VERSION"));
println!("=============="); println!("{}", title);
println!("{0:=<1$}", "", title.len());
print_help(); print_help();
// Initialize scripting engine // Initialize scripting engine

View File

@ -407,6 +407,13 @@ impl Hash for Dynamic {
value.hash(state); 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"))] #[cfg(not(feature = "no_closure"))]
Union::Shared(cell, _) => { Union::Shared(cell, _) => {
@ -1717,13 +1724,21 @@ impl From<&ImmutableString> for Dynamic {
impl From<&crate::Identifier> for Dynamic { impl From<&crate::Identifier> for Dynamic {
#[inline(always)] #[inline(always)]
fn from(value: &crate::Identifier) -> Self { fn from(value: &crate::Identifier) -> Self {
std::string::ToString::to_string(value).into() value.to_string().into()
} }
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
impl<T: Variant + Clone> From<std::vec::Vec<T>> for Dynamic { impl Dynamic {
/// Create a [`Dynamc`] from an [`Array`].
#[inline(always)] #[inline(always)]
fn from(value: std::vec::Vec<T>) -> Self { pub(crate) fn from_array(array: Array) -> Self {
Self(Union::Array(Box::new(array), AccessMode::ReadWrite))
}
}
#[cfg(not(feature = "no_index"))]
impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
#[inline(always)]
fn from(value: Vec<T>) -> Self {
Self(Union::Array( Self(Union::Array(
Box::new(value.into_iter().map(Dynamic::from).collect()), Box::new(value.into_iter().map(Dynamic::from).collect()),
AccessMode::ReadWrite, AccessMode::ReadWrite,
@ -1751,6 +1766,14 @@ impl<T: Variant + Clone> std::iter::FromIterator<T> for Dynamic {
} }
} }
#[cfg(not(feature = "no_object"))] #[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"))] #[cfg(not(feature = "no_std"))]
impl<K: Into<crate::Identifier>, T: Variant + Clone> From<std::collections::HashMap<K, T>> impl<K: Into<crate::Identifier>, T: Variant + Clone> From<std::collections::HashMap<K, T>>
for Dynamic for Dynamic

View File

@ -13,8 +13,8 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::syntax::CustomSyntax; use crate::syntax::CustomSyntax;
use crate::utils::get_hasher; use crate::utils::get_hasher;
use crate::{ use crate::{
Dynamic, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, Position, RhaiResult, Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
Scope, Shared, StaticVec, Shared, StaticVec,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -35,7 +35,7 @@ use crate::{calc_fn_hash, Array};
use crate::Map; use crate::Map;
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
use crate::ast::FnCallHash; use crate::ast::FnCallHashes;
pub type Precedence = NonZeroU8; pub type Precedence = NonZeroU8;
@ -574,6 +574,7 @@ impl State {
self.scope_level == 0 self.scope_level == 0
} }
/// Get a mutable reference to the current function resolution cache. /// Get a mutable reference to the current function resolution cache.
#[inline(always)]
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache { pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
if self.fn_resolution_caches.0.is_empty() { if self.fn_resolution_caches.0.is_empty() {
self.fn_resolution_caches.0.push(BTreeMap::new()); 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. /// Push an empty function resolution cache onto the stack and make it current.
#[allow(dead_code)] #[allow(dead_code)]
#[inline(always)]
pub fn push_fn_resolution_cache(&mut self) { pub fn push_fn_resolution_cache(&mut self) {
self.fn_resolution_caches self.fn_resolution_caches
.0 .0
@ -592,6 +594,7 @@ impl State {
/// # Panics /// # Panics
/// ///
/// Panics if there are no more function resolution cache in the stack. /// Panics if there are no more function resolution cache in the stack.
#[inline(always)]
pub fn pop_fn_resolution_cache(&mut self) { pub fn pop_fn_resolution_cache(&mut self) {
let mut cache = self.fn_resolution_caches.0.pop().unwrap(); let mut cache = self.fn_resolution_caches.0.pop().unwrap();
cache.clear(); 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")))] #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
if let Some(source) = _source { if let Some(source) = _source {
println!("{} @ {:?} | {}", source, _pos, _s); println!("{} @ {:?} | {}", source, _pos, _s);
} else if _pos.is_none() {
println!("{}", _s);
} else { } else {
println!("{:?} | {}", _pos, _s); println!("{:?} | {}", _pos, _s);
} }
@ -1186,7 +1191,7 @@ impl Engine {
let val_type_name = target.type_name(); let val_type_name = target.type_name();
let ((_, val_pos), _) = new_val; 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(), std::iter::empty(),
FN_IDX_SET, FN_IDX_SET,
3, 3,
@ -1227,11 +1232,11 @@ impl Engine {
ChainType::Dot => { ChainType::Dot => {
match rhs { match rhs {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(x, pos) if x.namespace.is_none() && new_val.is_none() => { Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
let FnCallExpr { name, hash, .. } = x.as_ref(); let FnCallExpr { name, hashes, .. } = x.as_ref();
let mut args = idx_val.as_fn_call_args(); let mut args = idx_val.as_fn_call_args();
self.make_method_call( 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(...) = ??? // xxx.fn_name(...) = ???
@ -1272,7 +1277,7 @@ impl Engine {
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap(); let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
if op_info.is_some() { 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 args = [target.as_mut()];
let (mut orig_val, _) = self.exec_fn_call( let (mut orig_val, _) = self.exec_fn_call(
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, mods, state, lib, getter, hash, &mut args, is_ref, true, *pos,
@ -1285,7 +1290,7 @@ impl Engine {
new_val = orig_val; 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]; let mut args = [target.as_mut(), &mut new_val];
self.exec_fn_call( self.exec_fn_call(
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None, mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
@ -1296,7 +1301,7 @@ impl Engine {
// xxx.id // xxx.id
Expr::Property(x) => { Expr::Property(x) => {
let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref(); 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()]; let mut args = [target.as_mut()];
self.exec_fn_call( self.exec_fn_call(
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None, mods, state, lib, getter, hash, &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 // {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() => { Expr::FnCall(x, pos) if !x.is_qualified() => {
let FnCallExpr { name, hash, .. } = x.as_ref(); let FnCallExpr { name, hashes, .. } = x.as_ref();
let mut args = idx_val.as_fn_call_args(); let mut args = idx_val.as_fn_call_args();
let (val, _) = self.make_method_call( 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() val.into()
} }
@ -1345,8 +1350,8 @@ impl Engine {
Expr::Property(p) => { Expr::Property(p) => {
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) = let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
p.as_ref(); p.as_ref();
let hash_get = FnCallHash::from_native(*hash_get); let hash_get = FnCallHashes::from_native(*hash_get);
let hash_set = FnCallHash::from_native(*hash_set); let hash_set = FnCallHashes::from_native(*hash_set);
let arg_values = &mut [target.as_mut(), &mut Default::default()]; let arg_values = &mut [target.as_mut(), &mut Default::default()];
let args = &mut arg_values[..1]; let args = &mut arg_values[..1];
let (mut val, updated) = self.exec_fn_call( let (mut val, updated) = self.exec_fn_call(
@ -1394,11 +1399,11 @@ impl Engine {
Ok((result, may_be_changed)) Ok((result, may_be_changed))
} }
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr // xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
Expr::FnCall(f, pos) if f.namespace.is_none() => { Expr::FnCall(f, pos) if !f.is_qualified() => {
let FnCallExpr { name, hash, .. } = f.as_ref(); let FnCallExpr { name, hashes, .. } = f.as_ref();
let mut args = idx_val.as_fn_call_args(); let mut args = idx_val.as_fn_call_args();
let (mut val, _) = self.make_method_call( 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 val = &mut val;
let target = &mut val.into(); let target = &mut val.into();
@ -1508,7 +1513,7 @@ impl Engine {
self.inc_operations(state, expr.position())?; self.inc_operations(state, expr.position())?;
match expr { 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_positions: StaticVec<_> = Default::default();
let mut arg_values = x let mut arg_values = x
@ -1547,7 +1552,7 @@ impl Engine {
} }
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
Expr::FnCall(x, _) 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(); let mut arg_positions: StaticVec<_> = Default::default();
@ -1704,7 +1709,7 @@ impl Engine {
let type_name = target.type_name(); let type_name = target.type_name();
let args = &mut [target, &mut _idx]; let args = &mut [target, &mut _idx];
let hash_get = 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( self.exec_fn_call(
_mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None, _mods, state, _lib, FN_IDX_GET, hash_get, args, _is_ref, true, idx_pos, None,
_level, _level,
@ -1749,7 +1754,6 @@ impl Engine {
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::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 Expr::Variable(None, var_pos, x) if x.0.is_none() && x.2 == KEYWORD_THIS => this_ptr
.as_deref() .as_deref()
@ -1828,40 +1832,40 @@ impl Engine {
Ok(map.into()) 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 // Namespace-qualified function call
Expr::FnCall(x, pos) if x.namespace.is_some() => { Expr::FnCall(x, pos) if x.is_qualified() => {
let FnCallExpr { let FnCallExpr {
name, name,
namespace, namespace,
hash, hashes,
args, args,
constant_args: c_args, constant_args: c_args,
.. ..
} = x.as_ref(); } = x.as_ref();
let namespace = namespace.as_ref(); let namespace = namespace.as_ref();
let hash = hash.native_hash(); let hash = hashes.native_hash();
self.make_qualified_function_call( self.make_qualified_function_call(
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos, scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
level, 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, _) => { Expr::And(x, _) => {
Ok((self Ok((self
.eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)? .eval_expr(scope, mods, state, lib, this_ptr, &x.lhs, level)?
@ -1916,7 +1920,11 @@ impl Engine {
_ => unreachable!("expression cannot be evaluated: {:?}", expr), _ => 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. /// Evaluate a statements block.
@ -2167,29 +2175,26 @@ impl Engine {
} }
// If statement // If statement
Stmt::If(expr, x, _) => self Stmt::If(expr, x, _) => {
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? let guard_val = self
.as_bool() .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position())) .as_bool()
.and_then(|guard_val| { .map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?;
if guard_val {
if !x.0.is_empty() { if guard_val {
self.eval_stmt_block( if !x.0.is_empty() {
scope, mods, state, lib, this_ptr, &x.0, true, level, self.eval_stmt_block(scope, mods, state, lib, this_ptr, &x.0, true, level)
)
} else {
Ok(Dynamic::UNIT)
}
} else { } else {
if !x.1.is_empty() { Ok(Dynamic::UNIT)
self.eval_stmt_block(
scope, mods, state, lib, this_ptr, &x.1, true, level,
)
} else {
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 // Switch statement
Stmt::Switch(match_expr, x, _) => { Stmt::Switch(match_expr, x, _) => {
@ -2259,17 +2264,16 @@ impl Engine {
if !condition { if !condition {
return Ok(Dynamic::UNIT); return Ok(Dynamic::UNIT);
} }
if body.is_empty() { if !body.is_empty() {
continue; match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level)
} {
Ok(_) => (),
match self.eval_stmt_block(scope, mods, state, lib, this_ptr, body, true, level) { Err(err) => match *err {
Ok(_) => (), EvalAltResult::LoopBreak(false, _) => (),
Err(err) => match *err { EvalAltResult::LoopBreak(true, _) => return Ok(Dynamic::UNIT),
EvalAltResult::LoopBreak(false, _) => (), _ => return Err(err),
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)? .eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.as_bool() .as_bool()
.map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))? .map_err(|err| self.make_type_mismatch_err::<bool>(err, expr.position()))?;
{
if !*is_while { if condition ^ *is_while {
return Ok(Dynamic::UNIT); return Ok(Dynamic::UNIT);
}
} else {
if *is_while {
return Ok(Dynamic::UNIT);
}
} }
}, },
@ -2365,9 +2364,11 @@ impl Engine {
continue; continue;
} }
match self.eval_stmt_block( let result = self.eval_stmt_block(
scope, mods, state, lib, this_ptr, statements, true, level, scope, mods, state, lib, this_ptr, statements, true, level,
) { );
match result {
Ok(_) => (), Ok(_) => (),
Err(err) => match *err { Err(err) => match *err {
EvalAltResult::LoopBreak(false, _) => (), EvalAltResult::LoopBreak(false, _) => (),
@ -2391,6 +2392,40 @@ impl Engine {
// Break statement // Break statement
Stmt::Break(pos) => EvalAltResult::LoopBreak(true, *pos).into(), 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 // Try/Catch statement
Stmt::TryCatch(x, _, _) => { Stmt::TryCatch(x, _, _) => {
let (try_stmt, err_var, catch_stmt) = x.as_ref(); let (try_stmt, err_var, catch_stmt) = x.as_ref();
@ -2478,25 +2513,25 @@ impl Engine {
} }
// Return value // Return value
Stmt::Return(ReturnType::Return, Some(expr), pos) => { Stmt::Return(ReturnType::Return, Some(expr), pos) => EvalAltResult::Return(
let value = self self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten(),
.flatten(); *pos,
EvalAltResult::Return(value, *pos).into() )
} .into(),
// Empty return // Empty return
Stmt::Return(ReturnType::Return, None, pos) => { Stmt::Return(ReturnType::Return, None, pos) => {
EvalAltResult::Return(Default::default(), *pos).into() EvalAltResult::Return(Dynamic::UNIT, *pos).into()
} }
// Throw value // Throw value
Stmt::Return(ReturnType::Exception, Some(expr), pos) => { Stmt::Return(ReturnType::Exception, Some(expr), pos) => EvalAltResult::ErrorRuntime(
let value = self self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)? .flatten(),
.flatten(); *pos,
EvalAltResult::ErrorRuntime(value, *pos).into() )
} .into(),
// Empty throw // Empty throw
Stmt::Return(ReturnType::Exception, None, pos) => { 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 result
} }
/// Check a result to ensure that the data size is within allowable limit. /// Check a result to ensure that the data size is within allowable limit.
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
#[inline(always)] fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box<EvalAltResult>> {
fn check_data_size(&self, result: RhaiResult, pos: Position) -> RhaiResult {
// Simply return all errors
if result.is_err() { if result.is_err() {
return result; return Ok(());
} }
// If no data size limits, just return // If no data size limits, just return
@ -2673,7 +2702,7 @@ impl Engine {
} }
if !_has_limit { if !_has_limit {
return result; return Ok(());
} }
// Recursively calculate the size of a value (especially `Array` and `Map`) // Recursively calculate the size of a value (especially `Array` and `Map`)
@ -2735,7 +2764,11 @@ impl Engine {
.max_string_size .max_string_size
.map_or(usize::MAX, NonZeroUsize::get) .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"))] #[cfg(not(feature = "no_index"))]
@ -2745,7 +2778,8 @@ impl Engine {
.max_array_size .max_array_size
.map_or(usize::MAX, NonZeroUsize::get) .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"))] #[cfg(not(feature = "no_object"))]
@ -2755,14 +2789,17 @@ impl Engine {
.max_map_size .max_map_size
.map_or(usize::MAX, NonZeroUsize::get) .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. /// Check if the number of operations stay within limit.
#[inline]
pub(crate) fn inc_operations( pub(crate) fn inc_operations(
&self, &self,
state: &mut State, state: &mut State,

View File

@ -59,7 +59,7 @@ impl Engine {
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
let mut param_type_names: crate::StaticVec<_> = F::param_names() let mut param_type_names: crate::StaticVec<_> = F::param_names()
.iter() .iter()
.map(|ty| std::format!("_: {}", self.map_type_name(ty))) .map(|ty| format!("_: {}", self.map_type_name(ty)))
.collect(); .collect();
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
@ -119,7 +119,7 @@ impl Engine {
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
let param_type_names: crate::StaticVec<_> = F::param_names() let param_type_names: crate::StaticVec<_> = F::param_names()
.iter() .iter()
.map(|ty| std::format!("_: {}", self.map_type_name(ty))) .map(|ty| format!("_: {}", self.map_type_name(ty)))
.chain(std::iter::once( .chain(std::iter::once(
self.map_type_name(F::return_type_name()).into(), 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| { let mut f = std::fs::File::open(path.clone()).map_err(|err| {
EvalAltResult::ErrorSystem( EvalAltResult::ErrorSystem(
std::format!("Cannot open script file '{}'", path.to_string_lossy()), format!("Cannot open script file '{}'", path.to_string_lossy()),
err.into(), err.into(),
) )
})?; })?;
@ -1181,7 +1181,7 @@ impl Engine {
f.read_to_string(&mut contents).map_err(|err| { f.read_to_string(&mut contents).map_err(|err| {
EvalAltResult::ErrorSystem( EvalAltResult::ErrorSystem(
std::format!("Cannot read script file '{}'", path.to_string_lossy()), format!("Cannot read script file '{}'", path.to_string_lossy()),
err.into(), err.into(),
) )
})?; })?;
@ -1802,7 +1802,7 @@ impl Engine {
/// ///
/// let engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// let ast = engine.compile(r" /// let ast = engine.compile("
/// fn add(x, y) { len(x) + y + foo } /// fn add(x, y) { len(x) + y + foo }
/// fn add1(x) { len(x) + 1 + foo } /// fn add1(x) { len(x) + 1 + foo }
/// fn bar() { foo/2 } /// fn bar() { foo/2 }
@ -1873,7 +1873,7 @@ impl Engine {
/// ///
/// let engine = Engine::new(); /// let engine = Engine::new();
/// ///
/// let ast = engine.compile(r" /// let ast = engine.compile("
/// fn add(x, y) { len(x) + y + foo } /// fn add(x, y) { len(x) + y + foo }
/// fn add1(x) { len(x) + 1 + foo } /// fn add1(x) { len(x) + 1 + foo }
/// fn bar() { foo/2 } /// fn bar() { foo/2 }
@ -2010,16 +2010,13 @@ impl Engine {
/// 2) Functions in registered sub-modules /// 2) Functions in registered sub-modules
/// 3) Functions in packages (optional) /// 3) Functions in packages (optional)
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
pub fn gen_fn_signatures(&self, include_packages: bool) -> std::vec::Vec<String> { pub fn gen_fn_signatures(&self, include_packages: bool) -> Vec<String> {
let mut signatures: std::vec::Vec<_> = Default::default(); let mut signatures: Vec<_> = Default::default();
signatures.extend(self.global_namespace.gen_fn_signatures()); signatures.extend(self.global_namespace.gen_fn_signatures());
self.global_sub_modules.iter().for_each(|(name, m)| { self.global_sub_modules.iter().for_each(|(name, m)| {
signatures.extend( signatures.extend(m.gen_fn_signatures().map(|f| format!("{}::{}", name, f)))
m.gen_fn_signatures()
.map(|f| std::format!("{}::{}", name, f)),
)
}); });
if include_packages { if include_packages {
@ -2169,7 +2166,10 @@ impl Engine {
/// ast.set_source("world"); /// ast.set_source("world");
/// engine.consume_ast(&ast)?; /// engine.consume_ast(&ast)?;
/// ///
/// #[cfg(not(feature = "no_position"))]
/// assert_eq!(*result.read().unwrap(), r#"world @ 1:18 > "hello""#); /// assert_eq!(*result.read().unwrap(), r#"world @ 1:18 > "hello""#);
/// #[cfg(feature = "no_position")]
/// assert_eq!(*result.read().unwrap(), r#"world @ none > "hello""#);
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```

View File

@ -5,7 +5,8 @@
use crate::dynamic::Variant; use crate::dynamic::Variant;
use crate::{Dynamic, StaticVec}; use crate::{Dynamic, StaticVec};
use std::vec::Vec; #[cfg(feature = "no_std")]
use std::prelude::v1::*;
/// Trait that parses arguments to a function call. /// Trait that parses arguments to a function call.
/// ///

View File

@ -1,6 +1,6 @@
//! Implement function-calling mechanism for [`Engine`]. //! Implement function-calling mechanism for [`Engine`].
use crate::ast::FnCallHash; use crate::ast::FnCallHashes;
use crate::engine::{ use crate::engine::{
FnResolutionCacheEntry, Imports, State, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, 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, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
@ -463,7 +463,7 @@ impl Engine {
) -> RhaiResult { ) -> RhaiResult {
#[inline(always)] #[inline(always)]
fn make_error( fn make_error(
name: std::string::String, name: String,
fn_def: &crate::ast::ScriptFnDef, fn_def: &crate::ast::ScriptFnDef,
state: &State, state: &State,
err: Box<EvalAltResult>, err: Box<EvalAltResult>,
@ -623,7 +623,7 @@ impl Engine {
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
fn_name: &str, fn_name: &str,
hash: FnCallHash, hash: FnCallHashes,
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref: bool, is_ref: bool,
_is_method: bool, _is_method: bool,
@ -893,7 +893,7 @@ impl Engine {
state: &mut State, state: &mut State,
lib: &[&Module], lib: &[&Module],
fn_name: &str, fn_name: &str,
mut hash: FnCallHash, mut hash: FnCallHashes,
target: &mut crate::engine::Target, target: &mut crate::engine::Target,
(call_args, call_arg_positions): &mut (StaticVec<Dynamic>, StaticVec<Position>), (call_args, call_arg_positions): &mut (StaticVec<Dynamic>, StaticVec<Position>),
pos: Position, pos: Position,
@ -913,7 +913,7 @@ impl Engine {
let fn_name = fn_ptr.fn_name(); let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len(); let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hashes // 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 // Arguments are passed as-is, adding the curried arguments
let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>(); let mut curry = fn_ptr.curry().iter().cloned().collect::<StaticVec<_>>();
let mut args = curry let mut args = curry
@ -948,7 +948,7 @@ impl Engine {
let fn_name = fn_ptr.fn_name(); let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len(); let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hash // 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),
calc_fn_hash(empty(), fn_name, args_len + 1), calc_fn_hash(empty(), fn_name, args_len + 1),
); );
@ -1022,7 +1022,7 @@ impl Engine {
call_arg_positions.insert(i, Position::NONE); call_arg_positions.insert(i, Position::NONE);
}); });
// Recalculate the hash based on the new function name and new arguments // 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()),
calc_fn_hash(empty(), fn_name, call_args.len() + 1), calc_fn_hash(empty(), fn_name, call_args.len() + 1),
); );
@ -1060,7 +1060,7 @@ impl Engine {
fn_name: &str, fn_name: &str,
args_expr: &[Expr], args_expr: &[Expr],
constant_args: &[(Dynamic, Position)], constant_args: &[(Dynamic, Position)],
mut hash: FnCallHash, mut hashes: FnCallHashes,
pos: Position, pos: Position,
capture_scope: bool, capture_scope: bool,
level: usize, level: usize,
@ -1108,10 +1108,10 @@ impl Engine {
// Recalculate hash // Recalculate hash
let args_len = total_args + curry.len(); let args_len = total_args + curry.len();
hash = if !hash.is_native_only() { hashes = if !hashes.is_native_only() {
FnCallHash::from_script(calc_fn_hash(empty(), name, args_len)) FnCallHashes::from_script(calc_fn_hash(empty(), name, args_len))
} else { } else {
FnCallHash::from_native(calc_fn_hash(empty(), name, args_len)) FnCallHashes::from_native(calc_fn_hash(empty(), name, args_len))
}; };
} }
// Handle Fn() // Handle Fn()
@ -1345,7 +1345,7 @@ impl Engine {
} }
self.exec_fn_call( 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) .map(|(v, _)| v)
} }

View File

@ -1,6 +1,6 @@
//! Module defining interfaces to native-Rust functions. //! Module defining interfaces to native-Rust functions.
use crate::ast::{FnAccess, FnCallHash}; use crate::ast::{FnAccess, FnCallHashes};
use crate::engine::Imports; use crate::engine::Imports;
use crate::plugin::PluginFunction; use crate::plugin::PluginFunction;
use crate::token::is_valid_identifier; use crate::token::is_valid_identifier;
@ -191,12 +191,12 @@ impl<'a> NativeCallContext<'a> {
let fn_name = fn_name.as_ref(); let fn_name = fn_name.as_ref();
let hash = if is_method { 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() - 1),
calc_fn_hash(empty(), fn_name, args.len()), calc_fn_hash(empty(), fn_name, args.len()),
) )
} else { } 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() self.engine()
@ -262,7 +262,7 @@ impl FnPtr {
} }
/// Create a new function pointer without checking its parameters. /// Create a new function pointer without checking its parameters.
#[inline(always)] #[inline(always)]
pub(crate) fn new_unchecked(name: impl Into<Identifier>, curry: StaticVec<Dynamic>) -> Self { pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self {
Self(name.into(), curry) Self(name.into(), curry)
} }
/// Get the name of the function. /// Get the name of the function.

View File

@ -220,7 +220,7 @@ pub use token::{
#[cfg(feature = "internals")] #[cfg(feature = "internals")]
#[deprecated = "this type is volatile and may change"] #[deprecated = "this type is volatile and may change"]
pub use ast::{ pub use ast::{
ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHash, Ident, ASTNode, BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, FnCallHashes, Ident,
OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock, OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock,
}; };

View File

@ -72,7 +72,7 @@ impl FuncInfo {
let mut sig = format!("{}(", self.name); let mut sig = format!("{}(", self.name);
if !self.param_names.is_empty() { if !self.param_names.is_empty() {
let mut params: std::vec::Vec<String> = let mut params: Vec<String> =
self.param_names.iter().map(|s| s.as_str().into()).collect(); self.param_names.iter().map(|s| s.as_str().into()).collect();
let return_type = params.pop().unwrap_or_else(|| "()".into()); let return_type = params.pop().unwrap_or_else(|| "()".into());
sig.push_str(&params.join(", ")); sig.push_str(&params.join(", "));
@ -200,7 +200,7 @@ impl fmt::Debug for Module {
&self &self
.functions .functions
.values() .values()
.map(|f| std::string::ToString::to_string(&f.func)) .map(|f| f.func.to_string())
.collect::<BTreeSet<_>>(), .collect::<BTreeSet<_>>(),
); );
} }

View File

@ -7,8 +7,8 @@ use crate::fn_builtin::get_builtin_binary_op_fn;
use crate::parser::map_dynamic_to_expr; use crate::parser::map_dynamic_to_expr;
use crate::utils::get_hasher; use crate::utils::get_hasher;
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, ImmutableString, Module, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString,
Position, Scope, StaticVec, AST, Module, Position, Scope, StaticVec, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; 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(); let catch_block = mem::take(x.2.statements()).into_vec();
*x.2.statements() = optimize_stmt_block(catch_block, state, false, true, false).into(); *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() => { Stmt::Expr(Expr::Stmt(x)) if x.is_empty() => {
state.set_dirty(); state.set_dirty();
@ -862,15 +873,16 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
} }
// Fn // Fn
Expr::FnCall(x, pos) Expr::FnCall(x, pos)
if x.namespace.is_none() // Non-qualified if !x.is_qualified() // Non-qualified
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations && state.optimization_level == OptimizationLevel::Simple // simple optimizations
&& x.num_args() == 1 && x.args_count() == 1
&& x.constant_args.len() == 1 && x.constant_args.len() == 1
&& x.constant_args[0].0.is::<ImmutableString>() && x.constant_args[0].0.is::<ImmutableString>()
&& x.name == KEYWORD_FN_PTR && x.name == KEYWORD_FN_PTR
=> { => {
state.set_dirty(); 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 // Do not call some special keywords
@ -880,9 +892,9 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// Call built-in operators // Call built-in operators
Expr::FnCall(x, pos) Expr::FnCall(x, pos)
if x.namespace.is_none() // Non-qualified if !x.is_qualified() // Non-qualified
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations && 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 && x.args.iter().all(Expr::is_constant) // all arguments are constants
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted //&& !is_valid_identifier(x.name.chars()) // cannot be scripted
=> { => {
@ -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(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
// Search for overloaded operators (can override built-in). // Search for overloaded operators (can override built-in).
if !has_native_fn(state, x.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]) if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
.and_then(|f| { .and_then(|f| {
let ctx = (state.engine, x.name.as_ref(), state.lib).into(); let ctx = (state.engine, x.name.as_ref(), state.lib).into();
@ -922,13 +934,13 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
// Eagerly call functions // Eagerly call functions
Expr::FnCall(x, pos) Expr::FnCall(x, pos)
if x.namespace.is_none() // Non-qualified if !x.is_qualified() // Non-qualified
&& state.optimization_level == OptimizationLevel::Full // full optimizations && state.optimization_level == OptimizationLevel::Full // full optimizations
&& x.args.iter().all(Expr::is_constant) // all arguments are constants && x.args.iter().all(Expr::is_constant) // all arguments are constants
=> { => {
// First search for script-defined functions (can override built-in) // First search for script-defined functions (can override built-in)
#[cfg(not(feature = "no_function"))] #[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")] #[cfg(feature = "no_function")]
let has_script_fn = false; let has_script_fn = false;

View File

@ -4,9 +4,6 @@ use std::ops::Range;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
#[cfg(not(feature = "unchecked"))]
use std::string::ToString;
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
use num_traits::{CheckedAdd as Add, CheckedSub as Sub}; 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<Self, Box<EvalAltResult>> { pub fn new(from: Decimal, to: Decimal, step: Decimal) -> Result<Self, Box<EvalAltResult>> {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
if step.is_zero() { if step.is_zero() {
use std::string::ToString;
return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(), return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)), Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)),
crate::Position::NONE, crate::Position::NONE,

View File

@ -103,7 +103,7 @@ mod print_debug_functions {
)] )]
pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString { pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString {
let len = array.len(); let len = array.len();
let mut result = std::string::String::with_capacity(len * 5 + 2); let mut result = String::with_capacity(len * 5 + 2);
result.push_str("["); result.push_str("[");
array.iter_mut().enumerate().for_each(|(i, x)| { 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 { pub fn format_map(ctx: NativeCallContext, map: &mut Map) -> ImmutableString {
let len = map.len(); 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("#{"); result.push_str("#{");
map.iter_mut().enumerate().for_each(|(i, (k, v))| { map.iter_mut().enumerate().for_each(|(i, (k, v))| {

View File

@ -1,7 +1,7 @@
//! Main module defining the lexer and parser. //! Main module defining the lexer and parser.
use crate::ast::{ use crate::ast::{
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType, BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType,
ScriptFnDef, Stmt, StmtBlock, ScriptFnDef, Stmt, StmtBlock,
}; };
use crate::dynamic::{AccessMode, Union}; use crate::dynamic::{AccessMode, Union};
@ -15,8 +15,8 @@ use crate::token::{
}; };
use crate::utils::{get_hasher, IdentifierBuilder}; use crate::utils::{get_hasher, IdentifierBuilder};
use crate::{ use crate::{
calc_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError, ParseErrorType, Position, calc_fn_hash, Dynamic, Engine, FnPtr, Identifier, LexError, ParseError, ParseErrorType,
Scope, Shared, StaticVec, AST, Position, Scope, Shared, StaticVec, AST,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -342,10 +342,10 @@ fn parse_fn_call(
calc_fn_hash(empty(), &id, 0) calc_fn_hash(empty(), &id, 0)
}; };
let hash = if is_valid_identifier(id.chars()) { let hashes = if is_valid_identifier(id.chars()) {
FnCallHash::from_script(hash) FnCallHashes::from_script(hash)
} else { } else {
FnCallHash::from_native(hash) FnCallHashes::from_native(hash)
}; };
return Ok(Expr::FnCall( return Ok(Expr::FnCall(
@ -353,7 +353,7 @@ fn parse_fn_call(
name: state.get_identifier(id), name: state.get_identifier(id),
capture, capture,
namespace, namespace,
hash, hashes,
args, args,
..Default::default() ..Default::default()
}), }),
@ -387,10 +387,10 @@ fn parse_fn_call(
calc_fn_hash(empty(), &id, args.len()) calc_fn_hash(empty(), &id, args.len())
}; };
let hash = if is_valid_identifier(id.chars()) { let hashes = if is_valid_identifier(id.chars()) {
FnCallHash::from_script(hash) FnCallHashes::from_script(hash)
} else { } else {
FnCallHash::from_native(hash) FnCallHashes::from_native(hash)
}; };
return Ok(Expr::FnCall( return Ok(Expr::FnCall(
@ -398,7 +398,7 @@ fn parse_fn_call(
name: state.get_identifier(id), name: state.get_identifier(id),
capture, capture,
namespace, namespace,
hash, hashes,
args, args,
..Default::default() ..Default::default()
}), }),
@ -1348,7 +1348,7 @@ fn parse_unary(
Ok(Expr::FnCall( Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: state.get_identifier("-"), name: state.get_identifier("-"),
hash: FnCallHash::from_native(calc_fn_hash(empty(), "-", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(empty(), "-", 1)),
args, args,
..Default::default() ..Default::default()
}), }),
@ -1374,7 +1374,7 @@ fn parse_unary(
Ok(Expr::FnCall( Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: state.get_identifier("+"), name: state.get_identifier("+"),
hash: FnCallHash::from_native(calc_fn_hash(empty(), "+", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(empty(), "+", 1)),
args, args,
..Default::default() ..Default::default()
}), }),
@ -1393,7 +1393,7 @@ fn parse_unary(
Ok(Expr::FnCall( Ok(Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: state.get_identifier("!"), name: state.get_identifier("!"),
hash: FnCallHash::from_native(calc_fn_hash(empty(), "!", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(empty(), "!", 1)),
args, args,
..Default::default() ..Default::default()
}), }),
@ -1415,20 +1415,20 @@ fn make_assignment_stmt<'a>(
rhs: Expr, rhs: Expr,
op_pos: Position, op_pos: Position,
) -> Result<Stmt, ParseError> { ) -> Result<Stmt, ParseError> {
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Position { fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> {
match expr { match expr {
Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs { Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs {
Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
ref e => e.position(), ref e => Some(e.position()),
}, },
Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs { Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs {
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
_ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))), _ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
}, },
Expr::Property(_) if parent_is_dot => Position::NONE, Expr::Property(_) if parent_is_dot => None,
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"), Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
e if parent_is_dot => e.position(), e if parent_is_dot => Some(e.position()),
_ => Position::NONE, _ => None,
} }
} }
@ -1464,7 +1464,7 @@ fn make_assignment_stmt<'a>(
// xxx[???]... = rhs, xxx.prop... = rhs // xxx[???]... = rhs, xxx.prop... = rhs
Expr::Index(x, _) | Expr::Dot(x, _) => { Expr::Index(x, _) | Expr::Dot(x, _) => {
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) { 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 // var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
Expr::Variable(None, _, x) if x.0.is_none() => { Expr::Variable(None, _, x) if x.0.is_none() => {
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos)) Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
@ -1488,7 +1488,7 @@ fn make_assignment_stmt<'a>(
Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(expr.position())) 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 // ??? && ??? = rhs, ??? || ??? = rhs
@ -1595,9 +1595,9 @@ fn make_dot_expr(
} }
Expr::FnCall(mut func, func_pos) => { Expr::FnCall(mut func, func_pos) => {
// Recalculate hash // Recalculate hash
func.hash = FnCallHash::from_script_and_native( func.hashes = FnCallHashes::from_script_and_native(
calc_fn_hash(empty(), &func.name, func.num_args()), calc_fn_hash(empty(), &func.name, func.args_count()),
calc_fn_hash(empty(), &func.name, func.num_args() + 1), calc_fn_hash(empty(), &func.name, func.args_count() + 1),
); );
let rhs = Expr::Dot( let rhs = Expr::Dot(
@ -1623,7 +1623,7 @@ fn make_dot_expr(
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
} }
// lhs.nnn::func(...) // 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") unreachable!("method call should not be namespace-qualified")
} }
// lhs.Fn() or lhs.eval() // lhs.Fn() or lhs.eval()
@ -1651,9 +1651,9 @@ fn make_dot_expr(
// lhs.func(...) // lhs.func(...)
(lhs, Expr::FnCall(mut func, func_pos)) => { (lhs, Expr::FnCall(mut func, func_pos)) => {
// Recalculate hash // Recalculate hash
func.hash = FnCallHash::from_script_and_native( func.hashes = FnCallHashes::from_script_and_native(
calc_fn_hash(empty(), &func.name, func.num_args()), calc_fn_hash(empty(), &func.name, func.args_count()),
calc_fn_hash(empty(), &func.name, func.num_args() + 1), calc_fn_hash(empty(), &func.name, func.args_count() + 1),
); );
let rhs = Expr::FnCall(func, func_pos); let rhs = Expr::FnCall(func, func_pos);
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos) Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
@ -1739,7 +1739,7 @@ fn parse_binary_op(
let op_base = FnCallExpr { let op_base = FnCallExpr {
name: state.get_identifier(op.as_ref()), name: state.get_identifier(op.as_ref()),
hash: FnCallHash::from_native(hash), hashes: FnCallHashes::from_native(hash),
capture: false, capture: false,
..Default::default() ..Default::default()
}; };
@ -1804,7 +1804,7 @@ fn parse_binary_op(
let hash = calc_fn_hash(empty(), OP_CONTAINS, 2); let hash = calc_fn_hash(empty(), OP_CONTAINS, 2);
Expr::FnCall( Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
hash: FnCallHash::from_script(hash), hashes: FnCallHashes::from_script(hash),
args, args,
name: state.get_identifier(OP_CONTAINS), name: state.get_identifier(OP_CONTAINS),
..op_base ..op_base
@ -1824,10 +1824,10 @@ fn parse_binary_op(
Expr::FnCall( Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
hash: if is_valid_identifier(s.chars()) { hashes: if is_valid_identifier(s.chars()) {
FnCallHash::from_script(hash) FnCallHashes::from_script(hash)
} else { } else {
FnCallHash::from_native(hash) FnCallHashes::from_native(hash)
}, },
args, args,
..op_base ..op_base
@ -2516,7 +2516,7 @@ fn parse_stmt(
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
let comments = { let comments = {
let mut comments: StaticVec<std::string::String> = Default::default(); let mut comments: StaticVec<String> = Default::default();
let mut comments_pos = Position::NONE; let mut comments_pos = Position::NONE;
// Handle doc-comments. // Handle doc-comments.
@ -2771,7 +2771,7 @@ fn parse_fn(
mut settings: ParseSettings, mut settings: ParseSettings,
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
#[cfg(feature = "metadata")] #[cfg(feature = "metadata")]
comments: StaticVec<std::string::String>, comments: StaticVec<String>,
) -> Result<ScriptFnDef, ParseError> { ) -> Result<ScriptFnDef, ParseError> {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
settings.ensure_level_within_max_limit(state.max_expr_depth)?; settings.ensure_level_within_max_limit(state.max_expr_depth)?;
@ -2894,7 +2894,7 @@ fn make_curry_from_externals(
let expr = Expr::FnCall( let expr = Expr::FnCall(
Box::new(FnCallExpr { Box::new(FnCallExpr {
name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY), 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(), empty(),
crate::engine::KEYWORD_FN_PTR_CURRY, crate::engine::KEYWORD_FN_PTR_CURRY,
num_externals + 1, num_externals + 1,
@ -3018,7 +3018,8 @@ fn parse_anon_fn(
comments: Default::default(), 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"))] #[cfg(not(feature = "no_closure"))]
let expr = make_curry_from_externals(state, expr, externals, settings.pos); let expr = make_curry_from_externals(state, expr, externals, settings.pos);

View File

@ -57,16 +57,28 @@ pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)] #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy)]
pub struct Position { pub struct Position {
/// Line number - 0 = none /// Line number - 0 = none
#[cfg(not(feature = "no_position"))]
line: u16, line: u16,
/// Character position - 0 = BOL /// Character position - 0 = BOL
#[cfg(not(feature = "no_position"))]
pos: u16, pos: u16,
} }
impl Position { impl Position {
/// A [`Position`] representing no 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. /// 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`]. /// Create a new [`Position`].
/// ///
@ -77,12 +89,14 @@ impl Position {
/// ///
/// Panics if `line` is zero. /// Panics if `line` is zero.
#[inline(always)] #[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"); assert!(line != 0, "line cannot be zero");
Self { Self {
#[cfg(not(feature = "no_position"))]
line, line,
pos: position, #[cfg(not(feature = "no_position"))]
pos: _position,
} }
} }
/// Get the line number (1-based), or [`None`] if there is no position. /// Get the line number (1-based), or [`None`] if there is no position.
@ -91,26 +105,40 @@ impl Position {
if self.is_none() { if self.is_none() {
None None
} else { } 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. /// Get the character position (1-based), or [`None`] if at beginning of a line.
#[inline(always)] #[inline(always)]
pub fn position(self) -> Option<usize> { pub fn position(self) -> Option<usize> {
if self.is_none() || self.pos == 0 { if self.is_none() {
None None
} else { } 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. /// Advance by one character position.
#[inline(always)] #[inline(always)]
pub(crate) fn advance(&mut self) { 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 // Advance up to maximum position
if self.pos < u16::MAX { if self.pos < u16::MAX {
self.pos += 1; self.pos += 1;
}
} }
} }
/// Go backwards by one character position. /// 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. /// Panics if already at beginning of a line - cannot rewind to a previous line.
#[inline(always)] #[inline(always)]
pub(crate) fn rewind(&mut self) { pub(crate) fn rewind(&mut self) {
assert!(!self.is_none(), "cannot rewind Position::none"); #[cfg(not(feature = "no_position"))]
assert!(self.pos > 0, "cannot rewind at position 0"); {
self.pos -= 1; 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. /// Advance to the next line.
#[inline(always)] #[inline(always)]
pub(crate) fn new_line(&mut self) { 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 // Advance up to maximum position
if self.line < u16::MAX { if self.line < u16::MAX {
self.line += 1; self.line += 1;
self.pos = 0; self.pos = 0;
}
} }
} }
/// Is this [`Position`] at the beginning of a line? /// Is this [`Position`] at the beginning of a line?
#[inline(always)] #[inline(always)]
pub fn is_beginning_of_line(self) -> bool { 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`]? /// Is there no [`Position`]?
#[inline(always)] #[inline(always)]
pub fn is_none(self) -> bool { 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)] #[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_none() { if self.is_none() {
write!(f, "none") write!(f, "none")?;
} else { } 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 { impl fmt::Debug for Position {
#[inline(always)] #[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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() { if rhs.is_none() {
self self
} else { } else {
Self { #[cfg(not(feature = "no_position"))]
return Self {
line: self.line + rhs.line - 1, line: self.line + rhs.line - 1,
pos: if rhs.is_beginning_of_line() { pos: if rhs.is_beginning_of_line() {
self.pos self.pos
} else { } else {
self.pos + rhs.pos - 1 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 mut escape = String::with_capacity(12);
let start = *pos; let start = *pos;
let mut skip_whitespace_until = 0;
let mut interpolated = false; let mut interpolated = false;
#[cfg(not(feature = "no_position"))]
let mut skip_whitespace_until = 0;
state.is_within_text_terminated_by = Some(termination_char); 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); assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape);
escape.clear(); escape.clear();
pos.new_line(); pos.new_line();
skip_whitespace_until = start.position().unwrap() + 1;
#[cfg(not(feature = "no_position"))]
{
skip_whitespace_until = start.position().unwrap() + 1;
}
} }
// Unterminated string // Unterminated string
@ -1062,13 +1120,18 @@ pub fn parse_string_literal(
} }
// Whitespace to skip // Whitespace to skip
#[cfg(not(feature = "no_position"))]
_ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {} _ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {}
// All other characters // All other characters
_ => { _ => {
escape.clear(); escape.clear();
result.push(next_char); 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<Position> = None;
while let Some(c) = stream.get_next() { while let Some(c) = stream.get_next() {
pos.advance(); 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, '-'); result.insert(0, '-');
negated_pos negated_pos
} else { } else {
@ -1511,7 +1574,7 @@ fn get_next_token_inner(
('+', _) if !state.non_unary => return Some((Token::UnaryPlus, start_pos)), ('+', _) if !state.non_unary => return Some((Token::UnaryPlus, start_pos)),
('+', _) => return Some((Token::Plus, 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)), ('-', '0'..='9') => return Some((Token::Minus, start_pos)),
('-', '=') => { ('-', '=') => {
eat_next(stream, pos); eat_next(stream, pos);

View File

@ -56,7 +56,7 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
convert_to_vec::<INT>(engine.eval( convert_to_vec::<INT>(engine.eval(
r" "
let x = [2, 9]; let x = [2, 9];
x.insert(-1, 1); x.insert(-1, 1);
x.insert(999, 3); x.insert(999, 3);
@ -76,7 +76,7 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
convert_to_vec::<INT>(engine.eval( convert_to_vec::<INT>(engine.eval(
r" "
let x = [1, 2, 3]; let x = [1, 2, 3];
x += [4, 5]; x += [4, 5];
x x
@ -86,7 +86,7 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
); );
assert_eq!( assert_eq!(
convert_to_vec::<INT>(engine.eval( convert_to_vec::<INT>(engine.eval(
r" "
let x = [1, 2, 3]; let x = [1, 2, 3];
let y = [4, 5]; let y = [4, 5];
x + y x + y
@ -136,7 +136,7 @@ fn test_array_with_structs() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let a = [new_ts()]; let a = [new_ts()];
a[0].x = 100; a[0].x = 100;
a[0].update(); a[0].update();
@ -158,7 +158,7 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
convert_to_vec::<INT>(engine.eval( convert_to_vec::<INT>(engine.eval(
r" "
let x = [1, 2, 3]; let x = [1, 2, 3];
x.filter(|v| v > 2) x.filter(|v| v > 2)
" "
@ -168,7 +168,7 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
convert_to_vec::<INT>(engine.eval( convert_to_vec::<INT>(engine.eval(
r" "
let x = [1, 2, 3]; let x = [1, 2, 3];
x.filter(|v, i| v > i) x.filter(|v, i| v > i)
" "
@ -178,7 +178,7 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
convert_to_vec::<INT>(engine.eval( convert_to_vec::<INT>(engine.eval(
r" "
let x = [1, 2, 3]; let x = [1, 2, 3];
x.map(|v| v * 2) x.map(|v| v * 2)
" "
@ -188,7 +188,7 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
convert_to_vec::<INT>(engine.eval( convert_to_vec::<INT>(engine.eval(
r" "
let x = [1, 2, 3]; let x = [1, 2, 3];
x.map(|v, i| v * i) x.map(|v, i| v * i)
" "

View File

@ -21,41 +21,38 @@ fn test_assignments_bad_lhs() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
assert_eq!( 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()) ParseErrorType::AssignmentToInvalidLHS("".to_string())
); );
assert_eq!( 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()) ParseErrorType::AssignmentToInvalidLHS("".to_string())
); );
assert_eq!( 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()) ParseErrorType::AssignmentToConstant("".to_string())
); );
assert_eq!( 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()) ParseErrorType::AssignmentToConstant("".to_string())
); );
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
assert_eq!(
*engine.compile("x.foo() = 42;").expect_err("should error").0,
ParseErrorType::AssignmentToInvalidLHS("".to_string())
);
assert_eq!( assert_eq!(
*engine *engine
.compile(r"x.foo() = 42;") .compile("x.foo().x.y = 42;")
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::AssignmentToInvalidLHS("".to_string()) ParseErrorType::AssignmentToInvalidLHS("".to_string())
); );
assert_eq!( assert_eq!(
*engine *engine
.compile(r"x.foo().x.y = 42;") .compile("x.y.z.foo() = 42;")
.expect_err("should error")
.0,
ParseErrorType::AssignmentToInvalidLHS("".to_string())
);
assert_eq!(
*engine
.compile(r"x.y.z.foo() = 42;")
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::AssignmentToInvalidLHS("".to_string()) ParseErrorType::AssignmentToInvalidLHS("".to_string())
@ -63,7 +60,7 @@ fn test_assignments_bad_lhs() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
assert_eq!( assert_eq!(
*engine *engine
.compile(r"x.foo()[0] = 42;") .compile("x.foo()[0] = 42;")
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::AssignmentToInvalidLHS("".to_string()) ParseErrorType::AssignmentToInvalidLHS("".to_string())
@ -71,7 +68,7 @@ fn test_assignments_bad_lhs() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
assert_eq!( assert_eq!(
*engine *engine
.compile(r"x[y].z.foo() = 42;") .compile("x[y].z.foo() = 42;")
.expect_err("should error") .expect_err("should error")
.0, .0,
ParseErrorType::AssignmentToInvalidLHS("".to_string()) ParseErrorType::AssignmentToInvalidLHS("".to_string())

View File

@ -38,7 +38,7 @@ fn test_bool_op_short_circuit() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<bool>( engine.eval::<bool>(
r" "
let x = true; let x = true;
x || { throw; }; x || { throw; };
" "
@ -48,7 +48,7 @@ fn test_bool_op_short_circuit() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<bool>( engine.eval::<bool>(
r" "
let x = false; let x = false;
x && { throw; }; x && { throw; };
" "
@ -65,7 +65,7 @@ fn test_bool_op_no_short_circuit1() {
assert!(engine assert!(engine
.eval::<bool>( .eval::<bool>(
r" "
let x = true; let x = true;
x | { throw; } x | { throw; }
" "
@ -79,7 +79,7 @@ fn test_bool_op_no_short_circuit2() {
assert!(engine assert!(engine
.eval::<bool>( .eval::<bool>(
r" "
let x = false; let x = false;
x & { throw; } x & { throw; }
" "

View File

@ -10,7 +10,7 @@ fn test_call_fn() -> Result<(), Box<EvalAltResult>> {
scope.push("foo", 42 as INT); scope.push("foo", 42 as INT);
let ast = engine.compile( let ast = engine.compile(
r" "
fn hello(x, y) { fn hello(x, y) {
x + y x + y
} }

View File

@ -7,7 +7,7 @@ fn test_chars() -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<char>("'y'")?, 'y'); assert_eq!(engine.eval::<char>("'y'")?, 'y');
assert_eq!(engine.eval::<char>(r"'\''")?, '\''); assert_eq!(engine.eval::<char>(r"'\''")?, '\'');
assert_eq!(engine.eval::<char>(r#"'"'"#)?, '"'); assert_eq!(engine.eval::<char>(r#"'"'"#)?, '"');
assert_eq!(engine.eval::<char>("'\\u2764'")?, '❤'); assert_eq!(engine.eval::<char>(r"'\u2764'")?, '❤');
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
{ {

View File

@ -54,7 +54,7 @@ fn test_closures() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let foo = #{ x: 42 }; let foo = #{ x: 42 };
let f = || { this.x }; let f = || { this.x };
foo.call(f) foo.call(f)

View File

@ -33,7 +33,7 @@ fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let mut engine = Engine::new();
let ast = engine.compile( let ast = engine.compile(
r" "
/// Hello world /// Hello world
@ -48,7 +48,7 @@ fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
assert!(engine assert!(engine
.compile( .compile(
r" "
/// Hello world /// Hello world
let x = 42; let x = 42;
" "
@ -56,7 +56,7 @@ fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
.is_err()); .is_err());
engine.compile( engine.compile(
r" "
/////////////// ///////////////
let x = 42; let x = 42;
@ -66,7 +66,7 @@ fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
)?; )?;
let ast = engine.compile( let ast = engine.compile(
r" "
/** Hello world /** Hello world
** how are you? ** how are you?
**/ **/
@ -82,7 +82,7 @@ fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
assert!(engine assert!(engine
.compile( .compile(
r" "
/** Hello world */ /** Hello world */
let x = 42; let x = 42;
" "
@ -92,7 +92,7 @@ fn test_comments_doc() -> Result<(), Box<EvalAltResult>> {
engine.enable_doc_comments(false); engine.enable_doc_comments(false);
engine.compile( engine.compile(
r" "
/// Hello world! /// Hello world!
let x = 42; let x = 42;

View File

@ -59,7 +59,7 @@ fn test_constant_mut() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval_with_scope::<INT>( engine.eval_with_scope::<INT>(
&mut scope, &mut scope,
r" "
MY_NUMBER.update_value(42); MY_NUMBER.update_value(42);
MY_NUMBER.value MY_NUMBER.value
", ",

View File

@ -91,7 +91,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .eval::<Array>(
r" "
let x = [1,2,3,4,5,6]; let x = [1,2,3,4,5,6];
let y = [7,8,9,10,11,12]; let y = [7,8,9,10,11,12];
x + y x + y
@ -105,7 +105,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .eval::<Array>(
r" "
let x = [1,2,3,4,5,6]; let x = [1,2,3,4,5,6];
x.pad(100, 42); x.pad(100, 42);
x x
@ -118,7 +118,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .eval::<Array>(
r" "
let x = [1,2,3]; let x = [1,2,3];
[x, x, x, x] [x, x, x, x]
" "
@ -131,7 +131,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .eval::<Array>(
r" "
let x = #{a:1, b:2, c:3}; let x = #{a:1, b:2, c:3};
[x, x, x, x] [x, x, x, x]
" "
@ -143,7 +143,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Array>( .eval::<Array>(
r" "
let x = [1]; let x = [1];
let y = [x, x]; let y = [x, x];
let z = [y, y]; let z = [y, y];
@ -159,7 +159,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine engine
.eval::<Array>( .eval::<Array>(
r" "
let x = [1,2,3,4,5,6]; let x = [1,2,3,4,5,6];
let y = [7,8,9,10,11,12]; let y = [7,8,9,10,11,12];
x + y x + y
@ -172,7 +172,7 @@ fn test_max_array_size() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine engine
.eval::<Array>( .eval::<Array>(
r" "
let x = [1,2,3]; let x = [1,2,3];
[x, x, x, x] [x, x, x, x]
" "
@ -209,7 +209,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Map>( .eval::<Map>(
r" "
let x = #{a:1,b:2,c:3,d:4,e:5,f:6}; 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}; let y = #{g:7,h:8,i:9,j:10,k:11,l:12};
x + y x + y
@ -222,7 +222,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Map>( .eval::<Map>(
r" "
let x = #{a:1,b:2,c:3}; let x = #{a:1,b:2,c:3};
#{u:x, v:x, w:x, z:x} #{u:x, v:x, w:x, z:x}
" "
@ -235,7 +235,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.eval::<Map>( .eval::<Map>(
r" "
let x = [1, 2, 3]; let x = [1, 2, 3];
#{u:x, v:x, w:x, z:x} #{u:x, v:x, w:x, z:x}
" "
@ -249,7 +249,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine engine
.eval::<Map>( .eval::<Map>(
r" "
let x = #{a:1,b:2,c:3,d:4,e:5,f:6}; 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}; let y = #{g:7,h:8,i:9,j:10,k:11,l:12};
x + y x + y
@ -262,7 +262,7 @@ fn test_max_map_size() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine engine
.eval::<Map>( .eval::<Map>(
r" "
let x = #{a:1,b:2,c:3}; let x = #{a:1,b:2,c:3};
#{u:x, v:x, w:x, z:x} #{u:x, v:x, w:x, z:x}
" "

View File

@ -5,7 +5,7 @@ use rhai::{Engine, EvalAltResult, Module, INT};
fn test_for() -> Result<(), Box<EvalAltResult>> { fn test_for() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
let script = r" let script = "
let sum1 = 0; let sum1 = 0;
let sum2 = 0; let sum2 = 0;
let inputs = [1, 2, 3, 4, 5]; let inputs = [1, 2, 3, 4, 5];
@ -29,7 +29,7 @@ fn test_for() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let sum = 0; let sum = 0;
for x in range(1, 10, 2) { sum += x; } for x in range(1, 10, 2) { sum += x; }
sum sum
@ -40,7 +40,7 @@ fn test_for() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let sum = 0; let sum = 0;
for x in range(10, 1, 2) { sum += x; } for x in range(10, 1, 2) { sum += x; }
sum sum
@ -51,7 +51,7 @@ fn test_for() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let sum = 0; let sum = 0;
for x in range(1, 10, -2) { sum += x; } for x in range(1, 10, -2) { sum += x; }
sum sum
@ -62,7 +62,7 @@ fn test_for() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let sum = 0; let sum = 0;
for x in range(10, 1, -2) { sum += x; } for x in range(10, 1, -2) { sum += x; }
sum sum
@ -80,7 +80,7 @@ fn test_for_overflow() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
let script = r" let script = "
let sum = 0; let sum = 0;
for x in range(9223372036854775807, 0, 9223372036854775807) { for x in range(9223372036854775807, 0, 9223372036854775807) {
@ -90,7 +90,7 @@ fn test_for_overflow() -> Result<(), Box<EvalAltResult>> {
sum sum
"; ";
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
let script = r" let script = "
let sum = 0; let sum = 0;
for x in range(2147483647 , 0, 2147483647 ) { for x in range(2147483647 , 0, 2147483647 ) {

View File

@ -67,7 +67,7 @@ fn test_functions_namespaces() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
const ANSWER = 42; const ANSWER = 42;
fn foo() { global::ANSWER } fn foo() { global::ANSWER }

View File

@ -13,7 +13,7 @@ fn test_if() -> Result<(), Box<EvalAltResult>> {
); );
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
if false { 55 } if false { 55 }
else if false { 33 } else if false { 33 }
else if false { 66 } else if false { 66 }
@ -34,7 +34,7 @@ fn test_if_expr() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let x = 42; let x = 42;
let y = 1 + if x > 40 { 100 } else { 0 } / x; let y = 1 + if x > 40 { 100 } else { 0 } / x;
y y

View File

@ -70,7 +70,7 @@ fn test_internal_fn_big() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
fn math_me(a, b, c, d, e, f) { fn math_me(a, b, c, d, e, f) {
a - b * c + d * e - f a - b * c + d * e - f
} }
@ -89,7 +89,7 @@ fn test_internal_fn_overloading() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
fn abc(x,y,z) { 2*x + 3*y + 4*z + 888 } fn abc(x,y,z) { 2*x + 3*y + 4*z + 888 }
fn abc(x,y) { x + 2*y + 88 } fn abc(x,y) { x + 2*y + 88 }
fn abc() { 42 } fn abc() { 42 }
@ -104,7 +104,7 @@ fn test_internal_fn_overloading() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
*engine *engine
.compile( .compile(
r" "
fn abc(x) { x + 42 } fn abc(x) { x + 42 }
fn abc(x) { x - 42 } fn abc(x) { x - 42 }
" "

View File

@ -6,7 +6,7 @@ fn test_loop() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let x = 0; let x = 0;
let i = 0; let i = 0;

View File

@ -72,7 +72,7 @@ b`: 1}; y["a\nb"]
); );
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let x = #{a: 1, b: 2, c: 3}; let x = #{a: 1, b: 2, c: 3};
let y = #{b: 42, d: 9}; let y = #{b: 42, d: 9};
x.mixin(y); x.mixin(y);
@ -83,7 +83,7 @@ b`: 1}; y["a\nb"]
); );
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let x = #{a: 1, b: 2, c: 3}; let x = #{a: 1, b: 2, c: 3};
x += #{b: 42, d: 9}; x += #{b: 42, d: 9};
x.len() + x.b x.len() + x.b
@ -94,7 +94,7 @@ b`: 1}; y["a\nb"]
assert_eq!( assert_eq!(
engine engine
.eval::<Map>( .eval::<Map>(
r" "
let x = #{a: 1, b: 2, c: 3}; let x = #{a: 1, b: 2, c: 3};
let y = #{b: 42, d: 9}; let y = #{b: 42, d: 9};
x + y x + y
@ -206,7 +206,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, _), pos) EvalAltResult::ErrorParsing(ParseErrorType::MissingToken(token, _), pos)
if token == "{" && pos.position() == Some(4) if token == "{"
)); ));
Ok(()) Ok(())

View File

@ -30,7 +30,7 @@ fn test_mismatched_op_custom_type() -> Result<(), Box<EvalAltResult>> {
.register_type_with_name::<TestStruct>("TestStruct") .register_type_with_name::<TestStruct>("TestStruct")
.register_fn("new_ts", TestStruct::new); .register_fn("new_ts", TestStruct::new);
assert!(matches!(*engine.eval::<bool>(r" assert!(matches!(*engine.eval::<bool>("
let x = new_ts(); let x = new_ts();
let y = new_ts(); let y = new_ts();
x == y x == y

View File

@ -381,13 +381,13 @@ fn test_module_export() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
assert!(matches!( 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 ParseError(x, _) if *x == ParseErrorType::WrongExport
)); ));
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
assert!(matches!( 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 ParseError(x, _) if *x == ParseErrorType::WrongExport
)); ));

View File

@ -72,7 +72,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
fn inc(x) { x + 1 } fn inc(x) { x + 1 }
let x = 0; let x = 0;
while x < 31 { while x < 36 {
print(x); print(x);
x = inc(x); x = inc(x);
} }

View File

@ -5,9 +5,9 @@ use rhai::{Engine, EvalAltResult, OptimizationLevel, INT};
#[test] #[test]
fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> { fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
fn run_test(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> { fn run_test(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
assert_eq!(engine.eval::<INT>(r"if true { 42 } else { 123 }")?, 42); assert_eq!(engine.eval::<INT>("if true { 42 } else { 123 }")?, 42);
assert_eq!( assert_eq!(
engine.eval::<INT>(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
42 42
); );
assert_eq!( assert_eq!(
@ -34,14 +34,14 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
engine.set_optimization_level(OptimizationLevel::Simple); engine.set_optimization_level(OptimizationLevel::Simple);
assert_eq!( assert_eq!(
engine.eval::<INT>(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
123 123
); );
engine.set_optimization_level(OptimizationLevel::Full); engine.set_optimization_level(OptimizationLevel::Full);
assert_eq!( assert_eq!(
engine.eval::<INT>(r"if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
123 123
); );
@ -49,6 +49,7 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_position"))]
#[test] #[test]
fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> { fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
let mut engine = Engine::new(); let mut engine = Engine::new();

View File

@ -60,7 +60,11 @@ fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
assert_eq!(logbook.read().unwrap()[0], "entry: 42"); assert_eq!(logbook.read().unwrap()[0], "entry: 42");
assert_eq!( assert_eq!(
logbook.read().unwrap()[1], 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() { for entry in logbook.read().unwrap().iter() {

View File

@ -49,7 +49,7 @@ fn test_side_effects_command() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval_with_scope::<INT>( engine.eval_with_scope::<INT>(
&mut scope, &mut scope,
r" "
// Drive the command object via the wrapper // Drive the command object via the wrapper
Command.action(30); Command.action(30);
Command.value Command.value

File diff suppressed because one or more lines are too long

View File

@ -14,7 +14,11 @@ fn test_string() -> Result<(), Box<EvalAltResult>> {
); );
assert_eq!( assert_eq!(
engine.eval::<String>(" \"Test string: \\u2764\\\n hello, world!\"")?, engine.eval::<String>(" \"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!( assert_eq!(
engine.eval::<String>(" `Test string: \\u2764\nhello,\\nworld!`")?, engine.eval::<String>(" `Test string: \\u2764\nhello,\\nworld!`")?,
@ -319,7 +323,7 @@ fn test_string_interpolated() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<String>( engine.eval::<String>(
r" "
let x = 40; let x = 40;
`hello ${x+2} worlds!` `hello ${x+2} worlds!`
" "
@ -339,7 +343,7 @@ fn test_string_interpolated() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<String>( engine.eval::<String>(
r" "
const x = 42; const x = 42;
`hello ${x} worlds!` `hello ${x} worlds!`
" "
@ -351,7 +355,7 @@ fn test_string_interpolated() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<String>( engine.eval::<String>(
r" "
const x = 42; const x = 42;
`${x} worlds!` `${x} worlds!`
" "
@ -361,7 +365,7 @@ fn test_string_interpolated() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<String>( engine.eval::<String>(
r" "
const x = 42; const x = 42;
`hello ${x}` `hello ${x}`
" "
@ -371,7 +375,7 @@ fn test_string_interpolated() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<String>( engine.eval::<String>(
r" "
const x = 20; const x = 20;
`hello ${let y = x + 1; `${y * 2}`} worlds!` `hello ${let y = x + 1; `${y * 2}`} worlds!`
" "

View File

@ -33,7 +33,7 @@ fn test_switch() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval_with_scope::<INT>( engine.eval_with_scope::<INT>(
&mut scope, &mut scope,
r" "
let y = [1, 2, 3]; let y = [1, 2, 3];
switch y { switch y {
@ -50,7 +50,7 @@ fn test_switch() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval_with_scope::<INT>( engine.eval_with_scope::<INT>(
&mut scope, &mut scope,
r" "
let y = #{a:1, b:true, c:'x'}; let y = #{a:1, b:true, c:'x'};
switch y { switch y {
@ -98,7 +98,7 @@ fn test_switch_condition() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval_with_scope::<INT>( engine.eval_with_scope::<INT>(
&mut scope, &mut scope,
r" "
switch x / 2 { switch x / 2 {
21 if x > 40 => 1, 21 if x > 40 => 1,
0 if x < 100 => 2, 0 if x < 100 => 2,
@ -113,7 +113,7 @@ fn test_switch_condition() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval_with_scope::<INT>( engine.eval_with_scope::<INT>(
&mut scope, &mut scope,
r" "
switch x / 2 { switch x / 2 {
21 if x < 40 => 1, 21 if x < 40 => 1,
0 if x < 100 => 2, 0 if x < 100 => 2,
@ -128,7 +128,7 @@ fn test_switch_condition() -> Result<(), Box<EvalAltResult>> {
assert!(matches!( assert!(matches!(
*engine *engine
.compile( .compile(
r" "
switch x { switch x {
21 if x < 40 => 1, 21 if x < 40 => 1,
21 if x == 10 => 10, 21 if x == 10 => 10,

View File

@ -57,7 +57,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let x = 0; let x = 0;
let foo = (exec |x| -> { x += 2 } while x < 42) * 10; let foo = (exec |x| -> { x += 2 } while x < 42) * 10;
foo foo
@ -67,7 +67,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
); );
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let x = 0; let x = 0;
exec |x| -> { x += 1 } while x < 42; exec |x| -> { x += 1 } while x < 42;
x x
@ -77,7 +77,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
); );
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
exec |x| -> { x += 1 } while x < 42; exec |x| -> { x += 1 } while x < 42;
x x
" "

View File

@ -40,7 +40,7 @@ fn test_timestamp() -> Result<(), Box<EvalAltResult>> {
); );
assert!(engine.eval::<bool>( assert!(engine.eval::<bool>(
r" "
let time1 = timestamp(); let time1 = timestamp();
for x in range(0, 10000) {} for x in range(0, 10000) {}
let time2 = timestamp(); let time2 = timestamp();

View File

@ -49,7 +49,7 @@ fn test_tokens_custom_operator_identifiers() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
fn foo(x, y) { y - x } fn foo(x, y) { y - x }
1 + 2 * 3 foo 4 - 5 / 6 1 + 2 * 3 foo 4 - 5 / 6
" "
@ -87,7 +87,7 @@ fn test_tokens_custom_operator_symbol() -> Result<(), Box<EvalAltResult>> {
fn test_tokens_unicode_xid_ident() -> Result<(), Box<EvalAltResult>> { fn test_tokens_unicode_xid_ident() -> Result<(), Box<EvalAltResult>> {
let engine = Engine::new(); let engine = Engine::new();
let result = engine.eval::<INT>( let result = engine.eval::<INT>(
r" "
fn () { 42 } fn () { 42 }
() ()
", ",
@ -99,7 +99,7 @@ fn test_tokens_unicode_xid_ident() -> Result<(), Box<EvalAltResult>> {
assert!(result.is_err()); assert!(result.is_err());
let result = engine.eval::<INT>( let result = engine.eval::<INT>(
r" "
fn _1() { 1 } fn _1() { 1 }
_1() _1()
", ",

View File

@ -6,7 +6,7 @@ fn test_while() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let x = 0; let x = 0;
while x < 10 { while x < 10 {
@ -31,7 +31,7 @@ fn test_do() -> Result<(), Box<EvalAltResult>> {
assert_eq!( assert_eq!(
engine.eval::<INT>( engine.eval::<INT>(
r" "
let x = 0; let x = 0;
do { do {