Add ranges.

This commit is contained in:
Stephen Chung 2021-12-15 12:06:17 +08:00
parent 7251f34bce
commit ef14079c61
35 changed files with 1206 additions and 269 deletions

View File

@ -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
============= =============

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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 }

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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]);
} }
*/ */

View File

@ -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;
} }
} }

View File

@ -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 % 100 == 0 {
/// *logger.write().unwrap() = ops; /// *logger.write().unwrap() = ops;
/// None /// None
/// } else { /// } else {
@ -166,7 +166,7 @@ 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(), 9600);

View File

@ -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,
}) })
} }

View File

@ -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, ExclusiveRange, Identifier,
Shared, StaticVec, INT, ImmutableString, InclusiveRange, 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,8 @@ 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(_, _, _, _) => false,
Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
} }
} }
/// Is the `Target` a temp value? /// Is the `Target` a temp value?
@ -520,7 +531,8 @@ 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(_, _, _, _) => false,
Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
} }
} }
/// Is the `Target` a shared value? /// Is the `Target` a shared value?
@ -534,7 +546,8 @@ 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(_, _, _, _) => false,
Self::BlobByte(_, _, _) | Self::StringChar(_, _, _) => false,
} }
} }
/// Is the `Target` a specific type? /// Is the `Target` a specific type?
@ -548,7 +561,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 +580,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 +613,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,16 +627,32 @@ 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 { } else {
*value &= !mask; *value &= !mask;
} }
} else {
unreachable!("bit-field index out of bounds: {}", 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"))] #[cfg(not(feature = "no_index"))]
Self::BlobByte(value, index, new_val) => { Self::BlobByte(value, index, new_val) => {
@ -696,9 +729,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, _) => r,
| Self::BlobByte(_, _, ref r) Self::BlobByte(_, _, ref r) | Self::StringChar(_, _, ref r) => r,
| Self::StringChar(_, _, ref r) => r,
} }
} }
} }
@ -719,9 +751,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, _) => 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 +2109,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::<InclusiveRange>() || idx.is::<ExclusiveRange>() =>
{
#[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::<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::<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 +2177,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 +2752,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;
} }
} }
let statements = &t.1; result = Some(stmt_block);
break;
}
Some(if !statements.is_empty() { result
} else {
// Nothing matches
None
}
} else {
// Non-hashable
None
};
if let Some(statements) = stmt_block {
if !statements.is_empty() {
self.eval_stmt_block( self.eval_stmt_block(
scope, mods, state, lib, this_ptr, statements, true, true, level, scope, mods, state, lib, this_ptr, statements, true, true, level,
) )
} else { } else {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
})
})
} else {
// Non-hashable values never match any specific clause
None
} }
.unwrap_or_else(|| { } 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 +2834,7 @@ impl Engine {
} else { } else {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
}) }
} }
// Loop // Loop
@ -3163,6 +3287,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 {

View File

@ -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,
}; };
} }

View File

@ -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;

View File

@ -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,14 +463,62 @@ 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_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::If(
condition,
Box::new((
mem::take(&mut block.1),
Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(),
)),
match_expr.position(),
);
} else {
// Promote the matched case
let new_pos = block.1.position();
let statements =
optimize_stmt_block(mem::take(&mut *block.1), state, true, true, false);
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
}
state.set_dirty();
return;
}
// 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_block = mem::take(&mut *x.1);
let def_stmt = optimize_stmt_block(def_block, state, true, true, false); 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() {
@ -497,54 +530,81 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
*stmt = Stmt::If( *stmt = Stmt::If(
condition, condition,
Box::new(( Box::new((
mem::take(&mut block.1), mem::take(stmt_block),
Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(), Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(),
)), )),
match_expr.position(), match_expr.position(),
); );
} else { } else {
// Promote the matched case // Promote the matched case
let new_pos = block.1.position(); let new_pos = stmt_block.position();
let statements = mem::take(&mut *block.1); let statements = mem::take(&mut **stmt_block);
let statements = optimize_stmt_block(statements, state, true, true, false); let statements =
optimize_stmt_block(statements, state, true, true, false);
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos); *stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
} }
state.set_dirty();
return;
}
} else { } 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 // Promote the default case
let def_block = mem::take(&mut *x.1); state.set_dirty();
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::Block(def_stmt.into_boxed_slice(), def_pos); *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| {
if let Some(mut condition) = mem::take(&mut block.0) {
optimize_expr(&mut condition, state, false); optimize_expr(&mut condition, state, false);
condition
},
);
match condition { match condition {
Expr::Unit(_) | Expr::BoolConstant(true, _) => (), Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
_ => { _ => block.0 = Some(condition),
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"))]

View File

@ -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()
} }

View File

@ -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();

View File

@ -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)
} }

View File

@ -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,29 @@ 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>"]);
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 +510,51 @@ 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>"]);
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
}
}

View File

@ -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,

View File

@ -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() {

View File

@ -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,10 +1050,20 @@ 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 guard = value.read_lock::<ExclusiveRange>();
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(); let hasher = &mut get_hasher();
value.hash(hasher); value.hash(hasher);
let hash = hasher.finish(); let hash = hasher.finish();
@ -1058,10 +1071,10 @@ fn parse_switch(
if table.contains_key(&hash) { if table.contains_key(&hash) {
return Err(PERR::DuplicatedSwitchCase.into_err(expr.position())); 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),
}; };
} }
} }

View File

@ -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() {
Some('.') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("...".into()), start_pos)); Token::Reserved("...".into())
} else {
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)),

View File

@ -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)
}
}

View File

@ -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"),

View File

@ -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(())
} }

View File

@ -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
); );

View File

@ -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,7 +269,9 @@ 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!(
engine.eval::<INT>(
r#"
let s = "hello"; let s = "hello";
let sum = 0; let sum = 0;
@ -256,9 +280,26 @@ fn test_for_string() -> Result<(), Box<EvalAltResult>> {
} }
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(())
} }

View File

@ -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();
} }
"#, "#,

View File

@ -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
)); ));

View File

@ -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
", ",
)?; )?;

View File

@ -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(())
}

View File

@ -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
" "