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