Replace unwrap with expect.
This commit is contained in:
parent
1545b602a7
commit
cc3e2d79a5
@ -4,10 +4,16 @@ Rhai Release Notes
|
||||
Version 0.20.2
|
||||
==============
|
||||
|
||||
This version adds a number of convenience features:
|
||||
|
||||
* Ability for a `Dynamic` to hold an `i32` _tag_ of arbitrary data
|
||||
|
||||
* Simplifies dynamic properties access by falling back to an indexer (passing the name of the property as a string) when a property is not found.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Constant propagation during optimization for constants held in a custom scope now works properly instead of always replacing by `()`.
|
||||
* Propagation of constants held in a custom scope now works properly instead of always replacing by `()`.
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
|
42
src/ast.rs
42
src/ast.rs
@ -909,6 +909,7 @@ impl DerefMut for StmtBlock {
|
||||
impl fmt::Debug for StmtBlock {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("Block")?;
|
||||
fmt::Debug::fmt(&self.0, f)?;
|
||||
self.1.debug_print(f)
|
||||
}
|
||||
@ -1225,6 +1226,7 @@ impl Stmt {
|
||||
path: &mut Vec<ASTNode<'a>>,
|
||||
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
|
||||
) -> bool {
|
||||
// Push the current node onto the path
|
||||
path.push(self.into());
|
||||
|
||||
if !on_node(path) {
|
||||
@ -1340,7 +1342,8 @@ impl Stmt {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
path.pop().unwrap();
|
||||
path.pop()
|
||||
.expect("never fails because `path` always contains the current node");
|
||||
|
||||
true
|
||||
}
|
||||
@ -1398,7 +1401,7 @@ impl OpAssignment {
|
||||
pub fn new(op: Token) -> Self {
|
||||
let op_raw = op
|
||||
.map_op_assignment()
|
||||
.expect("token must be an op-assignment operator")
|
||||
.expect("never fails because token must be an op-assignment operator")
|
||||
.keyword_syntax();
|
||||
let op_assignment = op.keyword_syntax();
|
||||
|
||||
@ -1492,13 +1495,9 @@ impl FnCallHashes {
|
||||
self.script.is_none()
|
||||
}
|
||||
/// Get the script function hash from this [`FnCallHashes`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the [`FnCallHashes`] is native Rust only.
|
||||
#[inline(always)]
|
||||
pub fn script_hash(&self) -> u64 {
|
||||
self.script.unwrap()
|
||||
pub fn script_hash(&self) -> Option<u64> {
|
||||
self.script
|
||||
}
|
||||
/// Get the naive Rust function hash from this [`FnCallHashes`].
|
||||
#[inline(always)]
|
||||
@ -1787,7 +1786,7 @@ impl fmt::Debug for Expr {
|
||||
}
|
||||
Self::Property(x) => write!(f, "Property({})", (x.2).0),
|
||||
Self::Stmt(x) => {
|
||||
f.write_str("Stmt")?;
|
||||
f.write_str("ExprStmtBlock")?;
|
||||
f.debug_list().entries(x.0.iter()).finish()
|
||||
}
|
||||
Self::FnCall(x, _) => {
|
||||
@ -1848,7 +1847,10 @@ impl Expr {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::Array(x, _) if self.is_constant() => {
|
||||
let mut arr = Array::with_capacity(x.len());
|
||||
arr.extend(x.iter().map(|v| v.get_constant_value().unwrap()));
|
||||
arr.extend(x.iter().map(|v| {
|
||||
v.get_constant_value()
|
||||
.expect("never fails because a constant array always has a constant value")
|
||||
}));
|
||||
Dynamic::from_array(arr)
|
||||
}
|
||||
|
||||
@ -1856,7 +1858,10 @@ impl Expr {
|
||||
Self::Map(x, _) if self.is_constant() => {
|
||||
let mut map = x.1.clone();
|
||||
x.0.iter().for_each(|(k, v)| {
|
||||
*map.get_mut(k.name.as_str()).unwrap() = v.get_constant_value().unwrap()
|
||||
*map.get_mut(k.name.as_str())
|
||||
.expect("never fails because the template should contain all the keys") = v
|
||||
.get_constant_value()
|
||||
.expect("never fails because a constant map always has a constant value")
|
||||
});
|
||||
Dynamic::from_map(map)
|
||||
}
|
||||
@ -1899,7 +1904,12 @@ impl Expr {
|
||||
| Self::FnCall(_, pos)
|
||||
| Self::Custom(_, pos) => *pos,
|
||||
|
||||
Self::InterpolatedString(x) => x.first().unwrap().position(),
|
||||
Self::InterpolatedString(x) => x
|
||||
.first()
|
||||
.expect(
|
||||
"never fails because an interpolated string always contains at least one item",
|
||||
)
|
||||
.position(),
|
||||
|
||||
Self::Property(x) => (x.2).1,
|
||||
Self::Stmt(x) => x.1,
|
||||
@ -1933,7 +1943,9 @@ impl Expr {
|
||||
| Self::Custom(_, pos) => *pos = new_pos,
|
||||
|
||||
Self::InterpolatedString(x) => {
|
||||
x.first_mut().unwrap().set_position(new_pos);
|
||||
x.first_mut()
|
||||
.expect("never fails because an interpolated string always contains at least one item")
|
||||
.set_position(new_pos);
|
||||
}
|
||||
|
||||
Self::Property(x) => (x.2).1 = new_pos,
|
||||
@ -2050,6 +2062,7 @@ impl Expr {
|
||||
path: &mut Vec<ASTNode<'a>>,
|
||||
on_node: &mut impl FnMut(&[ASTNode]) -> bool,
|
||||
) -> bool {
|
||||
// Push the current node onto the path
|
||||
path.push(self.into());
|
||||
|
||||
if !on_node(path) {
|
||||
@ -2103,7 +2116,8 @@ impl Expr {
|
||||
_ => (),
|
||||
}
|
||||
|
||||
path.pop().unwrap();
|
||||
path.pop()
|
||||
.expect("never fails because `path` always contains the current node");
|
||||
|
||||
true
|
||||
}
|
||||
|
182
src/dynamic.rs
182
src/dynamic.rs
@ -242,7 +242,9 @@ impl<'d, T: Any + Clone> Deref for DynamicReadLock<'d, T> {
|
||||
DynamicReadLockInner::Reference(reference) => *reference,
|
||||
// Unwrapping is safe because all checking is already done in its constructor
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
|
||||
DynamicReadLockInner::Guard(guard) => guard.downcast_ref().expect(
|
||||
"never fails because the read guard was created after checking the data type",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -284,7 +286,9 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
|
||||
DynamicWriteLockInner::Reference(reference) => *reference,
|
||||
// Unwrapping is safe because all checking is already done in its constructor
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().unwrap(),
|
||||
DynamicWriteLockInner::Guard(guard) => guard.downcast_ref().expect(
|
||||
"never fails because the read guard was created after checking the data type",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -296,7 +300,9 @@ impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
|
||||
DynamicWriteLockInner::Reference(reference) => *reference,
|
||||
// Unwrapping is safe because all checking is already done in its constructor
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().unwrap(),
|
||||
DynamicWriteLockInner::Guard(guard) => guard.downcast_mut().expect(
|
||||
"never fails because the write guard was created after checking the data type",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -573,38 +579,88 @@ impl fmt::Display for Dynamic {
|
||||
Union::Variant(value, _, _) => {
|
||||
let _type_id = (***value).type_id();
|
||||
|
||||
const CHECKED: &str = "never fails because the type was checked";
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
if _type_id == TypeId::of::<u8>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u8>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<u8>().expect(CHECKED),
|
||||
);
|
||||
} else if _type_id == TypeId::of::<u16>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u16>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<u16>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<u32>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u32>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<u32>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<u64>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u64>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<u64>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i8>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i8>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<i8>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i16>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i16>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<i16>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i32>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i32>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<i32>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i64>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i64>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<i64>().expect(CHECKED)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
if _type_id == TypeId::of::<f32>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<f32>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<f32>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<f64>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<f64>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<f64>().expect(CHECKED)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
if _type_id == TypeId::of::<u128>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<u128>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<u128>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i128>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<i128>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<i128>().expect(CHECKED)
|
||||
);
|
||||
}
|
||||
|
||||
f.write_str((***value).type_name())
|
||||
@ -652,31 +708,73 @@ impl fmt::Debug for Dynamic {
|
||||
Union::Variant(value, _, _) => {
|
||||
let _type_id = (***value).type_id();
|
||||
|
||||
const CHECKED: &str = "never fails because the type was checked";
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
if _type_id == TypeId::of::<u8>() {
|
||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u8>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<u8>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<u16>() {
|
||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u16>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<u16>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<u32>() {
|
||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u32>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<u32>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<u64>() {
|
||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<u64>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<u64>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i8>() {
|
||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i8>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<i8>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i16>() {
|
||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i16>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<i16>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i32>() {
|
||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i32>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<i32>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i64>() {
|
||||
return write!(f, "{:?}", (**value).as_any().downcast_ref::<i64>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<i64>().expect(CHECKED)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
if _type_id == TypeId::of::<f32>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<f32>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<f32>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<f64>() {
|
||||
return write!(f, "{}", (**value).as_any().downcast_ref::<f64>().unwrap());
|
||||
return write!(
|
||||
f,
|
||||
"{}",
|
||||
(**value).as_any().downcast_ref::<f64>().expect(CHECKED)
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
@ -684,13 +782,13 @@ impl fmt::Debug for Dynamic {
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<u128>().unwrap()
|
||||
(**value).as_any().downcast_ref::<u128>().expect(CHECKED)
|
||||
);
|
||||
} else if _type_id == TypeId::of::<i128>() {
|
||||
return write!(
|
||||
f,
|
||||
"{:?}",
|
||||
(**value).as_any().downcast_ref::<i128>().unwrap()
|
||||
(**value).as_any().downcast_ref::<i128>().expect(CHECKED)
|
||||
);
|
||||
}
|
||||
|
||||
@ -985,51 +1083,53 @@ impl Dynamic {
|
||||
pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||
|
||||
const CHECKED: &str = "never fails because the type was checked";
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return unsafe_try_cast::<_, Dynamic>(value).ok().unwrap();
|
||||
return unsafe_try_cast::<_, Dynamic>(value).ok().expect(CHECKED);
|
||||
}
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||
return <dyn Any>::downcast_ref::<INT>(&value)
|
||||
.unwrap()
|
||||
.expect(CHECKED)
|
||||
.clone()
|
||||
.into();
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||
return <dyn Any>::downcast_ref::<FLOAT>(&value)
|
||||
.unwrap()
|
||||
.expect(CHECKED)
|
||||
.clone()
|
||||
.into();
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
if TypeId::of::<T>() == TypeId::of::<Decimal>() {
|
||||
return <dyn Any>::downcast_ref::<Decimal>(&value)
|
||||
.unwrap()
|
||||
.expect(CHECKED)
|
||||
.clone()
|
||||
.into();
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return <dyn Any>::downcast_ref::<bool>(&value)
|
||||
.unwrap()
|
||||
.expect(CHECKED)
|
||||
.clone()
|
||||
.into();
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||
return <dyn Any>::downcast_ref::<char>(&value)
|
||||
.unwrap()
|
||||
.expect(CHECKED)
|
||||
.clone()
|
||||
.into();
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||
return <dyn Any>::downcast_ref::<ImmutableString>(&value)
|
||||
.unwrap()
|
||||
.expect(CHECKED)
|
||||
.clone()
|
||||
.into();
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<&str>() {
|
||||
return <dyn Any>::downcast_ref::<&str>(&value)
|
||||
.unwrap()
|
||||
.expect(CHECKED)
|
||||
.deref()
|
||||
.into();
|
||||
}
|
||||
@ -1455,9 +1555,9 @@ impl Dynamic {
|
||||
#[cfg(feature = "sync")]
|
||||
let value = cell.read().unwrap();
|
||||
|
||||
let type_id = (*value).type_id();
|
||||
|
||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||
if (*value).type_id() != TypeId::of::<T>()
|
||||
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
|
||||
{
|
||||
return None;
|
||||
} else {
|
||||
return Some(DynamicReadLock(DynamicReadLockInner::Guard(value)));
|
||||
@ -1488,9 +1588,9 @@ impl Dynamic {
|
||||
#[cfg(feature = "sync")]
|
||||
let value = cell.write().unwrap();
|
||||
|
||||
let type_id = (*value).type_id();
|
||||
|
||||
if type_id != TypeId::of::<T>() && TypeId::of::<Dynamic>() != TypeId::of::<T>() {
|
||||
if (*value).type_id() != TypeId::of::<T>()
|
||||
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
|
||||
{
|
||||
return None;
|
||||
} else {
|
||||
return Some(DynamicWriteLock(DynamicWriteLockInner::Guard(value)));
|
||||
|
175
src/engine.rs
175
src/engine.rs
@ -429,15 +429,15 @@ impl<'a> Target<'a> {
|
||||
/// This has no effect except for string indexing.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
pub fn propagate_changed_value(&mut self) {
|
||||
pub fn propagate_changed_value(&mut self) -> Result<(), Box<EvalAltResult>> {
|
||||
match self {
|
||||
Self::Ref(_) | Self::Value(_) => (),
|
||||
Self::Ref(_) | Self::Value(_) => Ok(()),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::LockGuard(_) => (),
|
||||
Self::LockGuard(_) => Ok(()),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar(_, _, ch) => {
|
||||
let char_value = ch.clone();
|
||||
self.set_value(char_value, Position::NONE).unwrap();
|
||||
self.set_value(char_value, Position::NONE)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -455,7 +455,9 @@ impl<'a> Target<'a> {
|
||||
Self::Value(_) => panic!("cannot update a value"),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::StringChar(s, index, _) => {
|
||||
let s = &mut *s.write_lock::<ImmutableString>().unwrap();
|
||||
let s = &mut *s
|
||||
.write_lock::<ImmutableString>()
|
||||
.expect("never fails because `StringChar` always holds an `ImmutableString`");
|
||||
|
||||
// Replace the character at the specified index position
|
||||
let new_ch = new_val.as_char().map_err(|err| {
|
||||
@ -488,7 +490,12 @@ impl<'a> From<&'a mut Dynamic> for Target<'a> {
|
||||
if value.is_shared() {
|
||||
// Cloning is cheap for a shared value
|
||||
let container = value.clone();
|
||||
return Self::LockGuard((value.write_lock::<Dynamic>().unwrap(), container));
|
||||
return Self::LockGuard((
|
||||
value
|
||||
.write_lock::<Dynamic>()
|
||||
.expect("never fails when casting to `Dynamic`"),
|
||||
container,
|
||||
));
|
||||
}
|
||||
|
||||
Self::Ref(value)
|
||||
@ -598,9 +605,12 @@ impl State {
|
||||
#[inline(always)]
|
||||
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
|
||||
if self.fn_resolution_caches.0.is_empty() {
|
||||
// Push a new function resolution cache if the stack is empty
|
||||
self.fn_resolution_caches.0.push(BTreeMap::new());
|
||||
}
|
||||
self.fn_resolution_caches.0.last_mut().unwrap()
|
||||
self.fn_resolution_caches.0.last_mut().expect(
|
||||
"never fails because there is at least one function resolution cache by this point",
|
||||
)
|
||||
}
|
||||
/// Push an empty function resolution cache onto the stack and make it current.
|
||||
#[allow(dead_code)]
|
||||
@ -617,7 +627,11 @@ impl State {
|
||||
/// Panics if there are no more function resolution cache in the stack.
|
||||
#[inline(always)]
|
||||
pub fn pop_fn_resolution_cache(&mut self) {
|
||||
let mut cache = self.fn_resolution_caches.0.pop().unwrap();
|
||||
let mut cache = self
|
||||
.fn_resolution_caches
|
||||
.0
|
||||
.pop()
|
||||
.expect("there should be at least one function resolution cache");
|
||||
cache.clear();
|
||||
self.fn_resolution_caches.1.push(cache);
|
||||
}
|
||||
@ -996,10 +1010,16 @@ impl Engine {
|
||||
|
||||
if let Some(index) = index {
|
||||
let offset = mods.len() - index.get();
|
||||
Some(mods.get(offset).expect("invalid index in Imports"))
|
||||
Some(
|
||||
mods.get(offset)
|
||||
.expect("never fails because offset should be within range"),
|
||||
)
|
||||
} else {
|
||||
mods.find(root)
|
||||
.map(|n| mods.get(n).expect("invalid index in Imports"))
|
||||
.map(|n| {
|
||||
mods.get(n)
|
||||
.expect("never fails because the index came from `find`")
|
||||
})
|
||||
.or_else(|| self.global_sub_modules.get(root).cloned())
|
||||
}
|
||||
}
|
||||
@ -1051,6 +1071,10 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Search for a variable within the scope
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `expr` is not [`Expr::Variable`].
|
||||
pub(crate) fn search_scope_only<'s>(
|
||||
&self,
|
||||
scope: &'s mut Scope,
|
||||
@ -1088,8 +1112,12 @@ impl Engine {
|
||||
this_ptr,
|
||||
level: 0,
|
||||
};
|
||||
if let Some(mut result) =
|
||||
resolve_var(expr.get_variable_name(true).unwrap(), index, &context)
|
||||
if let Some(mut result) = resolve_var(
|
||||
expr.get_variable_name(true)
|
||||
.expect("`expr` should be `Variable`"),
|
||||
index,
|
||||
&context,
|
||||
)
|
||||
.map_err(|err| err.fill_position(var_pos))?
|
||||
{
|
||||
result.set_access_mode(AccessMode::ReadOnly);
|
||||
@ -1101,7 +1129,9 @@ impl Engine {
|
||||
scope.len() - index
|
||||
} else {
|
||||
// Find the variable in the scope
|
||||
let var_name = expr.get_variable_name(true).unwrap();
|
||||
let var_name = expr
|
||||
.get_variable_name(true)
|
||||
.expect("`expr` should be `Variable`");
|
||||
scope
|
||||
.get_index(var_name)
|
||||
.ok_or_else(|| EvalAltResult::ErrorVariableNotFound(var_name.to_string(), var_pos))?
|
||||
@ -1130,18 +1160,22 @@ impl Engine {
|
||||
level: usize,
|
||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||
fn match_chain_type(expr: &Expr) -> ChainType {
|
||||
match expr {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(_, _) => ChainType::Index,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(_, _) => ChainType::Dot,
|
||||
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
let is_ref = target.is_ref();
|
||||
|
||||
let rhs_chain = match rhs {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(_, _) => Some(ChainType::Index),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(_, _) => Some(ChainType::Dot),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Pop the last index value
|
||||
let idx_val = idx_values.pop().unwrap();
|
||||
let idx_val = idx_values
|
||||
.pop()
|
||||
.expect("never fails because an index chain is never empty");
|
||||
|
||||
match chain_type {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -1156,7 +1190,7 @@ impl Engine {
|
||||
let obj_ptr = &mut self.get_indexed_mut(
|
||||
mods, state, lib, target, idx_val, idx_pos, false, true, level,
|
||||
)?;
|
||||
let rhs_chain = rhs_chain.unwrap();
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values,
|
||||
@ -1166,7 +1200,8 @@ impl Engine {
|
||||
}
|
||||
// xxx[rhs] op= new_val
|
||||
_ if new_val.is_some() => {
|
||||
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||
let ((mut new_val, new_pos), (op_info, op_pos)) =
|
||||
new_val.expect("never fails because `new_val` is `Some`");
|
||||
let idx_val = idx_val.as_index_value();
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -1239,7 +1274,8 @@ impl Engine {
|
||||
let val = self.get_indexed_mut(
|
||||
mods, state, lib, target, index, *pos, true, false, level,
|
||||
)?;
|
||||
let ((new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||
let ((new_val, new_pos), (op_info, op_pos)) =
|
||||
new_val.expect("never fails because `new_val` is `Some`");
|
||||
self.eval_op_assignment(
|
||||
mods, state, lib, op_info, op_pos, val, root, new_val, new_pos,
|
||||
)?;
|
||||
@ -1258,7 +1294,8 @@ impl Engine {
|
||||
// xxx.id op= ???
|
||||
Expr::Property(x) if new_val.is_some() => {
|
||||
let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref();
|
||||
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||
let ((mut new_val, new_pos), (op_info, op_pos)) =
|
||||
new_val.expect("never fails because `new_val` is `Some`");
|
||||
|
||||
if op_info.is_some() {
|
||||
let hash = FnCallHashes::from_native(*hash_get);
|
||||
@ -1376,7 +1413,7 @@ impl Engine {
|
||||
// Others - syntax error
|
||||
expr => unreachable!("invalid dot expression: {:?}", expr),
|
||||
};
|
||||
let rhs_chain = rhs_chain.unwrap();
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, this_ptr, &mut val, root, &x.rhs, idx_values,
|
||||
@ -1391,7 +1428,7 @@ impl Engine {
|
||||
Expr::Property(p) => {
|
||||
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
|
||||
p.as_ref();
|
||||
let rhs_chain = rhs_chain.unwrap();
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
let hash_get = FnCallHashes::from_native(*hash_get);
|
||||
let hash_set = FnCallHashes::from_native(*hash_set);
|
||||
let mut arg_values = [target.as_mut(), &mut Default::default()];
|
||||
@ -1479,7 +1516,7 @@ impl Engine {
|
||||
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
||||
Expr::FnCall(f, pos) if !f.is_qualified() => {
|
||||
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
||||
let rhs_chain = rhs_chain.unwrap();
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
let mut args = idx_val.as_fn_call_args();
|
||||
let (mut val, _) = self.make_method_call(
|
||||
mods, state, lib, name, *hashes, target, &mut args, *pos, level,
|
||||
@ -1924,7 +1961,10 @@ impl Engine {
|
||||
Expr::Map(x, _) => {
|
||||
let mut map = x.1.clone();
|
||||
for (Ident { name: key, .. }, expr) in &x.0 {
|
||||
*map.get_mut(key.as_str()).unwrap() = self
|
||||
let value_ref = map
|
||||
.get_mut(key.as_str())
|
||||
.expect("never fails because the template should contain all the keys");
|
||||
*value_ref = self
|
||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||
.flatten();
|
||||
}
|
||||
@ -1941,7 +1981,9 @@ impl Engine {
|
||||
literal_args: c_args,
|
||||
..
|
||||
} = x.as_ref();
|
||||
let namespace = namespace.as_ref();
|
||||
let namespace = namespace
|
||||
.as_ref()
|
||||
.expect("never fails because function call is qualified");
|
||||
let hash = hashes.native_hash();
|
||||
self.make_qualified_function_call(
|
||||
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
||||
@ -2000,10 +2042,12 @@ impl Engine {
|
||||
.iter()
|
||||
.map(Into::into)
|
||||
.collect::<StaticVec<_>>();
|
||||
let key_token = custom.tokens.first().expect(
|
||||
"never fails because a custom syntax stream must contain at least one token",
|
||||
);
|
||||
let custom_def = self
|
||||
.custom_syntax
|
||||
.get(custom.tokens.first().unwrap())
|
||||
.unwrap();
|
||||
.custom_syntax.get(key_token)
|
||||
.expect("never fails because the custom syntax leading token should match with definition");
|
||||
let mut context = EvalContext {
|
||||
engine: self,
|
||||
scope,
|
||||
@ -2134,7 +2178,9 @@ impl Engine {
|
||||
let target_is_shared = false;
|
||||
|
||||
if target_is_shared {
|
||||
lock_guard = target.write_lock::<Dynamic>().unwrap();
|
||||
lock_guard = target
|
||||
.write_lock::<Dynamic>()
|
||||
.expect("never fails when casting to `Dynamic`");
|
||||
lhs_ptr_inner = &mut *lock_guard;
|
||||
} else {
|
||||
lhs_ptr_inner = &mut *target;
|
||||
@ -2204,11 +2250,12 @@ impl Engine {
|
||||
let (lhs_ptr, pos) =
|
||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
|
||||
|
||||
let var_name = lhs_expr
|
||||
.get_variable_name(false)
|
||||
.expect("never fails because `lhs_ptr` is a `Variable`s");
|
||||
|
||||
if !lhs_ptr.is_ref() {
|
||||
return EvalAltResult::ErrorAssignmentToConstant(
|
||||
lhs_expr.get_variable_name(false).unwrap().to_string(),
|
||||
pos,
|
||||
)
|
||||
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
|
||||
.into();
|
||||
}
|
||||
|
||||
@ -2222,7 +2269,7 @@ impl Engine {
|
||||
op_info.clone(),
|
||||
*op_pos,
|
||||
lhs_ptr,
|
||||
(lhs_expr.get_variable_name(false).unwrap(), pos),
|
||||
(var_name, pos),
|
||||
rhs_val,
|
||||
rhs_expr.position(),
|
||||
)?;
|
||||
@ -2447,7 +2494,10 @@ impl Engine {
|
||||
let loop_var_is_shared = false;
|
||||
|
||||
if loop_var_is_shared {
|
||||
*loop_var.write_lock().unwrap() = value;
|
||||
let mut value_ref = loop_var
|
||||
.write_lock()
|
||||
.expect("never fails when casting to `Dynamic`");
|
||||
*value_ref = value;
|
||||
} else {
|
||||
*loop_var = value;
|
||||
}
|
||||
@ -2497,7 +2547,9 @@ impl Engine {
|
||||
literal_args: c_args,
|
||||
..
|
||||
} = x.as_ref();
|
||||
let namespace = namespace.as_ref();
|
||||
let namespace = namespace
|
||||
.as_ref()
|
||||
.expect("never fails because function call is qualified");
|
||||
let hash = hashes.native_hash();
|
||||
self.make_qualified_function_call(
|
||||
scope, mods, state, lib, this_ptr, namespace, name, args, c_args, hash, *pos,
|
||||
@ -2558,19 +2610,14 @@ impl Engine {
|
||||
if err_pos.is_none() {
|
||||
// No position info
|
||||
} else {
|
||||
err_map.insert(
|
||||
"line".into(),
|
||||
(err_pos.line().unwrap() as INT).into(),
|
||||
);
|
||||
err_map.insert(
|
||||
"position".into(),
|
||||
if err_pos.is_beginning_of_line() {
|
||||
let line = err_pos.line().expect("never fails because a non-NONE `Position` always has a line number") as INT;
|
||||
let position = if err_pos.is_beginning_of_line() {
|
||||
0
|
||||
} else {
|
||||
err_pos.position().unwrap() as INT
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
err_pos.position().expect("never fails because a non-NONE `Position` always has a character position")
|
||||
} as INT;
|
||||
err_map.insert("line".into(), line.into());
|
||||
err_map.insert("position".into(), position.into());
|
||||
}
|
||||
|
||||
err.dump_fields(&mut err_map);
|
||||
@ -2650,7 +2697,10 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if entry_type == AccessMode::ReadOnly && lib.iter().any(|&m| !m.is_empty()) {
|
||||
let global = if let Some(index) = mods.find(KEYWORD_GLOBAL) {
|
||||
match mods.get_mut(index).unwrap() {
|
||||
match mods
|
||||
.get_mut(index)
|
||||
.expect("never fails because the index came from `find`")
|
||||
{
|
||||
m if m.internal => Some(m),
|
||||
_ => None,
|
||||
}
|
||||
@ -2659,11 +2709,15 @@ impl Engine {
|
||||
let mut global = Module::new();
|
||||
global.internal = true;
|
||||
mods.push(KEYWORD_GLOBAL, global);
|
||||
Some(mods.get_mut(mods.len() - 1).unwrap())
|
||||
Some(
|
||||
mods.get_mut(mods.len() - 1)
|
||||
.expect("never fails because the global module was just added"),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(global) = global {
|
||||
let global = Shared::get_mut(global).unwrap();
|
||||
let global = Shared::get_mut(global)
|
||||
.expect("never fails because the global module is never shared");
|
||||
global.set_var(name.clone(), value.clone());
|
||||
}
|
||||
}
|
||||
@ -2778,9 +2832,10 @@ impl Engine {
|
||||
/// Check a result to ensure that the data size is within allowable limit.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
fn check_data_size(&self, result: &RhaiResult) -> Result<(), Box<EvalAltResult>> {
|
||||
if result.is_err() {
|
||||
return Ok(());
|
||||
}
|
||||
let result = match result {
|
||||
Err(_) => return Ok(()),
|
||||
Ok(r) => r,
|
||||
};
|
||||
|
||||
// If no data size limits, just return
|
||||
let mut _has_limit = self.limits.max_string_size.is_some();
|
||||
@ -2849,7 +2904,7 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
|
||||
let (_arr, _map, s) = calc_size(result.as_ref().unwrap());
|
||||
let (_arr, _map, s) = calc_size(result);
|
||||
|
||||
if s > self
|
||||
.limits
|
||||
|
@ -951,8 +951,14 @@ impl Engine {
|
||||
}
|
||||
} else {
|
||||
let mut iter = name.as_ref().splitn(2, separator.as_ref());
|
||||
let sub_module = iter.next().unwrap().trim();
|
||||
let remainder = iter.next().unwrap().trim();
|
||||
let sub_module = iter
|
||||
.next()
|
||||
.expect("never fails because the name contains a separator")
|
||||
.trim();
|
||||
let remainder = iter
|
||||
.next()
|
||||
.expect("never fails because the name contains a separator")
|
||||
.trim();
|
||||
|
||||
if !root.contains_key(sub_module) {
|
||||
let mut m: Module = Default::default();
|
||||
@ -960,7 +966,9 @@ impl Engine {
|
||||
m.build_index();
|
||||
root.insert(sub_module.into(), m.into());
|
||||
} else {
|
||||
let m = root.remove(sub_module).unwrap();
|
||||
let m = root
|
||||
.remove(sub_module)
|
||||
.expect("never fails because the root contains the sub-module");
|
||||
let mut m = crate::fn_native::shared_take_or_clone(m);
|
||||
register_static_module_raw(m.sub_modules_mut(), remainder, module);
|
||||
m.build_index();
|
||||
@ -1078,7 +1086,10 @@ impl Engine {
|
||||
resolver: &StaticModuleResolver,
|
||||
imports: &mut BTreeSet<Identifier>,
|
||||
) {
|
||||
ast.walk(&mut |path| match path.last().unwrap() {
|
||||
ast.walk(&mut |path| match path
|
||||
.last()
|
||||
.expect("never fails because `path` always contains the current node")
|
||||
{
|
||||
// Collect all `import` statements with a string constant path
|
||||
ASTNode::Stmt(Stmt::Import(Expr::StringConstant(s, _), _, _))
|
||||
if !resolver.contains_path(s) && !imports.contains(s.as_str()) =>
|
||||
|
@ -49,7 +49,12 @@ impl<'a> ArgBackup<'a> {
|
||||
///
|
||||
/// This method blindly casts a reference to another lifetime, which saves allocation and string cloning.
|
||||
///
|
||||
/// If `restore_first_arg` is called before the end of the scope, the shorter lifetime will not leak.
|
||||
/// As long as `restore_first_arg` is called before the end of the scope, the shorter lifetime
|
||||
/// will not leak.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when `args` is empty.
|
||||
#[inline(always)]
|
||||
fn change_first_arg_to_copy(&mut self, args: &mut FnCallArgs<'a>) {
|
||||
// Clone the original value.
|
||||
@ -239,7 +244,8 @@ impl Engine {
|
||||
FnResolutionCacheEntry { func, source: None }
|
||||
})
|
||||
} else {
|
||||
let (first, second) = args.split_first().unwrap();
|
||||
let (first, second) = args.split_first()
|
||||
.expect("never fails because an op-assignment must have two arguments");
|
||||
|
||||
get_builtin_op_assignment_fn(fn_name, *first, second[0]).map(
|
||||
|f| {
|
||||
@ -257,7 +263,9 @@ impl Engine {
|
||||
// Try all permutations with `Dynamic` wildcards
|
||||
None => {
|
||||
let hash_params = calc_fn_params_hash(
|
||||
args.as_ref().unwrap().iter().enumerate().map(|(i, a)| {
|
||||
args.as_ref().expect("never fails because there are no permutations if there are no arguments")
|
||||
.iter().enumerate().map(|(i, a)|
|
||||
{
|
||||
let mask = 1usize << (num_args - i - 1);
|
||||
if bitmask & mask != 0 {
|
||||
// Replace with `Dynamic`
|
||||
@ -646,8 +654,12 @@ impl Engine {
|
||||
crate::engine::KEYWORD_IS_DEF_FN
|
||||
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
|
||||
{
|
||||
let fn_name = &*args[0].read_lock::<ImmutableString>().unwrap();
|
||||
let num_params = args[1].as_int().unwrap();
|
||||
let fn_name = &*args[0]
|
||||
.read_lock::<ImmutableString>()
|
||||
.expect("never fails because `args[0]` is `FnPtr`");
|
||||
let num_params = args[1]
|
||||
.as_int()
|
||||
.expect("never fails because `args[1]` is `INT`");
|
||||
|
||||
return Ok((
|
||||
if num_params < 0 {
|
||||
@ -704,11 +716,7 @@ impl Engine {
|
||||
|
||||
// Scripted function call?
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
let hash_script = if hash.is_native_only() {
|
||||
None
|
||||
} else {
|
||||
Some(hash.script_hash())
|
||||
};
|
||||
let hash_script = hash.script_hash();
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
if let Some(f) = hash_script.and_then(|hash| {
|
||||
@ -743,7 +751,9 @@ impl Engine {
|
||||
|
||||
let result = if _is_method {
|
||||
// Method call of script function - map first argument to `this`
|
||||
let (first, rest) = args.split_first_mut().unwrap();
|
||||
let (first, rest) = args
|
||||
.split_first_mut()
|
||||
.expect("never fails because a method call always has a first parameter");
|
||||
|
||||
let orig_source = state.source.take();
|
||||
state.source = source;
|
||||
@ -906,7 +916,9 @@ impl Engine {
|
||||
let (result, updated) = match fn_name {
|
||||
KEYWORD_FN_PTR_CALL if obj.is::<FnPtr>() => {
|
||||
// FnPtr call
|
||||
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
||||
let fn_ptr = obj
|
||||
.read_lock::<FnPtr>()
|
||||
.expect("never fails because `obj` is `FnPtr`");
|
||||
// Redirect function name
|
||||
let fn_name = fn_ptr.fn_name();
|
||||
let args_len = call_args.len() + fn_ptr.curry().len();
|
||||
@ -970,7 +982,9 @@ impl Engine {
|
||||
));
|
||||
}
|
||||
|
||||
let fn_ptr = obj.read_lock::<FnPtr>().unwrap();
|
||||
let fn_ptr = obj
|
||||
.read_lock::<FnPtr>()
|
||||
.expect("never fails because `obj` is `FnPtr`");
|
||||
|
||||
// Curry call
|
||||
Ok((
|
||||
@ -1041,7 +1055,10 @@ impl Engine {
|
||||
|
||||
// Propagate the changed value back to the source if necessary
|
||||
if updated {
|
||||
target.propagate_changed_value();
|
||||
target.propagate_changed_value().map_err(|mut err| {
|
||||
err.set_position(pos);
|
||||
err
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok((result, updated))
|
||||
@ -1324,9 +1341,8 @@ impl Engine {
|
||||
} else {
|
||||
// Turn it into a method call only if the object is not shared and not a simple value
|
||||
is_ref = true;
|
||||
once(target.take_ref().unwrap())
|
||||
.chain(arg_values.iter_mut())
|
||||
.collect()
|
||||
let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared");
|
||||
once(obj_ref).chain(arg_values.iter_mut()).collect()
|
||||
};
|
||||
} else {
|
||||
// func(..., ...)
|
||||
@ -1357,7 +1373,7 @@ impl Engine {
|
||||
state: &mut State,
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
namespace: Option<&NamespaceRef>,
|
||||
namespace: &NamespaceRef,
|
||||
fn_name: &str,
|
||||
args_expr: &[Expr],
|
||||
literal_args: &[(Dynamic, Position)],
|
||||
@ -1365,7 +1381,6 @@ impl Engine {
|
||||
pos: Position,
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
let namespace = namespace.unwrap();
|
||||
let mut arg_values: StaticVec<_>;
|
||||
let mut first_arg_value = None;
|
||||
let mut args: StaticVec<_>;
|
||||
@ -1410,11 +1425,12 @@ impl Engine {
|
||||
arg_values[0] = target.take_or_clone().flatten();
|
||||
args = arg_values.iter_mut().collect();
|
||||
} else {
|
||||
let (first, rest) = arg_values.split_first_mut().unwrap();
|
||||
let (first, rest) = arg_values
|
||||
.split_first_mut()
|
||||
.expect("never fails because the arguments list is not empty");
|
||||
first_arg_value = Some(first);
|
||||
args = once(target.take_ref().unwrap())
|
||||
.chain(rest.iter_mut())
|
||||
.collect();
|
||||
let obj_ref = target.take_ref().expect("never fails because `target` is a reference if it is not a value and not shared");
|
||||
args = once(obj_ref).chain(rest.iter_mut()).collect();
|
||||
}
|
||||
} else {
|
||||
// func(..., ...) or func(mod::x, ...)
|
||||
|
@ -253,7 +253,9 @@ pub fn shared_try_take<T>(value: Shared<T>) -> Result<T, Shared<T>> {
|
||||
/// Panics if the resource is shared (i.e. has other outstanding references).
|
||||
#[inline(always)]
|
||||
pub fn shared_take<T>(value: Shared<T>) -> T {
|
||||
shared_try_take(value).map_err(|_| ()).unwrap()
|
||||
shared_try_take(value)
|
||||
.ok()
|
||||
.expect("resource should have no outstanding references")
|
||||
}
|
||||
|
||||
/// Arguments to a function call, which is a list of [`&mut Dynamic`][Dynamic].
|
||||
|
@ -32,7 +32,8 @@ pub struct Mut<T>(T);
|
||||
#[inline(always)]
|
||||
pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> DynamicWriteLock<T> {
|
||||
// Directly cast the &mut Dynamic into DynamicWriteLock to access the underlying data.
|
||||
data.write_lock::<T>().unwrap()
|
||||
data.write_lock::<T>()
|
||||
.expect("never fails because the type was checked")
|
||||
}
|
||||
|
||||
/// Dereference into value.
|
||||
@ -43,12 +44,15 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||
data.flatten_in_place();
|
||||
let ref_str = data
|
||||
.as_str_ref()
|
||||
.expect("argument passed by value should not be shared");
|
||||
.expect("never fails because argument passed by value should not be shared");
|
||||
let ref_t = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
||||
ref_t.clone()
|
||||
} else if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||
// If T is `String`, data must be `ImmutableString`, so map directly to it
|
||||
unsafe_try_cast(mem::take(data).take_string().unwrap()).unwrap()
|
||||
let value = mem::take(data)
|
||||
.take_string()
|
||||
.expect("never fails because the type was checked");
|
||||
unsafe_try_cast(value).expect("never fails because the type was checked")
|
||||
} else {
|
||||
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
||||
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
||||
@ -118,7 +122,7 @@ macro_rules! def_register {
|
||||
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
let r = self($($arg),*);
|
||||
@ -146,7 +150,7 @@ macro_rules! def_register {
|
||||
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
let r = self(ctx, $($arg),*);
|
||||
@ -174,7 +178,7 @@ macro_rules! def_register {
|
||||
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
self($($arg),*).map(Dynamic::from)
|
||||
@ -199,7 +203,7 @@ macro_rules! def_register {
|
||||
|
||||
// The arguments are assumed to be of the correct number and types!
|
||||
let mut _drain = args.iter_mut();
|
||||
$($let $par = ($clone)(_drain.next().unwrap()); )*
|
||||
$($let $par = ($clone)(_drain.next().expect("never fails because arguments list is fixed")); )*
|
||||
|
||||
// Call the function with each argument value
|
||||
self(ctx, $($arg),*).map(Dynamic::from)
|
||||
|
@ -1415,7 +1415,10 @@ impl Module {
|
||||
match aliases.len() {
|
||||
0 => (),
|
||||
1 => {
|
||||
module.set_var(aliases.pop().unwrap(), value);
|
||||
let alias = aliases
|
||||
.pop()
|
||||
.expect("never fails because the list has one item");
|
||||
module.set_var(alias, value);
|
||||
}
|
||||
_ => aliases.into_iter().for_each(|alias| {
|
||||
module.set_var(alias, value.clone());
|
||||
|
@ -45,29 +45,31 @@ fn collect_fn_metadata(ctx: NativeCallContext) -> crate::Array {
|
||||
namespace: Option<Identifier>,
|
||||
f: &ScriptFnDef,
|
||||
) -> Map {
|
||||
const DICT: &str = "never fails because the dictionary is pre-filled with all the keys";
|
||||
|
||||
let mut map = Map::new();
|
||||
|
||||
if let Some(ns) = namespace {
|
||||
map.insert(dict.get("namespace").unwrap().clone().into(), ns.into());
|
||||
map.insert(dict.get("namespace").expect(DICT).clone().into(), ns.into());
|
||||
}
|
||||
map.insert(
|
||||
dict.get("name").unwrap().clone().into(),
|
||||
dict.get("name").expect(DICT).clone().into(),
|
||||
f.name.clone().into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("access").unwrap().clone().into(),
|
||||
dict.get("access").expect(DICT).clone().into(),
|
||||
match f.access {
|
||||
FnAccess::Public => dict.get("public").unwrap().clone(),
|
||||
FnAccess::Private => dict.get("private").unwrap().clone(),
|
||||
FnAccess::Public => dict.get("public").expect(DICT).clone(),
|
||||
FnAccess::Private => dict.get("private").expect(DICT).clone(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("is_anonymous").unwrap().clone().into(),
|
||||
dict.get("is_anonymous").expect(DICT).clone().into(),
|
||||
f.name.starts_with(crate::engine::FN_ANONYMOUS).into(),
|
||||
);
|
||||
map.insert(
|
||||
dict.get("params").unwrap().clone().into(),
|
||||
dict.get("params").expect(DICT).clone().into(),
|
||||
f.params
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -311,12 +311,8 @@ mod decimal_functions {
|
||||
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn sqrt(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) {
|
||||
x.sqrt()
|
||||
.ok_or_else(|| make_err(format!("Error taking the square root of {}", x,)))
|
||||
} else {
|
||||
Ok(x.sqrt().unwrap())
|
||||
}
|
||||
}
|
||||
#[rhai_fn(return_raw)]
|
||||
pub fn exp(x: Decimal) -> Result<Decimal, Box<EvalAltResult>> {
|
||||
|
@ -27,9 +27,9 @@ pub fn print_with_func(
|
||||
value: &mut Dynamic,
|
||||
) -> crate::ImmutableString {
|
||||
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
||||
Ok(result) if result.is::<crate::ImmutableString>() => {
|
||||
result.take_immutable_string().unwrap()
|
||||
}
|
||||
Ok(result) if result.is::<crate::ImmutableString>() => result
|
||||
.take_immutable_string()
|
||||
.expect("never fails as the result is `ImmutableString`"),
|
||||
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
||||
}
|
||||
|
209
src/parser.rs
209
src/parser.rs
@ -36,6 +36,8 @@ type PERR = ParseErrorType;
|
||||
|
||||
type FunctionsLib = BTreeMap<u64, Shared<ScriptFnDef>>;
|
||||
|
||||
const NEVER_ENDS: &str = "never fails because `TokenStream` never ends";
|
||||
|
||||
/// A type that encapsulates the current state of the parser.
|
||||
#[derive(Debug)]
|
||||
pub struct ParseState<'e> {
|
||||
@ -241,7 +243,7 @@ impl Expr {
|
||||
|
||||
/// Consume a particular [token][Token], checking that it is the expected one.
|
||||
fn eat_token(input: &mut TokenStream, token: Token) -> Position {
|
||||
let (t, pos) = input.next().unwrap();
|
||||
let (t, pos) = input.next().expect(NEVER_ENDS);
|
||||
|
||||
if t != token {
|
||||
unreachable!(
|
||||
@ -256,7 +258,7 @@ fn eat_token(input: &mut TokenStream, token: Token) -> Position {
|
||||
|
||||
/// Match a particular [token][Token], consuming it if matched.
|
||||
fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) {
|
||||
let (t, pos) = input.peek().unwrap();
|
||||
let (t, pos) = input.peek().expect(NEVER_ENDS);
|
||||
if *t == token {
|
||||
(true, eat_token(input, token))
|
||||
} else {
|
||||
@ -283,7 +285,7 @@ fn parse_paren_expr(
|
||||
|
||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
// ( xxx )
|
||||
(Token::RightParen, _) => Ok(expr),
|
||||
// ( <error>
|
||||
@ -310,7 +312,7 @@ fn parse_fn_call(
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
let (token, token_pos) = input.peek().unwrap();
|
||||
let (token, token_pos) = input.peek().expect(NEVER_ENDS);
|
||||
|
||||
let mut args = StaticVec::new();
|
||||
|
||||
@ -365,13 +367,13 @@ fn parse_fn_call(
|
||||
let settings = settings.level_up();
|
||||
|
||||
loop {
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
// id(...args, ) - handle trailing comma
|
||||
(Token::RightParen, _) => (),
|
||||
_ => args.push(parse_expr(input, state, lib, settings)?),
|
||||
}
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
// id(...args)
|
||||
(Token::RightParen, _) => {
|
||||
eat_token(input, Token::RightParen);
|
||||
@ -554,12 +556,12 @@ fn parse_index_chain(
|
||||
}
|
||||
|
||||
// Check if there is a closing bracket
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::RightBracket, _) => {
|
||||
eat_token(input, Token::RightBracket);
|
||||
|
||||
// Any more indexing following?
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
// If another indexing level, right-bind it
|
||||
(Token::LeftBracket, _) => {
|
||||
let prev_pos = settings.pos;
|
||||
@ -614,10 +616,10 @@ fn parse_array_literal(
|
||||
"Size of array literal".to_string(),
|
||||
state.engine.max_array_size(),
|
||||
)
|
||||
.into_err(input.peek().unwrap().1));
|
||||
.into_err(input.peek().expect(NEVER_ENDS).1));
|
||||
}
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::RightBracket, _) => {
|
||||
eat_token(input, Token::RightBracket);
|
||||
break;
|
||||
@ -634,7 +636,7 @@ fn parse_array_literal(
|
||||
}
|
||||
}
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::Comma, _) => {
|
||||
eat_token(input, Token::Comma);
|
||||
}
|
||||
@ -681,7 +683,7 @@ fn parse_map_literal(
|
||||
loop {
|
||||
const MISSING_RBRACE: &str = "to end this object map literal";
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::RightBrace, _) => {
|
||||
eat_token(input, Token::RightBrace);
|
||||
break;
|
||||
@ -695,7 +697,7 @@ fn parse_map_literal(
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let (name, pos) = match input.next().unwrap() {
|
||||
let (name, pos) = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), pos) | (Token::StringConstant(s), pos) => {
|
||||
if map.iter().any(|(p, _)| p.name == s) {
|
||||
return Err(PERR::DuplicatedProperty(s).into_err(pos));
|
||||
@ -722,7 +724,7 @@ fn parse_map_literal(
|
||||
(_, pos) => return Err(PERR::PropertyExpected.into_err(pos)),
|
||||
};
|
||||
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Colon, _) => (),
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
@ -743,7 +745,7 @@ fn parse_map_literal(
|
||||
"Number of properties in object map literal".to_string(),
|
||||
state.engine.max_map_size(),
|
||||
)
|
||||
.into_err(input.peek().unwrap().1));
|
||||
.into_err(input.peek().expect(NEVER_ENDS).1));
|
||||
}
|
||||
|
||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||
@ -751,7 +753,7 @@ fn parse_map_literal(
|
||||
template.insert(name.clone().into(), Default::default());
|
||||
map.push((Ident { name, pos }, expr));
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::Comma, _) => {
|
||||
eat_token(input, Token::Comma);
|
||||
}
|
||||
@ -793,7 +795,7 @@ fn parse_switch(
|
||||
|
||||
let item = parse_expr(input, state, lib, settings.level_up())?;
|
||||
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::LeftBrace, _) => (),
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
@ -812,7 +814,7 @@ fn parse_switch(
|
||||
loop {
|
||||
const MISSING_RBRACE: &str = "to end this switch block";
|
||||
|
||||
let (expr, condition) = match input.peek().unwrap() {
|
||||
let (expr, condition) = match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::RightBrace, _) => {
|
||||
eat_token(input, Token::RightBrace);
|
||||
break;
|
||||
@ -868,7 +870,7 @@ fn parse_switch(
|
||||
None
|
||||
};
|
||||
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::DoubleArrow, _) => (),
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
@ -891,7 +893,7 @@ fn parse_switch(
|
||||
Some(stmt.into())
|
||||
};
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::Comma, _) => {
|
||||
eat_token(input, Token::Comma);
|
||||
}
|
||||
@ -934,7 +936,7 @@ fn parse_primary(
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
let (token, token_pos) = input.peek().unwrap();
|
||||
let (token, token_pos) = input.peek().expect(NEVER_ENDS);
|
||||
settings.pos = *token_pos;
|
||||
|
||||
let mut root_expr = match token {
|
||||
@ -944,7 +946,7 @@ fn parse_primary(
|
||||
| Token::CharConstant(_)
|
||||
| Token::StringConstant(_)
|
||||
| Token::True
|
||||
| Token::False => match input.next().unwrap().0 {
|
||||
| Token::False => match input.next().expect(NEVER_ENDS).0 {
|
||||
Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos),
|
||||
Token::CharConstant(c) => Expr::CharConstant(c, settings.pos),
|
||||
Token::StringConstant(s) => {
|
||||
@ -957,13 +959,13 @@ fn parse_primary(
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Token::FloatConstant(x) => {
|
||||
let x = (*x).into();
|
||||
input.next().unwrap();
|
||||
input.next().expect(NEVER_ENDS);
|
||||
Expr::FloatConstant(x, settings.pos)
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
Token::DecimalConstant(x) => {
|
||||
let x = (*x).into();
|
||||
input.next().unwrap();
|
||||
input.next().expect(NEVER_ENDS);
|
||||
Expr::DynamicConstant(Box::new(x), settings.pos)
|
||||
}
|
||||
|
||||
@ -1025,7 +1027,7 @@ fn parse_primary(
|
||||
Token::InterpolatedString(_) => {
|
||||
let mut segments: StaticVec<Expr> = Default::default();
|
||||
|
||||
if let (Token::InterpolatedString(s), pos) = input.next().unwrap() {
|
||||
if let (Token::InterpolatedString(s), pos) = input.next().expect(NEVER_ENDS) {
|
||||
segments.push(Expr::StringConstant(s.into(), pos));
|
||||
} else {
|
||||
unreachable!();
|
||||
@ -1043,7 +1045,7 @@ fn parse_primary(
|
||||
control.is_within_text = true;
|
||||
state.tokenizer_control.set(control);
|
||||
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::StringConstant(s), pos) => {
|
||||
if !s.is_empty() {
|
||||
segments.push(Expr::StringConstant(s.into(), pos));
|
||||
@ -1080,12 +1082,12 @@ fn parse_primary(
|
||||
|
||||
// Identifier
|
||||
Token::Identifier(_) => {
|
||||
let s = match input.next().unwrap() {
|
||||
let s = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), _) => s,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match input.peek().unwrap().0 {
|
||||
match input.peek().expect(NEVER_ENDS).0 {
|
||||
// Function call
|
||||
Token::LeftParen | Token::Bang => {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
@ -1134,12 +1136,12 @@ fn parse_primary(
|
||||
|
||||
// Reserved keyword or symbol
|
||||
Token::Reserved(_) => {
|
||||
let s = match input.next().unwrap() {
|
||||
let s = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Reserved(s), _) => s,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match input.peek().unwrap().0 {
|
||||
match input.peek().expect(NEVER_ENDS).0 {
|
||||
// Function call is allowed to have reserved keyword
|
||||
Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable(
|
||||
None,
|
||||
@ -1164,7 +1166,7 @@ fn parse_primary(
|
||||
}
|
||||
}
|
||||
|
||||
Token::LexError(_) => match input.next().unwrap() {
|
||||
Token::LexError(_) => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::LexError(err), _) => return Err(err.into_err(settings.pos)),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
@ -1176,13 +1178,13 @@ fn parse_primary(
|
||||
|
||||
// Tail processing all possible postfix operators
|
||||
loop {
|
||||
let (tail_token, _) = input.peek().unwrap();
|
||||
let (tail_token, _) = input.peek().expect(NEVER_ENDS);
|
||||
|
||||
if !root_expr.is_valid_postfix(tail_token) {
|
||||
break;
|
||||
}
|
||||
|
||||
let (tail_token, tail_pos) = input.next().unwrap();
|
||||
let (tail_token, tail_pos) = input.next().expect(NEVER_ENDS);
|
||||
settings.pos = tail_pos;
|
||||
|
||||
root_expr = match (root_expr, tail_token) {
|
||||
@ -1222,7 +1224,8 @@ fn parse_primary(
|
||||
parse_fn_call(input, state, lib, name, false, ns, settings.level_up())?
|
||||
}
|
||||
// module access
|
||||
(Expr::Variable(_, var_pos, x), Token::DoubleColon) => match input.next().unwrap() {
|
||||
(Expr::Variable(_, var_pos, x), Token::DoubleColon) => {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(id2), pos2) => {
|
||||
let (_, mut namespace, var_name) = *x;
|
||||
let var_name_def = Ident {
|
||||
@ -1248,7 +1251,8 @@ fn parse_primary(
|
||||
return Err(PERR::Reserved(id2).into_err(pos2));
|
||||
}
|
||||
(_, pos2) => return Err(PERR::VariableExpected.into_err(pos2)),
|
||||
},
|
||||
}
|
||||
}
|
||||
// Indexing
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
(expr, Token::LeftBracket) => {
|
||||
@ -1258,7 +1262,7 @@ fn parse_primary(
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
(expr, Token::Period) => {
|
||||
// Expression after dot must start with an identifier
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(_), _) => {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
{
|
||||
@ -1313,7 +1317,7 @@ fn parse_unary(
|
||||
lib: &mut FunctionsLib,
|
||||
mut settings: ParseSettings,
|
||||
) -> Result<Expr, ParseError> {
|
||||
let (token, token_pos) = input.peek().unwrap();
|
||||
let (token, token_pos) = input.peek().expect(NEVER_ENDS);
|
||||
settings.pos = *token_pos;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
@ -1449,7 +1453,16 @@ fn make_assignment_stmt<'a>(
|
||||
// var (indexed) = rhs
|
||||
Expr::Variable(i, var_pos, x) => {
|
||||
let (index, _, name) = x.as_ref();
|
||||
let index = i.map_or_else(|| index.unwrap().get(), |n| n.get() as usize);
|
||||
let index = i.map_or_else(
|
||||
|| {
|
||||
index
|
||||
.expect(
|
||||
"never fails because the long index is `Some` when the short index is `None`",
|
||||
)
|
||||
.get()
|
||||
},
|
||||
|n| n.get() as usize,
|
||||
);
|
||||
match state.stack[state.stack.len() - index].1 {
|
||||
AccessMode::ReadWrite => {
|
||||
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||
@ -1498,12 +1511,15 @@ fn parse_op_assignment_stmt(
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
let (token, token_pos) = input.peek().unwrap();
|
||||
let (token, token_pos) = input.peek().expect(NEVER_ENDS);
|
||||
settings.pos = *token_pos;
|
||||
|
||||
let (op, pos) = match token {
|
||||
Token::Equals => (None, input.next().unwrap().1),
|
||||
_ if token.is_op_assignment() => input.next().map(|(op, pos)| (Some(op), pos)).unwrap(),
|
||||
Token::Equals => (None, input.next().expect(NEVER_ENDS).1),
|
||||
_ if token.is_op_assignment() => input
|
||||
.next()
|
||||
.map(|(op, pos)| (Some(op), pos))
|
||||
.expect(NEVER_ENDS),
|
||||
_ => return Ok(Stmt::Expr(lhs)),
|
||||
};
|
||||
|
||||
@ -1544,7 +1560,8 @@ fn make_dot_expr(
|
||||
}
|
||||
// lhs.module::id - syntax error
|
||||
(_, Expr::Variable(_, _, x)) if x.1.is_some() => {
|
||||
return Err(PERR::PropertyExpected.into_err(x.1.unwrap().1[0].pos))
|
||||
return Err(PERR::PropertyExpected
|
||||
.into_err(x.1.expect("never fails because the namespace is `Some`").1[0].pos))
|
||||
}
|
||||
// lhs.prop
|
||||
(lhs, prop @ Expr::Property(_)) => {
|
||||
@ -1649,7 +1666,7 @@ fn parse_binary_op(
|
||||
let mut root = lhs;
|
||||
|
||||
loop {
|
||||
let (current_op, current_pos) = input.peek().unwrap();
|
||||
let (current_op, current_pos) = input.peek().expect(NEVER_ENDS);
|
||||
let precedence = match current_op {
|
||||
Token::Custom(c) => state
|
||||
.engine
|
||||
@ -1670,11 +1687,11 @@ fn parse_binary_op(
|
||||
return Ok(root);
|
||||
}
|
||||
|
||||
let (op_token, pos) = input.next().unwrap();
|
||||
let (op_token, pos) = input.next().expect(NEVER_ENDS);
|
||||
|
||||
let rhs = parse_unary(input, state, lib, settings)?;
|
||||
|
||||
let (next_op, next_pos) = input.peek().unwrap();
|
||||
let (next_op, next_pos) = input.peek().expect(NEVER_ENDS);
|
||||
let next_precedence = match next_op {
|
||||
Token::Custom(c) => state
|
||||
.engine
|
||||
@ -1744,8 +1761,12 @@ fn parse_binary_op(
|
||||
}
|
||||
|
||||
Token::Or => {
|
||||
let rhs = args.pop().unwrap();
|
||||
let current_lhs = args.pop().unwrap();
|
||||
let rhs = args
|
||||
.pop()
|
||||
.expect("never fails because `||` has two arguments");
|
||||
let current_lhs = args
|
||||
.pop()
|
||||
.expect("never fails because `||` has two arguments");
|
||||
Expr::Or(
|
||||
Box::new(BinaryExpr {
|
||||
lhs: current_lhs,
|
||||
@ -1755,8 +1776,12 @@ fn parse_binary_op(
|
||||
)
|
||||
}
|
||||
Token::And => {
|
||||
let rhs = args.pop().unwrap();
|
||||
let current_lhs = args.pop().unwrap();
|
||||
let rhs = args
|
||||
.pop()
|
||||
.expect("never fails because `&&` has two arguments");
|
||||
let current_lhs = args
|
||||
.pop()
|
||||
.expect("never fails because `&&` has two arguments");
|
||||
Expr::And(
|
||||
Box::new(BinaryExpr {
|
||||
lhs: current_lhs,
|
||||
@ -1841,7 +1866,7 @@ fn parse_custom_syntax(
|
||||
tokens.push(key.into());
|
||||
|
||||
loop {
|
||||
let (fwd_token, fwd_pos) = input.peek().unwrap();
|
||||
let (fwd_token, fwd_pos) = input.peek().expect(NEVER_ENDS);
|
||||
settings.pos = *fwd_pos;
|
||||
let settings = settings.level_up();
|
||||
|
||||
@ -1854,7 +1879,7 @@ fn parse_custom_syntax(
|
||||
};
|
||||
|
||||
match required_token.as_str() {
|
||||
MARKER_IDENT => match input.next().unwrap() {
|
||||
MARKER_IDENT => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), pos) => {
|
||||
let name = state.get_identifier(s);
|
||||
segments.push(name.clone().into());
|
||||
@ -1881,7 +1906,7 @@ fn parse_custom_syntax(
|
||||
}
|
||||
stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt),
|
||||
},
|
||||
s => match input.next().unwrap() {
|
||||
s => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(t, _) if t.syntax().as_ref() == s => {
|
||||
segments.push(required_token.clone());
|
||||
@ -1921,18 +1946,18 @@ fn parse_expr(
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
settings.pos = input.peek().unwrap().1;
|
||||
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
||||
|
||||
// Check if it is a custom syntax.
|
||||
if !state.engine.custom_syntax.is_empty() {
|
||||
let (token, pos) = input.peek().unwrap();
|
||||
let (token, pos) = input.peek().expect(NEVER_ENDS);
|
||||
let token_pos = *pos;
|
||||
|
||||
match token {
|
||||
Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key) => {
|
||||
match state.engine.custom_syntax.get_key_value(key.as_str()) {
|
||||
Some((key, syntax)) => {
|
||||
input.next().unwrap();
|
||||
input.next().expect(NEVER_ENDS);
|
||||
return parse_custom_syntax(
|
||||
input, state, lib, settings, key, syntax, token_pos,
|
||||
);
|
||||
@ -1958,7 +1983,7 @@ fn parse_expr(
|
||||
|
||||
/// Make sure that the expression is not a statement expression (i.e. wrapped in `{}`).
|
||||
fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result<(), ParseError> {
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
// Disallow statement expressions
|
||||
(Token::LeftBrace, pos) | (Token::EOF, pos) => {
|
||||
Err(PERR::ExprExpected(type_name.to_string()).into_err(*pos))
|
||||
@ -1970,7 +1995,7 @@ fn ensure_not_statement_expr(input: &mut TokenStream, type_name: &str) -> Result
|
||||
|
||||
/// Make sure that the expression is not a mis-typed assignment (i.e. `a = b` instead of `a == b`).
|
||||
fn ensure_not_assignment(input: &mut TokenStream) -> Result<(), ParseError> {
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::Equals, pos) => Err(LexError::ImproperSymbol(
|
||||
"=".to_string(),
|
||||
"Possibly a typo of '=='?".to_string(),
|
||||
@ -2017,7 +2042,7 @@ fn parse_if(
|
||||
|
||||
// if guard { if_body } else ...
|
||||
let else_body = if match_token(input, Token::Else).0 {
|
||||
if let (Token::If, _) = input.peek().unwrap() {
|
||||
if let (Token::If, _) = input.peek().expect(NEVER_ENDS) {
|
||||
// if guard { if_body } else if ...
|
||||
parse_if(input, state, lib, settings.level_up())?
|
||||
} else {
|
||||
@ -2046,7 +2071,7 @@ fn parse_while_loop(
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
// while|loops ...
|
||||
let (guard, token_pos) = match input.next().unwrap() {
|
||||
let (guard, token_pos) = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::While, pos) => {
|
||||
ensure_not_statement_expr(input, "a boolean")?;
|
||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||
@ -2081,7 +2106,7 @@ fn parse_do(
|
||||
settings.is_breakable = true;
|
||||
let body = parse_block(input, state, lib, settings.level_up())?;
|
||||
|
||||
let is_while = match input.next().unwrap() {
|
||||
let is_while = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::While, _) => true,
|
||||
(Token::Until, _) => false,
|
||||
(_, pos) => {
|
||||
@ -2119,7 +2144,7 @@ fn parse_for(
|
||||
settings.pos = eat_token(input, Token::For);
|
||||
|
||||
// for name ...
|
||||
let (name, name_pos) = match input.next().unwrap() {
|
||||
let (name, name_pos) = match input.next().expect(NEVER_ENDS) {
|
||||
// Variable name
|
||||
(Token::Identifier(s), pos) => (s, pos),
|
||||
// Reserved keyword
|
||||
@ -2133,7 +2158,7 @@ fn parse_for(
|
||||
};
|
||||
|
||||
// for name in ...
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::In, _) => (),
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
@ -2183,10 +2208,10 @@ fn parse_let(
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
// let/const... (specified in `var_type`)
|
||||
settings.pos = input.next().unwrap().1;
|
||||
settings.pos = input.next().expect(NEVER_ENDS).1;
|
||||
|
||||
// let name ...
|
||||
let (name, pos) = match input.next().unwrap() {
|
||||
let (name, pos) = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), pos) => (s, pos),
|
||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||
return Err(PERR::Reserved(s).into_err(pos));
|
||||
@ -2242,7 +2267,7 @@ fn parse_import(
|
||||
}
|
||||
|
||||
// import expr as name ...
|
||||
let (name, name_pos) = match input.next().unwrap() {
|
||||
let (name, name_pos) = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), pos) => (s, pos),
|
||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||
return Err(PERR::Reserved(s).into_err(pos));
|
||||
@ -2280,7 +2305,7 @@ fn parse_export(
|
||||
|
||||
settings.pos = eat_token(input, Token::Export);
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::Let, pos) => {
|
||||
let pos = *pos;
|
||||
let mut stmt = parse_let(input, state, lib, AccessMode::ReadWrite, true, settings)?;
|
||||
@ -2299,7 +2324,7 @@ fn parse_export(
|
||||
let mut exports = Vec::with_capacity(4);
|
||||
|
||||
loop {
|
||||
let (id, id_pos) = match input.next().unwrap() {
|
||||
let (id, id_pos) = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), pos) => (s.clone(), pos),
|
||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||
return Err(PERR::Reserved(s).into_err(pos));
|
||||
@ -2309,7 +2334,7 @@ fn parse_export(
|
||||
};
|
||||
|
||||
let rename = if match_token(input, Token::As).0 {
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), pos) => Some(Ident {
|
||||
name: state.get_identifier(s),
|
||||
pos,
|
||||
@ -2332,7 +2357,7 @@ fn parse_export(
|
||||
rename,
|
||||
));
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::Comma, _) => {
|
||||
eat_token(input, Token::Comma);
|
||||
}
|
||||
@ -2358,7 +2383,7 @@ fn parse_block(
|
||||
mut settings: ParseSettings,
|
||||
) -> Result<Stmt, ParseError> {
|
||||
// Must start with {
|
||||
settings.pos = match input.next().unwrap() {
|
||||
settings.pos = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::LeftBrace, pos) => pos,
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
(_, pos) => {
|
||||
@ -2383,7 +2408,7 @@ fn parse_block(
|
||||
|
||||
loop {
|
||||
// Terminated?
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::RightBrace, _) => {
|
||||
eat_token(input, Token::RightBrace);
|
||||
break;
|
||||
@ -2412,7 +2437,7 @@ fn parse_block(
|
||||
|
||||
statements.push(stmt);
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
// { ... stmt }
|
||||
(Token::RightBrace, _) => {
|
||||
eat_token(input, Token::RightBrace);
|
||||
@ -2461,7 +2486,7 @@ fn parse_expr_stmt(
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
settings.pos = input.peek().unwrap().1;
|
||||
settings.pos = input.peek().expect(NEVER_ENDS).1;
|
||||
|
||||
let expr = parse_expr(input, state, lib, settings.level_up())?;
|
||||
let stmt = parse_op_assignment_stmt(input, state, lib, expr, settings.level_up())?;
|
||||
@ -2484,7 +2509,7 @@ fn parse_stmt(
|
||||
let mut comments_pos = Position::NONE;
|
||||
|
||||
// Handle doc-comments.
|
||||
while let (Token::Comment(ref comment), pos) = input.peek().unwrap() {
|
||||
while let (Token::Comment(ref comment), pos) = input.peek().expect(INPUT_NEVER_ENDS) {
|
||||
if comments_pos.is_none() {
|
||||
comments_pos = *pos;
|
||||
}
|
||||
@ -2497,11 +2522,11 @@ fn parse_stmt(
|
||||
return Err(PERR::WrongDocComment.into_err(comments_pos));
|
||||
}
|
||||
|
||||
match input.next().unwrap().0 {
|
||||
match input.next().expect(NEVER_ENDS).0 {
|
||||
Token::Comment(comment) => {
|
||||
comments.push(comment);
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(INPUT_NEVER_ENDS) {
|
||||
(Token::Fn, _) | (Token::Private, _) => break,
|
||||
(Token::Comment(_), _) => (),
|
||||
_ => return Err(PERR::WrongDocComment.into_err(comments_pos)),
|
||||
@ -2514,7 +2539,7 @@ fn parse_stmt(
|
||||
comments
|
||||
};
|
||||
|
||||
let (token, token_pos) = match input.peek().unwrap() {
|
||||
let (token, token_pos) = match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::EOF, pos) => return Ok(Stmt::Noop(*pos)),
|
||||
x => x,
|
||||
};
|
||||
@ -2546,7 +2571,7 @@ fn parse_stmt(
|
||||
FnAccess::Public
|
||||
};
|
||||
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Fn, pos) => {
|
||||
let mut new_state =
|
||||
ParseState::new(state.engine, state.tokenizer_control.clone());
|
||||
@ -2631,9 +2656,9 @@ fn parse_stmt(
|
||||
pos,
|
||||
)
|
||||
})
|
||||
.unwrap();
|
||||
.expect(NEVER_ENDS);
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
// `return`/`throw` at <EOF>
|
||||
(Token::EOF, _) => Ok(Stmt::Return(return_type, None, token_pos)),
|
||||
// `return;` or `throw;`
|
||||
@ -2692,7 +2717,7 @@ fn parse_try_catch(
|
||||
|
||||
// try { body } catch (
|
||||
let var_def = if match_token(input, Token::LeftParen).0 {
|
||||
let id = match input.next().unwrap() {
|
||||
let id = match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Identifier(s), pos) => Ident {
|
||||
name: state.get_identifier(s),
|
||||
pos,
|
||||
@ -2740,7 +2765,7 @@ fn parse_fn(
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
settings.ensure_level_within_max_limit(state.max_expr_depth)?;
|
||||
|
||||
let (token, pos) = input.next().unwrap();
|
||||
let (token, pos) = input.next().expect(NEVER_ENDS);
|
||||
|
||||
let name = token
|
||||
.into_function_name_for_override()
|
||||
@ -2749,7 +2774,7 @@ fn parse_fn(
|
||||
_ => PERR::FnMissingName.into_err(pos),
|
||||
})?;
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::LeftParen, _) => eat_token(input, Token::LeftParen),
|
||||
(_, pos) => return Err(PERR::FnMissingParams(name).into_err(*pos)),
|
||||
};
|
||||
@ -2760,7 +2785,7 @@ fn parse_fn(
|
||||
let sep_err = format!("to separate the parameters of function '{}'", name);
|
||||
|
||||
loop {
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::RightParen, _) => break,
|
||||
(Token::Identifier(s), pos) => {
|
||||
if params.iter().any(|(p, _)| p == &s) {
|
||||
@ -2780,7 +2805,7 @@ fn parse_fn(
|
||||
}
|
||||
}
|
||||
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::RightParen, _) => break,
|
||||
(Token::Comma, _) => (),
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
@ -2792,7 +2817,7 @@ fn parse_fn(
|
||||
}
|
||||
|
||||
// Parse function body
|
||||
let body = match input.peek().unwrap() {
|
||||
let body = match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::LeftBrace, _) => {
|
||||
settings.is_breakable = false;
|
||||
parse_block(input, state, lib, settings.level_up())?
|
||||
@ -2892,10 +2917,10 @@ fn parse_anon_fn(
|
||||
|
||||
let mut params: StaticVec<_> = Default::default();
|
||||
|
||||
if input.next().unwrap().0 != Token::Or {
|
||||
if input.next().expect(NEVER_ENDS).0 != Token::Or {
|
||||
if !match_token(input, Token::Pipe).0 {
|
||||
loop {
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Pipe, _) => break,
|
||||
(Token::Identifier(s), pos) => {
|
||||
if params.iter().any(|(p, _)| p == &s) {
|
||||
@ -2915,7 +2940,7 @@ fn parse_anon_fn(
|
||||
}
|
||||
}
|
||||
|
||||
match input.next().unwrap() {
|
||||
match input.next().expect(NEVER_ENDS) {
|
||||
(Token::Pipe, _) => break,
|
||||
(Token::Comma, _) => (),
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
@ -3019,7 +3044,7 @@ impl Engine {
|
||||
|
||||
assert!(functions.is_empty());
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
(Token::EOF, _) => (),
|
||||
// Return error if the expression doesn't end
|
||||
(token, pos) => {
|
||||
@ -3044,7 +3069,7 @@ impl Engine {
|
||||
let mut statements = Vec::with_capacity(16);
|
||||
let mut functions = BTreeMap::new();
|
||||
|
||||
while !input.peek().unwrap().0.is_eof() {
|
||||
while !input.peek().expect(NEVER_ENDS).0.is_eof() {
|
||||
let settings = ParseSettings {
|
||||
allow_if_expr: true,
|
||||
allow_switch_expr: true,
|
||||
@ -3067,7 +3092,7 @@ impl Engine {
|
||||
|
||||
statements.push(stmt);
|
||||
|
||||
match input.peek().unwrap() {
|
||||
match input.peek().expect(NEVER_ENDS) {
|
||||
// EOF
|
||||
(Token::EOF, _) => break,
|
||||
// stmt ;
|
||||
|
@ -181,7 +181,9 @@ impl fmt::Display for EvalAltResult {
|
||||
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
|
||||
|
||||
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
|
||||
let s = &*d.read_lock::<ImmutableString>().unwrap();
|
||||
let s = &*d
|
||||
.read_lock::<ImmutableString>()
|
||||
.expect("never fails because the type was checked");
|
||||
write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })?
|
||||
}
|
||||
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
||||
@ -336,7 +338,11 @@ impl EvalAltResult {
|
||||
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
|
||||
map.insert(
|
||||
"error".into(),
|
||||
format!("{:?}", self).split('(').next().unwrap().into(),
|
||||
format!("{:?}", self)
|
||||
.split('(')
|
||||
.next()
|
||||
.expect("never fails because the debug format of an error is `ErrorXXX(...)`")
|
||||
.into(),
|
||||
);
|
||||
|
||||
match self {
|
||||
|
38
src/scope.rs
38
src/scope.rs
@ -373,7 +373,11 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name),
|
||||
Some((index, AccessMode::ReadWrite)) => {
|
||||
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
|
||||
let value_ref = self
|
||||
.values
|
||||
.get_mut(index)
|
||||
.expect("never fails because the index is returned by `get_index`");
|
||||
*value_ref = Dynamic::from(value);
|
||||
}
|
||||
}
|
||||
self
|
||||
@ -406,24 +410,38 @@ impl<'a> Scope<'a> {
|
||||
})
|
||||
}
|
||||
/// Get a mutable reference to an entry in the [`Scope`] based on the index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
|
||||
self.values.get_mut(index).expect("invalid index in Scope")
|
||||
self.values
|
||||
.get_mut(index)
|
||||
.expect("never fails unless the index is out of bounds")
|
||||
}
|
||||
/// Update the access type of an entry in the [`Scope`].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn add_entry_alias(
|
||||
&mut self,
|
||||
index: usize,
|
||||
alias: impl Into<Identifier> + PartialEq<Identifier>,
|
||||
) -> &mut Self {
|
||||
let entry = self.names.get_mut(index).expect("invalid index in Scope");
|
||||
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
||||
let entry = self
|
||||
.names
|
||||
.get_mut(index)
|
||||
.expect("never fails unless the index is out of bounds");
|
||||
if entry.1.is_none() {
|
||||
// Initialize the alias list if it is empty.
|
||||
entry.1 = Some(Default::default());
|
||||
}
|
||||
if !entry.1.as_ref().unwrap().iter().any(|a| &alias == a) {
|
||||
entry.1.as_mut().unwrap().push(alias.into());
|
||||
let list = entry
|
||||
.1
|
||||
.as_mut()
|
||||
.expect("never fails because the list is initialized");
|
||||
if !list.iter().any(|a| &alias == a) {
|
||||
list.push(alias);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
59
src/token.rs
59
src/token.rs
@ -1181,7 +1181,10 @@ pub fn parse_string_literal(
|
||||
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
{
|
||||
skip_whitespace_until = start.position().unwrap() + 1;
|
||||
let start_position = start
|
||||
.position()
|
||||
.expect("never fails because the string must have a starting position");
|
||||
skip_whitespace_until = start_position + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1201,7 +1204,11 @@ pub fn parse_string_literal(
|
||||
|
||||
// Whitespace to skip
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
_ if next_char.is_whitespace() && pos.position().unwrap() < skip_whitespace_until => {}
|
||||
_ if next_char.is_whitespace()
|
||||
&& pos
|
||||
.position()
|
||||
.expect("never fails because a character must have a position")
|
||||
< skip_whitespace_until => {}
|
||||
|
||||
// All other characters
|
||||
_ => {
|
||||
@ -1237,12 +1244,14 @@ fn scan_block_comment(
|
||||
stream: &mut impl InputStream,
|
||||
mut level: usize,
|
||||
pos: &mut Position,
|
||||
comment: &mut Option<String>,
|
||||
mut comment: Option<&mut String>,
|
||||
) -> usize {
|
||||
let comment = &mut comment;
|
||||
|
||||
while let Some(c) = stream.get_next() {
|
||||
pos.advance();
|
||||
|
||||
if let Some(ref mut comment) = comment {
|
||||
if let Some(comment) = comment {
|
||||
comment.push(c);
|
||||
}
|
||||
|
||||
@ -1251,7 +1260,7 @@ fn scan_block_comment(
|
||||
if let Some(c2) = stream.peek_next() {
|
||||
if c2 == '*' {
|
||||
eat_next(stream, pos);
|
||||
if let Some(ref mut comment) = comment {
|
||||
if let Some(comment) = comment {
|
||||
comment.push(c2);
|
||||
}
|
||||
level += 1;
|
||||
@ -1262,7 +1271,7 @@ fn scan_block_comment(
|
||||
if let Some(c2) = stream.peek_next() {
|
||||
if c2 == '/' {
|
||||
eat_next(stream, pos);
|
||||
if let Some(ref mut comment) = comment {
|
||||
if let Some(comment) = comment {
|
||||
comment.push(c2);
|
||||
}
|
||||
level -= 1;
|
||||
@ -1347,21 +1356,27 @@ fn get_next_token_inner(
|
||||
None
|
||||
};
|
||||
|
||||
state.comment_level = scan_block_comment(stream, state.comment_level, pos, &mut comment);
|
||||
state.comment_level =
|
||||
scan_block_comment(stream, state.comment_level, pos, comment.as_mut());
|
||||
|
||||
let include_comments = state.include_comments;
|
||||
let return_comment = state.include_comments;
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[cfg(feature = "metadata")]
|
||||
let include_comments = if is_doc_comment(comment.as_ref().unwrap()) {
|
||||
true
|
||||
} else {
|
||||
include_comments
|
||||
};
|
||||
let return_comment = return_comment
|
||||
|| is_doc_comment(
|
||||
comment
|
||||
.as_ref()
|
||||
.expect("never fails because `include_comments` is true"),
|
||||
);
|
||||
|
||||
if include_comments {
|
||||
return Some((Token::Comment(comment.unwrap()), start_pos));
|
||||
} else if state.comment_level > 0 {
|
||||
if return_comment {
|
||||
return Some((
|
||||
Token::Comment(comment.expect("never fails because `return_comment` is true")),
|
||||
start_pos,
|
||||
));
|
||||
}
|
||||
if state.comment_level > 0 {
|
||||
// Reached EOF without ending comment block
|
||||
return None;
|
||||
}
|
||||
@ -1409,7 +1424,7 @@ fn get_next_token_inner(
|
||||
}
|
||||
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
|
||||
'.' => {
|
||||
stream.get_next().unwrap();
|
||||
stream.get_next().expect("never fails because it is `.`");
|
||||
|
||||
// Check if followed by digits or something that cannot start a property name
|
||||
match stream.peek_next().unwrap_or('\0') {
|
||||
@ -1443,7 +1458,7 @@ fn get_next_token_inner(
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
'e' => {
|
||||
stream.get_next().unwrap();
|
||||
stream.get_next().expect("never fails it is `e`");
|
||||
|
||||
// Check if followed by digits or +/-
|
||||
match stream.peek_next().unwrap_or('\0') {
|
||||
@ -1456,7 +1471,11 @@ fn get_next_token_inner(
|
||||
'+' | '-' => {
|
||||
result.push(next_char);
|
||||
pos.advance();
|
||||
result.push(stream.get_next().unwrap());
|
||||
result.push(
|
||||
stream
|
||||
.get_next()
|
||||
.expect("never fails because it is `+` or `-`"),
|
||||
);
|
||||
pos.advance();
|
||||
}
|
||||
// Not a floating-point number
|
||||
@ -1749,7 +1768,7 @@ fn get_next_token_inner(
|
||||
};
|
||||
|
||||
state.comment_level =
|
||||
scan_block_comment(stream, state.comment_level, pos, &mut comment);
|
||||
scan_block_comment(stream, state.comment_level, pos, comment.as_mut());
|
||||
|
||||
if let Some(comment) = comment {
|
||||
return Some((Token::Comment(comment), start_pos));
|
||||
|
@ -59,21 +59,21 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert_eq!(
|
||||
format!("{:?}", ast),
|
||||
"AST { source: None, body: [Expr(123 @ 1:53)], functions: Module, resolver: None }"
|
||||
"AST { source: None, body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }"
|
||||
);
|
||||
|
||||
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{:?}", ast),
|
||||
r#"AST { source: None, body: [Const(false @ 1:18, "DECISION" @ 1:7, false, 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
|
||||
r#"AST { source: None, body: Block[Const(false @ 1:18, "DECISION" @ 1:7, false, 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
|
||||
);
|
||||
|
||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||
|
||||
assert_eq!(
|
||||
format!("{:?}", ast),
|
||||
"AST { source: None, body: [], functions: Module, resolver: None }"
|
||||
"AST { source: None, body: Block[], functions: Module, resolver: None }"
|
||||
);
|
||||
|
||||
engine.set_optimization_level(OptimizationLevel::Full);
|
||||
@ -82,7 +82,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert_eq!(
|
||||
format!("{:?}", ast),
|
||||
"AST { source: None, body: [Expr(42 @ 1:1)], functions: Module, resolver: None }"
|
||||
"AST { source: None, body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
@ -61,7 +61,7 @@ fn test_scope_eval() -> Result<(), Box<EvalAltResult>> {
|
||||
// First invocation
|
||||
engine
|
||||
.eval_with_scope::<()>(&mut scope, " let x = 4 + 5 - y + z; y = 1;")
|
||||
.expect("y and z not found?");
|
||||
.expect("variables y and z should exist");
|
||||
|
||||
// Second invocation using the same state
|
||||
let result = engine.eval_with_scope::<INT>(&mut scope, "x")?;
|
||||
|
Loading…
Reference in New Issue
Block a user