commit
1290556c0a
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- v1.2-fixes
|
- v1.3-fixes
|
||||||
pull_request: {}
|
pull_request: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,6 +1,17 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
Version 1.4.0
|
||||||
|
=============
|
||||||
|
|
||||||
|
This version adds support for integer _ranges_ via the `..` and `..=` operators.
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Added support for integer _ranges_ via the `..` and `..=` operators.
|
||||||
|
|
||||||
|
|
||||||
Version 1.3.0
|
Version 1.3.0
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ members = [".", "codegen"]
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
|
@ -101,7 +101,7 @@ print("Ready... Go!");
|
|||||||
let result;
|
let result;
|
||||||
let now = timestamp();
|
let now = timestamp();
|
||||||
|
|
||||||
for n in range(0, REPEAT) {
|
for n in 0..REPEAT {
|
||||||
result = fib(TARGET);
|
result = fib(TARGET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ fn bench_eval_array_loop(bench: &mut Bencher) {
|
|||||||
let script = r#"
|
let script = r#"
|
||||||
let list = [];
|
let list = [];
|
||||||
|
|
||||||
for i in range(0, 10_000) {
|
for i in 0..10_000 {
|
||||||
list.push(i);
|
list.push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ fn bench_eval_call(bench: &mut Bencher) {
|
|||||||
fn bench_eval_loop_number(bench: &mut Bencher) {
|
fn bench_eval_loop_number(bench: &mut Bencher) {
|
||||||
let script = r#"
|
let script = r#"
|
||||||
let s = 0;
|
let s = 0;
|
||||||
for x in range(0, 10000) {
|
for x in 0..10000 {
|
||||||
s += 1;
|
s += 1;
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
@ -127,7 +127,7 @@ fn bench_eval_loop_number(bench: &mut Bencher) {
|
|||||||
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;
|
let s;
|
||||||
for x in range(0, 10000) {
|
for x in 0..10000 {
|
||||||
s = "hello, world!" + "hello, world!";
|
s = "hello, world!" + "hello, world!";
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
@ -144,7 +144,7 @@ fn bench_eval_loop_strings_build(bench: &mut Bencher) {
|
|||||||
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;
|
let s;
|
||||||
for x in range(0, 10000) {
|
for x in 0..10000 {
|
||||||
s = "hello" + "";
|
s = "hello" + "";
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
@ -163,7 +163,7 @@ fn bench_eval_switch(bench: &mut Bencher) {
|
|||||||
let sum = 0;
|
let sum = 0;
|
||||||
let rem = 0;
|
let rem = 0;
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in 0..10 {
|
||||||
rem = x % 10;
|
rem = x % 10;
|
||||||
|
|
||||||
sum += switch rem {
|
sum += switch rem {
|
||||||
@ -195,7 +195,7 @@ fn bench_eval_nested_if(bench: &mut Bencher) {
|
|||||||
let sum = 0;
|
let sum = 0;
|
||||||
let rem = 0;
|
let rem = 0;
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in 0..10 {
|
||||||
rem = x % 10;
|
rem = x % 10;
|
||||||
|
|
||||||
sum += if rem == 0 { 10 }
|
sum += if rem == 0 { 10 }
|
||||||
|
@ -51,7 +51,7 @@ fn bench_iterations_array(bench: &mut Bencher) {
|
|||||||
let script = r#"
|
let script = r#"
|
||||||
let x = [];
|
let x = [];
|
||||||
x.pad(1000, 0);
|
x.pad(1000, 0);
|
||||||
for i in range(0, 1000) { x[i] = i % 256; }
|
for i in 0..1000 { x[i] = i % 256; }
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
@ -66,7 +66,7 @@ fn bench_iterations_array(bench: &mut Bencher) {
|
|||||||
fn bench_iterations_blob(bench: &mut Bencher) {
|
fn bench_iterations_blob(bench: &mut Bencher) {
|
||||||
let script = r#"
|
let script = r#"
|
||||||
let x = blob(1000, 0);
|
let x = blob(1000, 0);
|
||||||
for i in range(0, 1000) { x[i] = i % 256; }
|
for i in 0..1000 { x[i] = i % 256; }
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
@ -79,7 +79,7 @@ fn bench_parse_primes(bench: &mut Bencher) {
|
|||||||
|
|
||||||
let total_primes_found = 0;
|
let total_primes_found = 0;
|
||||||
|
|
||||||
for p in range(2, MAX_NUMBER_TO_CHECK) {
|
for p in 2..=MAX_NUMBER_TO_CHECK {
|
||||||
if prime_mask[p] {
|
if prime_mask[p] {
|
||||||
print(p);
|
print(p);
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ prime_mask[1] = false;
|
|||||||
|
|
||||||
let total_primes_found = 0;
|
let total_primes_found = 0;
|
||||||
|
|
||||||
for p in range(2, MAX_NUMBER_TO_CHECK) {
|
for p in 2..=MAX_NUMBER_TO_CHECK {
|
||||||
if prime_mask[p] {
|
if prime_mask[p] {
|
||||||
total_primes_found += 1;
|
total_primes_found += 1;
|
||||||
let i = 2 * p;
|
let i = 2 * p;
|
||||||
|
@ -19,7 +19,7 @@ print("Ready... Go!");
|
|||||||
let result;
|
let result;
|
||||||
let now = timestamp();
|
let now = timestamp();
|
||||||
|
|
||||||
for n in range(0, REPEAT) {
|
for n in 0..REPEAT {
|
||||||
result = fib(TARGET);
|
result = fib(TARGET);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ let now = timestamp();
|
|||||||
|
|
||||||
let list = [];
|
let list = [];
|
||||||
|
|
||||||
for i in range(0, MAX) {
|
for i in 0..MAX {
|
||||||
list.push(i);
|
list.push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ fn mat_gen() {
|
|||||||
const tmp = 1.0 / n / n;
|
const tmp = 1.0 / n / n;
|
||||||
let m = new_mat(n, n);
|
let m = new_mat(n, n);
|
||||||
|
|
||||||
for i in range(0, n) {
|
for i in 0..n {
|
||||||
for j in range(0, n) {
|
for j in 0..n {
|
||||||
m[i][j] = tmp * (i - j) * (i + j);
|
m[i][j] = tmp * (i - j) * (i + j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -27,19 +27,19 @@ fn mat_gen() {
|
|||||||
fn mat_mul(a, b) {
|
fn mat_mul(a, b) {
|
||||||
let b2 = new_mat(a[0].len, b[0].len);
|
let b2 = new_mat(a[0].len, b[0].len);
|
||||||
|
|
||||||
for i in range(0, a[0].len) {
|
for i in 0..a[0].len {
|
||||||
for j in range(0, b[0].len) {
|
for j in 0..b[0].len {
|
||||||
b2[j][i] = b[i][j];
|
b2[j][i] = b[i][j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let c = new_mat(a.len, b[0].len);
|
let c = new_mat(a.len, b[0].len);
|
||||||
|
|
||||||
for i in range(0, c.len) {
|
for i in 0..c.len {
|
||||||
for j in range(0, c[i].len) {
|
for j in 0..c[i].len {
|
||||||
c[i][j] = 0.0;
|
c[i][j] = 0.0;
|
||||||
|
|
||||||
for z in range(0, a[i].len) {
|
for z in 0..a[i].len {
|
||||||
c[i][j] += a[i][z] * b2[j][z];
|
c[i][j] += a[i][z] * b2[j][z];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,7 +55,7 @@ const b = mat_gen();
|
|||||||
const c = mat_mul(a, b);
|
const c = mat_mul(a, b);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
for i in range(0, SIZE) {
|
for i in 0..SIZE) {
|
||||||
print(c[i]);
|
print(c[i]);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
@ -5,21 +5,21 @@ let now = timestamp();
|
|||||||
const MAX_NUMBER_TO_CHECK = 1_000_000; // 9592 primes <= 100000
|
const MAX_NUMBER_TO_CHECK = 1_000_000; // 9592 primes <= 100000
|
||||||
|
|
||||||
let prime_mask = [];
|
let prime_mask = [];
|
||||||
prime_mask.pad(MAX_NUMBER_TO_CHECK, true);
|
prime_mask.pad(MAX_NUMBER_TO_CHECK + 1, true);
|
||||||
|
|
||||||
prime_mask[0] = false;
|
prime_mask[0] = false;
|
||||||
prime_mask[1] = false;
|
prime_mask[1] = false;
|
||||||
|
|
||||||
let total_primes_found = 0;
|
let total_primes_found = 0;
|
||||||
|
|
||||||
for p in range(2, MAX_NUMBER_TO_CHECK) {
|
for p in 2..=MAX_NUMBER_TO_CHECK {
|
||||||
if !prime_mask[p] { continue; }
|
if !prime_mask[p] { continue; }
|
||||||
|
|
||||||
//print(p);
|
//print(p);
|
||||||
|
|
||||||
total_primes_found += 1;
|
total_primes_found += 1;
|
||||||
|
|
||||||
for i in range(2 * p, MAX_NUMBER_TO_CHECK, p) {
|
for i in range(2 * p, MAX_NUMBER_TO_CHECK + 1, p) {
|
||||||
prime_mask[i] = false;
|
prime_mask[i] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,9 +156,9 @@ impl Engine {
|
|||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// engine.on_progress(move |ops| {
|
/// engine.on_progress(move |ops| {
|
||||||
/// if ops > 10000 {
|
/// if ops > 1000 {
|
||||||
/// Some("Over 10,000 operations!".into())
|
/// Some("Over 1,000 operations!".into())
|
||||||
/// } else if ops % 800 == 0 {
|
/// } else if ops % 123 == 0 {
|
||||||
/// *logger.write().unwrap() = ops;
|
/// *logger.write().unwrap() = ops;
|
||||||
/// None
|
/// None
|
||||||
/// } else {
|
/// } else {
|
||||||
@ -166,10 +166,10 @@ impl Engine {
|
|||||||
/// }
|
/// }
|
||||||
/// });
|
/// });
|
||||||
///
|
///
|
||||||
/// engine.run("for x in range(0, 50000) {}")
|
/// engine.run("for x in 0..5000 { print(x); }")
|
||||||
/// .expect_err("should error");
|
/// .expect_err("should error");
|
||||||
///
|
///
|
||||||
/// assert_eq!(*result.read().unwrap(), 9600);
|
/// assert_eq!(*result.read().unwrap(), 984);
|
||||||
///
|
///
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
|
50
src/ast.rs
50
src/ast.rs
@ -1,6 +1,7 @@
|
|||||||
//! Module defining the AST (abstract syntax tree).
|
//! Module defining the AST (abstract syntax tree).
|
||||||
|
|
||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
|
use crate::engine::{OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||||
use crate::func::hashing::ALT_ZERO_HASH;
|
use crate::func::hashing::ALT_ZERO_HASH;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
@ -1262,7 +1263,11 @@ pub enum Stmt {
|
|||||||
/// `switch` expr `if` condition `{` literal or _ `=>` stmt `,` ... `}`
|
/// `switch` expr `if` condition `{` literal or _ `=>` stmt `,` ... `}`
|
||||||
Switch(
|
Switch(
|
||||||
Expr,
|
Expr,
|
||||||
Box<(BTreeMap<u64, Box<(Option<Expr>, StmtBlock)>>, StmtBlock)>,
|
Box<(
|
||||||
|
BTreeMap<u64, Box<(Option<Expr>, StmtBlock)>>,
|
||||||
|
StmtBlock,
|
||||||
|
StaticVec<(INT, INT, bool, Option<Expr>, StmtBlock)>,
|
||||||
|
)>,
|
||||||
Position,
|
Position,
|
||||||
),
|
),
|
||||||
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
|
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
|
||||||
@ -1501,6 +1506,10 @@ impl Stmt {
|
|||||||
block.0.as_ref().map(Expr::is_pure).unwrap_or(true)
|
block.0.as_ref().map(Expr::is_pure).unwrap_or(true)
|
||||||
&& (block.1).0.iter().all(Stmt::is_pure)
|
&& (block.1).0.iter().all(Stmt::is_pure)
|
||||||
})
|
})
|
||||||
|
&& (x.2).iter().all(|(_, _, _, condition, stmt)| {
|
||||||
|
condition.as_ref().map(Expr::is_pure).unwrap_or(true)
|
||||||
|
&& stmt.0.iter().all(Stmt::is_pure)
|
||||||
|
})
|
||||||
&& (x.1).0.iter().all(Stmt::is_pure)
|
&& (x.1).0.iter().all(Stmt::is_pure)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1617,6 +1626,16 @@ impl Stmt {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (_, _, _, c, stmt) in &x.2 {
|
||||||
|
if !c.as_ref().map(|e| e.walk(path, on_node)).unwrap_or(true) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for s in &stmt.0 {
|
||||||
|
if !s.walk(path, on_node) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for s in &(x.1).0 {
|
for s in &(x.1).0 {
|
||||||
if !s.walk(path, on_node) {
|
if !s.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
@ -2235,6 +2254,35 @@ impl Expr {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Binary operators
|
||||||
|
Self::FnCall(x, _) if x.args.len() == 2 => match x.name.as_str() {
|
||||||
|
// x..y
|
||||||
|
OP_EXCLUSIVE_RANGE => {
|
||||||
|
if let Expr::IntegerConstant(ref start, _) = x.args[0] {
|
||||||
|
if let Expr::IntegerConstant(ref end, _) = x.args[1] {
|
||||||
|
(*start..*end).into()
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// x..=y
|
||||||
|
OP_INCLUSIVE_RANGE => {
|
||||||
|
if let Expr::IntegerConstant(ref start, _) = x.args[0] {
|
||||||
|
if let Expr::IntegerConstant(ref end, _) = x.args[1] {
|
||||||
|
(*start..=*end).into()
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
},
|
||||||
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
260
src/engine.rs
260
src/engine.rs
@ -13,8 +13,8 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
|||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
use crate::types::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
use crate::types::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
|
calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, Identifier, ImmutableString,
|
||||||
Shared, StaticVec, INT,
|
Module, Position, RhaiResult, Scope, Shared, StaticVec, INT,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -367,6 +367,12 @@ pub const OP_EQUALS: &str = "==";
|
|||||||
/// 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 exclusive range operator.
|
||||||
|
pub const OP_EXCLUSIVE_RANGE: &str = "..";
|
||||||
|
|
||||||
|
/// Standard inclusive range operator.
|
||||||
|
pub const OP_INCLUSIVE_RANGE: &str = "..=";
|
||||||
|
|
||||||
/// Standard concatenation operator token.
|
/// Standard concatenation operator token.
|
||||||
pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign;
|
pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign;
|
||||||
|
|
||||||
@ -484,7 +490,11 @@ pub enum Target<'a> {
|
|||||||
/// The target is a bit inside an [`INT`][crate::INT].
|
/// The target is a bit inside an [`INT`][crate::INT].
|
||||||
/// This is necessary because directly pointing to a bit inside an [`INT`][crate::INT] is impossible.
|
/// This is necessary because directly pointing to a bit inside an [`INT`][crate::INT] is impossible.
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
BitField(&'a mut Dynamic, usize, Dynamic),
|
Bit(&'a mut Dynamic, Dynamic, u8),
|
||||||
|
/// The target is a range of bits inside an [`INT`][crate::INT].
|
||||||
|
/// This is necessary because directly pointing to a range of bits inside an [`INT`][crate::INT] is impossible.
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
BitField(&'a mut Dynamic, INT, Dynamic, u8),
|
||||||
/// The target is a byte inside a Blob.
|
/// The target is a byte inside a Blob.
|
||||||
/// This is necessary because directly pointing to a byte (in [`Dynamic`] form) inside a blob is impossible.
|
/// This is necessary because directly pointing to a byte (in [`Dynamic`] form) inside a blob is impossible.
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -507,7 +517,10 @@ impl<'a> Target<'a> {
|
|||||||
Self::LockGuard(_) => true,
|
Self::LockGuard(_) => true,
|
||||||
Self::TempValue(_) => false,
|
Self::TempValue(_) => false,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
|
Self::Bit(_, _, _)
|
||||||
|
| Self::BitField(_, _, _, _)
|
||||||
|
| Self::BlobByte(_, _, _)
|
||||||
|
| Self::StringChar(_, _, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the `Target` a temp value?
|
/// Is the `Target` a temp value?
|
||||||
@ -520,7 +533,10 @@ impl<'a> Target<'a> {
|
|||||||
Self::LockGuard(_) => false,
|
Self::LockGuard(_) => false,
|
||||||
Self::TempValue(_) => true,
|
Self::TempValue(_) => true,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
|
Self::Bit(_, _, _)
|
||||||
|
| Self::BitField(_, _, _, _)
|
||||||
|
| Self::BlobByte(_, _, _)
|
||||||
|
| Self::StringChar(_, _, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the `Target` a shared value?
|
/// Is the `Target` a shared value?
|
||||||
@ -534,7 +550,10 @@ impl<'a> Target<'a> {
|
|||||||
Self::LockGuard(_) => true,
|
Self::LockGuard(_) => true,
|
||||||
Self::TempValue(r) => r.is_shared(),
|
Self::TempValue(r) => r.is_shared(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BitField(_, _, _) | Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
|
Self::Bit(_, _, _)
|
||||||
|
| Self::BitField(_, _, _, _)
|
||||||
|
| Self::BlobByte(_, _, _)
|
||||||
|
| Self::StringChar(_, _, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is the `Target` a specific type?
|
/// Is the `Target` a specific type?
|
||||||
@ -548,7 +567,9 @@ impl<'a> Target<'a> {
|
|||||||
Self::LockGuard((r, _)) => r.is::<T>(),
|
Self::LockGuard((r, _)) => r.is::<T>(),
|
||||||
Self::TempValue(r) => r.is::<T>(),
|
Self::TempValue(r) => r.is::<T>(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BitField(_, _, _) => TypeId::of::<T>() == TypeId::of::<bool>(),
|
Self::Bit(_, _, _) => TypeId::of::<T>() == TypeId::of::<bool>(),
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Self::BitField(_, _, _, _) => TypeId::of::<T>() == TypeId::of::<INT>(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BlobByte(_, _, _) => TypeId::of::<T>() == TypeId::of::<crate::Blob>(),
|
Self::BlobByte(_, _, _) => TypeId::of::<T>() == TypeId::of::<crate::Blob>(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -565,7 +586,9 @@ impl<'a> Target<'a> {
|
|||||||
Self::LockGuard((_, orig)) => orig, // Original value is simply taken
|
Self::LockGuard((_, orig)) => orig, // Original value is simply taken
|
||||||
Self::TempValue(v) => v, // Owned value is simply taken
|
Self::TempValue(v) => v, // Owned value is simply taken
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BitField(_, _, value) => value, // Boolean is taken
|
Self::Bit(_, value, _) => value, // Boolean is taken
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Self::BitField(_, _, value, _) => value, // INT is taken
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BlobByte(_, _, value) => value, // Byte is taken
|
Self::BlobByte(_, _, value) => value, // Byte is taken
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -596,7 +619,7 @@ impl<'a> Target<'a> {
|
|||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::LockGuard(_) => (),
|
Self::LockGuard(_) => (),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BitField(value, index, new_val) => {
|
Self::Bit(value, new_val, index) => {
|
||||||
// Replace the bit at the specified index position
|
// Replace the bit at the specified index position
|
||||||
let new_bit = new_val.as_bool().map_err(|err| {
|
let new_bit = new_val.as_bool().map_err(|err| {
|
||||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||||
@ -610,18 +633,34 @@ impl<'a> Target<'a> {
|
|||||||
|
|
||||||
let index = *index;
|
let index = *index;
|
||||||
|
|
||||||
if index < std::mem::size_of_val(value) * 8 {
|
let mask = 1 << index;
|
||||||
let mask = 1 << index;
|
if new_bit {
|
||||||
if new_bit {
|
*value |= mask;
|
||||||
*value |= mask;
|
|
||||||
} else {
|
|
||||||
*value &= !mask;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
unreachable!("bit-field index out of bounds: {}", index);
|
*value &= !mask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Self::BitField(value, mask, new_val, shift) => {
|
||||||
|
let shift = *shift;
|
||||||
|
let mask = *mask;
|
||||||
|
|
||||||
|
// Replace the bit at the specified index position
|
||||||
|
let new_value = new_val.as_int().map_err(|err| {
|
||||||
|
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||||
|
"integer".to_string(),
|
||||||
|
err.to_string(),
|
||||||
|
Position::NONE,
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let new_value = (new_value << shift) & mask;
|
||||||
|
let value = &mut *value.write_lock::<crate::INT>().expect("`INT`");
|
||||||
|
|
||||||
|
*value &= !mask;
|
||||||
|
*value |= new_value;
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BlobByte(value, index, new_val) => {
|
Self::BlobByte(value, index, new_val) => {
|
||||||
// Replace the byte at the specified index position
|
// Replace the byte at the specified index position
|
||||||
let new_byte = new_val.as_int().map_err(|err| {
|
let new_byte = new_val.as_int().map_err(|err| {
|
||||||
@ -696,7 +735,8 @@ impl Deref for Target<'_> {
|
|||||||
Self::LockGuard((r, _)) => &**r,
|
Self::LockGuard((r, _)) => &**r,
|
||||||
Self::TempValue(ref r) => r,
|
Self::TempValue(ref r) => r,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BitField(_, _, ref r)
|
Self::Bit(_, ref r, _)
|
||||||
|
| Self::BitField(_, _, ref r, _)
|
||||||
| Self::BlobByte(_, _, ref r)
|
| Self::BlobByte(_, _, ref r)
|
||||||
| Self::StringChar(_, _, ref r) => r,
|
| Self::StringChar(_, _, ref r) => r,
|
||||||
}
|
}
|
||||||
@ -719,7 +759,8 @@ impl DerefMut for Target<'_> {
|
|||||||
Self::LockGuard((r, _)) => r.deref_mut(),
|
Self::LockGuard((r, _)) => r.deref_mut(),
|
||||||
Self::TempValue(ref mut r) => r,
|
Self::TempValue(ref mut r) => r,
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::BitField(_, _, ref mut r)
|
Self::Bit(_, ref mut r, _)
|
||||||
|
| Self::BitField(_, _, ref mut r, _)
|
||||||
| Self::BlobByte(_, _, ref mut r)
|
| Self::BlobByte(_, _, ref mut r)
|
||||||
| Self::StringChar(_, _, ref mut r) => r,
|
| Self::StringChar(_, _, ref mut r) => r,
|
||||||
}
|
}
|
||||||
@ -2078,6 +2119,67 @@ impl Engine {
|
|||||||
.unwrap_or_else(|| Target::from(Dynamic::UNIT)))
|
.unwrap_or_else(|| Target::from(Dynamic::UNIT)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Dynamic(Union::Int(value, _, _))
|
||||||
|
if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() =>
|
||||||
|
{
|
||||||
|
#[cfg(not(feature = "only_i32"))]
|
||||||
|
type BASE = u64;
|
||||||
|
#[cfg(feature = "only_i32")]
|
||||||
|
type BASE = u32;
|
||||||
|
|
||||||
|
// val_int[range]
|
||||||
|
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
||||||
|
|
||||||
|
let (shift, mask) = if let Some(range) = idx.read_lock::<crate::ExclusiveRange>() {
|
||||||
|
let start = range.start;
|
||||||
|
let end = range.end;
|
||||||
|
|
||||||
|
if start < 0 || start as usize >= BITS {
|
||||||
|
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, start, idx_pos).into());
|
||||||
|
} else if end < 0 || end as usize >= BITS {
|
||||||
|
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, end, idx_pos).into());
|
||||||
|
} else if end <= start {
|
||||||
|
(0, 0)
|
||||||
|
} else if end as usize == BITS && start == 0 {
|
||||||
|
// -1 = all bits set
|
||||||
|
(0, -1)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start as u8,
|
||||||
|
// 2^bits - 1
|
||||||
|
(((2 as BASE).pow((end - start) as u32) - 1) as INT) << start,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if let Some(range) = idx.read_lock::<crate::InclusiveRange>() {
|
||||||
|
let start = *range.start();
|
||||||
|
let end = *range.end();
|
||||||
|
|
||||||
|
if start < 0 || start as usize >= BITS {
|
||||||
|
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, start, idx_pos).into());
|
||||||
|
} else if end < 0 || end as usize >= BITS {
|
||||||
|
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, end, idx_pos).into());
|
||||||
|
} else if end < start {
|
||||||
|
(0, 0)
|
||||||
|
} else if end as usize == BITS - 1 && start == 0 {
|
||||||
|
// -1 = all bits set
|
||||||
|
(0, -1)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
start as u8,
|
||||||
|
// 2^bits - 1
|
||||||
|
(((2 as BASE).pow((end - start + 1) as u32) - 1) as INT) << start,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!("`Range` or `RangeInclusive`");
|
||||||
|
};
|
||||||
|
|
||||||
|
let field_value = (*value & mask) >> shift;
|
||||||
|
|
||||||
|
Ok(Target::BitField(target, mask, field_value.into(), shift))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Int(value, _, _)) => {
|
Dynamic(Union::Int(value, _, _)) => {
|
||||||
// val_int[idx]
|
// val_int[idx]
|
||||||
@ -2085,38 +2187,38 @@ impl Engine {
|
|||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
||||||
|
|
||||||
let bits = std::mem::size_of_val(value) * 8;
|
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
||||||
|
|
||||||
let (bit_value, offset) = if index >= 0 {
|
let (bit_value, offset) = if index >= 0 {
|
||||||
let offset = index as usize;
|
let offset = index as usize;
|
||||||
(
|
(
|
||||||
if offset >= bits {
|
if offset >= BITS {
|
||||||
return Err(
|
return Err(
|
||||||
EvalAltResult::ErrorBitFieldBounds(bits, index, idx_pos).into()
|
EvalAltResult::ErrorBitFieldBounds(BITS, index, idx_pos).into()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
(*value & (1 << offset)) != 0
|
(*value & (1 << offset)) != 0
|
||||||
},
|
},
|
||||||
offset,
|
offset as u8,
|
||||||
)
|
)
|
||||||
} else if let Some(abs_index) = index.checked_abs() {
|
} else if let Some(abs_index) = index.checked_abs() {
|
||||||
let offset = abs_index as usize;
|
let offset = abs_index as usize;
|
||||||
(
|
(
|
||||||
// Count from end if negative
|
// Count from end if negative
|
||||||
if offset > bits {
|
if offset > BITS {
|
||||||
return Err(
|
return Err(
|
||||||
EvalAltResult::ErrorBitFieldBounds(bits, index, idx_pos).into()
|
EvalAltResult::ErrorBitFieldBounds(BITS, index, idx_pos).into()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
(*value & (1 << (bits - offset))) != 0
|
(*value & (1 << (BITS - offset))) != 0
|
||||||
},
|
},
|
||||||
offset,
|
offset as u8,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return Err(EvalAltResult::ErrorBitFieldBounds(bits, index, idx_pos).into());
|
return Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, idx_pos).into());
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Target::BitField(target, offset, bit_value.into()))
|
Ok(Target::Bit(target, bit_value.into(), offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -2660,48 +2762,80 @@ impl Engine {
|
|||||||
|
|
||||||
// Switch statement
|
// Switch statement
|
||||||
Stmt::Switch(match_expr, x, _) => {
|
Stmt::Switch(match_expr, x, _) => {
|
||||||
let (table, def_stmt) = x.as_ref();
|
let (table, def_stmt, ranges) = x.as_ref();
|
||||||
|
|
||||||
let value = self.eval_expr(scope, mods, state, lib, this_ptr, match_expr, level)?;
|
let value = self.eval_expr(scope, mods, state, lib, this_ptr, match_expr, level)?;
|
||||||
|
|
||||||
if value.is_hashable() {
|
let stmt_block = if value.is_hashable() {
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
value.hash(hasher);
|
value.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
table.get(&hash).and_then(|t| {
|
// First check hashes
|
||||||
if let Some(condition) = &t.0 {
|
if let Some(t) = table.get(&hash) {
|
||||||
match self
|
if let Some(ref c) = t.0 {
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &condition, level)
|
if self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, &c, level)
|
||||||
.and_then(|v| {
|
.and_then(|v| {
|
||||||
v.as_bool().map_err(|typ| {
|
v.as_bool().map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<bool>(
|
self.make_type_mismatch_err::<bool>(typ, c.position())
|
||||||
typ,
|
|
||||||
condition.position(),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}) {
|
})?
|
||||||
Ok(true) => (),
|
{
|
||||||
Ok(false) => return None,
|
Some(&t.1)
|
||||||
Err(err) => return Some(Err(err)),
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Some(&t.1)
|
||||||
|
}
|
||||||
|
} else if value.is::<INT>() && !ranges.is_empty() {
|
||||||
|
// Then check integer ranges
|
||||||
|
let value = value.as_int().expect("`INT`");
|
||||||
|
let mut result = None;
|
||||||
|
|
||||||
|
for (_, _, _, condition, stmt_block) in
|
||||||
|
ranges.iter().filter(|&&(start, end, inclusive, _, _)| {
|
||||||
|
(!inclusive && (start..end).contains(&value))
|
||||||
|
|| (inclusive && (start..=end).contains(&value))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if let Some(c) = condition {
|
||||||
|
if !self
|
||||||
|
.eval_expr(scope, mods, state, lib, this_ptr, &c, level)
|
||||||
|
.and_then(|v| {
|
||||||
|
v.as_bool().map_err(|typ| {
|
||||||
|
self.make_type_mismatch_err::<bool>(typ, c.position())
|
||||||
|
})
|
||||||
|
})?
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = Some(stmt_block);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let statements = &t.1;
|
result
|
||||||
|
} else {
|
||||||
Some(if !statements.is_empty() {
|
// Nothing matches
|
||||||
self.eval_stmt_block(
|
None
|
||||||
scope, mods, state, lib, this_ptr, statements, true, true, level,
|
}
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
// Non-hashable values never match any specific clause
|
// Non-hashable
|
||||||
None
|
None
|
||||||
}
|
};
|
||||||
.unwrap_or_else(|| {
|
|
||||||
|
if let Some(statements) = stmt_block {
|
||||||
|
if !statements.is_empty() {
|
||||||
|
self.eval_stmt_block(
|
||||||
|
scope, mods, state, lib, this_ptr, statements, true, true, level,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Ok(Dynamic::UNIT)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Default match clause
|
// Default match clause
|
||||||
if !def_stmt.is_empty() {
|
if !def_stmt.is_empty() {
|
||||||
self.eval_stmt_block(
|
self.eval_stmt_block(
|
||||||
@ -2710,7 +2844,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop
|
// Loop
|
||||||
@ -3163,6 +3297,18 @@ impl Engine {
|
|||||||
.map_err(|err| err.fill_position(stmt.position()))
|
.map_err(|err| err.fill_position(stmt.position()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Has a system function a Rust-native override?
|
||||||
|
pub(crate) fn has_native_fn_override(&self, hash_script: u64, arg_types: &[TypeId]) -> bool {
|
||||||
|
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
|
||||||
|
let hash = combine_hashes(hash_script, hash_params);
|
||||||
|
|
||||||
|
// First check the global namespace and packages, but skip modules that are standard because
|
||||||
|
// they should never conflict with system functions.
|
||||||
|
self.global_modules.iter().filter(|m| !m.standard).any(|m| m.contains_fn(hash))
|
||||||
|
// Then check sub-modules
|
||||||
|
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
|
||||||
|
}
|
||||||
|
|
||||||
/// Check a result to ensure that the data size is within allowable limit.
|
/// Check a result to ensure that the data size is within allowable limit.
|
||||||
fn check_return_value(&self, mut result: RhaiResult) -> RhaiResult {
|
fn check_return_value(&self, mut result: RhaiResult) -> RhaiResult {
|
||||||
if let Ok(ref mut r) = result {
|
if let Ok(ref mut r) = result {
|
||||||
|
@ -352,6 +352,16 @@ pub fn get_builtin_binary_op_fn(
|
|||||||
"&" => Some(impl_op!(INT => as_int & as_int)),
|
"&" => Some(impl_op!(INT => as_int & as_int)),
|
||||||
"|" => Some(impl_op!(INT => as_int | as_int)),
|
"|" => Some(impl_op!(INT => as_int | as_int)),
|
||||||
"^" => Some(impl_op!(INT => as_int ^ as_int)),
|
"^" => Some(impl_op!(INT => as_int ^ as_int)),
|
||||||
|
".." => Some(|_, args| {
|
||||||
|
let x = args[0].as_int().expect(BUILTIN);
|
||||||
|
let y = args[1].as_int().expect(BUILTIN);
|
||||||
|
Ok((x..y).into())
|
||||||
|
}),
|
||||||
|
"..=" => Some(|_, args| {
|
||||||
|
let x = args[0].as_int().expect(BUILTIN);
|
||||||
|
let y = args[1].as_int().expect(BUILTIN);
|
||||||
|
Ok((x..=y).into())
|
||||||
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -115,9 +115,14 @@ pub type FLOAT = f64;
|
|||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
pub type FLOAT = f32;
|
pub type FLOAT = f32;
|
||||||
|
|
||||||
|
pub type ExclusiveRange = std::ops::Range<INT>;
|
||||||
|
pub type InclusiveRange = std::ops::RangeInclusive<INT>;
|
||||||
|
|
||||||
pub use ast::{FnAccess, AST};
|
pub use ast::{FnAccess, AST};
|
||||||
pub use custom_syntax::Expression;
|
pub use custom_syntax::Expression;
|
||||||
pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS};
|
pub use engine::{
|
||||||
|
Engine, EvalContext, OP_CONTAINS, OP_EQUALS, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE,
|
||||||
|
};
|
||||||
pub use func::{NativeCallContext, RegisterNativeFunction};
|
pub use func::{NativeCallContext, RegisterNativeFunction};
|
||||||
pub use module::{FnNamespace, Module};
|
pub use module::{FnNamespace, Module};
|
||||||
pub use tokenizer::Position;
|
pub use tokenizer::Position;
|
||||||
|
162
src/optimizer.rs
162
src/optimizer.rs
@ -9,14 +9,10 @@ use crate::func::builtin::get_builtin_binary_op_fn;
|
|||||||
use crate::func::hashing::get_hasher;
|
use crate::func::hashing::get_hasher;
|
||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{
|
use crate::{calc_fn_hash, Dynamic, Engine, FnPtr, Position, Scope, StaticVec, AST, INT};
|
||||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope,
|
|
||||||
StaticVec, AST,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
mem,
|
mem,
|
||||||
@ -155,17 +151,6 @@ impl<'a> OptimizerState<'a> {
|
|||||||
.ok()
|
.ok()
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
// Has a system function a Rust-native override?
|
|
||||||
pub fn has_native_fn_override(&self, hash_script: u64, arg_types: &[TypeId]) -> bool {
|
|
||||||
let hash_params = calc_fn_params_hash(arg_types.iter().cloned());
|
|
||||||
let hash = combine_hashes(hash_script, hash_params);
|
|
||||||
|
|
||||||
// First check the global namespace and packages, but skip modules that are standard because
|
|
||||||
// they should never conflict with system functions.
|
|
||||||
self.engine.global_modules.iter().filter(|m| !m.standard).any(|m| m.contains_fn(hash))
|
|
||||||
// Then check sub-modules
|
|
||||||
|| self.engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize a block of [statements][Stmt].
|
/// Optimize a block of [statements][Stmt].
|
||||||
@ -478,21 +463,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
value.hash(hasher);
|
value.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
state.set_dirty();
|
|
||||||
let table = &mut x.0;
|
let table = &mut x.0;
|
||||||
|
|
||||||
|
// First check hashes
|
||||||
if let Some(block) = table.get_mut(&hash) {
|
if let Some(block) = table.get_mut(&hash) {
|
||||||
if let Some(mut condition) = mem::take(&mut block.0) {
|
if let Some(mut condition) = mem::take(&mut block.0) {
|
||||||
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
|
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||||
optimize_expr(&mut condition, state, false);
|
optimize_expr(&mut condition, state, false);
|
||||||
|
|
||||||
let def_block = mem::take(&mut *x.1);
|
|
||||||
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
|
|
||||||
let def_pos = if x.1.position().is_none() {
|
let def_pos = if x.1.position().is_none() {
|
||||||
*pos
|
*pos
|
||||||
} else {
|
} else {
|
||||||
x.1.position()
|
x.1.position()
|
||||||
};
|
};
|
||||||
|
let def_stmt =
|
||||||
|
optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
|
||||||
|
|
||||||
*stmt = Stmt::If(
|
*stmt = Stmt::If(
|
||||||
condition,
|
condition,
|
||||||
@ -505,46 +490,121 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
} else {
|
} else {
|
||||||
// Promote the matched case
|
// Promote the matched case
|
||||||
let new_pos = block.1.position();
|
let new_pos = block.1.position();
|
||||||
let statements = mem::take(&mut *block.1);
|
let statements =
|
||||||
let statements = optimize_stmt_block(statements, state, true, true, false);
|
optimize_stmt_block(mem::take(&mut *block.1), state, true, true, false);
|
||||||
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
|
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Promote the default case
|
state.set_dirty();
|
||||||
let def_block = mem::take(&mut *x.1);
|
return;
|
||||||
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
|
|
||||||
let def_pos = if x.1.position().is_none() {
|
|
||||||
*pos
|
|
||||||
} else {
|
|
||||||
x.1.position()
|
|
||||||
};
|
|
||||||
*stmt = Stmt::Block(def_stmt.into_boxed_slice(), def_pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then check ranges
|
||||||
|
let ranges = &mut x.2;
|
||||||
|
|
||||||
|
if value.is::<INT>() && !ranges.is_empty() {
|
||||||
|
let value = value.as_int().expect("`INT`");
|
||||||
|
|
||||||
|
// Only one range or all ranges without conditions
|
||||||
|
if ranges.len() == 1 || ranges.iter().all(|(_, _, _, c, _)| c.is_none()) {
|
||||||
|
for (_, _, _, condition, stmt_block) in
|
||||||
|
ranges
|
||||||
|
.iter_mut()
|
||||||
|
.filter(|&&mut (start, end, inclusive, _, _)| {
|
||||||
|
(!inclusive && (start..end).contains(&value))
|
||||||
|
|| (inclusive && (start..=end).contains(&value))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if let Some(mut condition) = mem::take(condition) {
|
||||||
|
// switch const { range if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||||
|
optimize_expr(&mut condition, state, false);
|
||||||
|
|
||||||
|
let def_block = mem::take(&mut *x.1);
|
||||||
|
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
|
||||||
|
let def_pos = if x.1.position().is_none() {
|
||||||
|
*pos
|
||||||
|
} else {
|
||||||
|
x.1.position()
|
||||||
|
};
|
||||||
|
|
||||||
|
*stmt = Stmt::If(
|
||||||
|
condition,
|
||||||
|
Box::new((
|
||||||
|
mem::take(stmt_block),
|
||||||
|
Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(),
|
||||||
|
)),
|
||||||
|
match_expr.position(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Promote the matched case
|
||||||
|
let new_pos = stmt_block.position();
|
||||||
|
let statements = mem::take(&mut **stmt_block);
|
||||||
|
let statements =
|
||||||
|
optimize_stmt_block(statements, state, true, true, false);
|
||||||
|
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
state.set_dirty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Multiple ranges - clear the table and just keep the right ranges
|
||||||
|
if !table.is_empty() {
|
||||||
|
state.set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
table.clear();
|
||||||
|
|
||||||
|
let old_ranges_len = ranges.len();
|
||||||
|
|
||||||
|
ranges.retain(|&mut (start, end, inclusive, _, _)| {
|
||||||
|
(!inclusive && (start..end).contains(&value))
|
||||||
|
|| (inclusive && (start..=end).contains(&value))
|
||||||
|
});
|
||||||
|
|
||||||
|
if ranges.len() != old_ranges_len {
|
||||||
|
state.set_dirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, _, _, condition, stmt_block) in ranges.iter_mut() {
|
||||||
|
let statements = mem::take(&mut **stmt_block);
|
||||||
|
**stmt_block =
|
||||||
|
optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||||
|
|
||||||
|
if let Some(mut c) = mem::take(condition) {
|
||||||
|
optimize_expr(&mut c, state, false);
|
||||||
|
match c {
|
||||||
|
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
|
||||||
|
_ => *condition = Some(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Promote the default case
|
||||||
|
state.set_dirty();
|
||||||
|
let def_pos = if x.1.position().is_none() {
|
||||||
|
*pos
|
||||||
|
} else {
|
||||||
|
x.1.position()
|
||||||
|
};
|
||||||
|
let def_stmt = optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
|
||||||
|
*stmt = Stmt::Block(def_stmt.into_boxed_slice(), def_pos);
|
||||||
}
|
}
|
||||||
// switch
|
// switch
|
||||||
Stmt::Switch(match_expr, x, _) => {
|
Stmt::Switch(match_expr, x, _) => {
|
||||||
optimize_expr(match_expr, state, false);
|
optimize_expr(match_expr, state, false);
|
||||||
x.0.values_mut().for_each(|block| {
|
x.0.values_mut().for_each(|block| {
|
||||||
let condition = mem::take(&mut block.0).map_or_else(
|
let statements = mem::take(block.1.deref_mut());
|
||||||
|| Expr::Unit(Position::NONE),
|
*block.1 = optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||||
|mut condition| {
|
|
||||||
optimize_expr(&mut condition, state, false);
|
|
||||||
condition
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match condition {
|
if let Some(mut condition) = mem::take(&mut block.0) {
|
||||||
Expr::Unit(_) | Expr::BoolConstant(true, _) => (),
|
optimize_expr(&mut condition, state, false);
|
||||||
_ => {
|
match condition {
|
||||||
block.0 = Some(condition);
|
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
|
||||||
|
_ => block.0 = Some(condition),
|
||||||
*block.1 = optimize_stmt_block(
|
|
||||||
mem::take(block.1.deref_mut()),
|
|
||||||
state,
|
|
||||||
preserve_result,
|
|
||||||
true,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -990,7 +1050,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Overloaded operators can override built-in.
|
// Overloaded operators can override built-in.
|
||||||
_ if x.args.len() == 2 && !state.has_native_fn_override(x.hashes.native, arg_types.as_ref()) => {
|
_ if x.args.len() == 2 && !state.engine.has_native_fn_override(x.hashes.native, arg_types.as_ref()) => {
|
||||||
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|f| {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
@ -121,12 +121,15 @@ macro_rules! gen_arithmetic_functions {
|
|||||||
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
||||||
x ^ y
|
x ^ y
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: $arg_type) -> bool {
|
pub fn is_zero(x: $arg_type) -> bool {
|
||||||
x == 0
|
x == 0
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
||||||
pub fn is_odd(x: $arg_type) -> bool {
|
pub fn is_odd(x: $arg_type) -> bool {
|
||||||
x % 2 != 0
|
x % 2 != 0
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(get = "is_even", name = "is_even")]
|
||||||
pub fn is_even(x: $arg_type) -> bool {
|
pub fn is_even(x: $arg_type) -> bool {
|
||||||
x % 2 == 0
|
x % 2 == 0
|
||||||
}
|
}
|
||||||
@ -209,12 +212,15 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod int_functions {
|
mod int_functions {
|
||||||
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: INT) -> bool {
|
pub fn is_zero(x: INT) -> bool {
|
||||||
x == 0
|
x == 0
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
||||||
pub fn is_odd(x: INT) -> bool {
|
pub fn is_odd(x: INT) -> bool {
|
||||||
x % 2 != 0
|
x % 2 != 0
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(get = "is_even", name = "is_even")]
|
||||||
pub fn is_even(x: INT) -> bool {
|
pub fn is_even(x: INT) -> bool {
|
||||||
x % 2 == 0
|
x % 2 == 0
|
||||||
}
|
}
|
||||||
@ -335,6 +341,7 @@ mod f32_functions {
|
|||||||
x => Ok(x as INT),
|
x => Ok(x as INT),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: f32) -> bool {
|
pub fn is_zero(x: f32) -> bool {
|
||||||
x == 0.0
|
x == 0.0
|
||||||
}
|
}
|
||||||
@ -442,6 +449,7 @@ mod f64_functions {
|
|||||||
x => Ok(x as INT),
|
x => Ok(x as INT),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: f64) -> bool {
|
pub fn is_zero(x: f64) -> bool {
|
||||||
x == 0.0
|
x == 0.0
|
||||||
}
|
}
|
||||||
@ -536,6 +544,7 @@ pub mod decimal_functions {
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||||
pub fn is_zero(x: Decimal) -> bool {
|
pub fn is_zero(x: Decimal) -> bool {
|
||||||
x.is_zero()
|
x.is_zero()
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
|
|
||||||
use crate::engine::OP_EQUALS;
|
use crate::engine::OP_EQUALS;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, Array, Dynamic, EvalAltResult, FnPtr, NativeCallContext, Position, INT};
|
use crate::{
|
||||||
|
def_package, Array, Dynamic, EvalAltResult, ExclusiveRange, FnPtr, InclusiveRange,
|
||||||
|
NativeCallContext, Position, INT,
|
||||||
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::TypeId, cmp::Ordering, mem};
|
use std::{any::TypeId, cmp::Ordering, mem};
|
||||||
@ -145,6 +148,18 @@ mod array_functions {
|
|||||||
array.reverse();
|
array.reverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "splice")]
|
||||||
|
pub fn splice_range(array: &mut Array, range: ExclusiveRange, replace: Array) {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
splice(array, start, end - start, replace)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "splice")]
|
||||||
|
pub fn splice_inclusive_range(array: &mut Array, range: InclusiveRange, replace: Array) {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
splice(array, start, end - start + 1, replace)
|
||||||
|
}
|
||||||
pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) {
|
pub fn splice(array: &mut Array, start: INT, len: INT, replace: Array) {
|
||||||
if array.is_empty() {
|
if array.is_empty() {
|
||||||
*array = replace;
|
*array = replace;
|
||||||
@ -173,6 +188,18 @@ mod array_functions {
|
|||||||
|
|
||||||
array.splice(start..start + len, replace.into_iter());
|
array.splice(start..start + len, replace.into_iter());
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "extract")]
|
||||||
|
pub fn extract_range(array: &mut Array, range: ExclusiveRange) -> Array {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
extract(array, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "extract")]
|
||||||
|
pub fn extract_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
extract(array, start, end - start + 1)
|
||||||
|
}
|
||||||
pub fn extract(array: &mut Array, start: INT, len: INT) -> Array {
|
pub fn extract(array: &mut Array, start: INT, len: INT) -> Array {
|
||||||
if array.is_empty() || len <= 0 {
|
if array.is_empty() || len <= 0 {
|
||||||
return Array::new();
|
return Array::new();
|
||||||
@ -911,6 +938,18 @@ mod array_functions {
|
|||||||
drain(ctx, array, FnPtr::new(filter)?)
|
drain(ctx, array, FnPtr::new(filter)?)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "drain")]
|
#[rhai_fn(name = "drain")]
|
||||||
|
pub fn drain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
drain_range(array, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "drain")]
|
||||||
|
pub fn drain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
drain_range(array, start, end - start + 1)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "drain")]
|
||||||
pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array {
|
pub fn drain_range(array: &mut Array, start: INT, len: INT) -> Array {
|
||||||
if array.is_empty() || len <= 0 {
|
if array.is_empty() || len <= 0 {
|
||||||
return Array::new();
|
return Array::new();
|
||||||
@ -993,6 +1032,18 @@ mod array_functions {
|
|||||||
retain(ctx, array, FnPtr::new(filter)?)
|
retain(ctx, array, FnPtr::new(filter)?)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "retain")]
|
#[rhai_fn(name = "retain")]
|
||||||
|
pub fn retain_exclusive_range(array: &mut Array, range: ExclusiveRange) -> Array {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
retain_range(array, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "retain")]
|
||||||
|
pub fn retain_inclusive_range(array: &mut Array, range: InclusiveRange) -> Array {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
retain_range(array, start, end - start + 1)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "retain")]
|
||||||
pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array {
|
pub fn retain_range(array: &mut Array, start: INT, len: INT) -> Array {
|
||||||
if array.is_empty() || len <= 0 {
|
if array.is_empty() || len <= 0 {
|
||||||
return Array::new();
|
return Array::new();
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, Blob, Dynamic, EvalAltResult, NativeCallContext, Position, INT};
|
use crate::{
|
||||||
|
def_package, Blob, Dynamic, EvalAltResult, ExclusiveRange, InclusiveRange, NativeCallContext,
|
||||||
|
Position, INT,
|
||||||
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::TypeId, mem};
|
use std::{any::TypeId, mem};
|
||||||
@ -184,6 +187,18 @@ mod blob_functions {
|
|||||||
blob.reverse();
|
blob.reverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "splice")]
|
||||||
|
pub fn splice_range(blob: &mut Blob, range: ExclusiveRange, replace: Blob) {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
splice(blob, start, end - start, replace)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "splice")]
|
||||||
|
pub fn splice_range_inclusive(blob: &mut Blob, range: InclusiveRange, replace: Blob) {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
splice(blob, start, end - start + 1, replace)
|
||||||
|
}
|
||||||
pub fn splice(blob: &mut Blob, start: INT, len: INT, replace: Blob) {
|
pub fn splice(blob: &mut Blob, start: INT, len: INT, replace: Blob) {
|
||||||
if blob.is_empty() {
|
if blob.is_empty() {
|
||||||
*blob = replace;
|
*blob = replace;
|
||||||
@ -212,6 +227,18 @@ mod blob_functions {
|
|||||||
|
|
||||||
blob.splice(start..start + len, replace.into_iter());
|
blob.splice(start..start + len, replace.into_iter());
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "extract")]
|
||||||
|
pub fn extract_range(blob: &mut Blob, range: ExclusiveRange) -> Blob {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
extract(blob, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "extract")]
|
||||||
|
pub fn extract_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
extract(blob, start, end - start + 1)
|
||||||
|
}
|
||||||
pub fn extract(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
pub fn extract(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
||||||
if blob.is_empty() || len <= 0 {
|
if blob.is_empty() || len <= 0 {
|
||||||
return Blob::new();
|
return Blob::new();
|
||||||
@ -264,6 +291,18 @@ mod blob_functions {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "drain")]
|
||||||
|
pub fn drain_range(blob: &mut Blob, range: ExclusiveRange) -> Blob {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
drain(blob, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "drain")]
|
||||||
|
pub fn drain_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
drain(blob, start, end - start + 1)
|
||||||
|
}
|
||||||
pub fn drain(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
pub fn drain(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
||||||
if blob.is_empty() || len <= 0 {
|
if blob.is_empty() || len <= 0 {
|
||||||
return Blob::new();
|
return Blob::new();
|
||||||
@ -288,6 +327,18 @@ mod blob_functions {
|
|||||||
|
|
||||||
blob.drain(start..start + len).collect()
|
blob.drain(start..start + len).collect()
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "retain")]
|
||||||
|
pub fn retain_range(blob: &mut Blob, range: ExclusiveRange) -> Blob {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
retain(blob, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "retain")]
|
||||||
|
pub fn retain_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> Blob {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
retain(blob, start, end - start + 1)
|
||||||
|
}
|
||||||
pub fn retain(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
pub fn retain(blob: &mut Blob, start: INT, len: INT) -> Blob {
|
||||||
if blob.is_empty() || len <= 0 {
|
if blob.is_empty() || len <= 0 {
|
||||||
return Blob::new();
|
return Blob::new();
|
||||||
@ -374,9 +425,33 @@ mod blob_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "parse_le_int")]
|
||||||
|
pub fn parse_le_int_range(blob: &mut Blob, range: ExclusiveRange) -> INT {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
parse_le_int(blob, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "parse_le_int")]
|
||||||
|
pub fn parse_le_int_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> INT {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
parse_le_int(blob, start, end - start + 1)
|
||||||
|
}
|
||||||
pub fn parse_le_int(blob: &mut Blob, start: INT, len: INT) -> INT {
|
pub fn parse_le_int(blob: &mut Blob, start: INT, len: INT) -> INT {
|
||||||
parse_int(blob, start, len, true)
|
parse_int(blob, start, len, true)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "parse_be_int")]
|
||||||
|
pub fn parse_be_int_range(blob: &mut Blob, range: ExclusiveRange) -> INT {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
parse_be_int(blob, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "parse_be_int")]
|
||||||
|
pub fn parse_be_int_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> INT {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
parse_be_int(blob, start, end - start + 1)
|
||||||
|
}
|
||||||
pub fn parse_be_int(blob: &mut Blob, start: INT, len: INT) -> INT {
|
pub fn parse_be_int(blob: &mut Blob, start: INT, len: INT) -> INT {
|
||||||
parse_int(blob, start, len, false)
|
parse_int(blob, start, len, false)
|
||||||
}
|
}
|
||||||
@ -418,11 +493,35 @@ mod blob_functions {
|
|||||||
|
|
||||||
blob[start..][..len].copy_from_slice(&buf[..len]);
|
blob[start..][..len].copy_from_slice(&buf[..len]);
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "write_le_int")]
|
||||||
|
pub fn write_le_int_range(blob: &mut Blob, range: ExclusiveRange, value: INT) {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
write_le_int(blob, start, end - start, value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "write_le_int")]
|
||||||
|
pub fn write_le_int_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: INT) {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
write_le_int(blob, start, end - start + 1, value)
|
||||||
|
}
|
||||||
#[rhai_fn(name = "write_le")]
|
#[rhai_fn(name = "write_le")]
|
||||||
pub fn write_le_int(blob: &mut Blob, start: INT, len: INT, value: INT) {
|
pub fn write_le_int(blob: &mut Blob, start: INT, len: INT, value: INT) {
|
||||||
write_int(blob, start, len, value, true)
|
write_int(blob, start, len, value, true)
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "write_be")]
|
#[rhai_fn(name = "write_be")]
|
||||||
|
pub fn write_be_int_range(blob: &mut Blob, range: ExclusiveRange, value: INT) {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
write_be_int(blob, start, end - start, value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "write_be")]
|
||||||
|
pub fn write_be_int_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: INT) {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
write_be_int(blob, start, end - start + 1, value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "write_be")]
|
||||||
pub fn write_be_int(blob: &mut Blob, start: INT, len: INT, value: INT) {
|
pub fn write_be_int(blob: &mut Blob, start: INT, len: INT, value: INT) {
|
||||||
write_int(blob, start, len, value, false)
|
write_int(blob, start, len, value, false)
|
||||||
}
|
}
|
||||||
@ -466,11 +565,39 @@ mod blob_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[rhai_fn(name = "parse_le_float")]
|
||||||
|
pub fn parse_le_float_range(blob: &mut Blob, range: ExclusiveRange) -> FLOAT {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
parse_le_float(blob, start, end - start)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[rhai_fn(name = "parse_le_float")]
|
||||||
|
pub fn parse_le_float_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> FLOAT {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
parse_le_float(blob, start, end - start + 1)
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub fn parse_le_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
pub fn parse_le_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
||||||
parse_float(blob, start, len, true)
|
parse_float(blob, start, len, true)
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[rhai_fn(name = "parse_be_float")]
|
||||||
|
pub fn parse_be_float_range(blob: &mut Blob, range: ExclusiveRange) -> FLOAT {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
parse_be_float(blob, start, end - start)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[rhai_fn(name = "parse_be_float")]
|
||||||
|
pub fn parse_be_float_range_inclusive(blob: &mut Blob, range: InclusiveRange) -> FLOAT {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
parse_be_float(blob, start, end - start + 1)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub fn parse_be_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
pub fn parse_be_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
||||||
parse_float(blob, start, len, false)
|
parse_float(blob, start, len, false)
|
||||||
}
|
}
|
||||||
@ -514,12 +641,40 @@ mod blob_functions {
|
|||||||
blob[start..][..len].copy_from_slice(&buf[..len]);
|
blob[start..][..len].copy_from_slice(&buf[..len]);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[rhai_fn(name = "write_le_float")]
|
||||||
|
pub fn write_le_float_range(blob: &mut Blob, range: ExclusiveRange, value: FLOAT) {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
write_le_float(blob, start, end - start, value)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[rhai_fn(name = "write_le_float")]
|
||||||
|
pub fn write_le_float_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: FLOAT) {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
write_le_float(blob, start, end - start + 1, value)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "write_le")]
|
#[rhai_fn(name = "write_le")]
|
||||||
pub fn write_le_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) {
|
pub fn write_le_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) {
|
||||||
write_float(blob, start, len, value, true)
|
write_float(blob, start, len, value, true)
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[rhai_fn(name = "write_be")]
|
#[rhai_fn(name = "write_be")]
|
||||||
|
pub fn write_be_float_range(blob: &mut Blob, range: ExclusiveRange, value: FLOAT) {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
write_be_float(blob, start, end - start, value)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[rhai_fn(name = "write_be")]
|
||||||
|
pub fn write_be_float_range_inclusive(blob: &mut Blob, range: InclusiveRange, value: FLOAT) {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
write_be_float(blob, start, end - start + 1, value)
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[rhai_fn(name = "write_be")]
|
||||||
pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) {
|
pub fn write_be_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) {
|
||||||
write_float(blob, start, len, value, false)
|
write_float(blob, start, len, value, false)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
|
use crate::plugin::*;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{def_package, EvalAltResult, INT};
|
use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, INT};
|
||||||
use std::iter::{ExactSizeIterator, FusedIterator};
|
use std::iter::{ExactSizeIterator, FusedIterator};
|
||||||
use std::ops::Range;
|
use std::ops::{Range, RangeInclusive};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -271,6 +272,7 @@ 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>>();
|
||||||
|
$lib.set_iterator::<RangeInclusive<$y>>();
|
||||||
let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to));
|
let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to));
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
@ -449,6 +451,22 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
// Register string iterator
|
// Register string iterator
|
||||||
lib.set_iterator::<CharsStream>();
|
lib.set_iterator::<CharsStream>();
|
||||||
|
|
||||||
|
let _hash = lib.set_native_fn("chars", |string, range: ExclusiveRange| {
|
||||||
|
let from = INT::max(range.start, 0);
|
||||||
|
let to = INT::max(range.end, from);
|
||||||
|
Ok(CharsStream::new(string, from, to - from))
|
||||||
|
});
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["string: &str", "range: Range", "Iterator<Item=char>"]);
|
||||||
|
|
||||||
|
let _hash = lib.set_native_fn("chars", |string, range: InclusiveRange| {
|
||||||
|
let from = INT::max(*range.start(), 0);
|
||||||
|
let to = INT::max(*range.end(), from - 1);
|
||||||
|
Ok(CharsStream::new(string, from, to-from + 1))
|
||||||
|
});
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["string: &str", "range: RangeInclusive", "Iterator<Item=char>"]);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len)));
|
let _hash = lib.set_native_fn("chars", |string, from, len| Ok(CharsStream::new(string, from, len)));
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator<Item=char>"]);
|
lib.update_fn_metadata(_hash, &["string: &str", "from: INT", "len: INT", "Iterator<Item=char>"]);
|
||||||
@ -461,9 +479,32 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["string: &str", "Iterator<Item=char>"]);
|
lib.update_fn_metadata(_hash, &["string: &str", "Iterator<Item=char>"]);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
let _hash = lib.set_getter_fn("chars", |string: &mut ImmutableString| Ok(CharsStream::new(string, 0, INT::MAX)));
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["string: &mut ImmutableString", "Iterator<Item=char>"]);
|
||||||
|
}
|
||||||
|
|
||||||
// Register bit-field iterator
|
// Register bit-field iterator
|
||||||
lib.set_iterator::<BitRange>();
|
lib.set_iterator::<BitRange>();
|
||||||
|
|
||||||
|
let _hash = lib.set_native_fn("bits", |value, range: ExclusiveRange| {
|
||||||
|
let from = INT::max(range.start, 0);
|
||||||
|
let to = INT::max(range.end, from);
|
||||||
|
BitRange::new(value, from, to - from)
|
||||||
|
});
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["value: INT", "range: Range", "Iterator<Item=bool>"]);
|
||||||
|
|
||||||
|
let _hash = lib.set_native_fn("bits", |value, range: InclusiveRange| {
|
||||||
|
let from = INT::max(*range.start(), 0);
|
||||||
|
let to = INT::max(*range.end(), from - 1);
|
||||||
|
BitRange::new(value, from, to - from + 1)
|
||||||
|
});
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["value: INT", "range: RangeInclusive", "Iterator<Item=bool>"]);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", BitRange::new);
|
let _hash = lib.set_native_fn("bits", BitRange::new);
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "len: INT", "Iterator<Item=bool>"]);
|
||||||
@ -472,7 +513,54 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata(_hash, &["value: INT", "from: INT", "Iterator<Item=bool>"]);
|
||||||
|
|
||||||
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX));
|
let _hash = lib.set_native_fn("bits", |value| BitRange::new(value, 0, INT::MAX) );
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
lib.update_fn_metadata(_hash, &["value: INT", "Iterator<Item=bool>"]);
|
lib.update_fn_metadata(_hash, &["value: INT", "Iterator<Item=bool>"]);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
{
|
||||||
|
let _hash = lib.set_getter_fn("bits", |value: &mut INT| BitRange::new(*value, 0, INT::MAX) );
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
lib.update_fn_metadata(_hash, &["value: &mut INT", "range: Range", "Iterator<Item=bool>"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
combine_with_exported_module!(lib, "range", range_functions);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#[export_module]
|
||||||
|
mod range_functions {
|
||||||
|
#[rhai_fn(get = "start", name = "start", pure)]
|
||||||
|
pub fn range_start(range: &mut ExclusiveRange) -> INT {
|
||||||
|
range.start
|
||||||
|
}
|
||||||
|
#[rhai_fn(get = "end", name = "end", pure)]
|
||||||
|
pub fn range_end(range: &mut ExclusiveRange) -> INT {
|
||||||
|
range.end
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "contains", pure)]
|
||||||
|
pub fn range_contains(range: &mut ExclusiveRange, value: INT) -> bool {
|
||||||
|
range.contains(&value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
|
||||||
|
pub fn range_is_inclusive(range: &mut ExclusiveRange) -> bool {
|
||||||
|
let _range = range;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
#[rhai_fn(get = "start", name = "start", pure)]
|
||||||
|
pub fn range_inclusive_start(range: &mut InclusiveRange) -> INT {
|
||||||
|
*range.start()
|
||||||
|
}
|
||||||
|
#[rhai_fn(get = "end", name = "end", pure)]
|
||||||
|
pub fn range_inclusive_end(range: &mut InclusiveRange) -> INT {
|
||||||
|
*range.end()
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "contains", pure)]
|
||||||
|
pub fn range_inclusive_contains(range: &mut InclusiveRange, value: INT) -> bool {
|
||||||
|
range.contains(&value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(get = "is_inclusive", name = "is_inclusive", pure)]
|
||||||
|
pub fn range_inclusive_is_inclusive(range: &mut InclusiveRange) -> bool {
|
||||||
|
let _range = range;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, EvalAltResult, INT};
|
use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
@ -256,6 +256,21 @@ mod bit_field_functions {
|
|||||||
Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
|
Err(EvalAltResult::ErrorBitFieldBounds(BITS, index, Position::NONE).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "get_bits", return_raw)]
|
||||||
|
pub fn get_bits_range(value: INT, range: ExclusiveRange) -> Result<INT, Box<EvalAltResult>> {
|
||||||
|
let from = INT::max(range.start, 0);
|
||||||
|
let to = INT::max(range.end, from);
|
||||||
|
get_bits(value, from, to - from)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "get_bits", return_raw)]
|
||||||
|
pub fn get_bits_range_inclusive(
|
||||||
|
value: INT,
|
||||||
|
range: InclusiveRange,
|
||||||
|
) -> Result<INT, Box<EvalAltResult>> {
|
||||||
|
let from = INT::max(*range.start(), 0);
|
||||||
|
let to = INT::max(*range.end(), from - 1);
|
||||||
|
get_bits(value, from, to - from + 1)
|
||||||
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn get_bits(value: INT, index: INT, bits: INT) -> Result<INT, Box<EvalAltResult>> {
|
pub fn get_bits(value: INT, index: INT, bits: INT) -> Result<INT, Box<EvalAltResult>> {
|
||||||
if bits < 1 {
|
if bits < 1 {
|
||||||
@ -298,6 +313,26 @@ mod bit_field_functions {
|
|||||||
|
|
||||||
Ok(((value & (mask << index)) >> index) & mask)
|
Ok(((value & (mask << index)) >> index) & mask)
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "set_bits", return_raw)]
|
||||||
|
pub fn set_bits_range(
|
||||||
|
value: &mut INT,
|
||||||
|
range: ExclusiveRange,
|
||||||
|
new_value: INT,
|
||||||
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let from = INT::max(range.start, 0);
|
||||||
|
let to = INT::max(range.end, from);
|
||||||
|
set_bits(value, from, to - from, new_value)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "set_bits", return_raw)]
|
||||||
|
pub fn set_bits_range_inclusive(
|
||||||
|
value: &mut INT,
|
||||||
|
range: InclusiveRange,
|
||||||
|
new_value: INT,
|
||||||
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let from = INT::max(*range.start(), 0);
|
||||||
|
let to = INT::max(*range.end(), from - 1);
|
||||||
|
set_bits(value, from, to - from + 1, new_value)
|
||||||
|
}
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn set_bits(
|
pub fn set_bits(
|
||||||
value: &mut INT,
|
value: &mut INT,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, Dynamic, StaticVec, INT};
|
use crate::{def_package, Dynamic, ExclusiveRange, InclusiveRange, StaticVec, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::TypeId, mem};
|
use std::{any::TypeId, mem};
|
||||||
@ -302,6 +302,26 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "sub_string")]
|
||||||
|
pub fn sub_string_range(
|
||||||
|
ctx: NativeCallContext,
|
||||||
|
string: &str,
|
||||||
|
range: ExclusiveRange,
|
||||||
|
) -> ImmutableString {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
sub_string(ctx, string, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "sub_string")]
|
||||||
|
pub fn sub_string_inclusive_range(
|
||||||
|
ctx: NativeCallContext,
|
||||||
|
string: &str,
|
||||||
|
range: InclusiveRange,
|
||||||
|
) -> ImmutableString {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
sub_string(ctx, string, start, end - start + 1)
|
||||||
|
}
|
||||||
pub fn sub_string(
|
pub fn sub_string(
|
||||||
ctx: NativeCallContext,
|
ctx: NativeCallContext,
|
||||||
string: &str,
|
string: &str,
|
||||||
@ -365,6 +385,18 @@ mod string_functions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rhai_fn(name = "crop")]
|
||||||
|
pub fn crop_range(string: &mut ImmutableString, range: ExclusiveRange) {
|
||||||
|
let start = INT::max(range.start, 0);
|
||||||
|
let end = INT::max(range.end, start);
|
||||||
|
crop(string, start, end - start)
|
||||||
|
}
|
||||||
|
#[rhai_fn(name = "crop")]
|
||||||
|
pub fn crop_inclusive_range(string: &mut ImmutableString, range: InclusiveRange) {
|
||||||
|
let start = INT::max(*range.start(), 0);
|
||||||
|
let end = INT::max(*range.end(), start);
|
||||||
|
crop(string, start, end - start + 1)
|
||||||
|
}
|
||||||
#[rhai_fn(name = "crop")]
|
#[rhai_fn(name = "crop")]
|
||||||
pub fn crop(string: &mut ImmutableString, start: INT, len: INT) {
|
pub fn crop(string: &mut ImmutableString, start: INT, len: INT) {
|
||||||
if string.is_empty() {
|
if string.is_empty() {
|
||||||
|
@ -15,8 +15,9 @@ use crate::tokenizer::{
|
|||||||
};
|
};
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, Identifier,
|
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, ExclusiveRange,
|
||||||
ImmutableString, LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
Identifier, ImmutableString, InclusiveRange, LexError, ParseError, ParseErrorType, Position,
|
||||||
|
Scope, Shared, StaticVec, AST, INT,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -1003,6 +1004,7 @@ fn parse_switch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut table = BTreeMap::<u64, Box<(Option<Expr>, StmtBlock)>>::new();
|
let mut table = BTreeMap::<u64, Box<(Option<Expr>, StmtBlock)>>::new();
|
||||||
|
let mut ranges = StaticVec::<(INT, INT, bool, Option<Expr>, StmtBlock)>::new();
|
||||||
let mut def_pos = Position::NONE;
|
let mut def_pos = Position::NONE;
|
||||||
let mut def_stmt = None;
|
let mut def_stmt = None;
|
||||||
|
|
||||||
@ -1033,6 +1035,7 @@ fn parse_switch(
|
|||||||
(None, None)
|
(None, None)
|
||||||
}
|
}
|
||||||
(Token::Underscore, pos) => return Err(PERR::DuplicatedSwitchCase.into_err(*pos)),
|
(Token::Underscore, pos) => return Err(PERR::DuplicatedSwitchCase.into_err(*pos)),
|
||||||
|
|
||||||
_ if def_stmt.is_some() => return Err(PERR::WrongSwitchDefaultCase.into_err(def_pos)),
|
_ if def_stmt.is_some() => return Err(PERR::WrongSwitchDefaultCase.into_err(def_pos)),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
@ -1047,21 +1050,31 @@ fn parse_switch(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let hash = if let Some(expr) = expr {
|
let (hash, range) = if let Some(expr) = expr {
|
||||||
let value = expr.get_literal_value().ok_or_else(|| {
|
let value = expr.get_literal_value().ok_or_else(|| {
|
||||||
PERR::ExprExpected("a literal".to_string()).into_err(expr.position())
|
PERR::ExprExpected("a literal".to_string()).into_err(expr.position())
|
||||||
})?;
|
})?;
|
||||||
let hasher = &mut get_hasher();
|
|
||||||
value.hash(hasher);
|
|
||||||
let hash = hasher.finish();
|
|
||||||
|
|
||||||
if table.contains_key(&hash) {
|
let guard = value.read_lock::<ExclusiveRange>();
|
||||||
return Err(PERR::DuplicatedSwitchCase.into_err(expr.position()));
|
|
||||||
|
if let Some(range) = guard {
|
||||||
|
(None, Some((range.start, range.end, false)))
|
||||||
|
} else if let Some(range) = value.read_lock::<InclusiveRange>() {
|
||||||
|
(None, Some((*range.start(), *range.end(), true)))
|
||||||
|
} else if value.is::<INT>() && !ranges.is_empty() {
|
||||||
|
return Err(PERR::WrongSwitchIntegerCase.into_err(expr.position()));
|
||||||
|
} else {
|
||||||
|
let hasher = &mut get_hasher();
|
||||||
|
value.hash(hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
|
||||||
|
if table.contains_key(&hash) {
|
||||||
|
return Err(PERR::DuplicatedSwitchCase.into_err(expr.position()));
|
||||||
|
}
|
||||||
|
(Some(hash), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(hash)
|
|
||||||
} else {
|
} else {
|
||||||
None
|
(None, None)
|
||||||
};
|
};
|
||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
@ -1080,12 +1093,19 @@ fn parse_switch(
|
|||||||
|
|
||||||
let need_comma = !stmt.is_self_terminated();
|
let need_comma = !stmt.is_self_terminated();
|
||||||
|
|
||||||
def_stmt = match hash {
|
def_stmt = match (hash, range) {
|
||||||
Some(hash) => {
|
(None, Some(range)) => {
|
||||||
|
ranges.push((range.0, range.1, range.2, condition, stmt.into()));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
(Some(hash), None) => {
|
||||||
table.insert(hash, (condition, stmt.into()).into());
|
table.insert(hash, (condition, stmt.into()).into());
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
None => Some(stmt.into()),
|
(None, None) => Some(stmt.into()),
|
||||||
|
(Some(_), Some(_)) => {
|
||||||
|
unreachable!("cannot have both a hash and a range in a `switch` statement")
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
@ -1115,7 +1135,7 @@ fn parse_switch(
|
|||||||
|
|
||||||
Ok(Stmt::Switch(
|
Ok(Stmt::Switch(
|
||||||
item,
|
item,
|
||||||
(table, def_stmt_block).into(),
|
(table, def_stmt_block, ranges).into(),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -1975,18 +1995,6 @@ fn parse_binary_op(
|
|||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
|
|
||||||
root = match op_token {
|
root = match op_token {
|
||||||
Token::Plus
|
|
||||||
| Token::Minus
|
|
||||||
| Token::Multiply
|
|
||||||
| Token::Divide
|
|
||||||
| Token::LeftShift
|
|
||||||
| Token::RightShift
|
|
||||||
| Token::Modulo
|
|
||||||
| Token::PowerOf
|
|
||||||
| Token::Ampersand
|
|
||||||
| Token::Pipe
|
|
||||||
| Token::XOr => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
|
||||||
|
|
||||||
// '!=' defaults to true when passed invalid operands
|
// '!=' defaults to true when passed invalid operands
|
||||||
Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
||||||
|
|
||||||
@ -2058,7 +2066,7 @@ fn parse_binary_op(
|
|||||||
.into_fn_call_expr(pos)
|
.into_fn_call_expr(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)),
|
_ => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -356,6 +356,10 @@ pub enum Token {
|
|||||||
Comma,
|
Comma,
|
||||||
/// `.`
|
/// `.`
|
||||||
Period,
|
Period,
|
||||||
|
/// `..`
|
||||||
|
ExclusiveRange,
|
||||||
|
/// `..=`
|
||||||
|
InclusiveRange,
|
||||||
/// `#{`
|
/// `#{`
|
||||||
MapStart,
|
MapStart,
|
||||||
/// `=`
|
/// `=`
|
||||||
@ -507,6 +511,8 @@ impl Token {
|
|||||||
Underscore => "_",
|
Underscore => "_",
|
||||||
Comma => ",",
|
Comma => ",",
|
||||||
Period => ".",
|
Period => ".",
|
||||||
|
ExclusiveRange => "..",
|
||||||
|
InclusiveRange => "..=",
|
||||||
MapStart => "#{",
|
MapStart => "#{",
|
||||||
Equals => "=",
|
Equals => "=",
|
||||||
True => "true",
|
True => "true",
|
||||||
@ -701,6 +707,8 @@ impl Token {
|
|||||||
"_" => Underscore,
|
"_" => Underscore,
|
||||||
"," => Comma,
|
"," => Comma,
|
||||||
"." => Period,
|
"." => Period,
|
||||||
|
".." => ExclusiveRange,
|
||||||
|
"..=" => InclusiveRange,
|
||||||
"#{" => MapStart,
|
"#{" => MapStart,
|
||||||
"=" => Equals,
|
"=" => Equals,
|
||||||
"true" => True,
|
"true" => True,
|
||||||
@ -805,6 +813,8 @@ impl Token {
|
|||||||
Colon | // #{ foo: - is unary
|
Colon | // #{ foo: - is unary
|
||||||
Comma | // ( ... , -expr ) - is unary
|
Comma | // ( ... , -expr ) - is unary
|
||||||
//Period |
|
//Period |
|
||||||
|
ExclusiveRange | // .. - is unary
|
||||||
|
InclusiveRange | // ..= - is unary
|
||||||
LeftBrace | // { -expr } - is unary
|
LeftBrace | // { -expr } - is unary
|
||||||
// RightBrace | { expr } - expr not unary & is closing
|
// RightBrace | { expr } - expr not unary & is closing
|
||||||
LeftParen | // ( -expr ) - is unary
|
LeftParen | // ( -expr ) - is unary
|
||||||
@ -868,6 +878,8 @@ impl Token {
|
|||||||
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
|
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
|
||||||
| ModuloAssign => 0,
|
| ModuloAssign => 0,
|
||||||
|
|
||||||
|
ExclusiveRange | InclusiveRange => 10,
|
||||||
|
|
||||||
Or | XOr | Pipe => 30,
|
Or | XOr | Pipe => 30,
|
||||||
|
|
||||||
And | Ampersand => 60,
|
And | Ampersand => 60,
|
||||||
@ -921,11 +933,12 @@ impl Token {
|
|||||||
match self {
|
match self {
|
||||||
LeftBrace | RightBrace | LeftParen | RightParen | LeftBracket | RightBracket | Plus
|
LeftBrace | RightBrace | LeftParen | RightParen | LeftBracket | RightBracket | Plus
|
||||||
| UnaryPlus | Minus | UnaryMinus | Multiply | Divide | Modulo | PowerOf | LeftShift
|
| UnaryPlus | Minus | UnaryMinus | Multiply | Divide | Modulo | PowerOf | LeftShift
|
||||||
| RightShift | SemiColon | Colon | DoubleColon | Comma | Period | MapStart | Equals
|
| RightShift | SemiColon | Colon | DoubleColon | Comma | Period | ExclusiveRange
|
||||||
| LessThan | GreaterThan | LessThanEqualsTo | GreaterThanEqualsTo | EqualsTo
|
| InclusiveRange | MapStart | Equals | LessThan | GreaterThan | LessThanEqualsTo
|
||||||
| NotEqualsTo | Bang | Pipe | Or | XOr | Ampersand | And | PlusAssign | MinusAssign
|
| GreaterThanEqualsTo | EqualsTo | NotEqualsTo | Bang | Pipe | Or | XOr | Ampersand
|
||||||
| MultiplyAssign | DivideAssign | LeftShiftAssign | RightShiftAssign | AndAssign
|
| And | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
||||||
| OrAssign | XOrAssign | ModuloAssign | PowerOfAssign => true,
|
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
||||||
|
| PowerOfAssign => true,
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -1794,13 +1807,20 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
('.', '.') => {
|
('.', '.') => {
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
|
return Some((
|
||||||
if stream.peek_next() == Some('.') {
|
match stream.peek_next() {
|
||||||
eat_next(stream, pos);
|
Some('.') => {
|
||||||
return Some((Token::Reserved("...".into()), start_pos));
|
eat_next(stream, pos);
|
||||||
} else {
|
Token::Reserved("...".into())
|
||||||
return Some((Token::Reserved("..".into()), start_pos));
|
}
|
||||||
}
|
Some('=') => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
Token::InclusiveRange
|
||||||
|
}
|
||||||
|
_ => Token::ExclusiveRange,
|
||||||
|
},
|
||||||
|
start_pos,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
('.', _) => return Some((Token::Period, start_pos)),
|
('.', _) => return Some((Token::Period, start_pos)),
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::func::native::SendSync;
|
use crate::func::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::{ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -588,6 +588,12 @@ pub(crate) fn map_std_type_name(name: &str) -> &str {
|
|||||||
if name == type_name::<Instant>() {
|
if name == type_name::<Instant>() {
|
||||||
return "timestamp";
|
return "timestamp";
|
||||||
}
|
}
|
||||||
|
if name == type_name::<ExclusiveRange>() {
|
||||||
|
return "range";
|
||||||
|
}
|
||||||
|
if name == type_name::<InclusiveRange>() {
|
||||||
|
return "range=";
|
||||||
|
}
|
||||||
|
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@ -655,6 +661,14 @@ impl fmt::Display for Dynamic {
|
|||||||
return fmt::Display::fmt(_value_any.downcast_ref::<i128>().expect(CHECKED), f);
|
return fmt::Display::fmt(_value_any.downcast_ref::<i128>().expect(CHECKED), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _type_id == TypeId::of::<ExclusiveRange>() {
|
||||||
|
let range = _value_any.downcast_ref::<ExclusiveRange>().expect(CHECKED);
|
||||||
|
return write!(f, "{}..{}", range.start, range.end);
|
||||||
|
} else if _type_id == TypeId::of::<InclusiveRange>() {
|
||||||
|
let range = _value_any.downcast_ref::<InclusiveRange>().expect(CHECKED);
|
||||||
|
return write!(f, "{}..={}", range.start(), range.end());
|
||||||
|
}
|
||||||
|
|
||||||
f.write_str((***value).type_name())
|
f.write_str((***value).type_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -737,6 +751,14 @@ impl fmt::Debug for Dynamic {
|
|||||||
return fmt::Debug::fmt(_value_any.downcast_ref::<i128>().expect(CHECKED), f);
|
return fmt::Debug::fmt(_value_any.downcast_ref::<i128>().expect(CHECKED), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _type_id == TypeId::of::<ExclusiveRange>() {
|
||||||
|
let range = _value_any.downcast_ref::<ExclusiveRange>().expect(CHECKED);
|
||||||
|
return write!(f, "{}..{}", range.start, range.end);
|
||||||
|
} else if _type_id == TypeId::of::<InclusiveRange>() {
|
||||||
|
let range = _value_any.downcast_ref::<InclusiveRange>().expect(CHECKED);
|
||||||
|
return write!(f, "{}..={}", range.start(), range.end());
|
||||||
|
}
|
||||||
|
|
||||||
f.write_str((***value).type_name())
|
f.write_str((***value).type_name())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -809,6 +831,13 @@ impl Default for Dynamic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
|
use std::f32::consts as FloatConstants;
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
use std::f64::consts as FloatConstants;
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
/// A [`Dynamic`] containing a `()`.
|
/// A [`Dynamic`] containing a `()`.
|
||||||
pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite));
|
pub const UNIT: Self = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite));
|
||||||
@ -818,20 +847,24 @@ impl Dynamic {
|
|||||||
pub const FALSE: Self = Self::from_bool(false);
|
pub const FALSE: Self = Self::from_bool(false);
|
||||||
/// A [`Dynamic`] containing the integer zero.
|
/// A [`Dynamic`] containing the integer zero.
|
||||||
pub const ZERO: Self = Self::from_int(0);
|
pub const ZERO: Self = Self::from_int(0);
|
||||||
/// A [`Dynamic`] containing the integer one.
|
/// A [`Dynamic`] containing the integer 1.
|
||||||
pub const ONE: Self = Self::from_int(1);
|
pub const ONE: Self = Self::from_int(1);
|
||||||
/// A [`Dynamic`] containing the integer two.
|
/// A [`Dynamic`] containing the integer 2.
|
||||||
pub const TWO: Self = Self::from_int(2);
|
pub const TWO: Self = Self::from_int(2);
|
||||||
/// A [`Dynamic`] containing the integer three.
|
/// A [`Dynamic`] containing the integer 3.
|
||||||
pub const THREE: Self = Self::from_int(3);
|
pub const THREE: Self = Self::from_int(3);
|
||||||
/// A [`Dynamic`] containing the integer ten.
|
/// A [`Dynamic`] containing the integer 10.
|
||||||
pub const TEN: Self = Self::from_int(10);
|
pub const TEN: Self = Self::from_int(10);
|
||||||
/// A [`Dynamic`] containing the integer one hundred.
|
/// A [`Dynamic`] containing the integer 100.
|
||||||
pub const HUNDRED: Self = Self::from_int(100);
|
pub const HUNDRED: Self = Self::from_int(100);
|
||||||
/// A [`Dynamic`] containing the integer one thousand.
|
/// A [`Dynamic`] containing the integer 1,000.
|
||||||
pub const THOUSAND: Self = Self::from_int(1000);
|
pub const THOUSAND: Self = Self::from_int(1000);
|
||||||
/// A [`Dynamic`] containing the integer negative one.
|
/// A [`Dynamic`] containing the integer 1,000,000.
|
||||||
|
pub const MILLION: Self = Self::from_int(1000000);
|
||||||
|
/// A [`Dynamic`] containing the integer -1.
|
||||||
pub const NEGATIVE_ONE: Self = Self::from_int(-1);
|
pub const NEGATIVE_ONE: Self = Self::from_int(-1);
|
||||||
|
/// A [`Dynamic`] containing the integer -2.
|
||||||
|
pub const NEGATIVE_TWO: Self = Self::from_int(-2);
|
||||||
/// A [`Dynamic`] containing `0.0`.
|
/// A [`Dynamic`] containing `0.0`.
|
||||||
///
|
///
|
||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
@ -862,54 +895,97 @@ impl Dynamic {
|
|||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_THOUSAND: Self = Self::from_float(1000.0);
|
pub const FLOAT_THOUSAND: Self = Self::from_float(1000.0);
|
||||||
|
/// A [`Dynamic`] containing `1000000.0`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_MILLION: Self = Self::from_float(1000000.0);
|
||||||
/// A [`Dynamic`] containing `-1.0`.
|
/// A [`Dynamic`] containing `-1.0`.
|
||||||
///
|
///
|
||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_NEGATIVE_ONE: Self = Self::from_float(-1.0);
|
pub const FLOAT_NEGATIVE_ONE: Self = Self::from_float(-1.0);
|
||||||
|
/// A [`Dynamic`] containing `-2.0`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_NEGATIVE_TWO: Self = Self::from_float(-2.0);
|
||||||
|
/// A [`Dynamic`] containing `0.5`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_HALF: Self = Self::from_float(0.5);
|
||||||
|
/// A [`Dynamic`] containing `0.25`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_QUARTER: Self = Self::from_float(0.25);
|
||||||
|
/// A [`Dynamic`] containing `0.2`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_FIFTH: Self = Self::from_float(0.2);
|
||||||
|
/// A [`Dynamic`] containing `0.1`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_TENTH: Self = Self::from_float(0.1);
|
||||||
|
/// A [`Dynamic`] containing `0.01`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_HUNDREDTH: Self = Self::from_float(0.01);
|
||||||
|
/// A [`Dynamic`] containing `0.001`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_THOUSANDTH: Self = Self::from_float(0.001);
|
||||||
|
/// A [`Dynamic`] containing `0.000001`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_MILLIONTH: Self = Self::from_float(0.000001);
|
||||||
/// A [`Dynamic`] containing π.
|
/// A [`Dynamic`] containing π.
|
||||||
///
|
///
|
||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[cfg(not(feature = "f32_float"))]
|
#[cfg(not(feature = "f32_float"))]
|
||||||
pub const FLOAT_PI: Self = Self::from_float(
|
pub const FLOAT_PI: Self = Self::from_float(FloatConstants::PI);
|
||||||
#[cfg(not(feature = "f32_float"))]
|
|
||||||
{
|
|
||||||
std::f64::consts::PI
|
|
||||||
},
|
|
||||||
#[cfg(feature = "f32_float")]
|
|
||||||
{
|
|
||||||
std::f32::consts::PI
|
|
||||||
},
|
|
||||||
);
|
|
||||||
/// A [`Dynamic`] containing π/2.
|
/// A [`Dynamic`] containing π/2.
|
||||||
///
|
///
|
||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_HALF_PI: Self = Self::from_float(
|
pub const FLOAT_HALF_PI: Self = Self::from_float(FloatConstants::FRAC_PI_2);
|
||||||
#[cfg(not(feature = "f32_float"))]
|
/// A [`Dynamic`] containing π/4.
|
||||||
{
|
///
|
||||||
std::f64::consts::PI / 2.0
|
/// Not available under `no_float`.
|
||||||
},
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[cfg(feature = "f32_float")]
|
pub const FLOAT_QUARTER_PI: Self = Self::from_float(FloatConstants::FRAC_PI_4);
|
||||||
{
|
|
||||||
std::f32::consts::PI / 2.0
|
|
||||||
},
|
|
||||||
);
|
|
||||||
/// A [`Dynamic`] containing 2π.
|
/// A [`Dynamic`] containing 2π.
|
||||||
///
|
///
|
||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_TWO_PI: Self = Self::from_float(
|
pub const FLOAT_TWO_PI: Self = Self::from_float(FloatConstants::TAU);
|
||||||
#[cfg(not(feature = "f32_float"))]
|
/// A [`Dynamic`] containing 1/π.
|
||||||
{
|
///
|
||||||
std::f64::consts::PI * 2.0
|
/// Not available under `no_float`.
|
||||||
},
|
#[cfg(not(feature = "no_float"))]
|
||||||
#[cfg(feature = "f32_float")]
|
pub const FLOAT_INVERSE_PI: Self = Self::from_float(FloatConstants::FRAC_1_PI);
|
||||||
{
|
/// A [`Dynamic`] containing _e_.
|
||||||
std::f32::consts::PI * 2.0
|
///
|
||||||
},
|
/// Not available under `no_float`.
|
||||||
);
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_E: Self = Self::from_float(FloatConstants::E);
|
||||||
|
/// A [`Dynamic`] containing `log` _e_.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_LOG_E: Self = Self::from_float(FloatConstants::LOG10_E);
|
||||||
|
/// A [`Dynamic`] containing `ln 10`.
|
||||||
|
///
|
||||||
|
/// Not available under `no_float`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
pub const FLOAT_LN_10: Self = Self::from_float(FloatConstants::LN_10);
|
||||||
|
|
||||||
/// Create a new [`Dynamic`] from a [`bool`].
|
/// Create a new [`Dynamic`] from a [`bool`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -2264,3 +2340,16 @@ impl From<crate::Shared<crate::Locked<Dynamic>>> for Dynamic {
|
|||||||
Self(Union::Shared(value, DEFAULT_TAG_VALUE, ReadWrite))
|
Self(Union::Shared(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ExclusiveRange> for Dynamic {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(value: ExclusiveRange) -> Self {
|
||||||
|
Dynamic::from(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<InclusiveRange> for Dynamic {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(value: InclusiveRange) -> Self {
|
||||||
|
Dynamic::from(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -108,6 +108,8 @@ pub enum ParseErrorType {
|
|||||||
DuplicatedSwitchCase,
|
DuplicatedSwitchCase,
|
||||||
/// A variable name is duplicated. Wrapped value is the variable name.
|
/// A variable name is duplicated. Wrapped value is the variable name.
|
||||||
DuplicatedVariable(String),
|
DuplicatedVariable(String),
|
||||||
|
/// An integer case of a `switch` statement is after a range case.
|
||||||
|
WrongSwitchIntegerCase,
|
||||||
/// The default case of a `switch` statement is not the last.
|
/// The default case of a `switch` statement is not the last.
|
||||||
WrongSwitchDefaultCase,
|
WrongSwitchDefaultCase,
|
||||||
/// The case condition of a `switch` statement is not appropriate.
|
/// The case condition of a `switch` statement is not appropriate.
|
||||||
@ -260,8 +262,9 @@ impl fmt::Display for ParseErrorType {
|
|||||||
Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max),
|
Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max),
|
||||||
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
||||||
Self::UnexpectedEOF => f.write_str("Script is incomplete"),
|
Self::UnexpectedEOF => f.write_str("Script is incomplete"),
|
||||||
Self::WrongSwitchDefaultCase => f.write_str("Default switch case is not the last"),
|
Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"),
|
||||||
Self::WrongSwitchCaseCondition => f.write_str("Default switch case cannot have condition"),
|
Self::WrongSwitchDefaultCase => f.write_str("Default switch case must be the last"),
|
||||||
|
Self::WrongSwitchCaseCondition => f.write_str("This switch case cannot have a condition"),
|
||||||
Self::PropertyExpected => f.write_str("Expecting name of a property"),
|
Self::PropertyExpected => f.write_str("Expecting name of a property"),
|
||||||
Self::VariableExpected => f.write_str("Expecting name of a variable"),
|
Self::VariableExpected => f.write_str("Expecting name of a variable"),
|
||||||
Self::WrongFnDefinition => f.write_str("Function definitions must be at global level and cannot be inside a block or another function"),
|
Self::WrongFnDefinition => f.write_str("Function definitions must be at global level and cannot be inside a block or another function"),
|
||||||
|
@ -26,10 +26,12 @@ fn test_bit_fields() -> Result<(), Box<EvalAltResult>> {
|
|||||||
9
|
9
|
||||||
);
|
);
|
||||||
assert_eq!(engine.eval::<INT>("let x = 10; get_bits(x, 1, 3)")?, 5);
|
assert_eq!(engine.eval::<INT>("let x = 10; get_bits(x, 1, 3)")?, 5);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x[1..=3]")?, 5);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>("let x = 10; set_bits(x, 1, 3, 7); x")?,
|
engine.eval::<INT>("let x = 10; set_bits(x, 1, 3, 7); x")?,
|
||||||
14
|
14
|
||||||
);
|
);
|
||||||
|
assert_eq!(engine.eval::<INT>("let x = 10; x[1..4] = 7; x")?, 14);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
@ -45,6 +47,21 @@ fn test_bit_fields() -> Result<(), Box<EvalAltResult>> {
|
|||||||
)?,
|
)?,
|
||||||
5
|
5
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = 0b001101101010001;
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for b in bits(x, 2..=11) {
|
||||||
|
if b { count += 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
count
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
5
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -81,57 +81,83 @@ fn test_blobs_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,0)"
|
.eval::<INT>("let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,2,0)")?,
|
||||||
)?,
|
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine
|
||||||
|
.eval::<INT>("let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,2,9)")?,
|
||||||
|
0x0908070605040302
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,9)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,2..=11)"
|
||||||
)?,
|
)?,
|
||||||
0x0908070605040302
|
0x0908070605040302
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_be_int(x,2,10)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,2..11)"
|
||||||
|
)?,
|
||||||
|
0x0908070605040302
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_be_int(x,2,10)"
|
||||||
)?,
|
)?,
|
||||||
0x0203040506070809
|
0x0203040506070809
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,99)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_be_int(x,2..12)"
|
||||||
|
)?,
|
||||||
|
0x0203040506070809
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,-5,99)"
|
||||||
)?,
|
)?,
|
||||||
0x0f0e0d0c0b
|
0x0f0e0d0c0b
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,2)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,-5,2)"
|
||||||
)?,
|
)?,
|
||||||
0x0c0b
|
0x0c0b
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-99,99)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,-99,99)"
|
||||||
)?,
|
)?,
|
||||||
0x0706050403020100
|
0x0706050403020100
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_be(x, 3, 3, -98765432); parse_be_int(x, 3, 3)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } write_be(x, 3, 3, -98765432); parse_be_int(x, 3, 3)"
|
||||||
)?,
|
)?,
|
||||||
0xffffff0000000000_u64 as INT
|
0xffffff0000000000_u64 as INT
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_le(x, 3, 3, -98765432); parse_le_int(x, 3, 3)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } write_be(x, 3..=5, -98765432); parse_be_int(x, 3..6)"
|
||||||
|
)?,
|
||||||
|
0xffffff0000000000_u64 as INT
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } write_le(x, 3, 3, -98765432); parse_le_int(x, 3, 3)"
|
||||||
)?,
|
)?,
|
||||||
0x1cf588
|
0x1cf588
|
||||||
);
|
);
|
||||||
@ -145,57 +171,62 @@ fn test_blobs_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,0)"
|
.eval::<INT>("let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,2,0)")?,
|
||||||
)?,
|
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,2,9)"
|
.eval::<INT>("let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,2,9)")?,
|
||||||
)?,
|
|
||||||
0x05040302
|
0x05040302
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_be_int(x,2,10)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_be_int(x,2,10)"
|
||||||
)?,
|
)?,
|
||||||
0x02030405
|
0x02030405
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,99)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,-5,99)"
|
||||||
)?,
|
)?,
|
||||||
0x0e0d0c0b
|
0x0e0d0c0b
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-5,2)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,-5,2)"
|
||||||
)?,
|
)?,
|
||||||
0x0c0b
|
0x0c0b
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } parse_le_int(x,-99,99)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } parse_le_int(x,-99,99)"
|
||||||
)?,
|
)?,
|
||||||
0x03020100
|
0x03020100
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_be(x, 3, 3, -98765432); parse_be_int(x, 3, 3)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } write_be(x, 3, 3, -98765432); parse_be_int(x, 3, 3)"
|
||||||
)?,
|
)?,
|
||||||
0xfa1cf500_u32 as INT
|
0xfa1cf500_u32 as INT
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"let x = blob(16, 0); for n in range(0, 16) { x[n] = n; } write_le(x, 3, 3, -98765432); parse_le_int(x, 3, 3)"
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } write_be(x, 3..=5, -98765432); parse_be_int(x, 3..6)"
|
||||||
|
)?,
|
||||||
|
0xfa1cf500_u32 as INT
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"let x = blob(16, 0); for n in 0..16 { x[n] = n; } write_le(x, 3, 3, -98765432); parse_le_int(x, 3, 3)"
|
||||||
)?,
|
)?,
|
||||||
0x1cf588
|
0x1cf588
|
||||||
);
|
);
|
||||||
|
59
tests/for.rs
59
tests/for.rs
@ -64,6 +64,28 @@ fn test_for_loop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
45
|
45
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let sum = 0;
|
||||||
|
for x in 1..10 { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
45
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let sum = 0;
|
||||||
|
for x in 1..=10 { sum += x; }
|
||||||
|
sum
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
55
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
"
|
"
|
||||||
@ -247,18 +269,37 @@ fn test_for_overflow() -> Result<(), Box<EvalAltResult>> {
|
|||||||
fn test_for_string() -> Result<(), Box<EvalAltResult>> {
|
fn test_for_string() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
let script = r#"
|
assert_eq!(
|
||||||
let s = "hello";
|
engine.eval::<INT>(
|
||||||
let sum = 0;
|
r#"
|
||||||
|
let s = "hello";
|
||||||
|
let sum = 0;
|
||||||
|
|
||||||
for ch in chars(s) {
|
for ch in chars(s) {
|
||||||
sum += to_int(ch);
|
sum += to_int(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
sum
|
sum
|
||||||
"#;
|
"#
|
||||||
|
)?,
|
||||||
|
532
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>(script)?, 532);
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let s = "hello";
|
||||||
|
let sum = 0;
|
||||||
|
|
||||||
|
for ch in chars(s, 2..=3) {
|
||||||
|
sum += to_int(ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
sum
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
216
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
r#"
|
r#"
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in 0..10 {
|
||||||
import "hello" as h;
|
import "hello" as h;
|
||||||
sum += h::answer;
|
sum += h::answer;
|
||||||
}
|
}
|
||||||
@ -229,7 +229,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
sum += h::answer;
|
sum += h::answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in 0..10 {
|
||||||
foo();
|
foo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +249,7 @@ fn test_module_resolver() -> Result<(), Box<EvalAltResult>> {
|
|||||||
import "hello" as h;
|
import "hello" as h;
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in 0..10 {
|
||||||
foo();
|
foo();
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
|
@ -4,6 +4,8 @@ use rhai::{Engine, EvalAltResult, INT};
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
engine.set_optimization_level(rhai::OptimizationLevel::None);
|
||||||
engine.set_max_operations(500);
|
engine.set_max_operations(500);
|
||||||
|
|
||||||
engine.on_progress(|count| {
|
engine.on_progress(|count| {
|
||||||
@ -17,14 +19,14 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>("for x in range(0, 500) {}")
|
.eval::<()>("for x in 0..500 {}")
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorTooManyOperations(_)
|
EvalAltResult::ErrorTooManyOperations(_)
|
||||||
));
|
));
|
||||||
|
|
||||||
engine.set_max_operations(0);
|
engine.set_max_operations(0);
|
||||||
|
|
||||||
engine.eval::<()>("for x in range(0, 10000) {}")?;
|
engine.eval::<()>("for x in 0..10000 {}")?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -101,7 +103,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
*engine
|
*engine
|
||||||
.eval::<()>(
|
.eval::<()>(
|
||||||
r#"
|
r#"
|
||||||
let script = "for x in range(0, 500) {}";
|
let script = "for x in 0..500 {}";
|
||||||
eval(script);
|
eval(script);
|
||||||
"#
|
"#
|
||||||
)
|
)
|
||||||
@ -115,6 +117,8 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
|
fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
|
engine.set_optimization_level(rhai::OptimizationLevel::None);
|
||||||
engine.set_max_operations(500);
|
engine.set_max_operations(500);
|
||||||
|
|
||||||
engine.on_progress(|count| {
|
engine.on_progress(|count| {
|
||||||
@ -127,7 +131,7 @@ fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.eval::<()>("for x in range(0, 500) {}")
|
.eval::<()>("for x in 0..500 {}")
|
||||||
.expect_err("should error"),
|
.expect_err("should error"),
|
||||||
EvalAltResult::ErrorTerminated(x, _) if x.as_int()? == 42
|
EvalAltResult::ErrorTerminated(x, _) if x.as_int()? == 42
|
||||||
));
|
));
|
||||||
|
@ -784,7 +784,7 @@ fn test_serde_blob() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let r = engine.eval::<Dynamic>(
|
let r = engine.eval::<Dynamic>(
|
||||||
"
|
"
|
||||||
let x = blob(10);
|
let x = blob(10);
|
||||||
for i in range(0, 10) { x[i] = i; }
|
for i in 0..10 { x[i] = i; }
|
||||||
x
|
x
|
||||||
",
|
",
|
||||||
)?;
|
)?;
|
||||||
|
@ -6,6 +6,33 @@ fn test_switch() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
scope.push("x", 42 as INT);
|
scope.push("x", 42 as INT);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<char>("switch 2 { 1 => (), 2 => 'a', 42 => true }")?,
|
||||||
|
'a'
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<()>("switch 3 { 1 => (), 2 => 'a', 42 => true }")?,
|
||||||
|
()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>("switch 3 { 1 => (), 2 => 'a', 42 => true, _ => 123 }")?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<INT>(
|
||||||
|
&mut scope,
|
||||||
|
"switch 2 { 1 => (), 2 if x < 40 => 'a', 42 => true, _ => 123 }"
|
||||||
|
)?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<char>(
|
||||||
|
&mut scope,
|
||||||
|
"switch 2 { 1 => (), 2 if x > 40 => 'a', 42 => true, _ => 123 }"
|
||||||
|
)?,
|
||||||
|
'a'
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<bool>(&mut scope, "switch x { 1 => (), 2 => 'a', 42 => true }")?,
|
engine.eval_with_scope::<bool>(&mut scope, "switch x { 1 => (), 2 => 'a', 42 => true }")?,
|
||||||
true
|
true
|
||||||
@ -210,3 +237,66 @@ mod test_switch_enum {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_switch_ranges() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
let mut scope = Scope::new();
|
||||||
|
scope.push("x", 42 as INT);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<char>(
|
||||||
|
&mut scope,
|
||||||
|
"switch x { 10..20 => (), 20..=42 => 'a', 25..45 => 'z', 30..100 => true }"
|
||||||
|
)?,
|
||||||
|
'a'
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<char>(
|
||||||
|
&mut scope,
|
||||||
|
"switch x { 10..20 => (), 20..=42 if x < 40 => 'a', 25..45 => 'z', 30..100 => true }"
|
||||||
|
)?,
|
||||||
|
'z'
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<char>(
|
||||||
|
&mut scope,
|
||||||
|
"switch x { 42 => 'x', 10..20 => (), 20..=42 => 'a', 25..45 => 'z', 30..100 => true, 'w' => true }"
|
||||||
|
)?,
|
||||||
|
'x'
|
||||||
|
);
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.compile("switch x { 10..20 => (), 20..=42 => 'a', 25..45 => 'z', 42 => 'x', 30..100 => true }")
|
||||||
|
.expect_err("should error").0,
|
||||||
|
ParseErrorType::WrongSwitchIntegerCase
|
||||||
|
));
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<char>(
|
||||||
|
&mut scope,
|
||||||
|
"
|
||||||
|
switch 5 {
|
||||||
|
'a' => true,
|
||||||
|
0..10 if x+2==1+2 => print(40+2),
|
||||||
|
_ => 'x'
|
||||||
|
}
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
'x'
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval_with_scope::<char>(
|
||||||
|
&mut scope,
|
||||||
|
"
|
||||||
|
switch 5 {
|
||||||
|
'a' => true,
|
||||||
|
0..10 if x+2==1+2 => print(40+2),
|
||||||
|
2..12 => 'z',
|
||||||
|
_ => 'x'
|
||||||
|
}
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
'z'
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
@ -42,7 +42,7 @@ fn test_timestamp() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert!(engine.eval::<bool>(
|
assert!(engine.eval::<bool>(
|
||||||
"
|
"
|
||||||
let time1 = timestamp();
|
let time1 = timestamp();
|
||||||
for x in range(0, 10000) {}
|
for x in 0..10000 {}
|
||||||
let time2 = timestamp();
|
let time2 = timestamp();
|
||||||
time1 <= time2
|
time1 <= time2
|
||||||
"
|
"
|
||||||
|
Loading…
Reference in New Issue
Block a user