Add ranges.
This commit is contained in:
@@ -156,9 +156,9 @@ impl Engine {
|
||||
/// let mut engine = Engine::new();
|
||||
///
|
||||
/// engine.on_progress(move |ops| {
|
||||
/// if ops > 10000 {
|
||||
/// Some("Over 10,000 operations!".into())
|
||||
/// } else if ops % 800 == 0 {
|
||||
/// if ops > 1000 {
|
||||
/// Some("Over 1,000 operations!".into())
|
||||
/// } else if ops % 100 == 0 {
|
||||
/// *logger.write().unwrap() = ops;
|
||||
/// None
|
||||
/// } 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");
|
||||
///
|
||||
/// assert_eq!(*result.read().unwrap(), 9600);
|
||||
|
50
src/ast.rs
50
src/ast.rs
@@ -1,6 +1,7 @@
|
||||
//! Module defining the AST (abstract syntax tree).
|
||||
|
||||
use crate::calc_fn_hash;
|
||||
use crate::engine::{OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||
use crate::func::hashing::ALT_ZERO_HASH;
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::tokenizer::Token;
|
||||
@@ -1262,7 +1263,11 @@ pub enum Stmt {
|
||||
/// `switch` expr `if` condition `{` literal or _ `=>` stmt `,` ... `}`
|
||||
Switch(
|
||||
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,
|
||||
),
|
||||
/// `while` expr `{` stmt `}` | `loop` `{` stmt `}`
|
||||
@@ -1501,6 +1506,10 @@ impl Stmt {
|
||||
block.0.as_ref().map(Expr::is_pure).unwrap_or(true)
|
||||
&& (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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
if !s.walk(path, on_node) {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
258
src/engine.rs
258
src/engine.rs
@@ -13,8 +13,8 @@ use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||
use crate::{
|
||||
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
|
||||
Shared, StaticVec, INT,
|
||||
calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ExclusiveRange, Identifier,
|
||||
ImmutableString, InclusiveRange, Module, Position, RhaiResult, Scope, Shared, StaticVec, INT,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -367,6 +367,12 @@ pub const OP_EQUALS: &str = "==";
|
||||
/// The `in` operator is implemented as a call to this method.
|
||||
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.
|
||||
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].
|
||||
/// This is necessary because directly pointing to a bit inside an [`INT`][crate::INT] is impossible.
|
||||
#[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.
|
||||
/// This is necessary because directly pointing to a byte (in [`Dynamic`] form) inside a blob is impossible.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@@ -507,7 +517,8 @@ impl<'a> Target<'a> {
|
||||
Self::LockGuard(_) => true,
|
||||
Self::TempValue(_) => false,
|
||||
#[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?
|
||||
@@ -520,7 +531,8 @@ impl<'a> Target<'a> {
|
||||
Self::LockGuard(_) => false,
|
||||
Self::TempValue(_) => true,
|
||||
#[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?
|
||||
@@ -534,7 +546,8 @@ impl<'a> Target<'a> {
|
||||
Self::LockGuard(_) => true,
|
||||
Self::TempValue(r) => r.is_shared(),
|
||||
#[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?
|
||||
@@ -548,7 +561,9 @@ impl<'a> Target<'a> {
|
||||
Self::LockGuard((r, _)) => r.is::<T>(),
|
||||
Self::TempValue(r) => r.is::<T>(),
|
||||
#[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"))]
|
||||
Self::BlobByte(_, _, _) => TypeId::of::<T>() == TypeId::of::<crate::Blob>(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@@ -565,7 +580,9 @@ impl<'a> Target<'a> {
|
||||
Self::LockGuard((_, orig)) => orig, // Original value is simply taken
|
||||
Self::TempValue(v) => v, // Owned value is simply taken
|
||||
#[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"))]
|
||||
Self::BlobByte(_, _, value) => value, // Byte is taken
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@@ -596,7 +613,7 @@ impl<'a> Target<'a> {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::LockGuard(_) => (),
|
||||
#[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
|
||||
let new_bit = new_val.as_bool().map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
@@ -610,18 +627,34 @@ impl<'a> Target<'a> {
|
||||
|
||||
let index = *index;
|
||||
|
||||
if index < std::mem::size_of_val(value) * 8 {
|
||||
let mask = 1 << index;
|
||||
if new_bit {
|
||||
*value |= mask;
|
||||
} else {
|
||||
*value &= !mask;
|
||||
}
|
||||
let mask = 1 << index;
|
||||
if new_bit {
|
||||
*value |= mask;
|
||||
} else {
|
||||
unreachable!("bit-field index out of bounds: {}", index);
|
||||
*value &= !mask;
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(value, mask, new_val, shift) => {
|
||||
let shift = *shift;
|
||||
let mask = *mask;
|
||||
|
||||
// Replace the bit at the specified index position
|
||||
let new_value = new_val.as_int().map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||
"integer".to_string(),
|
||||
err.to_string(),
|
||||
Position::NONE,
|
||||
))
|
||||
})?;
|
||||
|
||||
let new_value = (new_value << shift) & mask;
|
||||
let value = &mut *value.write_lock::<crate::INT>().expect("`INT`");
|
||||
|
||||
*value &= !mask;
|
||||
*value |= new_value;
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BlobByte(value, index, new_val) => {
|
||||
// Replace the byte at the specified index position
|
||||
let new_byte = new_val.as_int().map_err(|err| {
|
||||
@@ -696,9 +729,8 @@ impl Deref for Target<'_> {
|
||||
Self::LockGuard((r, _)) => &**r,
|
||||
Self::TempValue(ref r) => r,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, ref r)
|
||||
| Self::BlobByte(_, _, ref r)
|
||||
| Self::StringChar(_, _, ref r) => r,
|
||||
Self::Bit(_, ref r, _) | Self::BitField(_, _, ref r, _) => r,
|
||||
Self::BlobByte(_, _, ref r) | Self::StringChar(_, _, ref r) => r,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -719,9 +751,8 @@ impl DerefMut for Target<'_> {
|
||||
Self::LockGuard((r, _)) => r.deref_mut(),
|
||||
Self::TempValue(ref mut r) => r,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::BitField(_, _, ref mut r)
|
||||
| Self::BlobByte(_, _, ref mut r)
|
||||
| Self::StringChar(_, _, ref mut r) => r,
|
||||
Self::Bit(_, ref mut r, _) | Self::BitField(_, _, ref mut r, _) => r,
|
||||
Self::BlobByte(_, _, ref mut r) | Self::StringChar(_, _, ref mut r) => r,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2078,6 +2109,67 @@ impl Engine {
|
||||
.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"))]
|
||||
Dynamic(Union::Int(value, _, _)) => {
|
||||
// val_int[idx]
|
||||
@@ -2085,38 +2177,38 @@ impl Engine {
|
||||
.as_int()
|
||||
.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 offset = index as usize;
|
||||
(
|
||||
if offset >= bits {
|
||||
if offset >= BITS {
|
||||
return Err(
|
||||
EvalAltResult::ErrorBitFieldBounds(bits, index, idx_pos).into()
|
||||
EvalAltResult::ErrorBitFieldBounds(BITS, index, idx_pos).into()
|
||||
);
|
||||
} else {
|
||||
(*value & (1 << offset)) != 0
|
||||
},
|
||||
offset,
|
||||
offset as u8,
|
||||
)
|
||||
} else if let Some(abs_index) = index.checked_abs() {
|
||||
let offset = abs_index as usize;
|
||||
(
|
||||
// Count from end if negative
|
||||
if offset > bits {
|
||||
if offset > BITS {
|
||||
return Err(
|
||||
EvalAltResult::ErrorBitFieldBounds(bits, index, idx_pos).into()
|
||||
EvalAltResult::ErrorBitFieldBounds(BITS, index, idx_pos).into()
|
||||
);
|
||||
} else {
|
||||
(*value & (1 << (bits - offset))) != 0
|
||||
(*value & (1 << (BITS - offset))) != 0
|
||||
},
|
||||
offset,
|
||||
offset as u8,
|
||||
)
|
||||
} 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"))]
|
||||
@@ -2660,48 +2752,80 @@ impl Engine {
|
||||
|
||||
// Switch statement
|
||||
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)?;
|
||||
|
||||
if value.is_hashable() {
|
||||
let stmt_block = if value.is_hashable() {
|
||||
let hasher = &mut get_hasher();
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
table.get(&hash).and_then(|t| {
|
||||
if let Some(condition) = &t.0 {
|
||||
match self
|
||||
.eval_expr(scope, mods, state, lib, this_ptr, &condition, level)
|
||||
// First check hashes
|
||||
if let Some(t) = table.get(&hash) {
|
||||
if let Some(ref c) = t.0 {
|
||||
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,
|
||||
condition.position(),
|
||||
)
|
||||
self.make_type_mismatch_err::<bool>(typ, c.position())
|
||||
})
|
||||
}) {
|
||||
Ok(true) => (),
|
||||
Ok(false) => return None,
|
||||
Err(err) => return Some(Err(err)),
|
||||
})?
|
||||
{
|
||||
Some(&t.1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
Some(&t.1)
|
||||
}
|
||||
} else if value.is::<INT>() && !ranges.is_empty() {
|
||||
// Then check integer ranges
|
||||
let value = value.as_int().expect("`INT`");
|
||||
let mut result = None;
|
||||
|
||||
for (_, _, _, condition, stmt_block) in
|
||||
ranges.iter().filter(|&&(start, end, inclusive, _, _)| {
|
||||
(!inclusive && (start..end).contains(&value))
|
||||
|| (inclusive && (start..=end).contains(&value))
|
||||
})
|
||||
{
|
||||
if let Some(c) = condition {
|
||||
if !self
|
||||
.eval_expr(scope, mods, state, lib, this_ptr, &c, level)
|
||||
.and_then(|v| {
|
||||
v.as_bool().map_err(|typ| {
|
||||
self.make_type_mismatch_err::<bool>(typ, c.position())
|
||||
})
|
||||
})?
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result = Some(stmt_block);
|
||||
break;
|
||||
}
|
||||
|
||||
let statements = &t.1;
|
||||
|
||||
Some(if !statements.is_empty() {
|
||||
self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, statements, true, true, level,
|
||||
)
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
})
|
||||
})
|
||||
result
|
||||
} else {
|
||||
// Nothing matches
|
||||
None
|
||||
}
|
||||
} else {
|
||||
// Non-hashable values never match any specific clause
|
||||
// Non-hashable
|
||||
None
|
||||
}
|
||||
.unwrap_or_else(|| {
|
||||
};
|
||||
|
||||
if let Some(statements) = stmt_block {
|
||||
if !statements.is_empty() {
|
||||
self.eval_stmt_block(
|
||||
scope, mods, state, lib, this_ptr, statements, true, true, level,
|
||||
)
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
} else {
|
||||
// Default match clause
|
||||
if !def_stmt.is_empty() {
|
||||
self.eval_stmt_block(
|
||||
@@ -2710,7 +2834,7 @@ impl Engine {
|
||||
} else {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Loop
|
||||
@@ -3163,6 +3287,18 @@ impl Engine {
|
||||
.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.
|
||||
fn check_return_value(&self, mut result: RhaiResult) -> RhaiResult {
|
||||
if let Ok(ref mut r) = result {
|
||||
|
@@ -352,6 +352,16 @@ pub fn get_builtin_binary_op_fn(
|
||||
"&" => Some(impl_op!(INT => as_int & as_int)),
|
||||
"|" => Some(impl_op!(INT => as_int | as_int)),
|
||||
"^" => Some(impl_op!(INT => as_int ^ as_int)),
|
||||
".." => Some(|_, 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,
|
||||
};
|
||||
}
|
||||
|
@@ -115,9 +115,14 @@ pub type FLOAT = f64;
|
||||
#[cfg(feature = "f32_float")]
|
||||
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 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 module::{FnNamespace, Module};
|
||||
pub use tokenizer::Position;
|
||||
|
162
src/optimizer.rs
162
src/optimizer.rs
@@ -9,14 +9,10 @@ use crate::func::builtin::get_builtin_binary_op_fn;
|
||||
use crate::func::hashing::get_hasher;
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::{
|
||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope,
|
||||
StaticVec, AST,
|
||||
};
|
||||
use crate::{calc_fn_hash, Dynamic, Engine, FnPtr, Position, Scope, StaticVec, AST, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
any::TypeId,
|
||||
convert::TryFrom,
|
||||
hash::{Hash, Hasher},
|
||||
mem,
|
||||
@@ -155,17 +151,6 @@ impl<'a> OptimizerState<'a> {
|
||||
.ok()
|
||||
.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].
|
||||
@@ -478,21 +463,21 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
state.set_dirty();
|
||||
let table = &mut x.0;
|
||||
|
||||
// First check hashes
|
||||
if let Some(block) = table.get_mut(&hash) {
|
||||
if let Some(mut condition) = mem::take(&mut block.0) {
|
||||
// switch const { case if condition => stmt, _ => def } => if condition { stmt } else { def }
|
||||
optimize_expr(&mut condition, state, false);
|
||||
|
||||
let def_block = mem::take(&mut *x.1);
|
||||
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
|
||||
let def_pos = if x.1.position().is_none() {
|
||||
*pos
|
||||
} else {
|
||||
x.1.position()
|
||||
};
|
||||
let def_stmt =
|
||||
optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
|
||||
|
||||
*stmt = Stmt::If(
|
||||
condition,
|
||||
@@ -505,46 +490,121 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
||||
} else {
|
||||
// Promote the matched case
|
||||
let new_pos = block.1.position();
|
||||
let statements = mem::take(&mut *block.1);
|
||||
let statements = optimize_stmt_block(statements, state, true, true, false);
|
||||
let statements =
|
||||
optimize_stmt_block(mem::take(&mut *block.1), state, true, true, false);
|
||||
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
|
||||
}
|
||||
} else {
|
||||
// Promote the default case
|
||||
let def_block = mem::take(&mut *x.1);
|
||||
let def_stmt = optimize_stmt_block(def_block, state, true, true, false);
|
||||
let def_pos = if x.1.position().is_none() {
|
||||
*pos
|
||||
} else {
|
||||
x.1.position()
|
||||
};
|
||||
*stmt = Stmt::Block(def_stmt.into_boxed_slice(), def_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_stmt = optimize_stmt_block(def_block, state, true, true, false);
|
||||
let def_pos = if x.1.position().is_none() {
|
||||
*pos
|
||||
} else {
|
||||
x.1.position()
|
||||
};
|
||||
|
||||
*stmt = Stmt::If(
|
||||
condition,
|
||||
Box::new((
|
||||
mem::take(stmt_block),
|
||||
Stmt::Block(def_stmt.into_boxed_slice(), def_pos).into(),
|
||||
)),
|
||||
match_expr.position(),
|
||||
);
|
||||
} else {
|
||||
// Promote the matched case
|
||||
let new_pos = stmt_block.position();
|
||||
let statements = mem::take(&mut **stmt_block);
|
||||
let statements =
|
||||
optimize_stmt_block(statements, state, true, true, false);
|
||||
*stmt = Stmt::Block(statements.into_boxed_slice(), new_pos);
|
||||
}
|
||||
|
||||
state.set_dirty();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Multiple ranges - clear the table and just keep the right ranges
|
||||
if !table.is_empty() {
|
||||
state.set_dirty();
|
||||
}
|
||||
|
||||
table.clear();
|
||||
|
||||
let old_ranges_len = ranges.len();
|
||||
|
||||
ranges.retain(|&mut (start, end, inclusive, _, _)| {
|
||||
(!inclusive && (start..end).contains(&value))
|
||||
|| (inclusive && (start..=end).contains(&value))
|
||||
});
|
||||
|
||||
if ranges.len() != old_ranges_len {
|
||||
state.set_dirty();
|
||||
}
|
||||
|
||||
for (_, _, _, condition, stmt_block) in ranges.iter_mut() {
|
||||
let statements = mem::take(&mut **stmt_block);
|
||||
**stmt_block =
|
||||
optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||
|
||||
if let Some(mut c) = mem::take(condition) {
|
||||
optimize_expr(&mut c, state, false);
|
||||
match c {
|
||||
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
|
||||
_ => *condition = Some(c),
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Promote the default case
|
||||
state.set_dirty();
|
||||
let def_pos = if x.1.position().is_none() {
|
||||
*pos
|
||||
} else {
|
||||
x.1.position()
|
||||
};
|
||||
let def_stmt = optimize_stmt_block(mem::take(&mut *x.1), state, true, true, false);
|
||||
*stmt = Stmt::Block(def_stmt.into_boxed_slice(), def_pos);
|
||||
}
|
||||
// switch
|
||||
Stmt::Switch(match_expr, x, _) => {
|
||||
optimize_expr(match_expr, state, false);
|
||||
x.0.values_mut().for_each(|block| {
|
||||
let condition = mem::take(&mut block.0).map_or_else(
|
||||
|| Expr::Unit(Position::NONE),
|
||||
|mut condition| {
|
||||
optimize_expr(&mut condition, state, false);
|
||||
condition
|
||||
},
|
||||
);
|
||||
let statements = mem::take(block.1.deref_mut());
|
||||
*block.1 = optimize_stmt_block(statements, state, preserve_result, true, false);
|
||||
|
||||
match condition {
|
||||
Expr::Unit(_) | Expr::BoolConstant(true, _) => (),
|
||||
_ => {
|
||||
block.0 = Some(condition);
|
||||
|
||||
*block.1 = optimize_stmt_block(
|
||||
mem::take(block.1.deref_mut()),
|
||||
state,
|
||||
preserve_result,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
if let Some(mut condition) = mem::take(&mut block.0) {
|
||||
optimize_expr(&mut condition, state, false);
|
||||
match condition {
|
||||
Expr::Unit(_) | Expr::BoolConstant(true, _) => state.set_dirty(),
|
||||
_ => block.0 = Some(condition),
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -990,7 +1050,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
||||
return;
|
||||
}
|
||||
// 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])
|
||||
.and_then(|f| {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
|
@@ -121,12 +121,15 @@ macro_rules! gen_arithmetic_functions {
|
||||
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
||||
x ^ y
|
||||
}
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: $arg_type) -> bool {
|
||||
x == 0
|
||||
}
|
||||
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
||||
pub fn is_odd(x: $arg_type) -> bool {
|
||||
x % 2 != 0
|
||||
}
|
||||
#[rhai_fn(get = "is_even", name = "is_even")]
|
||||
pub fn is_even(x: $arg_type) -> bool {
|
||||
x % 2 == 0
|
||||
}
|
||||
@@ -209,12 +212,15 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||
|
||||
#[export_module]
|
||||
mod int_functions {
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: INT) -> bool {
|
||||
x == 0
|
||||
}
|
||||
#[rhai_fn(get = "is_odd", name = "is_odd")]
|
||||
pub fn is_odd(x: INT) -> bool {
|
||||
x % 2 != 0
|
||||
}
|
||||
#[rhai_fn(get = "is_even", name = "is_even")]
|
||||
pub fn is_even(x: INT) -> bool {
|
||||
x % 2 == 0
|
||||
}
|
||||
@@ -335,6 +341,7 @@ mod f32_functions {
|
||||
x => Ok(x as INT),
|
||||
}
|
||||
}
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: f32) -> bool {
|
||||
x == 0.0
|
||||
}
|
||||
@@ -442,6 +449,7 @@ mod f64_functions {
|
||||
x => Ok(x as INT),
|
||||
}
|
||||
}
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: f64) -> bool {
|
||||
x == 0.0
|
||||
}
|
||||
@@ -536,6 +544,7 @@ pub mod decimal_functions {
|
||||
1
|
||||
}
|
||||
}
|
||||
#[rhai_fn(get = "is_zero", name = "is_zero")]
|
||||
pub fn is_zero(x: Decimal) -> bool {
|
||||
x.is_zero()
|
||||
}
|
||||
|
@@ -3,7 +3,10 @@
|
||||
|
||||
use crate::engine::OP_EQUALS;
|
||||
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")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{any::TypeId, cmp::Ordering, mem};
|
||||
@@ -145,6 +148,18 @@ mod array_functions {
|
||||
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) {
|
||||
if array.is_empty() {
|
||||
*array = replace;
|
||||
@@ -173,6 +188,18 @@ mod array_functions {
|
||||
|
||||
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 {
|
||||
if array.is_empty() || len <= 0 {
|
||||
return Array::new();
|
||||
@@ -911,6 +938,18 @@ mod array_functions {
|
||||
drain(ctx, array, FnPtr::new(filter)?)
|
||||
}
|
||||
#[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 {
|
||||
if array.is_empty() || len <= 0 {
|
||||
return Array::new();
|
||||
@@ -993,6 +1032,18 @@ mod array_functions {
|
||||
retain(ctx, array, FnPtr::new(filter)?)
|
||||
}
|
||||
#[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 {
|
||||
if array.is_empty() || len <= 0 {
|
||||
return Array::new();
|
||||
|
@@ -2,7 +2,10 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
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")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{any::TypeId, mem};
|
||||
@@ -184,6 +187,18 @@ mod blob_functions {
|
||||
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) {
|
||||
if blob.is_empty() {
|
||||
*blob = replace;
|
||||
@@ -212,6 +227,18 @@ mod blob_functions {
|
||||
|
||||
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 {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
return Blob::new();
|
||||
@@ -264,6 +291,18 @@ mod blob_functions {
|
||||
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 {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
return Blob::new();
|
||||
@@ -288,6 +327,18 @@ mod blob_functions {
|
||||
|
||||
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 {
|
||||
if blob.is_empty() || len <= 0 {
|
||||
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 {
|
||||
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 {
|
||||
parse_int(blob, start, len, false)
|
||||
}
|
||||
@@ -418,11 +493,35 @@ mod blob_functions {
|
||||
|
||||
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")]
|
||||
pub fn write_le_int(blob: &mut Blob, start: INT, len: INT, value: INT) {
|
||||
write_int(blob, start, len, value, true)
|
||||
}
|
||||
#[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) {
|
||||
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"))]
|
||||
pub fn parse_le_float(blob: &mut Blob, start: INT, len: INT) -> FLOAT {
|
||||
parse_float(blob, start, len, true)
|
||||
}
|
||||
#[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 {
|
||||
parse_float(blob, start, len, false)
|
||||
}
|
||||
@@ -514,12 +641,40 @@ mod blob_functions {
|
||||
blob[start..][..len].copy_from_slice(&buf[..len]);
|
||||
}
|
||||
#[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")]
|
||||
pub fn write_le_float(blob: &mut Blob, start: INT, len: INT, value: FLOAT) {
|
||||
write_float(blob, start, len, value, true)
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[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) {
|
||||
write_float(blob, start, len, value, false)
|
||||
}
|
||||
|
@@ -1,7 +1,8 @@
|
||||
use crate::plugin::*;
|
||||
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::ops::Range;
|
||||
use std::ops::{Range, RangeInclusive};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -271,6 +272,7 @@ macro_rules! reg_range {
|
||||
($lib:ident | $x:expr => $( $y:ty ),*) => {
|
||||
$(
|
||||
$lib.set_iterator::<Range<$y>>();
|
||||
$lib.set_iterator::<RangeInclusive<$y>>();
|
||||
let _hash = $lib.set_native_fn($x, |from: $y, to: $y| Ok(from..to));
|
||||
|
||||
#[cfg(feature = "metadata")]
|
||||
@@ -449,6 +451,22 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
// Register string iterator
|
||||
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)));
|
||||
#[cfg(feature = "metadata")]
|
||||
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")]
|
||||
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
|
||||
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);
|
||||
#[cfg(feature = "metadata")]
|
||||
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")]
|
||||
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")]
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, EvalAltResult, INT};
|
||||
use crate::{def_package, EvalAltResult, ExclusiveRange, InclusiveRange, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@@ -256,6 +256,21 @@ mod bit_field_functions {
|
||||
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)]
|
||||
pub fn get_bits(value: INT, index: INT, bits: INT) -> Result<INT, Box<EvalAltResult>> {
|
||||
if bits < 1 {
|
||||
@@ -298,6 +313,26 @@ mod bit_field_functions {
|
||||
|
||||
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)]
|
||||
pub fn set_bits(
|
||||
value: &mut INT,
|
||||
|
@@ -1,7 +1,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::plugin::*;
|
||||
use crate::{def_package, Dynamic, StaticVec, INT};
|
||||
use crate::{def_package, Dynamic, ExclusiveRange, InclusiveRange, StaticVec, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
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(
|
||||
ctx: NativeCallContext,
|
||||
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")]
|
||||
pub fn crop(string: &mut ImmutableString, start: INT, len: INT) {
|
||||
if string.is_empty() {
|
||||
|
@@ -15,8 +15,9 @@ use crate::tokenizer::{
|
||||
};
|
||||
use crate::types::dynamic::AccessMode;
|
||||
use crate::{
|
||||
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, Identifier,
|
||||
ImmutableString, LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
||||
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Engine, ExclusiveRange,
|
||||
Identifier, ImmutableString, InclusiveRange, LexError, ParseError, ParseErrorType, Position,
|
||||
Scope, Shared, StaticVec, AST, INT,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@@ -1003,6 +1004,7 @@ fn parse_switch(
|
||||
}
|
||||
|
||||
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_stmt = None;
|
||||
|
||||
@@ -1033,6 +1035,7 @@ fn parse_switch(
|
||||
(None, None)
|
||||
}
|
||||
(Token::Underscore, pos) => return Err(PERR::DuplicatedSwitchCase.into_err(*pos)),
|
||||
|
||||
_ if def_stmt.is_some() => return Err(PERR::WrongSwitchDefaultCase.into_err(def_pos)),
|
||||
|
||||
_ => {
|
||||
@@ -1047,21 +1050,31 @@ fn parse_switch(
|
||||
}
|
||||
};
|
||||
|
||||
let hash = if let Some(expr) = expr {
|
||||
let (hash, range) = if let Some(expr) = expr {
|
||||
let value = expr.get_literal_value().ok_or_else(|| {
|
||||
PERR::ExprExpected("a literal".to_string()).into_err(expr.position())
|
||||
})?;
|
||||
let hasher = &mut get_hasher();
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
if table.contains_key(&hash) {
|
||||
return Err(PERR::DuplicatedSwitchCase.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();
|
||||
value.hash(hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
if table.contains_key(&hash) {
|
||||
return Err(PERR::DuplicatedSwitchCase.into_err(expr.position()));
|
||||
}
|
||||
(Some(hash), None)
|
||||
}
|
||||
|
||||
Some(hash)
|
||||
} else {
|
||||
None
|
||||
(None, None)
|
||||
};
|
||||
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
@@ -1080,12 +1093,19 @@ fn parse_switch(
|
||||
|
||||
let need_comma = !stmt.is_self_terminated();
|
||||
|
||||
def_stmt = match hash {
|
||||
Some(hash) => {
|
||||
def_stmt = match (hash, range) {
|
||||
(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());
|
||||
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) {
|
||||
@@ -1115,7 +1135,7 @@ fn parse_switch(
|
||||
|
||||
Ok(Stmt::Switch(
|
||||
item,
|
||||
(table, def_stmt_block).into(),
|
||||
(table, def_stmt_block, ranges).into(),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@@ -1975,18 +1995,6 @@ fn parse_binary_op(
|
||||
args.shrink_to_fit();
|
||||
|
||||
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
|
||||
Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
||||
|
||||
@@ -2058,7 +2066,7 @@ fn parse_binary_op(
|
||||
.into_fn_call_expr(pos)
|
||||
}
|
||||
|
||||
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)),
|
||||
_ => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -356,6 +356,10 @@ pub enum Token {
|
||||
Comma,
|
||||
/// `.`
|
||||
Period,
|
||||
/// `..`
|
||||
ExclusiveRange,
|
||||
/// `..=`
|
||||
InclusiveRange,
|
||||
/// `#{`
|
||||
MapStart,
|
||||
/// `=`
|
||||
@@ -507,6 +511,8 @@ impl Token {
|
||||
Underscore => "_",
|
||||
Comma => ",",
|
||||
Period => ".",
|
||||
ExclusiveRange => "..",
|
||||
InclusiveRange => "..=",
|
||||
MapStart => "#{",
|
||||
Equals => "=",
|
||||
True => "true",
|
||||
@@ -701,6 +707,8 @@ impl Token {
|
||||
"_" => Underscore,
|
||||
"," => Comma,
|
||||
"." => Period,
|
||||
".." => ExclusiveRange,
|
||||
"..=" => InclusiveRange,
|
||||
"#{" => MapStart,
|
||||
"=" => Equals,
|
||||
"true" => True,
|
||||
@@ -805,6 +813,8 @@ impl Token {
|
||||
Colon | // #{ foo: - is unary
|
||||
Comma | // ( ... , -expr ) - is unary
|
||||
//Period |
|
||||
ExclusiveRange | // .. - is unary
|
||||
InclusiveRange | // ..= - is unary
|
||||
LeftBrace | // { -expr } - is unary
|
||||
// RightBrace | { expr } - expr not unary & is closing
|
||||
LeftParen | // ( -expr ) - is unary
|
||||
@@ -868,6 +878,8 @@ impl Token {
|
||||
| LeftShiftAssign | RightShiftAssign | AndAssign | OrAssign | XOrAssign
|
||||
| ModuloAssign => 0,
|
||||
|
||||
ExclusiveRange | InclusiveRange => 10,
|
||||
|
||||
Or | XOr | Pipe => 30,
|
||||
|
||||
And | Ampersand => 60,
|
||||
@@ -921,11 +933,12 @@ impl Token {
|
||||
match self {
|
||||
LeftBrace | RightBrace | LeftParen | RightParen | LeftBracket | RightBracket | Plus
|
||||
| UnaryPlus | Minus | UnaryMinus | Multiply | Divide | Modulo | PowerOf | LeftShift
|
||||
| RightShift | SemiColon | Colon | DoubleColon | Comma | Period | MapStart | Equals
|
||||
| LessThan | GreaterThan | LessThanEqualsTo | GreaterThanEqualsTo | EqualsTo
|
||||
| NotEqualsTo | Bang | Pipe | Or | XOr | Ampersand | And | PlusAssign | MinusAssign
|
||||
| MultiplyAssign | DivideAssign | LeftShiftAssign | RightShiftAssign | AndAssign
|
||||
| OrAssign | XOrAssign | ModuloAssign | PowerOfAssign => true,
|
||||
| RightShift | SemiColon | Colon | DoubleColon | Comma | Period | ExclusiveRange
|
||||
| InclusiveRange | MapStart | Equals | LessThan | GreaterThan | LessThanEqualsTo
|
||||
| GreaterThanEqualsTo | EqualsTo | NotEqualsTo | Bang | Pipe | Or | XOr | Ampersand
|
||||
| And | PlusAssign | MinusAssign | MultiplyAssign | DivideAssign | LeftShiftAssign
|
||||
| RightShiftAssign | AndAssign | OrAssign | XOrAssign | ModuloAssign
|
||||
| PowerOfAssign => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
@@ -1794,13 +1807,20 @@ fn get_next_token_inner(
|
||||
|
||||
('.', '.') => {
|
||||
eat_next(stream, pos);
|
||||
|
||||
if stream.peek_next() == Some('.') {
|
||||
eat_next(stream, pos);
|
||||
return Some((Token::Reserved("...".into()), start_pos));
|
||||
} else {
|
||||
return Some((Token::Reserved("..".into()), start_pos));
|
||||
}
|
||||
return Some((
|
||||
match stream.peek_next() {
|
||||
Some('.') => {
|
||||
eat_next(stream, pos);
|
||||
Token::Reserved("...".into())
|
||||
}
|
||||
Some('=') => {
|
||||
eat_next(stream, pos);
|
||||
Token::InclusiveRange
|
||||
}
|
||||
_ => Token::ExclusiveRange,
|
||||
},
|
||||
start_pos,
|
||||
));
|
||||
}
|
||||
('.', _) => return Some((Token::Period, start_pos)),
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::func::native::SendSync;
|
||||
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")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
@@ -588,6 +588,12 @@ pub(crate) fn map_std_type_name(name: &str) -> &str {
|
||||
if name == type_name::<Instant>() {
|
||||
return "timestamp";
|
||||
}
|
||||
if name == type_name::<ExclusiveRange>() {
|
||||
return "range";
|
||||
}
|
||||
if name == type_name::<InclusiveRange>() {
|
||||
return "range=";
|
||||
}
|
||||
|
||||
name
|
||||
}
|
||||
@@ -655,6 +661,14 @@ impl fmt::Display for Dynamic {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -737,6 +751,14 @@ impl fmt::Debug for Dynamic {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
/// A [`Dynamic`] containing a `()`.
|
||||
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);
|
||||
/// A [`Dynamic`] containing the integer zero.
|
||||
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);
|
||||
/// A [`Dynamic`] containing the integer two.
|
||||
/// A [`Dynamic`] containing the integer 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);
|
||||
/// A [`Dynamic`] containing the integer ten.
|
||||
/// A [`Dynamic`] containing the integer 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);
|
||||
/// A [`Dynamic`] containing the integer one thousand.
|
||||
/// A [`Dynamic`] containing the integer 1,000.
|
||||
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);
|
||||
/// A [`Dynamic`] containing the integer -2.
|
||||
pub const NEGATIVE_TWO: Self = Self::from_int(-2);
|
||||
/// A [`Dynamic`] containing `0.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
@@ -862,54 +895,97 @@ impl Dynamic {
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
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`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
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 π.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
pub const FLOAT_PI: Self = Self::from_float(
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
{
|
||||
std::f64::consts::PI
|
||||
},
|
||||
#[cfg(feature = "f32_float")]
|
||||
{
|
||||
std::f32::consts::PI
|
||||
},
|
||||
);
|
||||
pub const FLOAT_PI: Self = Self::from_float(FloatConstants::PI);
|
||||
/// A [`Dynamic`] containing π/2.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_HALF_PI: Self = Self::from_float(
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
{
|
||||
std::f64::consts::PI / 2.0
|
||||
},
|
||||
#[cfg(feature = "f32_float")]
|
||||
{
|
||||
std::f32::consts::PI / 2.0
|
||||
},
|
||||
);
|
||||
pub const FLOAT_HALF_PI: Self = Self::from_float(FloatConstants::FRAC_PI_2);
|
||||
/// A [`Dynamic`] containing π/4.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_QUARTER_PI: Self = Self::from_float(FloatConstants::FRAC_PI_4);
|
||||
/// A [`Dynamic`] containing 2π.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_TWO_PI: Self = Self::from_float(
|
||||
#[cfg(not(feature = "f32_float"))]
|
||||
{
|
||||
std::f64::consts::PI * 2.0
|
||||
},
|
||||
#[cfg(feature = "f32_float")]
|
||||
{
|
||||
std::f32::consts::PI * 2.0
|
||||
},
|
||||
);
|
||||
pub const FLOAT_TWO_PI: Self = Self::from_float(FloatConstants::TAU);
|
||||
/// A [`Dynamic`] containing 1/π.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_INVERSE_PI: Self = Self::from_float(FloatConstants::FRAC_1_PI);
|
||||
/// A [`Dynamic`] containing _e_.
|
||||
///
|
||||
/// 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`].
|
||||
#[inline(always)]
|
||||
@@ -2264,3 +2340,16 @@ impl From<crate::Shared<crate::Locked<Dynamic>>> for Dynamic {
|
||||
Self(Union::Shared(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExclusiveRange> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: ExclusiveRange) -> Self {
|
||||
Dynamic::from(value)
|
||||
}
|
||||
}
|
||||
impl From<InclusiveRange> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: InclusiveRange) -> Self {
|
||||
Dynamic::from(value)
|
||||
}
|
||||
}
|
||||
|
@@ -108,6 +108,8 @@ pub enum ParseErrorType {
|
||||
DuplicatedSwitchCase,
|
||||
/// A variable name is duplicated. Wrapped value is the variable name.
|
||||
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.
|
||||
WrongSwitchDefaultCase,
|
||||
/// 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::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
||||
Self::UnexpectedEOF => f.write_str("Script is incomplete"),
|
||||
Self::WrongSwitchDefaultCase => f.write_str("Default switch case is not the last"),
|
||||
Self::WrongSwitchCaseCondition => f.write_str("Default switch case cannot have condition"),
|
||||
Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"),
|
||||
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::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"),
|
||||
|
Reference in New Issue
Block a user