commit
ee6c11e63d
@ -10,12 +10,14 @@ Bug fixes
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
* Fixed bug when position is zero in `insert` and `split_at` methods for arrays.
|
* Fixed bug when position is zero in `insert` and `split_at` methods for arrays.
|
||||||
|
* Indexing operations with pure index values are no longer considered pure due to the possibility of indexers.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
* `Dynamic::is_shared` and `Dynamic::is_locked` are removed under the `no_closure` feature. They used to always return `false`.
|
* `Dynamic::is_shared` and `Dynamic::is_locked` are removed under the `no_closure` feature. They used to always return `false`.
|
||||||
* `Engine::call_fn` now evaluates the `AST` before calling the function.
|
* `Engine::call_fn` now evaluates the `AST` before calling the function.
|
||||||
|
* `Engine::on_progress` is disabled with `unchecked`.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
12
README.md
12
README.md
@ -3,13 +3,15 @@ Rhai - Embedded Scripting for Rust
|
|||||||
|
|
||||||
![GitHub last commit](https://img.shields.io/github/last-commit/rhaiscript/rhai?logo=github)
|
![GitHub last commit](https://img.shields.io/github/last-commit/rhaiscript/rhai?logo=github)
|
||||||
[![Build Status](https://github.com/rhaiscript/rhai/workflows/Build/badge.svg)](https://github.com/rhaiscript/rhai/actions)
|
[![Build Status](https://github.com/rhaiscript/rhai/workflows/Build/badge.svg)](https://github.com/rhaiscript/rhai/actions)
|
||||||
[![stars](https://img.shields.io/github/stars/rhaiscript/rhai?logo=github)](https://github.com/rhaiscript/rhai)
|
[![Stars](https://img.shields.io/github/stars/rhaiscript/rhai?logo=github)](https://github.com/rhaiscript/rhai)
|
||||||
[![license](https://img.shields.io/crates/l/rhai)](https://github.com/license/rhaiscript/rhai)
|
[![License](https://img.shields.io/crates/l/rhai)](https://github.com/license/rhaiscript/rhai)
|
||||||
[![crates.io](https://img.shields.io/crates/v/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
[![crates.io](https://img.shields.io/crates/v/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
||||||
[![crates.io](https://img.shields.io/crates/d/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
[![crates.io](https://img.shields.io/crates/d/rhai?logo=rust)](https://crates.io/crates/rhai/)
|
||||||
[![API Docs](https://docs.rs/rhai/badge.svg?logo=docs.rs)](https://docs.rs/rhai/)
|
[![API Docs](https://docs.rs/rhai/badge.svg?logo=docs-rs)](https://docs.rs/rhai/)
|
||||||
[![chat](https://img.shields.io/discord/767611025456889857.svg?logo=discord)](https://discord.gg/HquqbYFcZ9)
|
[![VS Code plugin installs](https://img.shields.io/visual-studio-marketplace/i/rhaiscript.vscode-rhai?logo=visual-studio-code&label=vs%20code)](https://marketplace.visualstudio.com/items?itemName=rhaiscript.vscode-rhai)
|
||||||
[![Reddit](https://img.shields.io/reddit/subreddit-subscribers/Rhai?logo=reddit)](https://www.reddit.com/r/Rhai)
|
[![Sublime Text package downloads](https://img.shields.io/packagecontrol/dt/Rhai.svg?logo=sublime-text&label=sublime%20text)](https://packagecontrol.io/packages/Rhai)
|
||||||
|
[![Discord Chat](https://img.shields.io/discord/767611025456889857.svg?logo=discord&label=discord)](https://discord.gg/HquqbYFcZ9)
|
||||||
|
[![Reddit Channel](https://img.shields.io/reddit/subreddit-subscribers/Rhai?logo=reddit&label=reddit)](https://www.reddit.com/r/Rhai)
|
||||||
|
|
||||||
[![Rhai logo](https://rhai.rs/book/images/logo/rhai-banner-transparent-colour.svg)](https://rhai.rs)
|
[![Rhai logo](https://rhai.rs/book/images/logo/rhai-banner-transparent-colour.svg)](https://rhai.rs)
|
||||||
|
|
||||||
|
@ -126,9 +126,9 @@ fn bench_eval_loop_number(bench: &mut Bencher) {
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn bench_eval_loop_strings_build(bench: &mut Bencher) {
|
fn bench_eval_loop_strings_build(bench: &mut Bencher) {
|
||||||
let script = r#"
|
let script = r#"
|
||||||
let s = "hello";
|
let s;
|
||||||
for x in range(0, 10000) {
|
for x in range(0, 10000) {
|
||||||
s += "x";
|
s = "hello, world!" + "hello, world!";
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -143,9 +143,9 @@ fn bench_eval_loop_strings_build(bench: &mut Bencher) {
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn bench_eval_loop_strings_no_build(bench: &mut Bencher) {
|
fn bench_eval_loop_strings_no_build(bench: &mut Bencher) {
|
||||||
let script = r#"
|
let script = r#"
|
||||||
let s = "hello";
|
let s;
|
||||||
for x in range(0, 10000) {
|
for x in range(0, 10000) {
|
||||||
s += "";
|
s = "hello" + "";
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
name = "rhai_codegen"
|
name = "rhai_codegen"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["jhwgh1968"]
|
authors = ["jhwgh1968", "Stephen Chung"]
|
||||||
description = "Procedural macro support package for Rhai, a scripting language for Rust"
|
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
|
||||||
homepage = "https://rhai.rs/book/plugins/index.html"
|
homepage = "https://rhai.rs/book/plugins/index.html"
|
||||||
repository = "https://github.com/rhaiscript/rhai"
|
repository = "https://github.com/rhaiscript/rhai"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
183
src/ast.rs
183
src/ast.rs
@ -780,12 +780,8 @@ 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 {
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
write!(f, "{:?} @ {:?}", self.name, self.pos)?;
|
|
||||||
#[cfg(feature = "no_position")]
|
|
||||||
write!(f, "{:?}", self.name)?;
|
write!(f, "{:?}", self.name)?;
|
||||||
|
self.pos.debug_print(f)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -879,10 +875,7 @@ impl fmt::Debug for StmtBlock {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
fmt::Debug::fmt(&self.0, f)?;
|
fmt::Debug::fmt(&self.0, f)?;
|
||||||
if !self.1.is_none() {
|
self.1.debug_print(f)
|
||||||
write!(f, " @ {:?}", self.1)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1354,13 +1347,22 @@ pub struct OpAssignment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OpAssignment {
|
impl OpAssignment {
|
||||||
pub fn new(op: &'static str) -> Self {
|
/// Create a new [`OpAssignment`].
|
||||||
let op2 = &op[..op.len() - 1]; // extract operator without =
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the operator name is not an op-assignment operator.
|
||||||
|
pub fn new(op: Token) -> Self {
|
||||||
|
let op_raw = op
|
||||||
|
.map_op_assignment()
|
||||||
|
.expect("token must be an op-assignment operator")
|
||||||
|
.keyword_syntax();
|
||||||
|
let op_assignment = op.keyword_syntax();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
hash_op_assign: calc_fn_hash(empty(), op, 2),
|
hash_op_assign: calc_fn_hash(empty(), op_assignment, 2),
|
||||||
hash_op: calc_fn_hash(empty(), op2, 2),
|
hash_op: calc_fn_hash(empty(), op_raw, 2),
|
||||||
op,
|
op: op_assignment,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1691,60 +1693,33 @@ 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 {
|
let mut display_pos = self.position();
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
Self::DynamicConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
Self::BoolConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
Self::IntegerConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
Self::FloatConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
Self::CharConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
Self::StringConstant(value, pos) => write!(f, "{:?} @ {:?}", value, pos),
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
Self::Unit(pos) => write!(f, "() @ {:?}", pos),
|
|
||||||
|
|
||||||
#[cfg(feature = "no_position")]
|
match self {
|
||||||
Self::DynamicConstant(value, _) => write!(f, "{:?}", value),
|
Self::DynamicConstant(value, _) => write!(f, "{:?}", value),
|
||||||
#[cfg(feature = "no_position")]
|
|
||||||
Self::BoolConstant(value, _) => write!(f, "{:?}", value),
|
Self::BoolConstant(value, _) => write!(f, "{:?}", value),
|
||||||
#[cfg(feature = "no_position")]
|
|
||||||
Self::IntegerConstant(value, _) => write!(f, "{:?}", value),
|
Self::IntegerConstant(value, _) => write!(f, "{:?}", value),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[cfg(feature = "no_position")]
|
|
||||||
Self::FloatConstant(value, _) => write!(f, "{:?}", value),
|
Self::FloatConstant(value, _) => write!(f, "{:?}", value),
|
||||||
#[cfg(feature = "no_position")]
|
|
||||||
Self::CharConstant(value, _) => write!(f, "{:?}", value),
|
Self::CharConstant(value, _) => write!(f, "{:?}", value),
|
||||||
#[cfg(feature = "no_position")]
|
|
||||||
Self::StringConstant(value, _) => write!(f, "{:?}", value),
|
Self::StringConstant(value, _) => write!(f, "{:?}", value),
|
||||||
#[cfg(feature = "no_position")]
|
|
||||||
Self::Unit(_) => f.write_str("()"),
|
Self::Unit(_) => f.write_str("()"),
|
||||||
|
|
||||||
Self::InterpolatedString(x) => {
|
Self::InterpolatedString(x) => {
|
||||||
f.write_str("InterpolatedString")?;
|
f.write_str("InterpolatedString")?;
|
||||||
|
return f.debug_list().entries(x.iter()).finish();
|
||||||
|
}
|
||||||
|
Self::Array(x, _) => {
|
||||||
|
f.write_str("Array")?;
|
||||||
f.debug_list().entries(x.iter()).finish()
|
f.debug_list().entries(x.iter()).finish()
|
||||||
}
|
}
|
||||||
Self::Array(x, _pos) => {
|
Self::Map(x, _) => {
|
||||||
f.write_str("Array")?;
|
|
||||||
f.debug_list().entries(x.iter()).finish()?;
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
write!(f, " @ {:?}", _pos)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
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()
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
write!(f, " @ {:?}", _pos)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Self::Variable(i, _pos, x) => {
|
Self::Variable(i, _, 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)?,
|
||||||
@ -1755,23 +1730,14 @@ impl fmt::Debug for Expr {
|
|||||||
Some(n) => write!(f, ", {}", n)?,
|
Some(n) => write!(f, ", {}", n)?,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
f.write_str(")")?;
|
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),
|
||||||
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()
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
write!(f, " @ {:?}", x.1)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Self::FnCall(x, _pos) => {
|
Self::FnCall(x, _) => {
|
||||||
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);
|
||||||
@ -1785,12 +1751,9 @@ impl fmt::Debug for Expr {
|
|||||||
if x.capture {
|
if x.capture {
|
||||||
ff.field("capture", &x.capture);
|
ff.field("capture", &x.capture);
|
||||||
}
|
}
|
||||||
ff.finish()?;
|
ff.finish()
|
||||||
#[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",
|
||||||
@ -1799,21 +1762,17 @@ impl fmt::Debug for Expr {
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
display_pos = *pos;
|
||||||
|
|
||||||
f.debug_struct(op_name)
|
f.debug_struct(op_name)
|
||||||
.field("lhs", &x.lhs)
|
.field("lhs", &x.lhs)
|
||||||
.field("rhs", &x.rhs)
|
.field("rhs", &x.rhs)
|
||||||
.finish()?;
|
.finish()
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
write!(f, " @ {:?}", _pos)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
Self::Custom(x, _pos) => {
|
Self::Custom(x, _) => f.debug_tuple("Custom").field(x).finish(),
|
||||||
f.debug_tuple("Custom").field(x).finish()?;
|
}?;
|
||||||
#[cfg(not(feature = "no_position"))]
|
|
||||||
write!(f, " @ {:?}", _pos)?;
|
display_pos.debug_print(f)
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1875,26 +1834,26 @@ impl Expr {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Self::FloatConstant(_, pos) => *pos,
|
Self::FloatConstant(_, pos) => *pos,
|
||||||
|
|
||||||
Self::DynamicConstant(_, pos) => *pos,
|
Self::DynamicConstant(_, pos)
|
||||||
Self::BoolConstant(_, pos) => *pos,
|
| Self::BoolConstant(_, pos)
|
||||||
Self::IntegerConstant(_, pos) => *pos,
|
| Self::IntegerConstant(_, pos)
|
||||||
Self::CharConstant(_, pos) => *pos,
|
| Self::CharConstant(_, pos)
|
||||||
Self::StringConstant(_, pos) => *pos,
|
| Self::Unit(pos)
|
||||||
|
| Self::StringConstant(_, pos)
|
||||||
|
| Self::Array(_, pos)
|
||||||
|
| Self::Map(_, pos)
|
||||||
|
| Self::Variable(_, pos, _)
|
||||||
|
| Self::FnCall(_, pos)
|
||||||
|
| Self::Custom(_, pos) => *pos,
|
||||||
|
|
||||||
Self::InterpolatedString(x) => x.first().unwrap().position(),
|
Self::InterpolatedString(x) => x.first().unwrap().position(),
|
||||||
Self::Array(_, pos) => *pos,
|
|
||||||
Self::Map(_, pos) => *pos,
|
|
||||||
Self::Property(x) => (x.2).pos,
|
Self::Property(x) => (x.2).pos,
|
||||||
Self::Stmt(x) => x.1,
|
Self::Stmt(x) => x.1,
|
||||||
Self::Variable(_, pos, _) => *pos,
|
|
||||||
Self::FnCall(_, pos) => *pos,
|
|
||||||
|
|
||||||
Self::And(x, _) | Self::Or(x, _) => x.lhs.position(),
|
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => {
|
||||||
|
x.lhs.position()
|
||||||
Self::Unit(pos) => *pos,
|
}
|
||||||
|
|
||||||
Self::Dot(x, _) | Self::Index(x, _) => x.lhs.position(),
|
|
||||||
|
|
||||||
Self::Custom(_, pos) => *pos,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Override the [position][Position] of the expression.
|
/// Override the [position][Position] of the expression.
|
||||||
@ -1904,24 +1863,28 @@ impl Expr {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Self::FloatConstant(_, pos) => *pos = new_pos,
|
Self::FloatConstant(_, pos) => *pos = new_pos,
|
||||||
|
|
||||||
Self::DynamicConstant(_, pos) => *pos = new_pos,
|
Self::DynamicConstant(_, pos)
|
||||||
Self::BoolConstant(_, pos) => *pos = new_pos,
|
| Self::BoolConstant(_, pos)
|
||||||
Self::IntegerConstant(_, pos) => *pos = new_pos,
|
| Self::IntegerConstant(_, pos)
|
||||||
Self::CharConstant(_, pos) => *pos = new_pos,
|
| Self::CharConstant(_, pos)
|
||||||
Self::StringConstant(_, pos) => *pos = new_pos,
|
| Self::Unit(pos)
|
||||||
|
| Self::StringConstant(_, pos)
|
||||||
|
| Self::Array(_, pos)
|
||||||
|
| Self::Map(_, pos)
|
||||||
|
| Self::And(_, pos)
|
||||||
|
| Self::Or(_, pos)
|
||||||
|
| Self::Dot(_, pos)
|
||||||
|
| Self::Index(_, pos)
|
||||||
|
| Self::Variable(_, pos, _)
|
||||||
|
| Self::FnCall(_, pos)
|
||||||
|
| Self::Custom(_, pos) => *pos = new_pos,
|
||||||
|
|
||||||
Self::InterpolatedString(x) => {
|
Self::InterpolatedString(x) => {
|
||||||
x.first_mut().unwrap().set_position(new_pos);
|
x.first_mut().unwrap().set_position(new_pos);
|
||||||
}
|
}
|
||||||
Self::Array(_, pos) => *pos = new_pos,
|
|
||||||
Self::Map(_, pos) => *pos = new_pos,
|
|
||||||
Self::Variable(_, pos, _) => *pos = new_pos,
|
|
||||||
Self::Property(x) => (x.2).pos = new_pos,
|
Self::Property(x) => (x.2).pos = new_pos,
|
||||||
Self::Stmt(x) => x.1 = new_pos,
|
Self::Stmt(x) => x.1 = new_pos,
|
||||||
Self::FnCall(_, pos) => *pos = new_pos,
|
|
||||||
Self::And(_, pos) | Self::Or(_, pos) => *pos = new_pos,
|
|
||||||
Self::Unit(pos) => *pos = new_pos,
|
|
||||||
Self::Dot(_, pos) | Self::Index(_, pos) => *pos = new_pos,
|
|
||||||
Self::Custom(_, pos) => *pos = new_pos,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -1936,9 +1899,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure),
|
Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure),
|
||||||
|
|
||||||
Self::Index(x, _) | Self::And(x, _) | Self::Or(x, _) => {
|
Self::And(x, _) | Self::Or(x, _) => x.lhs.is_pure() && x.rhs.is_pure(),
|
||||||
x.lhs.is_pure() && x.rhs.is_pure()
|
|
||||||
}
|
|
||||||
|
|
||||||
Self::Stmt(x) => x.0.iter().all(Stmt::is_pure),
|
Self::Stmt(x) => x.0.iter().all(Stmt::is_pure),
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::fn_native::SendSync;
|
use crate::fn_native::SendSync;
|
||||||
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||||
use crate::{FnPtr, ImmutableString, INT};
|
use crate::{FnPtr, ImmutableString, SmartString, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -900,6 +900,12 @@ impl Dynamic {
|
|||||||
.deref()
|
.deref()
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<SmartString>() {
|
||||||
|
return <dyn Any>::downcast_ref::<SmartString>(&value)
|
||||||
|
.unwrap()
|
||||||
|
.clone()
|
||||||
|
.into();
|
||||||
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return ().into();
|
return ().into();
|
||||||
}
|
}
|
||||||
@ -1173,13 +1179,13 @@ impl Dynamic {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clone_cast<T: Any + Clone>(&self) -> T {
|
pub fn clone_cast<T: Any + Clone>(&self) -> T {
|
||||||
self.read_lock::<T>().unwrap().clone()
|
self.flatten_clone().cast::<T>()
|
||||||
}
|
}
|
||||||
/// Flatten the [`Dynamic`] and clone it.
|
/// Flatten the [`Dynamic`] and clone it.
|
||||||
///
|
///
|
||||||
/// If the [`Dynamic`] is not a shared value, it returns a cloned copy.
|
/// If the [`Dynamic`] is not a shared value, it returns a cloned copy.
|
||||||
///
|
///
|
||||||
/// If the [`Dynamic`] is a shared value, it a cloned copy of the shared value.
|
/// If the [`Dynamic`] is a shared value, it returns a cloned copy of the shared value.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn flatten_clone(&self) -> Self {
|
pub fn flatten_clone(&self) -> Self {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
@ -1361,7 +1367,7 @@ impl Dynamic {
|
|||||||
///
|
///
|
||||||
/// Returns [`None`] if the cast fails, or if the value is shared.
|
/// Returns [`None`] if the cast fails, or if the value is shared.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn downcast_ref<T: Any + Clone>(&self) -> Option<&T> {
|
pub(crate) fn downcast_ref<T: Any + Clone + ?Sized>(&self) -> Option<&T> {
|
||||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
@ -1396,12 +1402,6 @@ impl Dynamic {
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
|
||||||
return match &self.0 {
|
|
||||||
Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref() as &String),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Char(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Char(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
@ -1720,7 +1720,7 @@ impl From<&ImmutableString> for Dynamic {
|
|||||||
value.clone().into()
|
value.clone().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
#[cfg(not(feature = "no_smartstring_for_identifier"))]
|
||||||
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 {
|
||||||
|
208
src/engine.rs
208
src/engine.rs
@ -3,14 +3,14 @@
|
|||||||
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt};
|
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt};
|
||||||
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||||
use crate::fn_native::{
|
use crate::fn_native::{
|
||||||
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnVarCallback,
|
||||||
OnVarCallback,
|
|
||||||
};
|
};
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{Package, StandardPackage};
|
use crate::packages::{Package, StandardPackage};
|
||||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||||
use crate::syntax::CustomSyntax;
|
use crate::syntax::CustomSyntax;
|
||||||
|
use crate::token::Token;
|
||||||
use crate::utils::get_hasher;
|
use crate::utils::get_hasher;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
|
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
|
||||||
@ -51,35 +51,42 @@ pub type Precedence = NonZeroU8;
|
|||||||
// We cannot use Cow<str> here because `eval` may load a [module][Module] and
|
// We cannot use Cow<str> here because `eval` may load a [module][Module] and
|
||||||
// the module name will live beyond the AST of the eval script text.
|
// the module name will live beyond the AST of the eval script text.
|
||||||
// The best we can do is a shared reference.
|
// The best we can do is a shared reference.
|
||||||
|
//
|
||||||
|
// This implementation splits the module names from the shared modules to improve data locality.
|
||||||
|
// Most usage will be looking up a particular key from the list and then getting the module that
|
||||||
|
// corresponds to that key.
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct Imports(StaticVec<Identifier>, StaticVec<Shared<Module>>);
|
pub struct Imports {
|
||||||
|
keys: StaticVec<Identifier>,
|
||||||
|
modules: StaticVec<Shared<Module>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Imports {
|
impl Imports {
|
||||||
/// Get the length of this stack of imported [modules][Module].
|
/// Get the length of this stack of imported [modules][Module].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.keys.len()
|
||||||
}
|
}
|
||||||
/// Is this stack of imported [modules][Module] empty?
|
/// Is this stack of imported [modules][Module] empty?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.keys.is_empty()
|
||||||
}
|
}
|
||||||
/// Get the imported [modules][Module] at a particular index.
|
/// Get the imported [modules][Module] at a particular index.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
|
pub fn get(&self, index: usize) -> Option<Shared<Module>> {
|
||||||
self.1.get(index).cloned()
|
self.modules.get(index).cloned()
|
||||||
}
|
}
|
||||||
/// Get the imported [modules][Module] at a particular index.
|
/// Get the imported [modules][Module] at a particular index.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared<Module>> {
|
pub(crate) fn get_mut(&mut self, index: usize) -> Option<&mut Shared<Module>> {
|
||||||
self.1.get_mut(index)
|
self.modules.get_mut(index)
|
||||||
}
|
}
|
||||||
/// Get the index of an imported [modules][Module] by name.
|
/// Get the index of an imported [modules][Module] by name.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn find(&self, name: &str) -> Option<usize> {
|
pub fn find(&self, name: &str) -> Option<usize> {
|
||||||
self.0
|
self.keys
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev()
|
.rev()
|
||||||
@ -88,22 +95,22 @@ impl Imports {
|
|||||||
/// Push an imported [modules][Module] onto the stack.
|
/// Push an imported [modules][Module] onto the stack.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push(&mut self, name: impl Into<Identifier>, module: impl Into<Shared<Module>>) {
|
pub fn push(&mut self, name: impl Into<Identifier>, module: impl Into<Shared<Module>>) {
|
||||||
self.0.push(name.into());
|
self.keys.push(name.into());
|
||||||
self.1.push(module.into());
|
self.modules.push(module.into());
|
||||||
}
|
}
|
||||||
/// Truncate the stack of imported [modules][Module] to a particular length.
|
/// Truncate the stack of imported [modules][Module] to a particular length.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn truncate(&mut self, size: usize) {
|
pub fn truncate(&mut self, size: usize) {
|
||||||
self.0.truncate(size);
|
self.keys.truncate(size);
|
||||||
self.1.truncate(size);
|
self.modules.truncate(size);
|
||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
/// Get an iterator to this stack of imported [modules][Module] in reverse order.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&str, &Module)> {
|
||||||
self.0
|
self.keys
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.1.iter())
|
.zip(self.modules.iter())
|
||||||
.rev()
|
.rev()
|
||||||
.map(|(name, module)| (name.as_str(), module.as_ref()))
|
.map(|(name, module)| (name.as_str(), module.as_ref()))
|
||||||
}
|
}
|
||||||
@ -111,29 +118,32 @@ impl Imports {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
pub(crate) fn iter_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
||||||
self.0.iter().rev().zip(self.1.iter().rev())
|
self.keys.iter().rev().zip(self.modules.iter().rev())
|
||||||
}
|
}
|
||||||
/// Get an iterator to this stack of imported [modules][Module] in forward order.
|
/// Get an iterator to this stack of imported [modules][Module] in forward order.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
pub(crate) fn scan_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
||||||
self.0.iter().zip(self.1.iter())
|
self.keys.iter().zip(self.modules.iter())
|
||||||
}
|
}
|
||||||
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
|
/// Get a consuming iterator to this stack of imported [modules][Module] in reverse order.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
|
pub fn into_iter(self) -> impl Iterator<Item = (Identifier, Shared<Module>)> {
|
||||||
self.0.into_iter().rev().zip(self.1.into_iter().rev())
|
self.keys
|
||||||
|
.into_iter()
|
||||||
|
.rev()
|
||||||
|
.zip(self.modules.into_iter().rev())
|
||||||
}
|
}
|
||||||
/// Does the specified function hash key exist in this stack of imported [modules][Module]?
|
/// Does the specified function hash key exist in this stack of imported [modules][Module]?
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn contains_fn(&self, hash: u64) -> bool {
|
pub fn contains_fn(&self, hash: u64) -> bool {
|
||||||
self.1.iter().any(|m| m.contains_qualified_fn(hash))
|
self.modules.iter().any(|m| m.contains_qualified_fn(hash))
|
||||||
}
|
}
|
||||||
/// Get specified function via its hash key.
|
/// Get specified function via its hash key.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> {
|
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> {
|
||||||
self.1
|
self.modules
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||||
@ -143,12 +153,15 @@ impl Imports {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn contains_iter(&self, id: TypeId) -> bool {
|
pub fn contains_iter(&self, id: TypeId) -> bool {
|
||||||
self.1.iter().any(|m| m.contains_qualified_iter(id))
|
self.modules.iter().any(|m| m.contains_qualified_iter(id))
|
||||||
}
|
}
|
||||||
/// Get the specified [`TypeId`][std::any::TypeId] iterator.
|
/// Get the specified [`TypeId`][std::any::TypeId] iterator.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
pub fn get_iter(&self, id: TypeId) -> Option<IteratorFn> {
|
||||||
self.1.iter().rev().find_map(|m| m.get_qualified_iter(id))
|
self.modules
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.find_map(|m| m.get_qualified_iter(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +173,7 @@ impl fmt::Debug for Imports {
|
|||||||
f.debug_map().finish()
|
f.debug_map().finish()
|
||||||
} else {
|
} else {
|
||||||
f.debug_map()
|
f.debug_map()
|
||||||
.entries(self.0.iter().zip(self.1.iter()))
|
.entries(self.keys.iter().zip(self.modules.iter()))
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,35 +234,38 @@ pub const FN_ANONYMOUS: &str = "anon$";
|
|||||||
/// Standard equality comparison operator.
|
/// Standard equality comparison operator.
|
||||||
pub const OP_EQUALS: &str = "==";
|
pub const OP_EQUALS: &str = "==";
|
||||||
|
|
||||||
/// Standard concatenation operator.
|
|
||||||
pub const OP_CONCAT: &str = "+=";
|
|
||||||
|
|
||||||
/// Standard method function for containment testing.
|
/// Standard method function for containment testing.
|
||||||
///
|
///
|
||||||
/// The `in` operator is implemented as a call to this method.
|
/// The `in` operator is implemented as a call to this method.
|
||||||
pub const OP_CONTAINS: &str = "contains";
|
pub const OP_CONTAINS: &str = "contains";
|
||||||
|
|
||||||
|
/// Standard concatenation operator token.
|
||||||
|
pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign;
|
||||||
|
|
||||||
/// Method of chaining.
|
/// Method of chaining.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
pub enum ChainType {
|
enum ChainType {
|
||||||
/// Not a chaining type.
|
|
||||||
NonChaining,
|
|
||||||
/// Indexing.
|
/// Indexing.
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
Index,
|
Index,
|
||||||
/// Dotting.
|
/// Dotting.
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dot,
|
Dot,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Value of a chaining argument.
|
/// Value of a chaining argument.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub enum ChainArgument {
|
enum ChainArgument {
|
||||||
/// Dot-property access.
|
/// Dot-property access.
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
Property(Position),
|
Property(Position),
|
||||||
/// Arguments to a dot-function call.
|
/// Arguments to a dot method call.
|
||||||
FnCallArgs(StaticVec<Dynamic>, StaticVec<Position>),
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
MethodCallArgs(StaticVec<Dynamic>, StaticVec<Position>),
|
||||||
/// Index value.
|
/// Index value.
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
IndexValue(Dynamic, Position),
|
IndexValue(Dynamic, Position),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +280,8 @@ impl ChainArgument {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub fn as_index_value(self) -> Dynamic {
|
pub fn as_index_value(self) -> Dynamic {
|
||||||
match self {
|
match self {
|
||||||
Self::Property(_) | Self::FnCallArgs(_, _) => {
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Self::Property(_) | Self::MethodCallArgs(_, _) => {
|
||||||
panic!("expecting ChainArgument::IndexValue")
|
panic!("expecting ChainArgument::IndexValue")
|
||||||
}
|
}
|
||||||
Self::IndexValue(value, _) => value,
|
Self::IndexValue(value, _) => value,
|
||||||
@ -274,28 +291,32 @@ impl ChainArgument {
|
|||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// Panics if not `ChainArgument::FnCallArgs`.
|
/// Panics if not `ChainArgument::MethodCallArgs`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub fn as_fn_call_args(self) -> (StaticVec<Dynamic>, StaticVec<Position>) {
|
pub fn as_fn_call_args(self) -> (StaticVec<Dynamic>, StaticVec<Position>) {
|
||||||
match self {
|
match self {
|
||||||
Self::Property(_) | Self::IndexValue(_, _) => {
|
Self::Property(_) => {
|
||||||
panic!("expecting ChainArgument::FnCallArgs")
|
panic!("expecting ChainArgument::MethodCallArgs")
|
||||||
}
|
}
|
||||||
Self::FnCallArgs(values, positions) => (values, positions),
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Self::IndexValue(_, _) => {
|
||||||
|
panic!("expecting ChainArgument::MethodCallArgs")
|
||||||
|
}
|
||||||
|
Self::MethodCallArgs(values, positions) => (values, positions),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
impl From<(StaticVec<Dynamic>, StaticVec<Position>)> for ChainArgument {
|
impl From<(StaticVec<Dynamic>, StaticVec<Position>)> for ChainArgument {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from((values, positions): (StaticVec<Dynamic>, StaticVec<Position>)) -> Self {
|
fn from((values, positions): (StaticVec<Dynamic>, StaticVec<Position>)) -> Self {
|
||||||
Self::FnCallArgs(values, positions)
|
Self::MethodCallArgs(values, positions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
impl From<(Dynamic, Position)> for ChainArgument {
|
impl From<(Dynamic, Position)> for ChainArgument {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from((value, pos): (Dynamic, Position)) -> Self {
|
fn from((value, pos): (Dynamic, Position)) -> Self {
|
||||||
@ -774,7 +795,8 @@ pub struct Engine {
|
|||||||
/// Callback closure for implementing the `debug` command.
|
/// Callback closure for implementing the `debug` command.
|
||||||
pub(crate) debug: OnDebugCallback,
|
pub(crate) debug: OnDebugCallback,
|
||||||
/// Callback closure for progress reporting.
|
/// Callback closure for progress reporting.
|
||||||
pub(crate) progress: Option<OnProgressCallback>,
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
pub(crate) progress: Option<crate::fn_native::OnProgressCallback>,
|
||||||
|
|
||||||
/// Optimize the AST after compilation.
|
/// Optimize the AST after compilation.
|
||||||
pub(crate) optimization_level: OptimizationLevel,
|
pub(crate) optimization_level: OptimizationLevel,
|
||||||
@ -838,7 +860,7 @@ fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[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() {
|
} else if _pos.is_none() {
|
||||||
println!("{}", _s);
|
println!("{}", _s);
|
||||||
} else {
|
} else {
|
||||||
@ -878,6 +900,7 @@ impl Engine {
|
|||||||
debug: Box::new(default_debug),
|
debug: Box::new(default_debug),
|
||||||
|
|
||||||
// progress callback
|
// progress callback
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
progress: None,
|
progress: None,
|
||||||
|
|
||||||
// optimization level
|
// optimization level
|
||||||
@ -938,6 +961,8 @@ impl Engine {
|
|||||||
|
|
||||||
print: Box::new(|_| {}),
|
print: Box::new(|_| {}),
|
||||||
debug: Box::new(|_, _, _| {}),
|
debug: Box::new(|_, _, _| {}),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
progress: None,
|
progress: None,
|
||||||
|
|
||||||
optimization_level: if cfg!(feature = "no_optimize") {
|
optimization_level: if cfg!(feature = "no_optimize") {
|
||||||
@ -1125,14 +1150,14 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
assert!(chain_type != ChainType::NonChaining);
|
|
||||||
|
|
||||||
let is_ref = target.is_ref();
|
let is_ref = target.is_ref();
|
||||||
|
|
||||||
let next_chain = match rhs {
|
let rhs_chain = match rhs {
|
||||||
Expr::Index(_, _) => ChainType::Index,
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Dot(_, _) => ChainType::Dot,
|
Expr::Index(_, _) => Some(ChainType::Index),
|
||||||
_ => ChainType::NonChaining,
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::Dot(_, _) => Some(ChainType::Dot),
|
||||||
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pop the last index value
|
// Pop the last index value
|
||||||
@ -1151,9 +1176,10 @@ impl Engine {
|
|||||||
let obj_ptr = &mut self.get_indexed_mut(
|
let obj_ptr = &mut self.get_indexed_mut(
|
||||||
mods, state, lib, target, idx_val, idx_pos, false, is_ref, true, level,
|
mods, state, lib, target, idx_val, idx_pos, false, is_ref, true, level,
|
||||||
)?;
|
)?;
|
||||||
|
let rhs_chain = rhs_chain.unwrap();
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, next_chain,
|
mods, state, lib, this_ptr, obj_ptr, &x.rhs, idx_values, rhs_chain,
|
||||||
level, new_val,
|
level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
@ -1336,9 +1362,10 @@ impl Engine {
|
|||||||
// Others - syntax error
|
// Others - syntax error
|
||||||
expr => unreachable!("invalid dot expression: {:?}", expr),
|
expr => unreachable!("invalid dot expression: {:?}", expr),
|
||||||
};
|
};
|
||||||
|
let rhs_chain = rhs_chain.unwrap();
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, &mut val, &x.rhs, idx_values, next_chain,
|
mods, state, lib, this_ptr, &mut val, &x.rhs, idx_values, rhs_chain,
|
||||||
level, new_val,
|
level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
@ -1350,6 +1377,7 @@ 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 rhs_chain = rhs_chain.unwrap();
|
||||||
let hash_get = FnCallHashes::from_native(*hash_get);
|
let hash_get = FnCallHashes::from_native(*hash_get);
|
||||||
let hash_set = FnCallHashes::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()];
|
||||||
@ -1370,7 +1398,7 @@ impl Engine {
|
|||||||
&mut val.into(),
|
&mut val.into(),
|
||||||
&x.rhs,
|
&x.rhs,
|
||||||
idx_values,
|
idx_values,
|
||||||
next_chain,
|
rhs_chain,
|
||||||
level,
|
level,
|
||||||
new_val,
|
new_val,
|
||||||
)
|
)
|
||||||
@ -1401,6 +1429,7 @@ impl Engine {
|
|||||||
// 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.is_qualified() => {
|
Expr::FnCall(f, pos) if !f.is_qualified() => {
|
||||||
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
||||||
|
let rhs_chain = rhs_chain.unwrap();
|
||||||
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, *hashes, target, &mut args, *pos, level,
|
mods, state, lib, name, *hashes, target, &mut args, *pos, level,
|
||||||
@ -1410,7 +1439,7 @@ impl Engine {
|
|||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
mods, state, lib, this_ptr, target, &x.rhs, idx_values,
|
mods, state, lib, this_ptr, target, &x.rhs, idx_values,
|
||||||
next_chain, level, new_val,
|
rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
}
|
}
|
||||||
@ -1426,8 +1455,6 @@ impl Engine {
|
|||||||
_ => EvalAltResult::ErrorDotExpr("".into(), rhs.position()).into(),
|
_ => EvalAltResult::ErrorDotExpr("".into(), rhs.position()).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
chain_type => unreachable!("invalid ChainType: {:?}", chain_type),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1445,7 +1472,9 @@ impl Engine {
|
|||||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos),
|
Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos),
|
||||||
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
||||||
};
|
};
|
||||||
@ -1458,8 +1487,9 @@ impl Engine {
|
|||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(_, var_pos, x) => {
|
Expr::Variable(_, _var_pos, x) => {
|
||||||
self.inc_operations(state, *var_pos)?;
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
self.inc_operations(state, *_var_pos)?;
|
||||||
|
|
||||||
let (target, pos) =
|
let (target, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
||||||
@ -1505,15 +1535,17 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
parent_chain_type: ChainType,
|
_parent_chain_type: ChainType,
|
||||||
idx_values: &mut StaticVec<ChainArgument>,
|
idx_values: &mut StaticVec<ChainArgument>,
|
||||||
size: usize,
|
size: usize,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
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.is_qualified() => {
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
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
|
||||||
@ -1533,11 +1565,13 @@ impl Engine {
|
|||||||
|
|
||||||
idx_values.push((arg_values, arg_positions).into());
|
idx_values.push((arg_values, arg_positions).into());
|
||||||
}
|
}
|
||||||
Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => {
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Property(x) if parent_chain_type == ChainType::Dot => {
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
||||||
idx_values.push(ChainArgument::Property(x.2.pos))
|
idx_values.push(ChainArgument::Property(x.2.pos))
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
@ -1547,12 +1581,15 @@ impl Engine {
|
|||||||
|
|
||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
let lhs_val = match lhs {
|
let lhs_val = match lhs {
|
||||||
Expr::Property(x) if parent_chain_type == ChainType::Dot => {
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
||||||
ChainArgument::Property(x.2.pos)
|
ChainArgument::Property(x.2.pos)
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::FnCall(x, _)
|
Expr::FnCall(x, _)
|
||||||
if parent_chain_type == ChainType::Dot && !x.is_qualified() =>
|
if _parent_chain_type == ChainType::Dot && !x.is_qualified() =>
|
||||||
{
|
{
|
||||||
let mut arg_positions: StaticVec<_> = Default::default();
|
let mut arg_positions: StaticVec<_> = Default::default();
|
||||||
|
|
||||||
@ -1573,17 +1610,26 @@ impl Engine {
|
|||||||
|
|
||||||
(arg_values, arg_positions).into()
|
(arg_values, arg_positions).into()
|
||||||
}
|
}
|
||||||
Expr::FnCall(_, _) if parent_chain_type == ChainType::Dot => {
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||||
}
|
}
|
||||||
_ => self
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
expr if _parent_chain_type == ChainType::Dot => {
|
||||||
|
unreachable!("invalid dot expression: {:?}", expr);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
_ if _parent_chain_type == ChainType::Index => self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)
|
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)
|
||||||
.map(|v| (v.flatten(), lhs.position()).into())?,
|
.map(|v| (v.flatten(), lhs.position()).into())?,
|
||||||
|
expr => unreachable!("unknown chained expression: {:?}", expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Push in reverse order
|
// Push in reverse order
|
||||||
let chain_type = match expr {
|
let chain_type = match expr {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(_, _) => ChainType::Index,
|
Expr::Index(_, _) => ChainType::Index,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Dot(_, _) => ChainType::Dot,
|
Expr::Dot(_, _) => ChainType::Dot,
|
||||||
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
||||||
};
|
};
|
||||||
@ -1594,10 +1640,16 @@ impl Engine {
|
|||||||
idx_values.push(lhs_val);
|
idx_values.push(lhs_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => idx_values.push(
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
_ if _parent_chain_type == ChainType::Dot => {
|
||||||
|
unreachable!("invalid dot expression: {:?}", expr);
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
_ if _parent_chain_type == ChainType::Index => idx_values.push(
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||||
.map(|v| (v.flatten(), expr.position()).into())?,
|
.map(|v| (v.flatten(), expr.position()).into())?,
|
||||||
),
|
),
|
||||||
|
_ => unreachable!("unknown chained expression: {:?}", expr),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1619,6 +1671,7 @@ impl Engine {
|
|||||||
_indexers: bool,
|
_indexers: bool,
|
||||||
_level: usize,
|
_level: usize,
|
||||||
) -> Result<Target<'t>, Box<EvalAltResult>> {
|
) -> Result<Target<'t>, Box<EvalAltResult>> {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, Position::NONE)?;
|
self.inc_operations(state, Position::NONE)?;
|
||||||
|
|
||||||
match target {
|
match target {
|
||||||
@ -1631,6 +1684,7 @@ impl Engine {
|
|||||||
|
|
||||||
let arr_len = arr.len();
|
let arr_len = arr.len();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let arr_idx = if index < 0 {
|
let arr_idx = if index < 0 {
|
||||||
// Count from end if negative
|
// Count from end if negative
|
||||||
arr_len
|
arr_len
|
||||||
@ -1650,6 +1704,13 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
index as usize
|
index as usize
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "unchecked")]
|
||||||
|
let arr_idx = if index < 0 {
|
||||||
|
// Count from end if negative
|
||||||
|
arr_len - index.abs() as usize
|
||||||
|
} else {
|
||||||
|
index as usize
|
||||||
|
};
|
||||||
|
|
||||||
arr.get_mut(arr_idx)
|
arr.get_mut(arr_idx)
|
||||||
.map(Target::from)
|
.map(Target::from)
|
||||||
@ -1670,21 +1731,22 @@ impl Engine {
|
|||||||
Ok(map
|
Ok(map
|
||||||
.get_mut(index.as_str())
|
.get_mut(index.as_str())
|
||||||
.map(Target::from)
|
.map(Target::from)
|
||||||
.unwrap_or_else(|| Target::from(())))
|
.unwrap_or_else(|| Target::from(Dynamic::UNIT)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Str(s, _)) => {
|
Dynamic(Union::Str(s, _)) => {
|
||||||
// val_string[idx]
|
// val_string[idx]
|
||||||
let chars_len = s.chars().count();
|
|
||||||
let index = _idx
|
let index = _idx
|
||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
|
.map_err(|err| self.make_type_mismatch_err::<crate::INT>(err, idx_pos))?;
|
||||||
|
|
||||||
let (ch, offset) = if index >= 0 {
|
let (ch, offset) = if index >= 0 {
|
||||||
|
// Count from end if negative
|
||||||
let offset = index as usize;
|
let offset = index as usize;
|
||||||
(
|
(
|
||||||
s.chars().nth(offset).ok_or_else(|| {
|
s.chars().nth(offset).ok_or_else(|| {
|
||||||
|
let chars_len = s.chars().count();
|
||||||
EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos)
|
EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos)
|
||||||
})?,
|
})?,
|
||||||
offset,
|
offset,
|
||||||
@ -1693,11 +1755,13 @@ impl Engine {
|
|||||||
let offset = index as usize;
|
let offset = index as usize;
|
||||||
(
|
(
|
||||||
s.chars().rev().nth(offset - 1).ok_or_else(|| {
|
s.chars().rev().nth(offset - 1).ok_or_else(|| {
|
||||||
|
let chars_len = s.chars().count();
|
||||||
EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos)
|
EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos)
|
||||||
})?,
|
})?,
|
||||||
offset,
|
offset,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
let chars_len = s.chars().count();
|
||||||
return EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos).into();
|
return EvalAltResult::ErrorStringBounds(chars_len, index, idx_pos).into();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1710,6 +1774,7 @@ impl Engine {
|
|||||||
let args = &mut [target, &mut _idx];
|
let args = &mut [target, &mut _idx];
|
||||||
let hash_get =
|
let hash_get =
|
||||||
FnCallHashes::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,
|
||||||
@ -1745,6 +1810,7 @@ impl Engine {
|
|||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, expr.position())?;
|
self.inc_operations(state, expr.position())?;
|
||||||
|
|
||||||
let result = match expr {
|
let result = match expr {
|
||||||
@ -1792,7 +1858,7 @@ impl Engine {
|
|||||||
mods,
|
mods,
|
||||||
state,
|
state,
|
||||||
lib,
|
lib,
|
||||||
Some(OpAssignment::new(OP_CONCAT)),
|
Some(OpAssignment::new(TOKEN_OP_CONCAT)),
|
||||||
pos,
|
pos,
|
||||||
(&mut result).into(),
|
(&mut result).into(),
|
||||||
item,
|
item,
|
||||||
@ -2082,6 +2148,7 @@ impl Engine {
|
|||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, stmt.position())?;
|
self.inc_operations(state, stmt.position())?;
|
||||||
|
|
||||||
let result = match stmt {
|
let result = match stmt {
|
||||||
@ -2110,6 +2177,7 @@ impl Engine {
|
|||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(state, pos)?;
|
||||||
|
|
||||||
if lhs_ptr.is_read_only() {
|
if lhs_ptr.is_read_only() {
|
||||||
@ -2304,7 +2372,6 @@ impl Engine {
|
|||||||
// For loop
|
// For loop
|
||||||
Stmt::For(expr, x, _) => {
|
Stmt::For(expr, x, _) => {
|
||||||
let (Ident { name, .. }, statements) = x.as_ref();
|
let (Ident { name, .. }, statements) = x.as_ref();
|
||||||
let pos = statements.position();
|
|
||||||
let iter_obj = self
|
let iter_obj = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2358,7 +2425,8 @@ impl Engine {
|
|||||||
*loop_var = value;
|
*loop_var = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inc_operations(state, pos)?;
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
self.inc_operations(state, statements.position())?;
|
||||||
|
|
||||||
if statements.is_empty() {
|
if statements.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@ -2800,6 +2868,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the number of operations stay within limit.
|
/// Check if the number of operations stay within limit.
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
pub(crate) fn inc_operations(
|
pub(crate) fn inc_operations(
|
||||||
&self,
|
&self,
|
||||||
state: &mut State,
|
state: &mut State,
|
||||||
@ -2807,7 +2876,6 @@ impl Engine {
|
|||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
state.operations += 1;
|
state.operations += 1;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
|
||||||
// Guard against too many operations
|
// Guard against too many operations
|
||||||
if self.max_operations() > 0 && state.operations > self.max_operations() {
|
if self.max_operations() > 0 && state.operations > self.max_operations() {
|
||||||
return EvalAltResult::ErrorTooManyOperations(pos).into();
|
return EvalAltResult::ErrorTooManyOperations(pos).into();
|
||||||
|
@ -1074,7 +1074,7 @@ impl Engine {
|
|||||||
Some(Ok(module_ast)) => {
|
Some(Ok(module_ast)) => {
|
||||||
collect_imports(&module_ast, &mut resolver, &mut imports)
|
collect_imports(&module_ast, &mut resolver, &mut imports)
|
||||||
}
|
}
|
||||||
Some(err @ Err(_)) => return err,
|
Some(err) => return err,
|
||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2105,6 +2105,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_progress(
|
pub fn on_progress(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -294,6 +294,7 @@ impl Engine {
|
|||||||
is_op_assignment: bool,
|
is_op_assignment: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(state, pos)?;
|
||||||
|
|
||||||
let state_source = state.source.clone();
|
let state_source = state.source.clone();
|
||||||
@ -483,6 +484,7 @@ impl Engine {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(state, pos)?;
|
||||||
|
|
||||||
if fn_def.body.is_empty() {
|
if fn_def.body.is_empty() {
|
||||||
@ -844,10 +846,11 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
script: &str,
|
script: &str,
|
||||||
pos: Position,
|
_pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
self.inc_operations(state, pos)?;
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
self.inc_operations(state, _pos)?;
|
||||||
|
|
||||||
let script = script.trim();
|
let script = script.trim();
|
||||||
if script.is_empty() {
|
if script.is_empty() {
|
||||||
@ -1305,14 +1308,15 @@ impl Engine {
|
|||||||
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
.chain(constant_args.iter().map(|(v, _)| Ok(v.clone())))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let (mut target, pos) =
|
let (mut target, _pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
if target.as_ref().is_read_only() {
|
if target.as_ref().is_read_only() {
|
||||||
target = target.into_owned();
|
target = target.into_owned();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inc_operations(state, pos)?;
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
self.inc_operations(state, _pos)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
@ -1396,10 +1400,11 @@ impl Engine {
|
|||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
let (target, pos) =
|
let (target, _pos) =
|
||||||
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
self.inc_operations(state, pos)?;
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
self.inc_operations(state, _pos)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
@ -1439,6 +1444,7 @@ impl Engine {
|
|||||||
let func = match module.get_qualified_fn(hash) {
|
let func = match module.get_qualified_fn(hash) {
|
||||||
// Then search in Rust functions
|
// Then search in Rust functions
|
||||||
None => {
|
None => {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(state, pos)?;
|
self.inc_operations(state, pos)?;
|
||||||
|
|
||||||
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
let hash_params = calc_fn_params_hash(args.iter().map(|a| a.type_id()));
|
||||||
|
@ -418,9 +418,11 @@ pub type FnPlugin = dyn PluginFunction;
|
|||||||
pub type FnPlugin = dyn PluginFunction + Send + Sync;
|
pub type FnPlugin = dyn PluginFunction + Send + Sync;
|
||||||
|
|
||||||
/// A standard callback function for progress reporting.
|
/// A standard callback function for progress reporting.
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnProgressCallback = Box<dyn Fn(u64) -> Option<Dynamic> + 'static>;
|
pub type OnProgressCallback = Box<dyn Fn(u64) -> Option<Dynamic> + 'static>;
|
||||||
/// A standard callback function for progress reporting.
|
/// A standard callback function for progress reporting.
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnProgressCallback = Box<dyn Fn(u64) -> Option<Dynamic> + Send + Sync + 'static>;
|
pub type OnProgressCallback = Box<dyn Fn(u64) -> Option<Dynamic> + Send + Sync + 'static>;
|
||||||
|
|
||||||
|
12
src/lib.rs
12
src/lib.rs
@ -140,11 +140,11 @@ pub use utils::ImmutableString;
|
|||||||
|
|
||||||
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
||||||
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
#[cfg(not(feature = "no_smartstring_for_identifier"))]
|
||||||
pub type Identifier = smartstring::SmartString<smartstring::Compact>;
|
pub type Identifier = SmartString;
|
||||||
|
|
||||||
/// An identifier in Rhai.
|
/// An identifier in Rhai.
|
||||||
#[cfg(feature = "no_smartstring")]
|
#[cfg(feature = "no_smartstring_for_identifier")]
|
||||||
pub type Identifier = ImmutableString;
|
pub type Identifier = ImmutableString;
|
||||||
|
|
||||||
/// A trait to enable registering Rust functions.
|
/// A trait to enable registering Rust functions.
|
||||||
@ -305,6 +305,12 @@ type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
|||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
pub type StaticVec<T> = smallvec::SmallVec<[T; 4]>;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "internals"))]
|
||||||
|
pub(crate) type SmartString = smartstring::SmartString<smartstring::Compact>;
|
||||||
|
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
pub type SmartString = smartstring::SmartString<smartstring::Compact>;
|
||||||
|
|
||||||
// Compiler guards against mutually-exclusive feature flags
|
// Compiler guards against mutually-exclusive feature flags
|
||||||
|
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
|
@ -1661,6 +1661,16 @@ impl fmt::Debug for NamespaceRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for NamespaceRef {
|
||||||
|
#[inline(always)]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
for Ident { name, .. } in self.path.iter() {
|
||||||
|
write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Deref for NamespaceRef {
|
impl Deref for NamespaceRef {
|
||||||
type Target = StaticVec<Ident>;
|
type Target = StaticVec<Ident>;
|
||||||
|
|
||||||
@ -1677,16 +1687,6 @@ impl DerefMut for NamespaceRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for NamespaceRef {
|
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
for Ident { name, .. } in self.path.iter() {
|
|
||||||
write!(f, "{}{}", name, Token::DoubleColon.syntax())?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<StaticVec<Ident>> for NamespaceRef {
|
impl From<StaticVec<Ident>> for NamespaceRef {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(path: StaticVec<Ident>) -> Self {
|
fn from(path: StaticVec<Ident>) -> Self {
|
||||||
|
@ -342,7 +342,7 @@ impl ModuleResolver for FileModuleResolver {
|
|||||||
ast.set_source(path);
|
ast.set_source(path);
|
||||||
Some(Ok(ast))
|
Some(Ok(ast))
|
||||||
}
|
}
|
||||||
err @ Err(_) => Some(err),
|
err => Some(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
//! Module implementing the [`AST`] optimizer.
|
//! Module implementing the [`AST`] optimizer.
|
||||||
|
|
||||||
use crate::ast::{Expr, Stmt};
|
use crate::ast::{Expr, OpAssignment, Stmt};
|
||||||
use crate::dynamic::AccessMode;
|
use crate::dynamic::AccessMode;
|
||||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||||
use crate::fn_builtin::get_builtin_binary_op_fn;
|
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::token::Token;
|
||||||
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, FnPtr, ImmutableString,
|
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString,
|
||||||
@ -238,29 +239,18 @@ fn optimize_stmt_block(
|
|||||||
.find_map(|(i, stmt)| match stmt {
|
.find_map(|(i, stmt)| match stmt {
|
||||||
stmt if !is_pure(stmt) => Some(i),
|
stmt if !is_pure(stmt) => Some(i),
|
||||||
|
|
||||||
Stmt::Noop(_) | Stmt::Return(_, None, _) => None,
|
Stmt::Let(e, _, _, _) | Stmt::Const(e, _, _, _) | Stmt::Expr(e)
|
||||||
|
if !e.is_constant() =>
|
||||||
Stmt::Let(e, _, _, _)
|
|
||||||
| Stmt::Const(e, _, _, _)
|
|
||||||
| Stmt::Expr(e)
|
|
||||||
| Stmt::Return(_, Some(e), _)
|
|
||||||
if e.is_constant() =>
|
|
||||||
{
|
{
|
||||||
None
|
Some(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(e, _, _) if e.is_constant() => None,
|
Stmt::Import(e, _, _) if !e.is_constant() => Some(i),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
_ => None,
|
||||||
Stmt::Export(_, _) => None,
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
Stmt::Share(_) => None,
|
|
||||||
|
|
||||||
_ => Some(i),
|
|
||||||
})
|
})
|
||||||
.map_or(0, |n| statements.len() - n);
|
.map_or(0, |n| statements.len() - n - 1);
|
||||||
|
|
||||||
while index < statements.len() {
|
while index < statements.len() {
|
||||||
if preserve_result && index >= statements.len() - 1 {
|
if preserve_result && index >= statements.len() - 1 {
|
||||||
@ -381,6 +371,33 @@ fn optimize_stmt_block(
|
|||||||
/// Optimize a [statement][Stmt].
|
/// Optimize a [statement][Stmt].
|
||||||
fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||||
match stmt {
|
match stmt {
|
||||||
|
// var = var op expr => var op= expr
|
||||||
|
Stmt::Assignment(x, _)
|
||||||
|
if x.1.is_none()
|
||||||
|
&& x.0.is_variable_access(true)
|
||||||
|
&& matches!(&x.2, Expr::FnCall(x2, _)
|
||||||
|
if Token::lookup_from_syntax(&x2.name).map(|t| t.has_op_assignment()).unwrap_or(false)
|
||||||
|
&& x2.args_count() == 2 && x2.args.len() >= 1
|
||||||
|
&& x2.args[0].get_variable_name(true) == x.0.get_variable_name(true)
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
match &mut x.2 {
|
||||||
|
Expr::FnCall(x2, _) => {
|
||||||
|
state.set_dirty();
|
||||||
|
let op = Token::lookup_from_syntax(&x2.name).unwrap();
|
||||||
|
let op_assignment = op.make_op_assignment().unwrap();
|
||||||
|
x.1 = Some(OpAssignment::new(op_assignment));
|
||||||
|
x.2 = if x2.args.len() > 1 {
|
||||||
|
mem::take(&mut x2.args[1])
|
||||||
|
} else {
|
||||||
|
let (value, pos) = mem::take(&mut x2.constant_args[0]);
|
||||||
|
Expr::DynamicConstant(Box::new(value), pos)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// expr op= expr
|
// expr op= expr
|
||||||
Stmt::Assignment(x, _) => match x.0 {
|
Stmt::Assignment(x, _) => match x.0 {
|
||||||
Expr::Variable(_, _, _) => optimize_expr(&mut x.2, state),
|
Expr::Variable(_, _, _) => optimize_expr(&mut x.2, state),
|
||||||
|
@ -10,10 +10,6 @@ use num_traits::{CheckedAdd as Add, CheckedSub as Sub};
|
|||||||
#[cfg(feature = "unchecked")]
|
#[cfg(feature = "unchecked")]
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
fn get_range<T: Variant + Clone>(from: T, to: T) -> Result<Range<T>, Box<EvalAltResult>> {
|
|
||||||
Ok(from..to)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register range function with step
|
// Register range function with step
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
struct StepRange<T>(T, T, T)
|
struct StepRange<T>(T, T, T)
|
||||||
@ -130,18 +126,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_step_range<T>(from: T, to: T, step: T) -> Result<StepRange<T>, Box<EvalAltResult>>
|
|
||||||
where
|
|
||||||
T: Variant + Copy + PartialOrd + Add<Output = T> + Sub<Output = T>,
|
|
||||||
{
|
|
||||||
StepRange::<T>::new(from, to, step)
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! reg_range {
|
macro_rules! reg_range {
|
||||||
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
||||||
$(
|
$(
|
||||||
$lib.set_iterator::<Range<$y>>();
|
$lib.set_iterator::<Range<$y>>();
|
||||||
let _hash = $lib.set_native_fn($x, get_range::<$y>);
|
let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to));
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
$lib.update_fn_metadata(_hash, &[
|
$lib.update_fn_metadata(_hash, &[
|
||||||
@ -154,7 +143,7 @@ macro_rules! reg_range {
|
|||||||
($lib:ident | step $x:expr => $( $y:ty ),*) => {
|
($lib:ident | step $x:expr => $( $y:ty ),*) => {
|
||||||
$(
|
$(
|
||||||
$lib.set_iterator::<StepRange<$y>>();
|
$lib.set_iterator::<StepRange<$y>>();
|
||||||
let _hash = $lib.set_native_fn($x, get_step_range::<$y>);
|
let _hash = $lib.set_native_fn($x, |from: $y, to: $y, step: $y| StepRange::new(from, to, step));
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
$lib.update_fn_metadata(_hash, &[
|
$lib.update_fn_metadata(_hash, &[
|
||||||
@ -192,12 +181,72 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
use crate::FLOAT;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
struct StepFloatRange(FLOAT, FLOAT, FLOAT);
|
||||||
|
|
||||||
|
impl StepFloatRange {
|
||||||
|
pub fn new(from: FLOAT, to: FLOAT, step: FLOAT) -> Result<Self, Box<EvalAltResult>> {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if step == 0.0 {
|
||||||
|
return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
|
||||||
|
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)),
|
||||||
|
crate::Position::NONE,
|
||||||
|
).into();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self(from, to, step))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for StepFloatRange {
|
||||||
|
type Item = FLOAT;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<FLOAT> {
|
||||||
|
if self.0 == self.1 {
|
||||||
|
None
|
||||||
|
} else if self.0 < self.1 {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if self.2 < 0.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let v = self.0;
|
||||||
|
let n = self.0 + self.2;
|
||||||
|
|
||||||
|
self.0 = if n >= self.1 { self.1 } else { n };
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
if self.2 > 0.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let v = self.0;
|
||||||
|
let n = self.0 + self.2;
|
||||||
|
|
||||||
|
self.0 = if n <= self.1 { self.1 } else { n };
|
||||||
|
Some(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::iter::FusedIterator for StepFloatRange {}
|
||||||
|
|
||||||
|
lib.set_iterator::<StepFloatRange>();
|
||||||
|
|
||||||
|
let _hash = lib.set_native_fn("range", |from, to, step| StepFloatRange::new(from, to, step));
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["from: FLOAT", "to: FLOAT", "step: FLOAT", "Iterator<Item=FLOAT>"]);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "decimal")]
|
#[cfg(feature = "decimal")]
|
||||||
{
|
{
|
||||||
use rust_decimal::{
|
use rust_decimal::Decimal;
|
||||||
prelude::{One, Zero},
|
use num_traits::Zero;
|
||||||
Decimal,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
struct StepDecimalRange(Decimal, Decimal, Decimal);
|
struct StepDecimalRange(Decimal, Decimal, Decimal);
|
||||||
@ -252,10 +301,6 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
|
|
||||||
lib.set_iterator::<StepDecimalRange>();
|
lib.set_iterator::<StepDecimalRange>();
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("range", |from, to| StepDecimalRange::new(from, to, Decimal::one()));
|
|
||||||
#[cfg(feature = "metadata")]
|
|
||||||
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "Iterator<Item=Decimal>"]);
|
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step));
|
let _hash = lib.set_native_fn("range", |from, to, step| StepDecimalRange::new(from, to, step));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
lib.update_fn_metadata(_hash, &["from: Decimal", "to: Decimal", "step: Decimal", "Iterator<Item=Decimal>"]);
|
||||||
|
@ -1409,7 +1409,7 @@ fn parse_unary(
|
|||||||
|
|
||||||
/// Make an assignment statement.
|
/// Make an assignment statement.
|
||||||
fn make_assignment_stmt<'a>(
|
fn make_assignment_stmt<'a>(
|
||||||
op: &'static str,
|
op: Option<Token>,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
rhs: Expr,
|
rhs: Expr,
|
||||||
@ -1432,11 +1432,7 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let op_info = if op.is_empty() {
|
let op_info = op.map(|v| OpAssignment::new(v));
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(OpAssignment::new(op))
|
|
||||||
};
|
|
||||||
|
|
||||||
match &lhs {
|
match &lhs {
|
||||||
// const_expr = rhs
|
// const_expr = rhs
|
||||||
@ -1516,25 +1512,12 @@ fn parse_op_assignment_stmt(
|
|||||||
let (token, token_pos) = input.peek().unwrap();
|
let (token, token_pos) = input.peek().unwrap();
|
||||||
settings.pos = *token_pos;
|
settings.pos = *token_pos;
|
||||||
|
|
||||||
let op = match token {
|
let (op, pos) = match token {
|
||||||
Token::Equals => "",
|
Token::Equals => (None, input.next().unwrap().1),
|
||||||
|
_ if token.is_op_assignment() => input.next().map(|(op, pos)| (Some(op), pos)).unwrap(),
|
||||||
Token::PlusAssign
|
|
||||||
| Token::MinusAssign
|
|
||||||
| Token::MultiplyAssign
|
|
||||||
| Token::DivideAssign
|
|
||||||
| Token::LeftShiftAssign
|
|
||||||
| Token::RightShiftAssign
|
|
||||||
| Token::ModuloAssign
|
|
||||||
| Token::PowerOfAssign
|
|
||||||
| Token::AndAssign
|
|
||||||
| Token::OrAssign
|
|
||||||
| Token::XOrAssign => token.keyword_syntax(),
|
|
||||||
|
|
||||||
_ => return Ok(Stmt::Expr(lhs)),
|
_ => return Ok(Stmt::Expr(lhs)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (_, pos) = input.next().unwrap();
|
|
||||||
let rhs = parse_expr(input, state, lib, settings.level_up())?;
|
let rhs = parse_expr(input, state, lib, settings.level_up())?;
|
||||||
make_assignment_stmt(op, state, lhs, rhs, pos)
|
make_assignment_stmt(op, state, lhs, rhs, pos)
|
||||||
}
|
}
|
||||||
|
84
src/token.rs
84
src/token.rs
@ -185,6 +185,16 @@ impl Position {
|
|||||||
#[cfg(feature = "no_position")]
|
#[cfg(feature = "no_position")]
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
/// Print this [`Position`] for debug purposes.
|
||||||
|
#[inline(always)]
|
||||||
|
pub(crate) fn debug_print(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
|
if !self.is_none() {
|
||||||
|
write!(_f, " @ {:?}", self)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Position {
|
impl Default for Position {
|
||||||
@ -568,6 +578,80 @@ impl Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is this token an op-assignment operator?
|
||||||
|
#[inline]
|
||||||
|
pub fn is_op_assignment(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::PlusAssign
|
||||||
|
| Self::MinusAssign
|
||||||
|
| Self::MultiplyAssign
|
||||||
|
| Self::DivideAssign
|
||||||
|
| Self::LeftShiftAssign
|
||||||
|
| Self::RightShiftAssign
|
||||||
|
| Self::ModuloAssign
|
||||||
|
| Self::PowerOfAssign
|
||||||
|
| Self::AndAssign
|
||||||
|
| Self::OrAssign
|
||||||
|
| Self::XOrAssign => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the corresponding operator of the token if it is an op-assignment operator.
|
||||||
|
pub fn map_op_assignment(&self) -> Option<Self> {
|
||||||
|
Some(match self {
|
||||||
|
Self::PlusAssign => Self::Plus,
|
||||||
|
Self::MinusAssign => Self::Minus,
|
||||||
|
Self::MultiplyAssign => Self::Multiply,
|
||||||
|
Self::DivideAssign => Self::Divide,
|
||||||
|
Self::LeftShiftAssign => Self::LeftShift,
|
||||||
|
Self::RightShiftAssign => Self::RightShift,
|
||||||
|
Self::ModuloAssign => Self::Modulo,
|
||||||
|
Self::PowerOfAssign => Self::PowerOf,
|
||||||
|
Self::AndAssign => Self::Ampersand,
|
||||||
|
Self::OrAssign => Self::Pipe,
|
||||||
|
Self::XOrAssign => Self::XOr,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Has this token a corresponding op-assignment operator?
|
||||||
|
#[inline]
|
||||||
|
pub fn has_op_assignment(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Plus
|
||||||
|
| Self::Minus
|
||||||
|
| Self::Multiply
|
||||||
|
| Self::Divide
|
||||||
|
| Self::LeftShift
|
||||||
|
| Self::RightShift
|
||||||
|
| Self::Modulo
|
||||||
|
| Self::PowerOf
|
||||||
|
| Self::Ampersand
|
||||||
|
| Self::Pipe
|
||||||
|
| Self::XOr => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the corresponding op-assignment operator of the token.
|
||||||
|
pub fn make_op_assignment(&self) -> Option<Self> {
|
||||||
|
Some(match self {
|
||||||
|
Self::Plus => Self::PlusAssign,
|
||||||
|
Self::Minus => Self::MinusAssign,
|
||||||
|
Self::Multiply => Self::MultiplyAssign,
|
||||||
|
Self::Divide => Self::DivideAssign,
|
||||||
|
Self::LeftShift => Self::LeftShiftAssign,
|
||||||
|
Self::RightShift => Self::RightShiftAssign,
|
||||||
|
Self::Modulo => Self::ModuloAssign,
|
||||||
|
Self::PowerOf => Self::PowerOfAssign,
|
||||||
|
Self::Ampersand => Self::AndAssign,
|
||||||
|
Self::Pipe => Self::OrAssign,
|
||||||
|
Self::XOr => Self::XOrAssign,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Reverse lookup a token from a piece of syntax.
|
/// Reverse lookup a token from a piece of syntax.
|
||||||
pub fn lookup_from_syntax(syntax: &str) -> Option<Self> {
|
pub fn lookup_from_syntax(syntax: &str) -> Option<Self> {
|
||||||
use Token::*;
|
use Token::*;
|
||||||
|
92
src/utils.rs
92
src/utils.rs
@ -1,7 +1,7 @@
|
|||||||
//! Module containing various utility types and functions.
|
//! Module containing various utility types and functions.
|
||||||
|
|
||||||
use crate::fn_native::{shared_make_mut, shared_take};
|
use crate::fn_native::{shared_make_mut, shared_take};
|
||||||
use crate::{Identifier, Shared};
|
use crate::{Identifier, Shared, SmartString};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -141,10 +141,10 @@ pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
|
|||||||
/// assert_eq!(s, "hello, world!");
|
/// assert_eq!(s, "hello, world!");
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Eq, Ord, Hash, Default)]
|
#[derive(Clone, Eq, Ord, Hash, Default)]
|
||||||
pub struct ImmutableString(Shared<String>);
|
pub struct ImmutableString(Shared<SmartString>);
|
||||||
|
|
||||||
impl Deref for ImmutableString {
|
impl Deref for ImmutableString {
|
||||||
type Target = String;
|
type Target = SmartString;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
@ -152,9 +152,9 @@ impl Deref for ImmutableString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<String> for ImmutableString {
|
impl AsRef<SmartString> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn as_ref(&self) -> &String {
|
fn as_ref(&self) -> &SmartString {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,9 +166,9 @@ impl AsRef<str> for ImmutableString {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Borrow<String> for ImmutableString {
|
impl Borrow<SmartString> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn borrow(&self) -> &String {
|
fn borrow(&self) -> &SmartString {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,33 +183,31 @@ impl Borrow<str> for ImmutableString {
|
|||||||
impl From<&str> for ImmutableString {
|
impl From<&str> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: &str) -> Self {
|
fn from(value: &str) -> Self {
|
||||||
Self(value.to_string().into())
|
Self(Into::<SmartString>::into(value).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<&String> for ImmutableString {
|
impl From<&String> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: &String) -> Self {
|
fn from(value: &String) -> Self {
|
||||||
Self(value.to_string().into())
|
Self(Into::<SmartString>::into(value).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<String> for ImmutableString {
|
impl From<String> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: String) -> Self {
|
fn from(value: String) -> Self {
|
||||||
|
Self(Into::<SmartString>::into(value).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<SmartString> for ImmutableString {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(value: SmartString) -> Self {
|
||||||
Self(value.into())
|
Self(value.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl From<ImmutableString> for SmartString {
|
||||||
impl From<Box<String>> for ImmutableString {
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: Box<String>) -> Self {
|
fn from(mut value: ImmutableString) -> Self {
|
||||||
Self(value.into())
|
std::mem::take(shared_make_mut(&mut value.0))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<ImmutableString> for String {
|
|
||||||
#[inline(always)]
|
|
||||||
fn from(value: ImmutableString) -> Self {
|
|
||||||
value.into_owned()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,35 +216,35 @@ impl FromStr for ImmutableString {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(Self(s.to_string().into()))
|
Ok(Self(Into::<SmartString>::into(s).into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator<char> for ImmutableString {
|
impl FromIterator<char> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<&'a char> for ImmutableString {
|
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().cloned().collect::<String>().into())
|
Self(iter.into_iter().cloned().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<&'a str> for ImmutableString {
|
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromIterator<String> for ImmutableString {
|
impl<'a> FromIterator<String> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||||
Self(iter.into_iter().collect::<String>().into())
|
Self(iter.into_iter().collect::<SmartString>().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,7 +395,13 @@ impl Add<String> for &ImmutableString {
|
|||||||
impl AddAssign<String> for ImmutableString {
|
impl AddAssign<String> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn add_assign(&mut self, rhs: String) {
|
fn add_assign(&mut self, rhs: String) {
|
||||||
self.make_mut().push_str(&rhs);
|
if !rhs.is_empty() {
|
||||||
|
if self.is_empty() {
|
||||||
|
self.0 = Into::<SmartString>::into(rhs).into();
|
||||||
|
} else {
|
||||||
|
self.make_mut().push_str(&rhs);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,7 +470,7 @@ impl SubAssign<&ImmutableString> for ImmutableString {
|
|||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
self.0 = rhs.0.clone();
|
self.0 = rhs.0.clone();
|
||||||
} else {
|
} else {
|
||||||
self.0 = self.replace(rhs.as_str(), "").into();
|
self.0 = Into::<SmartString>::into(self.replace(rhs.as_str(), "")).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -479,7 +483,7 @@ impl SubAssign<ImmutableString> for ImmutableString {
|
|||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
self.0 = rhs.0;
|
self.0 = rhs.0;
|
||||||
} else {
|
} else {
|
||||||
self.0 = self.replace(rhs.as_str(), "").into();
|
self.0 = Into::<SmartString>::into(self.replace(rhs.as_str(), "")).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -518,7 +522,7 @@ impl Sub<String> for &ImmutableString {
|
|||||||
impl SubAssign<String> for ImmutableString {
|
impl SubAssign<String> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn sub_assign(&mut self, rhs: String) {
|
fn sub_assign(&mut self, rhs: String) {
|
||||||
self.0 = self.replace(&rhs, "").into();
|
self.0 = Into::<SmartString>::into(self.replace(&rhs, "")).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,7 +547,7 @@ impl Sub<char> for &ImmutableString {
|
|||||||
impl SubAssign<char> for ImmutableString {
|
impl SubAssign<char> for ImmutableString {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn sub_assign(&mut self, rhs: char) {
|
fn sub_assign(&mut self, rhs: char) {
|
||||||
self.0 = self.replace(rhs, "").into();
|
self.0 = Into::<SmartString>::into(self.replace(rhs, "")).into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,34 +592,18 @@ impl PartialOrd<ImmutableString> for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
|
||||||
impl From<ImmutableString> for Identifier {
|
|
||||||
#[inline(always)]
|
|
||||||
fn from(value: ImmutableString) -> Self {
|
|
||||||
value.into_owned().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
|
||||||
impl From<Identifier> for ImmutableString {
|
|
||||||
#[inline(always)]
|
|
||||||
fn from(value: Identifier) -> Self {
|
|
||||||
value.to_string().into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImmutableString {
|
impl ImmutableString {
|
||||||
/// Consume the [`ImmutableString`] and convert it into a [`String`].
|
/// Consume the [`ImmutableString`] and convert it into a [`String`].
|
||||||
/// If there are other references to the same string, a cloned copy is returned.
|
/// If there are other references to the same string, a cloned copy is returned.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn into_owned(mut self) -> String {
|
pub fn into_owned(mut self) -> String {
|
||||||
self.make_mut(); // Make sure it is unique reference
|
self.make_mut(); // Make sure it is unique reference
|
||||||
shared_take(self.0) // Should succeed
|
shared_take(self.0).into() // Should succeed
|
||||||
}
|
}
|
||||||
/// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references).
|
/// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references).
|
||||||
/// Then return a mutable reference to the [`String`].
|
/// Then return a mutable reference to the [`SmartString`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn make_mut(&mut self) -> &mut String {
|
pub(crate) fn make_mut(&mut self) -> &mut SmartString {
|
||||||
shared_make_mut(&mut self.0)
|
shared_make_mut(&mut self.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,17 +618,17 @@ impl ImmutableString {
|
|||||||
/// yet interned.
|
/// yet interned.
|
||||||
#[derive(Debug, Clone, Default, Hash)]
|
#[derive(Debug, Clone, Default, Hash)]
|
||||||
pub struct IdentifierBuilder(
|
pub struct IdentifierBuilder(
|
||||||
#[cfg(feature = "no_smartstring")] std::collections::BTreeSet<Identifier>,
|
#[cfg(feature = "no_smartstring_for_identifier")] std::collections::BTreeSet<Identifier>,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl IdentifierBuilder {
|
impl IdentifierBuilder {
|
||||||
/// Get an identifier from a text string.
|
/// Get an identifier from a text string.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
|
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> Identifier {
|
||||||
#[cfg(not(feature = "no_smartstring"))]
|
#[cfg(not(feature = "no_smartstring_for_identifier"))]
|
||||||
return text.as_ref().into();
|
return text.as_ref().into();
|
||||||
|
|
||||||
#[cfg(feature = "no_smartstring")]
|
#[cfg(feature = "no_smartstring_for_identifier")]
|
||||||
return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
|
return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
|
||||||
let s: Identifier = text.into();
|
let s: Identifier = text.into();
|
||||||
self.0.insert(s.clone());
|
self.0.insert(s.clone());
|
||||||
|
158
tests/for.rs
158
tests/for.rs
@ -1,31 +1,52 @@
|
|||||||
use rhai::{Engine, EvalAltResult, Module, INT};
|
use rhai::{Engine, EvalAltResult, Module, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use rhai::FLOAT;
|
||||||
|
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use rust_decimal::Decimal;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_for() -> Result<(), Box<EvalAltResult>> {
|
fn test_for_loop() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
let script = "
|
#[cfg(not(feature = "no_index"))]
|
||||||
let sum1 = 0;
|
assert_eq!(
|
||||||
let sum2 = 0;
|
engine.eval::<INT>(
|
||||||
let inputs = [1, 2, 3, 4, 5];
|
"
|
||||||
|
let sum1 = 0;
|
||||||
|
let sum2 = 0;
|
||||||
|
let inputs = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
for x in inputs {
|
for x in inputs {
|
||||||
sum1 += x;
|
sum1 += x;
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in range(1, 6) {
|
for x in range(1, 6) {
|
||||||
sum2 += x;
|
sum2 += x;
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in range(1, 6, 3) {
|
for x in range(1, 6, 3) {
|
||||||
sum2 += x;
|
sum2 += x;
|
||||||
}
|
}
|
||||||
|
|
||||||
sum1 + sum2
|
sum1 + sum2
|
||||||
";
|
"
|
||||||
|
)?,
|
||||||
|
35
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>(script)?, 35);
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let sum = 0;
|
||||||
|
for x in range(1, 10) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
45
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
@ -38,6 +59,7 @@ fn test_for() -> Result<(), Box<EvalAltResult>> {
|
|||||||
25
|
25
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
@ -49,6 +71,7 @@ fn test_for() -> Result<(), Box<EvalAltResult>> {
|
|||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
@ -71,6 +94,105 @@ fn test_for() -> Result<(), Box<EvalAltResult>> {
|
|||||||
30
|
30
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
{
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<FLOAT>(
|
||||||
|
"
|
||||||
|
let sum = 0.0;
|
||||||
|
for x in range(1.0, 10.0, 2.0) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
25.0
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<FLOAT>(
|
||||||
|
"
|
||||||
|
let sum = 0.0;
|
||||||
|
for x in range(10.0, 1.0, 2.0) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
0.0
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<FLOAT>(
|
||||||
|
"
|
||||||
|
let sum = 0.0;
|
||||||
|
for x in range(1.0, 10.0, -2.0) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
0.0
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<FLOAT>(
|
||||||
|
"
|
||||||
|
let sum = 0.0;
|
||||||
|
for x in range(10.0, 1.0, -2.0) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
30.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(feature = "decimal")]
|
||||||
|
{
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<Decimal>(
|
||||||
|
"
|
||||||
|
let sum = to_decimal(0);
|
||||||
|
for x in range(to_decimal(1), to_decimal(10), to_decimal(2)) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
Decimal::from(25)
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<Decimal>(
|
||||||
|
"
|
||||||
|
let sum = to_decimal(0);
|
||||||
|
for x in range(to_decimal(10), to_decimal(1), to_decimal(2)) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
Decimal::from(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unchecked"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<Decimal>(
|
||||||
|
"
|
||||||
|
let sum = to_decimal(0);
|
||||||
|
for x in range(to_decimal(1), to_decimal(10), to_decimal(-2)) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
Decimal::from(0)
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<Decimal>(
|
||||||
|
"
|
||||||
|
let sum = to_decimal(0);
|
||||||
|
for x in range(to_decimal(10), to_decimal(1), to_decimal(-2)) { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
Decimal::from(30)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ fn test_map_json() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine.parse_json(" 123", true).expect_err("should error"),
|
*engine.parse_json(" 123", true).expect_err("should error"),
|
||||||
EvalAltResult::ErrorParsing(ParseErrorType::MissingToken(token, _), pos)
|
EvalAltResult::ErrorParsing(ParseErrorType::MissingToken(token, _), _)
|
||||||
if token == "{"
|
if token == "{"
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user