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