Extract index calculataion into functions.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
//! Types to support chaining operations (i.e. indexing and dotting).
|
||||
#![cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
|
||||
use super::{EvalState, GlobalRuntimeState, Target};
|
||||
use super::{calc_index, EvalState, GlobalRuntimeState, Target};
|
||||
use crate::ast::{Expr, OpAssignment};
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR};
|
||||
@@ -771,7 +771,7 @@ impl Engine {
|
||||
lib: &[&Module],
|
||||
target: &'t mut Dynamic,
|
||||
idx: Dynamic,
|
||||
idx_pos: Position,
|
||||
pos: Position,
|
||||
add_if_not_found: bool,
|
||||
use_indexers: bool,
|
||||
level: usize,
|
||||
@@ -788,38 +788,12 @@ impl Engine {
|
||||
// val_array[idx]
|
||||
let index = idx
|
||||
.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, pos))?;
|
||||
let len = arr.len();
|
||||
let arr_idx =
|
||||
calc_index(len, index, true, || ERR::ErrorArrayBounds(len, index, pos))?;
|
||||
|
||||
let arr_len = arr.len();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let arr_idx = if index < 0 {
|
||||
// Count from end if negative
|
||||
arr_len
|
||||
- index
|
||||
.checked_abs()
|
||||
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos))
|
||||
.and_then(|n| {
|
||||
if n as usize > arr_len {
|
||||
Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
|
||||
} else {
|
||||
Ok(n as usize)
|
||||
}
|
||||
})?
|
||||
} else {
|
||||
index as usize
|
||||
};
|
||||
#[cfg(feature = "unchecked")]
|
||||
let arr_idx = if index < 0 {
|
||||
// Count from end if negative
|
||||
arr_len - index.abs() as usize
|
||||
} else {
|
||||
index as usize
|
||||
};
|
||||
|
||||
arr.get_mut(arr_idx)
|
||||
.map(Target::from)
|
||||
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
|
||||
Ok(arr.get_mut(arr_idx).map(Target::from).unwrap())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@@ -827,39 +801,13 @@ impl Engine {
|
||||
// val_blob[idx]
|
||||
let index = idx
|
||||
.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, pos))?;
|
||||
let len = arr.len();
|
||||
let arr_idx =
|
||||
calc_index(len, index, true, || ERR::ErrorArrayBounds(len, index, pos))?;
|
||||
|
||||
let arr_len = arr.len();
|
||||
let value = arr.get(arr_idx).map(|&v| (v as crate::INT).into()).unwrap();
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
let arr_idx = if index < 0 {
|
||||
// Count from end if negative
|
||||
arr_len
|
||||
- index
|
||||
.checked_abs()
|
||||
.ok_or_else(|| ERR::ErrorArrayBounds(arr_len, index, idx_pos))
|
||||
.and_then(|n| {
|
||||
if n as usize > arr_len {
|
||||
Err(ERR::ErrorArrayBounds(arr_len, index, idx_pos).into())
|
||||
} else {
|
||||
Ok(n as usize)
|
||||
}
|
||||
})?
|
||||
} else {
|
||||
index as usize
|
||||
};
|
||||
#[cfg(feature = "unchecked")]
|
||||
let arr_idx = if index < 0 {
|
||||
// Count from end if negative
|
||||
arr_len - index.abs() as usize
|
||||
} else {
|
||||
index as usize
|
||||
};
|
||||
|
||||
let value = arr
|
||||
.get(arr_idx)
|
||||
.map(|&v| (v as crate::INT).into())
|
||||
.ok_or_else(|| Box::new(ERR::ErrorArrayBounds(arr_len, index, idx_pos)))?;
|
||||
Ok(Target::BlobByte {
|
||||
source: target,
|
||||
value,
|
||||
@@ -871,7 +819,7 @@ impl Engine {
|
||||
Dynamic(Union::Map(map, _, _)) => {
|
||||
// val_map[idx]
|
||||
let index = idx.read_lock::<crate::ImmutableString>().ok_or_else(|| {
|
||||
self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), idx_pos)
|
||||
self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), pos)
|
||||
})?;
|
||||
|
||||
if _add_if_not_found && !map.contains_key(index.as_str()) {
|
||||
@@ -888,11 +836,6 @@ impl Engine {
|
||||
Dynamic(Union::Int(value, _, _))
|
||||
if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() =>
|
||||
{
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
type BASE = u64;
|
||||
#[cfg(feature = "only_i32")]
|
||||
type BASE = u32;
|
||||
|
||||
// val_int[range]
|
||||
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
|
||||
|
||||
@@ -900,40 +843,47 @@ impl Engine {
|
||||
let start = range.start;
|
||||
let end = range.end;
|
||||
|
||||
if start < 0 || start as usize >= BITS {
|
||||
return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into());
|
||||
} else if end < 0 || end as usize >= BITS {
|
||||
return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into());
|
||||
} else if end <= start {
|
||||
let start = calc_index(BITS, start, false, || {
|
||||
ERR::ErrorBitFieldBounds(BITS, start, pos)
|
||||
})?;
|
||||
let end = calc_index(BITS, end, false, || {
|
||||
ERR::ErrorBitFieldBounds(BITS, end, pos)
|
||||
})?;
|
||||
|
||||
if end <= start {
|
||||
(0, 0)
|
||||
} else if end as usize == BITS && start == 0 {
|
||||
} else if end == 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 crate::INT) << start,
|
||||
(((2 as crate::UINT).pow((end - start) as u32) - 1) as crate::INT)
|
||||
<< start,
|
||||
)
|
||||
}
|
||||
} else if let Some(range) = idx.read_lock::<crate::InclusiveRange>() {
|
||||
let start = *range.start();
|
||||
let end = *range.end();
|
||||
|
||||
if start < 0 || start as usize >= BITS {
|
||||
return Err(ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into());
|
||||
} else if end < 0 || end as usize >= BITS {
|
||||
return Err(ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into());
|
||||
} else if end < start {
|
||||
let start = calc_index(BITS, start, false, || {
|
||||
ERR::ErrorBitFieldBounds(BITS, start, pos)
|
||||
})?;
|
||||
let end = calc_index(BITS, end, false, || {
|
||||
ERR::ErrorBitFieldBounds(BITS, end, pos)
|
||||
})?;
|
||||
|
||||
if end < start {
|
||||
(0, 0)
|
||||
} else if end as usize == BITS - 1 && start == 0 {
|
||||
} else if end == 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 crate::INT)
|
||||
(((2 as crate::UINT).pow((end - start + 1) as u32) - 1) as crate::INT)
|
||||
<< start,
|
||||
)
|
||||
}
|
||||
@@ -956,39 +906,20 @@ impl Engine {
|
||||
// val_int[idx]
|
||||
let index = idx
|
||||
.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, pos))?;
|
||||
|
||||
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
|
||||
|
||||
let (bit_value, offset) = if index >= 0 {
|
||||
let offset = index as usize;
|
||||
(
|
||||
if offset >= BITS {
|
||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
|
||||
} else {
|
||||
(*value & (1 << offset)) != 0
|
||||
},
|
||||
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 {
|
||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
|
||||
} else {
|
||||
(*value & (1 << (BITS - offset))) != 0
|
||||
},
|
||||
offset as u8,
|
||||
)
|
||||
} else {
|
||||
return Err(ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into());
|
||||
};
|
||||
let bit = calc_index(BITS, index, true, || {
|
||||
ERR::ErrorBitFieldBounds(BITS, index, pos)
|
||||
})?;
|
||||
|
||||
let bit_value = (*value & (1 << bit)) != 0;
|
||||
|
||||
Ok(Target::Bit {
|
||||
source: target,
|
||||
value: bit_value.into(),
|
||||
bit: offset,
|
||||
bit: bit as u8,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -997,14 +928,14 @@ impl Engine {
|
||||
// val_string[idx]
|
||||
let index = idx
|
||||
.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, pos))?;
|
||||
|
||||
let (ch, offset) = if index >= 0 {
|
||||
let offset = index as usize;
|
||||
(
|
||||
s.chars().nth(offset).ok_or_else(|| {
|
||||
let chars_len = s.chars().count();
|
||||
ERR::ErrorStringBounds(chars_len, index, idx_pos)
|
||||
ERR::ErrorStringBounds(chars_len, index, pos)
|
||||
})?,
|
||||
offset,
|
||||
)
|
||||
@@ -1014,13 +945,13 @@ impl Engine {
|
||||
// Count from end if negative
|
||||
s.chars().rev().nth(offset - 1).ok_or_else(|| {
|
||||
let chars_len = s.chars().count();
|
||||
ERR::ErrorStringBounds(chars_len, index, idx_pos)
|
||||
ERR::ErrorStringBounds(chars_len, index, pos)
|
||||
})?,
|
||||
offset,
|
||||
)
|
||||
} else {
|
||||
let chars_len = s.chars().count();
|
||||
return Err(ERR::ErrorStringBounds(chars_len, index, idx_pos).into());
|
||||
return Err(ERR::ErrorStringBounds(chars_len, index, pos).into());
|
||||
};
|
||||
|
||||
Ok(Target::StringChar {
|
||||
@@ -1033,8 +964,6 @@ impl Engine {
|
||||
_ if use_indexers => {
|
||||
let args = &mut [target, &mut idx];
|
||||
let hash_get = crate::ast::FnCallHashes::from_native(global.hash_idx_get());
|
||||
let idx_pos = Position::NONE;
|
||||
|
||||
self.exec_fn_call(
|
||||
global,
|
||||
state,
|
||||
@@ -1044,7 +973,7 @@ impl Engine {
|
||||
args,
|
||||
true,
|
||||
true,
|
||||
idx_pos,
|
||||
Position::NONE,
|
||||
None,
|
||||
level,
|
||||
)
|
||||
|
@@ -12,4 +12,4 @@ pub use chaining::{ChainArgument, ChainType};
|
||||
pub use eval_context::EvalContext;
|
||||
pub use eval_state::EvalState;
|
||||
pub use global_state::GlobalRuntimeState;
|
||||
pub use target::Target;
|
||||
pub use target::{calc_index, calc_offset_len, Target};
|
||||
|
@@ -1,11 +1,77 @@
|
||||
//! Type to hold a mutable reference to the target of an evaluation.
|
||||
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::{Dynamic, RhaiResultOf};
|
||||
use crate::{Dynamic, EvalAltResult, RhaiResultOf, INT};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
// Calculate an offset+len pair given an actual length of the underlying array.
|
||||
//
|
||||
// Negative starting positions count from the end.
|
||||
//
|
||||
// Values going over bounds are limited to the actual length.
|
||||
#[inline(always)]
|
||||
pub fn calc_offset_len(length: usize, start: INT, len: INT) -> (usize, usize) {
|
||||
let start = if start < 0 {
|
||||
start.checked_abs().map_or(0, |positive_start| {
|
||||
length - usize::min(positive_start as usize, length)
|
||||
})
|
||||
} else if start as usize >= length {
|
||||
return (length, 0);
|
||||
} else {
|
||||
start as usize
|
||||
};
|
||||
|
||||
let len = if len <= 0 {
|
||||
0
|
||||
} else if len as usize > length - start {
|
||||
length - start
|
||||
} else {
|
||||
len as usize
|
||||
};
|
||||
|
||||
(start, len)
|
||||
}
|
||||
|
||||
// Calculate an offset+len pair given an actual length of the underlying array.
|
||||
//
|
||||
// Negative starting positions count from the end.
|
||||
//
|
||||
// Values going over bounds call the provided closure to create an error.
|
||||
#[inline(always)]
|
||||
pub fn calc_index(
|
||||
length: usize,
|
||||
start: INT,
|
||||
negative_count_from_end: bool,
|
||||
err: impl Fn() -> EvalAltResult,
|
||||
) -> RhaiResultOf<usize> {
|
||||
if start < 0 {
|
||||
if negative_count_from_end {
|
||||
// Count from end if negative
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
return start
|
||||
.checked_abs()
|
||||
.ok_or_else(|| err().into())
|
||||
.and_then(|positive_start| {
|
||||
if (positive_start as usize) > length {
|
||||
Err(err().into())
|
||||
} else {
|
||||
Ok(length - (positive_start as usize))
|
||||
}
|
||||
});
|
||||
#[cfg(feature = "unchecked")]
|
||||
return Ok(actual - (start.abs() as usize));
|
||||
} else {
|
||||
Err(err().into())
|
||||
}
|
||||
} else if start as usize >= length {
|
||||
Err(err().into())
|
||||
} else {
|
||||
Ok(start as usize)
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that encapsulates a mutation target for an expression with side effects.
|
||||
#[derive(Debug)]
|
||||
pub enum Target<'a> {
|
||||
|
Reference in New Issue
Block a user