Restructure code base.
This commit is contained in:
2127
src/types/dynamic.rs
Normal file
2127
src/types/dynamic.rs
Normal file
File diff suppressed because it is too large
Load Diff
455
src/types/error.rs
Normal file
455
src/types/error.rs
Normal file
@@ -0,0 +1,455 @@
|
||||
//! Module containing error definitions for the evaluation process.
|
||||
|
||||
use crate::{Dynamic, ImmutableString, ParseErrorType, Position, INT};
|
||||
#[cfg(feature = "no_std")]
|
||||
use core_error::Error;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// Evaluation result.
|
||||
///
|
||||
/// All wrapped [`Position`] values represent the location in the script where the error occurs.
|
||||
///
|
||||
/// # Thread Safety
|
||||
///
|
||||
/// Currently, [`EvalAltResult`] is neither [`Send`] nor [`Sync`].
|
||||
/// Turn on the `sync` feature to make it [`Send`] `+` [`Sync`].
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum EvalAltResult {
|
||||
/// System error. Wrapped values are the error message and the internal error.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
ErrorSystem(String, Box<dyn Error>),
|
||||
/// System error. Wrapped values are the error message and the internal error.
|
||||
#[cfg(feature = "sync")]
|
||||
ErrorSystem(String, Box<dyn Error + Send + Sync>),
|
||||
|
||||
/// Syntax error.
|
||||
ErrorParsing(ParseErrorType, Position),
|
||||
|
||||
/// Usage of an unknown variable. Wrapped value is the variable name.
|
||||
ErrorVariableNotFound(String, Position),
|
||||
/// Call to an unknown function. Wrapped value is the function signature.
|
||||
ErrorFunctionNotFound(String, Position),
|
||||
/// An error has occurred inside a called function.
|
||||
/// Wrapped values are the function name, function source, and the interior error.
|
||||
ErrorInFunctionCall(String, String, Box<EvalAltResult>, Position),
|
||||
/// Usage of an unknown [module][crate::Module]. Wrapped value is the [module][crate::Module] name.
|
||||
ErrorModuleNotFound(String, Position),
|
||||
/// An error has occurred while loading a [module][crate::Module].
|
||||
/// Wrapped value are the [module][crate::Module] name and the interior error.
|
||||
ErrorInModule(String, Box<EvalAltResult>, Position),
|
||||
/// Access to `this` that is not bound.
|
||||
ErrorUnboundThis(Position),
|
||||
/// Data is not of the required type.
|
||||
/// Wrapped values are the type requested and type of the actual result.
|
||||
ErrorMismatchDataType(String, String, Position),
|
||||
/// Returned type is not the same as the required output type.
|
||||
/// Wrapped values are the type requested and type of the actual result.
|
||||
ErrorMismatchOutputType(String, String, Position),
|
||||
/// Array access out-of-bounds.
|
||||
/// Wrapped values are the current number of elements in the array and the index number.
|
||||
ErrorArrayBounds(usize, INT, Position),
|
||||
/// String indexing out-of-bounds.
|
||||
/// Wrapped values are the current number of characters in the string and the index number.
|
||||
ErrorStringBounds(usize, INT, Position),
|
||||
/// Bit-field indexing out-of-bounds.
|
||||
/// Wrapped values are the current number of bits in the bit-field and the index number.
|
||||
ErrorBitFieldBounds(usize, INT, Position),
|
||||
/// Trying to index into a type that has no indexer function defined. Wrapped value is the type name.
|
||||
ErrorIndexingType(String, Position),
|
||||
/// The `for` statement encounters a type that is not an iterator.
|
||||
ErrorFor(Position),
|
||||
/// Data race detected when accessing a variable. Wrapped value is the variable name.
|
||||
ErrorDataRace(String, Position),
|
||||
/// Assignment to a constant variable. Wrapped value is the variable name.
|
||||
ErrorAssignmentToConstant(String, Position),
|
||||
/// Inappropriate property access. Wrapped value is the property name.
|
||||
ErrorDotExpr(String, Position),
|
||||
/// Arithmetic error encountered. Wrapped value is the error message.
|
||||
ErrorArithmetic(String, Position),
|
||||
/// Number of operations over maximum limit.
|
||||
ErrorTooManyOperations(Position),
|
||||
/// [Modules][crate::Module] over maximum limit.
|
||||
ErrorTooManyModules(Position),
|
||||
/// Call stack over maximum limit.
|
||||
ErrorStackOverflow(Position),
|
||||
/// Data value over maximum size limit. Wrapped value is the type name.
|
||||
ErrorDataTooLarge(String, Position),
|
||||
/// The script is prematurely terminated. Wrapped value is the termination token.
|
||||
ErrorTerminated(Dynamic, Position),
|
||||
/// Run-time error encountered. Wrapped value is the error token.
|
||||
ErrorRuntime(Dynamic, Position),
|
||||
|
||||
/// Breaking out of loops - not an error if within a loop.
|
||||
/// The wrapped value, if true, means breaking clean out of the loop (i.e. a `break` statement).
|
||||
/// The wrapped value, if false, means breaking the current context (i.e. a `continue` statement).
|
||||
LoopBreak(bool, Position),
|
||||
/// Not an error: Value returned from a script via the `return` keyword.
|
||||
/// Wrapped value is the result value.
|
||||
Return(Dynamic, Position),
|
||||
}
|
||||
|
||||
impl Error for EvalAltResult {}
|
||||
|
||||
impl fmt::Display for EvalAltResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::ErrorSystem(s, err) => match s.as_str() {
|
||||
"" => write!(f, "{}", err),
|
||||
s => write!(f, "{}: {}", s, err),
|
||||
}?,
|
||||
|
||||
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::ErrorInFunctionCall(s, src, err, _) if crate::engine::is_anonymous_fn(s) => {
|
||||
write!(f, "{} in call to closure", err)?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{}'", src)?;
|
||||
}
|
||||
}
|
||||
Self::ErrorInFunctionCall(s, src, err, _) => {
|
||||
write!(f, "{} in call to function {}", err, s)?;
|
||||
if !src.is_empty() {
|
||||
write!(f, " @ '{}'", src)?;
|
||||
}
|
||||
}
|
||||
|
||||
Self::ErrorInModule(s, err, _) if s.is_empty() => {
|
||||
write!(f, "Error in module: {}", err)?
|
||||
}
|
||||
Self::ErrorInModule(s, err, _) => write!(f, "Error in module {}: {}", s, err)?,
|
||||
|
||||
Self::ErrorFunctionNotFound(s, _) => write!(f, "Function not found: {}", s)?,
|
||||
Self::ErrorVariableNotFound(s, _) => write!(f, "Variable not found: {}", s)?,
|
||||
Self::ErrorModuleNotFound(s, _) => write!(f, "Module not found: {}", s)?,
|
||||
Self::ErrorDataRace(s, _) => {
|
||||
write!(f, "Data race detected when accessing variable: {}", s)?
|
||||
}
|
||||
Self::ErrorDotExpr(s, _) => match s.as_str() {
|
||||
"" => f.write_str("Malformed dot expression"),
|
||||
s => f.write_str(s),
|
||||
}?,
|
||||
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for {}", s)?,
|
||||
Self::ErrorUnboundThis(_) => f.write_str("'this' is not bound")?,
|
||||
Self::ErrorFor(_) => f.write_str("For loop expects a type with an iterator defined")?,
|
||||
Self::ErrorTooManyOperations(_) => f.write_str("Too many operations")?,
|
||||
Self::ErrorTooManyModules(_) => f.write_str("Too many modules imported")?,
|
||||
Self::ErrorStackOverflow(_) => f.write_str("Stack overflow")?,
|
||||
Self::ErrorTerminated(_, _) => f.write_str("Script terminated")?,
|
||||
|
||||
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str("Runtime error")?,
|
||||
Self::ErrorRuntime(d, _)
|
||||
if d.read_lock::<ImmutableString>()
|
||||
.map_or(false, |v| v.is_empty()) =>
|
||||
{
|
||||
write!(f, "Runtime error")?
|
||||
}
|
||||
Self::ErrorRuntime(d, _) => write!(f, "Runtime error: {}", d)?,
|
||||
|
||||
Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot modify constant {}", s)?,
|
||||
Self::ErrorMismatchOutputType(s, r, _) => match (r.as_str(), s.as_str()) {
|
||||
("", s) => write!(f, "Output type is incorrect, expecting {}", s),
|
||||
(r, "") => write!(f, "Output type is incorrect: {}", r),
|
||||
(r, s) => write!(f, "Output type is incorrect: {} (expecting {})", r, s),
|
||||
}?,
|
||||
Self::ErrorMismatchDataType(s, r, _) => match (r.as_str(), s.as_str()) {
|
||||
("", s) => write!(f, "Data type is incorrect, expecting {}", s),
|
||||
(r, "") => write!(f, "Data type is incorrect: {}", r),
|
||||
(r, s) => write!(f, "Data type is incorrect: {} (expecting {})", r, s),
|
||||
}?,
|
||||
Self::ErrorArithmetic(s, _) => match s.as_str() {
|
||||
"" => f.write_str("Arithmetic error"),
|
||||
s => f.write_str(s),
|
||||
}?,
|
||||
|
||||
Self::LoopBreak(true, _) => f.write_str("'break' not inside a loop")?,
|
||||
Self::LoopBreak(false, _) => f.write_str("'continue' not inside a loop")?,
|
||||
|
||||
Self::Return(_, _) => f.write_str("NOT AN ERROR - function returns value")?,
|
||||
|
||||
Self::ErrorArrayBounds(max, index, _) => match max {
|
||||
0 => write!(f, "Array index {} out of bounds: array is empty", index),
|
||||
1 => write!(
|
||||
f,
|
||||
"Array index {} out of bounds: only 1 element in the array",
|
||||
index
|
||||
),
|
||||
_ => write!(
|
||||
f,
|
||||
"Array index {} out of bounds: only {} elements in the array",
|
||||
index, max
|
||||
),
|
||||
}?,
|
||||
Self::ErrorStringBounds(max, index, _) => match max {
|
||||
0 => write!(f, "String index {} out of bounds: string is empty", index),
|
||||
1 => write!(
|
||||
f,
|
||||
"String index {} out of bounds: only 1 character in the string",
|
||||
index
|
||||
),
|
||||
_ => write!(
|
||||
f,
|
||||
"String index {} out of bounds: only {} characters in the string",
|
||||
index, max
|
||||
),
|
||||
}?,
|
||||
Self::ErrorBitFieldBounds(max, index, _) => write!(
|
||||
f,
|
||||
"Bit-field index {} out of bounds: only {} bits in the bit-field",
|
||||
index, max
|
||||
)?,
|
||||
Self::ErrorDataTooLarge(typ, _) => write!(f, "{} exceeds maximum limit", typ)?,
|
||||
}
|
||||
|
||||
// Do not write any position if None
|
||||
if !self.position().is_none() {
|
||||
write!(f, " ({})", self.position())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> From<T> for EvalAltResult {
|
||||
#[inline(never)]
|
||||
fn from(err: T) -> Self {
|
||||
Self::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str>> From<T> for Box<EvalAltResult> {
|
||||
#[inline(never)]
|
||||
fn from(err: T) -> Self {
|
||||
EvalAltResult::ErrorRuntime(err.as_ref().to_string().into(), Position::NONE).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EvalAltResult {
|
||||
/// Is this a pseudo error? A pseudo error is one that does not occur naturally.
|
||||
///
|
||||
/// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors.
|
||||
#[must_use]
|
||||
pub const fn is_pseudo_error(&self) -> bool {
|
||||
match self {
|
||||
Self::LoopBreak(_, _) | Self::Return(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
/// Can this error be caught?
|
||||
#[must_use]
|
||||
pub const fn is_catchable(&self) -> bool {
|
||||
match self {
|
||||
Self::ErrorSystem(_, _) => false,
|
||||
Self::ErrorParsing(_, _) => false,
|
||||
|
||||
Self::ErrorFunctionNotFound(_, _)
|
||||
| Self::ErrorInFunctionCall(_, _, _, _)
|
||||
| Self::ErrorInModule(_, _, _)
|
||||
| Self::ErrorUnboundThis(_)
|
||||
| Self::ErrorMismatchDataType(_, _, _)
|
||||
| Self::ErrorArrayBounds(_, _, _)
|
||||
| Self::ErrorStringBounds(_, _, _)
|
||||
| Self::ErrorBitFieldBounds(_, _, _)
|
||||
| Self::ErrorIndexingType(_, _)
|
||||
| Self::ErrorFor(_)
|
||||
| Self::ErrorVariableNotFound(_, _)
|
||||
| Self::ErrorModuleNotFound(_, _)
|
||||
| Self::ErrorDataRace(_, _)
|
||||
| Self::ErrorAssignmentToConstant(_, _)
|
||||
| Self::ErrorMismatchOutputType(_, _, _)
|
||||
| Self::ErrorDotExpr(_, _)
|
||||
| Self::ErrorArithmetic(_, _)
|
||||
| Self::ErrorRuntime(_, _) => true,
|
||||
|
||||
Self::ErrorTooManyOperations(_)
|
||||
| Self::ErrorTooManyModules(_)
|
||||
| Self::ErrorStackOverflow(_)
|
||||
| Self::ErrorDataTooLarge(_, _)
|
||||
| Self::ErrorTerminated(_, _) => false,
|
||||
|
||||
Self::LoopBreak(_, _) | Self::Return(_, _) => false,
|
||||
}
|
||||
}
|
||||
/// Is this error a system exception?
|
||||
#[must_use]
|
||||
pub const fn is_system_exception(&self) -> bool {
|
||||
match self {
|
||||
Self::ErrorSystem(_, _) => true,
|
||||
Self::ErrorParsing(_, _) => true,
|
||||
|
||||
Self::ErrorTooManyOperations(_)
|
||||
| Self::ErrorTooManyModules(_)
|
||||
| Self::ErrorStackOverflow(_)
|
||||
| Self::ErrorDataTooLarge(_, _) => true,
|
||||
|
||||
Self::ErrorTerminated(_, _) => true,
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
/// Get the [position][Position] of this error.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub(crate) fn dump_fields(&self, map: &mut crate::Map) {
|
||||
map.insert(
|
||||
"error".into(),
|
||||
format!("{:?}", self)
|
||||
.split('(')
|
||||
.next()
|
||||
.expect("debug format of error is `ErrorXXX(...)`")
|
||||
.into(),
|
||||
);
|
||||
|
||||
match self {
|
||||
Self::LoopBreak(_, _) | Self::Return(_, _) => (),
|
||||
|
||||
Self::ErrorSystem(_, _)
|
||||
| Self::ErrorParsing(_, _)
|
||||
| Self::ErrorUnboundThis(_)
|
||||
| Self::ErrorFor(_)
|
||||
| Self::ErrorArithmetic(_, _)
|
||||
| Self::ErrorTooManyOperations(_)
|
||||
| Self::ErrorTooManyModules(_)
|
||||
| Self::ErrorStackOverflow(_)
|
||||
| Self::ErrorRuntime(_, _) => (),
|
||||
|
||||
Self::ErrorFunctionNotFound(f, _) => {
|
||||
map.insert("function".into(), f.into());
|
||||
}
|
||||
Self::ErrorInFunctionCall(f, s, _, _) => {
|
||||
map.insert("function".into(), f.into());
|
||||
map.insert("source".into(), s.into());
|
||||
}
|
||||
Self::ErrorInModule(m, _, _) => {
|
||||
map.insert("module".into(), m.into());
|
||||
}
|
||||
Self::ErrorMismatchDataType(r, a, _) | Self::ErrorMismatchOutputType(r, a, _) => {
|
||||
map.insert("requested".into(), r.into());
|
||||
map.insert("actual".into(), a.into());
|
||||
}
|
||||
Self::ErrorArrayBounds(n, i, _)
|
||||
| Self::ErrorStringBounds(n, i, _)
|
||||
| Self::ErrorBitFieldBounds(n, i, _) => {
|
||||
map.insert("length".into(), (*n as INT).into());
|
||||
map.insert("index".into(), (*i as INT).into());
|
||||
}
|
||||
Self::ErrorIndexingType(t, _) => {
|
||||
map.insert("type".into(), t.into());
|
||||
}
|
||||
Self::ErrorVariableNotFound(v, _)
|
||||
| Self::ErrorDataRace(v, _)
|
||||
| Self::ErrorAssignmentToConstant(v, _) => {
|
||||
map.insert("variable".into(), v.into());
|
||||
}
|
||||
Self::ErrorModuleNotFound(m, _) => {
|
||||
map.insert("module".into(), m.into());
|
||||
}
|
||||
Self::ErrorDotExpr(p, _) => {
|
||||
map.insert("property".into(), p.into());
|
||||
}
|
||||
|
||||
Self::ErrorDataTooLarge(t, _) => {
|
||||
map.insert("type".into(), t.into());
|
||||
}
|
||||
Self::ErrorTerminated(t, _) => {
|
||||
map.insert("token".into(), t.clone());
|
||||
}
|
||||
};
|
||||
}
|
||||
/// Get the [position][Position] of this error.
|
||||
#[must_use]
|
||||
pub const fn position(&self) -> Position {
|
||||
match self {
|
||||
Self::ErrorSystem(_, _) => Position::NONE,
|
||||
|
||||
Self::ErrorParsing(_, pos)
|
||||
| Self::ErrorFunctionNotFound(_, pos)
|
||||
| Self::ErrorInFunctionCall(_, _, _, pos)
|
||||
| Self::ErrorInModule(_, _, pos)
|
||||
| Self::ErrorUnboundThis(pos)
|
||||
| Self::ErrorMismatchDataType(_, _, pos)
|
||||
| Self::ErrorArrayBounds(_, _, pos)
|
||||
| Self::ErrorStringBounds(_, _, pos)
|
||||
| Self::ErrorBitFieldBounds(_, _, pos)
|
||||
| Self::ErrorIndexingType(_, pos)
|
||||
| Self::ErrorFor(pos)
|
||||
| Self::ErrorVariableNotFound(_, pos)
|
||||
| Self::ErrorModuleNotFound(_, pos)
|
||||
| Self::ErrorDataRace(_, pos)
|
||||
| Self::ErrorAssignmentToConstant(_, pos)
|
||||
| Self::ErrorMismatchOutputType(_, _, pos)
|
||||
| Self::ErrorDotExpr(_, pos)
|
||||
| Self::ErrorArithmetic(_, pos)
|
||||
| Self::ErrorTooManyOperations(pos)
|
||||
| Self::ErrorTooManyModules(pos)
|
||||
| Self::ErrorStackOverflow(pos)
|
||||
| Self::ErrorDataTooLarge(_, pos)
|
||||
| Self::ErrorTerminated(_, pos)
|
||||
| Self::ErrorRuntime(_, pos)
|
||||
| Self::LoopBreak(_, pos)
|
||||
| Self::Return(_, pos) => *pos,
|
||||
}
|
||||
}
|
||||
/// Remove the [position][Position] information from this error.
|
||||
///
|
||||
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
|
||||
pub fn clear_position(&mut self) -> &mut Self {
|
||||
self.set_position(Position::NONE)
|
||||
}
|
||||
/// Remove the [position][Position] information from this error and return it.
|
||||
///
|
||||
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
|
||||
pub fn take_position(&mut self) -> Position {
|
||||
let pos = self.position();
|
||||
self.set_position(Position::NONE);
|
||||
pos
|
||||
}
|
||||
/// Override the [position][Position] of this error.
|
||||
pub fn set_position(&mut self, new_position: Position) -> &mut Self {
|
||||
match self {
|
||||
Self::ErrorSystem(_, _) => (),
|
||||
|
||||
Self::ErrorParsing(_, pos)
|
||||
| Self::ErrorFunctionNotFound(_, pos)
|
||||
| Self::ErrorInFunctionCall(_, _, _, pos)
|
||||
| Self::ErrorInModule(_, _, pos)
|
||||
| Self::ErrorUnboundThis(pos)
|
||||
| Self::ErrorMismatchDataType(_, _, pos)
|
||||
| Self::ErrorArrayBounds(_, _, pos)
|
||||
| Self::ErrorStringBounds(_, _, pos)
|
||||
| Self::ErrorBitFieldBounds(_, _, pos)
|
||||
| Self::ErrorIndexingType(_, pos)
|
||||
| Self::ErrorFor(pos)
|
||||
| Self::ErrorVariableNotFound(_, pos)
|
||||
| Self::ErrorModuleNotFound(_, pos)
|
||||
| Self::ErrorDataRace(_, pos)
|
||||
| Self::ErrorAssignmentToConstant(_, pos)
|
||||
| Self::ErrorMismatchOutputType(_, _, pos)
|
||||
| Self::ErrorDotExpr(_, pos)
|
||||
| Self::ErrorArithmetic(_, pos)
|
||||
| Self::ErrorTooManyOperations(pos)
|
||||
| Self::ErrorTooManyModules(pos)
|
||||
| Self::ErrorStackOverflow(pos)
|
||||
| Self::ErrorDataTooLarge(_, pos)
|
||||
| Self::ErrorTerminated(_, pos)
|
||||
| Self::ErrorRuntime(_, pos)
|
||||
| Self::LoopBreak(_, pos)
|
||||
| Self::Return(_, pos) => *pos = new_position,
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Consume the current [`EvalAltResult`] and return a new one with the specified [`Position`]
|
||||
/// if the current position is [`Position::None`].
|
||||
#[inline(never)]
|
||||
#[must_use]
|
||||
pub(crate) fn fill_position(mut self: Box<Self>, new_position: Position) -> Box<Self> {
|
||||
if self.position().is_none() {
|
||||
self.set_position(new_position);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
195
src/types/fn_ptr.rs
Normal file
195
src/types/fn_ptr.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
//! The `FnPtr` type.
|
||||
|
||||
use crate::tokenizer::is_valid_identifier;
|
||||
use crate::{
|
||||
Dynamic, EvalAltResult, Identifier, NativeCallContext, Position, RhaiResult, StaticVec,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt, mem,
|
||||
};
|
||||
|
||||
/// A general function pointer, which may carry additional (i.e. curried) argument values
|
||||
/// to be passed onto a function during a call.
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct FnPtr(Identifier, StaticVec<Dynamic>);
|
||||
|
||||
impl fmt::Debug for FnPtr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !self.is_curried() {
|
||||
write!(f, "Fn({})", self.fn_name())
|
||||
} else {
|
||||
f.debug_tuple("Fn").field(&self.0).field(&self.1).finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FnPtr {
|
||||
/// Create a new function pointer.
|
||||
#[inline(always)]
|
||||
pub fn new(name: impl Into<Identifier>) -> Result<Self, Box<EvalAltResult>> {
|
||||
name.into().try_into()
|
||||
}
|
||||
/// Create a new function pointer without checking its parameters.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn new_unchecked(name: Identifier, curry: StaticVec<Dynamic>) -> Self {
|
||||
Self(name, curry)
|
||||
}
|
||||
/// Get the name of the function.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn fn_name(&self) -> &str {
|
||||
self.fn_name_raw().as_ref()
|
||||
}
|
||||
/// Get the name of the function.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn fn_name_raw(&self) -> &Identifier {
|
||||
&self.0
|
||||
}
|
||||
/// Get the underlying data of the function pointer.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn take_data(self) -> (Identifier, StaticVec<Dynamic>) {
|
||||
(self.0, self.1)
|
||||
}
|
||||
/// Get the curried arguments.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn curry(&self) -> &[Dynamic] {
|
||||
self.1.as_ref()
|
||||
}
|
||||
/// Add a new curried argument.
|
||||
#[inline(always)]
|
||||
pub fn add_curry(&mut self, value: Dynamic) -> &mut Self {
|
||||
self.1.push(value);
|
||||
self
|
||||
}
|
||||
/// Set curried arguments to the function pointer.
|
||||
#[inline]
|
||||
pub fn set_curry(&mut self, values: impl IntoIterator<Item = Dynamic>) -> &mut Self {
|
||||
self.1 = values.into_iter().collect();
|
||||
self
|
||||
}
|
||||
/// Is the function pointer curried?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_curried(&self) -> bool {
|
||||
!self.1.is_empty()
|
||||
}
|
||||
/// Get the number of curried arguments.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn num_curried(&self) -> usize {
|
||||
self.1.len()
|
||||
}
|
||||
/// Does the function pointer refer to an anonymous function?
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_anonymous(&self) -> bool {
|
||||
self.0.starts_with(crate::engine::FN_ANONYMOUS)
|
||||
}
|
||||
/// Call the function pointer with curried arguments (if any).
|
||||
///
|
||||
/// If this function is a script-defined function, it must not be marked private.
|
||||
///
|
||||
/// # WARNING
|
||||
///
|
||||
/// All the arguments are _consumed_, meaning that they're replaced by `()`.
|
||||
/// This is to avoid unnecessarily cloning the arguments.
|
||||
/// Do not use the arguments after this call. If they are needed afterwards,
|
||||
/// clone them _before_ calling this function.
|
||||
#[inline]
|
||||
pub fn call_dynamic(
|
||||
&self,
|
||||
ctx: &NativeCallContext,
|
||||
this_ptr: Option<&mut Dynamic>,
|
||||
mut arg_values: impl AsMut<[Dynamic]>,
|
||||
) -> RhaiResult {
|
||||
let mut arg_values = arg_values.as_mut();
|
||||
let mut args_data;
|
||||
|
||||
if self.num_curried() > 0 {
|
||||
args_data = StaticVec::with_capacity(self.num_curried() + arg_values.len());
|
||||
args_data.extend(self.curry().iter().cloned());
|
||||
args_data.extend(arg_values.iter_mut().map(mem::take));
|
||||
arg_values = args_data.as_mut();
|
||||
};
|
||||
|
||||
let is_method = this_ptr.is_some();
|
||||
|
||||
let mut args = StaticVec::with_capacity(arg_values.len() + 1);
|
||||
if let Some(obj) = this_ptr {
|
||||
args.push(obj);
|
||||
}
|
||||
args.extend(arg_values.iter_mut());
|
||||
|
||||
ctx.call_fn_raw(self.fn_name(), is_method, is_method, &mut args)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FnPtr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Fn({})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Identifier> for FnPtr {
|
||||
type Error = Box<EvalAltResult>;
|
||||
|
||||
#[inline]
|
||||
fn try_from(value: Identifier) -> Result<Self, Self::Error> {
|
||||
if is_valid_identifier(value.chars()) {
|
||||
Ok(Self(value, StaticVec::new()))
|
||||
} else {
|
||||
Err(EvalAltResult::ErrorFunctionNotFound(value.to_string(), Position::NONE).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
impl TryFrom<crate::ImmutableString> for FnPtr {
|
||||
type Error = Box<EvalAltResult>;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: crate::ImmutableString) -> Result<Self, Self::Error> {
|
||||
let s: Identifier = value.into();
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for FnPtr {
|
||||
type Error = Box<EvalAltResult>;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
let s: Identifier = value.into();
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Box<str>> for FnPtr {
|
||||
type Error = Box<EvalAltResult>;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: Box<str>) -> Result<Self, Self::Error> {
|
||||
let s: Identifier = value.into();
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for FnPtr {
|
||||
type Error = Box<EvalAltResult>;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
let s: Identifier = value.into();
|
||||
Self::try_from(s)
|
||||
}
|
||||
}
|
580
src/types/immutable_string.rs
Normal file
580
src/types/immutable_string.rs
Normal file
@@ -0,0 +1,580 @@
|
||||
//! The `ImmutableString` type.
|
||||
|
||||
use crate::func::native::{shared_make_mut, shared_take};
|
||||
use crate::{Shared, SmartString};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{
|
||||
borrow::Borrow,
|
||||
cmp::Ordering,
|
||||
fmt,
|
||||
hash::Hash,
|
||||
iter::FromIterator,
|
||||
ops::{Add, AddAssign, Deref, Sub, SubAssign},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
/// The system immutable string type.
|
||||
///
|
||||
/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`SmartString`][smartstring::SmartString]`>`
|
||||
/// (or [`Arc`][std::sync::Arc]`<`[`SmartString`][smartstring::SmartString]`>` under the `sync` feature)
|
||||
/// so that it can be simply shared and not cloned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::ImmutableString;
|
||||
///
|
||||
/// let s1: ImmutableString = "hello".into();
|
||||
///
|
||||
/// // No actual cloning of the string is involved below.
|
||||
/// let s2 = s1.clone();
|
||||
/// let s3 = s2.clone();
|
||||
///
|
||||
/// assert_eq!(s1, s2);
|
||||
///
|
||||
/// // Clones the underlying string (because it is already shared) and extracts it.
|
||||
/// let mut s: String = s1.into_owned();
|
||||
///
|
||||
/// // Changing the clone has no impact on the previously shared version.
|
||||
/// s.push_str(", world!");
|
||||
///
|
||||
/// // The old version still exists.
|
||||
/// assert_eq!(s2, s3);
|
||||
/// assert_eq!(s2.as_str(), "hello");
|
||||
///
|
||||
/// // Not equals!
|
||||
/// assert_ne!(s2.as_str(), s.as_str());
|
||||
/// assert_eq!(s, "hello, world!");
|
||||
/// ```
|
||||
#[derive(Clone, Eq, Ord, Hash, Default)]
|
||||
pub struct ImmutableString(Shared<SmartString>);
|
||||
|
||||
impl Deref for ImmutableString {
|
||||
type Target = SmartString;
|
||||
|
||||
#[inline(always)]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<SmartString> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &SmartString {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<SmartString> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &SmartString {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn borrow(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: &str) -> Self {
|
||||
let value: SmartString = value.into();
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
impl From<Box<str>> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: Box<str>) -> Self {
|
||||
let value: SmartString = value.into();
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
impl From<&String> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: &String) -> Self {
|
||||
let value: SmartString = value.into();
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
impl From<String> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: String) -> Self {
|
||||
let value: SmartString = value.into();
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
impl From<&SmartString> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: &SmartString) -> Self {
|
||||
Self(value.clone().into())
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
impl From<SmartString> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: SmartString) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
impl From<&ImmutableString> for SmartString {
|
||||
#[inline(always)]
|
||||
fn from(value: &ImmutableString) -> Self {
|
||||
value.as_str().into()
|
||||
}
|
||||
}
|
||||
impl From<ImmutableString> for SmartString {
|
||||
#[inline(always)]
|
||||
fn from(mut value: ImmutableString) -> Self {
|
||||
std::mem::take(shared_make_mut(&mut value.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ImmutableString {
|
||||
type Err = ();
|
||||
|
||||
#[inline(always)]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s: SmartString = s.into();
|
||||
Ok(Self(s.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<char> for ImmutableString {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<SmartString>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a char> for ImmutableString {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().cloned().collect::<SmartString>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<&'a str> for ImmutableString {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<SmartString>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<String> for ImmutableString {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<SmartString>().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
impl<'a> FromIterator<SmartString> for ImmutableString {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = SmartString>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect::<SmartString>().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.0.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.0.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(mut self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self
|
||||
} else if self.is_empty() {
|
||||
rhs
|
||||
} else {
|
||||
self.make_mut().push_str(rhs.0.as_str());
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self.clone()
|
||||
} else if self.is_empty() {
|
||||
rhs.clone()
|
||||
} else {
|
||||
let mut s = self.clone();
|
||||
s.make_mut().push_str(rhs.0.as_str());
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&ImmutableString> for ImmutableString {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: &ImmutableString) {
|
||||
if !rhs.is_empty() {
|
||||
if self.is_empty() {
|
||||
self.0 = rhs.0.clone();
|
||||
} else {
|
||||
self.make_mut().push_str(rhs.0.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<ImmutableString> for ImmutableString {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: ImmutableString) {
|
||||
if !rhs.is_empty() {
|
||||
if self.is_empty() {
|
||||
self.0 = rhs.0;
|
||||
} else {
|
||||
self.make_mut().push_str(rhs.0.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&str> for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(mut self, rhs: &str) -> Self::Output {
|
||||
if !rhs.is_empty() {
|
||||
self.make_mut().push_str(rhs);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<&str> for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: &str) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self.clone()
|
||||
} else {
|
||||
let mut s = self.clone();
|
||||
s.make_mut().push_str(rhs);
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<&str> for ImmutableString {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: &str) {
|
||||
if !rhs.is_empty() {
|
||||
self.make_mut().push_str(rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<String> for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(mut self, rhs: String) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self
|
||||
} else if self.is_empty() {
|
||||
rhs.into()
|
||||
} else {
|
||||
self.make_mut().push_str(&rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<String> for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: String) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self.clone()
|
||||
} else if self.is_empty() {
|
||||
rhs.into()
|
||||
} else {
|
||||
let mut s = self.clone();
|
||||
s.make_mut().push_str(&rhs);
|
||||
s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<String> for ImmutableString {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: String) {
|
||||
if !rhs.is_empty() {
|
||||
if self.is_empty() {
|
||||
let rhs: SmartString = rhs.into();
|
||||
self.0 = rhs.into();
|
||||
} else {
|
||||
self.make_mut().push_str(&rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<char> for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn add(mut self, rhs: char) -> Self::Output {
|
||||
self.make_mut().push(rhs);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<char> for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
#[inline]
|
||||
fn add(self, rhs: char) -> Self::Output {
|
||||
let mut s = self.clone();
|
||||
s.make_mut().push(rhs);
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<char> for ImmutableString {
|
||||
#[inline]
|
||||
fn add_assign(&mut self, rhs: char) {
|
||||
self.make_mut().push(rhs);
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self
|
||||
} else if self.is_empty() {
|
||||
rhs
|
||||
} else {
|
||||
self.replace(rhs.as_str(), "").into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self.clone()
|
||||
} else if self.is_empty() {
|
||||
rhs.clone()
|
||||
} else {
|
||||
self.replace(rhs.as_str(), "").into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<&ImmutableString> for ImmutableString {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: &ImmutableString) {
|
||||
if !rhs.is_empty() {
|
||||
if self.is_empty() {
|
||||
self.0 = rhs.0.clone();
|
||||
} else {
|
||||
let rhs: SmartString = self.replace(rhs.as_str(), "").into();
|
||||
self.0 = rhs.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<ImmutableString> for ImmutableString {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: ImmutableString) {
|
||||
if !rhs.is_empty() {
|
||||
if self.is_empty() {
|
||||
self.0 = rhs.0;
|
||||
} else {
|
||||
let rhs: SmartString = self.replace(rhs.as_str(), "").into();
|
||||
self.0 = rhs.into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<String> for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: String) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self
|
||||
} else if self.is_empty() {
|
||||
rhs.into()
|
||||
} else {
|
||||
self.replace(&rhs, "").into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<String> for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, rhs: String) -> Self::Output {
|
||||
if rhs.is_empty() {
|
||||
self.clone()
|
||||
} else if self.is_empty() {
|
||||
rhs.into()
|
||||
} else {
|
||||
self.replace(&rhs, "").into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<String> for ImmutableString {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: String) {
|
||||
let rhs: SmartString = self.replace(&rhs, "").into();
|
||||
self.0 = rhs.into();
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<char> for ImmutableString {
|
||||
type Output = Self;
|
||||
|
||||
#[inline(always)]
|
||||
fn sub(self, rhs: char) -> Self::Output {
|
||||
self.replace(rhs, "").into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub<char> for &ImmutableString {
|
||||
type Output = ImmutableString;
|
||||
|
||||
#[inline(always)]
|
||||
fn sub(self, rhs: char) -> Self::Output {
|
||||
self.replace(rhs, "").into()
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign<char> for ImmutableString {
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, rhs: char) {
|
||||
let rhs: SmartString = self.replace(rhs, "").into();
|
||||
self.0 = rhs.into();
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> PartialEq<S> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &S) -> bool {
|
||||
self.as_str().eq(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<ImmutableString> for str {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &ImmutableString) -> bool {
|
||||
self.eq(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<ImmutableString> for String {
|
||||
#[inline(always)]
|
||||
fn eq(&self, other: &ImmutableString) -> bool {
|
||||
self.eq(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> PartialOrd<S> for ImmutableString {
|
||||
fn partial_cmp(&self, other: &S) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<ImmutableString> for str {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &ImmutableString) -> Option<Ordering> {
|
||||
self.partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<ImmutableString> for String {
|
||||
#[inline(always)]
|
||||
fn partial_cmp(&self, other: &ImmutableString) -> Option<Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl ImmutableString {
|
||||
/// Create a new [`ImmutableString`].
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(SmartString::new().into())
|
||||
}
|
||||
/// Consume the [`ImmutableString`] and convert it into a [`String`].
|
||||
/// If there are other references to the same string, a cloned copy is returned.
|
||||
#[inline]
|
||||
pub fn into_owned(mut self) -> String {
|
||||
self.make_mut(); // Make sure it is unique reference
|
||||
shared_take(self.0).into() // Should succeed
|
||||
}
|
||||
/// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references).
|
||||
/// Then return a mutable reference to the [`SmartString`].
|
||||
#[inline(always)]
|
||||
pub(crate) fn make_mut(&mut self) -> &mut SmartString {
|
||||
shared_make_mut(&mut self.0)
|
||||
}
|
||||
/// Returns `true` if the two [`ImmutableString`]'s point to the same allocation.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::ImmutableString;
|
||||
///
|
||||
/// let s1: ImmutableString = "hello".into();
|
||||
/// let s2 = s1.clone();
|
||||
/// let s3: ImmutableString = "hello".into();
|
||||
///
|
||||
/// assert_eq!(s1, s2);
|
||||
/// assert_eq!(s1, s3);
|
||||
/// assert_eq!(s2, s3);
|
||||
///
|
||||
/// assert!(s1.ptr_eq(&s2));
|
||||
/// assert!(!s1.ptr_eq(&s3));
|
||||
/// assert!(!s2.ptr_eq(&s3));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn ptr_eq(&self, other: &Self) -> bool {
|
||||
Shared::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
8
src/types/mod.rs
Normal file
8
src/types/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
//! Module defining Rhai data types.
|
||||
|
||||
pub mod dynamic;
|
||||
pub mod error;
|
||||
pub mod fn_ptr;
|
||||
pub mod immutable_string;
|
||||
pub mod parse_error;
|
||||
pub mod scope;
|
325
src/types/parse_error.rs
Normal file
325
src/types/parse_error.rs
Normal file
@@ -0,0 +1,325 @@
|
||||
//! Module containing error definitions for the parsing process.
|
||||
|
||||
use crate::{EvalAltResult, Position};
|
||||
#[cfg(feature = "no_std")]
|
||||
use core_error::Error;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
/// _(internals)_ Error encountered when tokenizing the script text.
|
||||
/// Exported under the `internals` feature only.
|
||||
///
|
||||
/// # Volatile Data Structure
|
||||
///
|
||||
/// This type is volatile and may change.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum LexError {
|
||||
/// An unexpected symbol is encountered when tokenizing the script text.
|
||||
UnexpectedInput(String),
|
||||
/// A string literal is not terminated before a new-line or EOF.
|
||||
UnterminatedString,
|
||||
/// An identifier is in an invalid format.
|
||||
StringTooLong(usize),
|
||||
/// An string/character/numeric escape sequence is in an invalid format.
|
||||
MalformedEscapeSequence(String),
|
||||
/// An numeric literal is in an invalid format.
|
||||
MalformedNumber(String),
|
||||
/// An character literal is in an invalid format.
|
||||
MalformedChar(String),
|
||||
/// An identifier is in an invalid format.
|
||||
MalformedIdentifier(String),
|
||||
/// Bad symbol encountered when tokenizing the script text.
|
||||
ImproperSymbol(String, String),
|
||||
}
|
||||
|
||||
impl Error for LexError {}
|
||||
|
||||
impl fmt::Display for LexError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
|
||||
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s),
|
||||
Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s),
|
||||
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s),
|
||||
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s),
|
||||
Self::UnterminatedString => f.write_str("Open string is not terminated"),
|
||||
Self::StringTooLong(max) => write!(
|
||||
f,
|
||||
"Length of string literal exceeds the maximum limit ({})",
|
||||
max
|
||||
),
|
||||
Self::ImproperSymbol(s, d) if d.is_empty() => {
|
||||
write!(f, "Invalid symbol encountered: '{}'", s)
|
||||
}
|
||||
Self::ImproperSymbol(_, d) => f.write_str(d),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LexError {
|
||||
/// Convert a [`LexError`] into a [`ParseError`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn into_err(self, pos: Position) -> ParseError {
|
||||
ParseError(Box::new(self.into()), pos)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of error encountered when parsing a script.
|
||||
///
|
||||
/// Some errors never appear when certain features are turned on.
|
||||
/// They still exist so that the application can turn features on and off without going through
|
||||
/// massive code changes to remove/add back enum variants in match statements.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum ParseErrorType {
|
||||
/// The script ends prematurely.
|
||||
UnexpectedEOF,
|
||||
/// Error in the script text. Wrapped value is the lex error.
|
||||
BadInput(LexError),
|
||||
/// An unknown operator is encountered. Wrapped value is the operator.
|
||||
UnknownOperator(String),
|
||||
/// Expecting a particular token but not finding one. Wrapped values are the token and description.
|
||||
MissingToken(String, String),
|
||||
/// Expecting a particular symbol but not finding one. Wrapped value is the description.
|
||||
MissingSymbol(String),
|
||||
/// An expression in function call arguments `()` has syntax error. Wrapped value is the error
|
||||
/// description (if any).
|
||||
MalformedCallExpr(String),
|
||||
/// An expression in indexing brackets `[]` has syntax error. Wrapped value is the error
|
||||
/// description (if any).
|
||||
///
|
||||
/// Never appears under the `no_index` feature.
|
||||
MalformedIndexExpr(String),
|
||||
/// An expression in an `in` expression has syntax error. Wrapped value is the error description
|
||||
/// (if any).
|
||||
///
|
||||
/// Never appears under the `no_object` and `no_index` features combination.
|
||||
MalformedInExpr(String),
|
||||
/// A capturing has syntax error. Wrapped value is the error description (if any).
|
||||
///
|
||||
/// Never appears under the `no_closure` feature.
|
||||
MalformedCapture(String),
|
||||
/// A map definition has duplicated property names. Wrapped value is the property name.
|
||||
///
|
||||
/// Never appears under the `no_object` feature.
|
||||
DuplicatedProperty(String),
|
||||
/// A `switch` case is duplicated.
|
||||
DuplicatedSwitchCase,
|
||||
/// A variable name is duplicated. Wrapped value is the variable name.
|
||||
DuplicatedVariable(String),
|
||||
/// The default case of a `switch` statement is not the last.
|
||||
WrongSwitchDefaultCase,
|
||||
/// The case condition of a `switch` statement is not appropriate.
|
||||
WrongSwitchCaseCondition,
|
||||
/// Missing a property name for custom types and maps.
|
||||
///
|
||||
/// Never appears under the `no_object` feature.
|
||||
PropertyExpected,
|
||||
/// Missing a variable name after the `let`, `const`, `for` or `catch` keywords.
|
||||
VariableExpected,
|
||||
/// An identifier is a reserved keyword.
|
||||
Reserved(String),
|
||||
/// An expression is of the wrong type.
|
||||
/// Wrapped values are the type requested and type of the actual result.
|
||||
MismatchedType(String, String),
|
||||
/// Missing an expression. Wrapped value is the expression type.
|
||||
ExprExpected(String),
|
||||
/// Defining a doc-comment in an appropriate place (e.g. not at global level).
|
||||
///
|
||||
/// Never appears under the `no_function` feature.
|
||||
WrongDocComment,
|
||||
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
||||
///
|
||||
/// Never appears under the `no_function` feature.
|
||||
WrongFnDefinition,
|
||||
/// Defining a function with a name that conflicts with an existing function.
|
||||
/// Wrapped values are the function name and number of parameters.
|
||||
///
|
||||
/// Never appears under the `no_object` feature.
|
||||
FnDuplicatedDefinition(String, usize),
|
||||
/// Missing a function name after the `fn` keyword.
|
||||
///
|
||||
/// Never appears under the `no_function` feature.
|
||||
FnMissingName,
|
||||
/// A function definition is missing the parameters list. Wrapped value is the function name.
|
||||
///
|
||||
/// Never appears under the `no_function` feature.
|
||||
FnMissingParams(String),
|
||||
/// A function definition has duplicated parameters. Wrapped values are the function name and
|
||||
/// parameter name.
|
||||
///
|
||||
/// Never appears under the `no_function` feature.
|
||||
FnDuplicatedParam(String, String),
|
||||
/// A function definition is missing the body. Wrapped value is the function name.
|
||||
///
|
||||
/// Never appears under the `no_function` feature.
|
||||
FnMissingBody(String),
|
||||
/// Export statement not at global level.
|
||||
///
|
||||
/// Never appears under the `no_module` feature.
|
||||
WrongExport,
|
||||
/// Assignment to an a constant variable. Wrapped value is the constant variable name.
|
||||
AssignmentToConstant(String),
|
||||
/// Assignment to an inappropriate LHS (left-hand-side) expression.
|
||||
/// Wrapped value is the error message (if any).
|
||||
AssignmentToInvalidLHS(String),
|
||||
/// Expression exceeding the maximum levels of complexity.
|
||||
///
|
||||
/// Never appears under the `unchecked` feature.
|
||||
ExprTooDeep,
|
||||
/// Literal exceeding the maximum size. Wrapped values are the data type name and the maximum size.
|
||||
///
|
||||
/// Never appears under the `unchecked` feature.
|
||||
LiteralTooLarge(String, usize),
|
||||
/// Break statement not inside a loop.
|
||||
LoopBreak,
|
||||
}
|
||||
|
||||
impl ParseErrorType {
|
||||
/// Make a [`ParseError`] using the current type and position.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
||||
ParseError(self.into(), pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseErrorType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::BadInput(err) => write!(f, "{}", err),
|
||||
|
||||
Self::UnknownOperator(s) => write!(f, "Unknown operator: '{}'", s),
|
||||
|
||||
Self::MalformedCallExpr(s) => match s.as_str() {
|
||||
"" => f.write_str("Invalid expression in function call arguments"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
Self::MalformedIndexExpr(s) => match s.as_str() {
|
||||
"" => f.write_str("Invalid index in indexing expression"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
Self::MalformedInExpr(s) => match s.as_str() {
|
||||
"" => f.write_str("Invalid 'in' expression"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
Self::MalformedCapture(s) => match s.as_str() {
|
||||
"" => f.write_str("Invalid capturing"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
|
||||
Self::FnDuplicatedDefinition(s, n) => {
|
||||
write!(f, "Function '{}' with ", s)?;
|
||||
match n {
|
||||
0 => f.write_str("no parameters already exists"),
|
||||
1 => f.write_str("1 parameter already exists"),
|
||||
_ => write!(f, "{} parameters already exists", n),
|
||||
}
|
||||
}
|
||||
Self::FnMissingBody(s) => match s.as_str() {
|
||||
"" => f.write_str("Expecting body statement block for anonymous function"),
|
||||
s => write!(f, "Expecting body statement block for function '{}'", s)
|
||||
},
|
||||
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s),
|
||||
Self::FnDuplicatedParam(s, arg) => write!(f, "Duplicated parameter '{}' for function '{}'", arg, s),
|
||||
|
||||
Self::DuplicatedProperty(s) => write!(f, "Duplicated property '{}' for object map literal", s),
|
||||
Self::DuplicatedSwitchCase => f.write_str("Duplicated switch case"),
|
||||
Self::DuplicatedVariable(s) => write!(f, "Duplicated variable name '{}'", s),
|
||||
|
||||
Self::MismatchedType(r, a) => write!(f, "Expecting {}, not {}", r, a),
|
||||
Self::ExprExpected(s) => write!(f, "Expecting {} expression", s),
|
||||
Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s),
|
||||
|
||||
Self::MissingSymbol(s) if s.is_empty() => f.write_str("Expecting a symbol"),
|
||||
Self::MissingSymbol(s) => f.write_str(s),
|
||||
|
||||
Self::AssignmentToConstant(s) => match s.as_str() {
|
||||
"" => f.write_str("Cannot assign to a constant value"),
|
||||
s => write!(f, "Cannot assign to constant '{}'", s)
|
||||
},
|
||||
Self::AssignmentToInvalidLHS(s) => match s.as_str() {
|
||||
"" => f.write_str("Expression cannot be assigned to"),
|
||||
s => f.write_str(s)
|
||||
},
|
||||
|
||||
Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max),
|
||||
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
||||
Self::UnexpectedEOF => f.write_str("Script is incomplete"),
|
||||
Self::WrongSwitchDefaultCase => f.write_str("Default switch case is not the last"),
|
||||
Self::WrongSwitchCaseCondition => f.write_str("Default switch case cannot have condition"),
|
||||
Self::PropertyExpected => f.write_str("Expecting name of a property"),
|
||||
Self::VariableExpected => f.write_str("Expecting name of a variable"),
|
||||
Self::WrongFnDefinition => f.write_str("Function definitions must be at global level and cannot be inside a block or another function"),
|
||||
Self::FnMissingName => f.write_str("Expecting function name in function declaration"),
|
||||
Self::WrongDocComment => f.write_str("Doc-comment must be followed immediately by a function definition"),
|
||||
Self::WrongExport => f.write_str("Export statement can only appear at global level"),
|
||||
Self::ExprTooDeep => f.write_str("Expression exceeds maximum complexity"),
|
||||
Self::LoopBreak => f.write_str("Break statement should only be used inside a loop"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LexError> for ParseErrorType {
|
||||
#[inline(never)]
|
||||
fn from(err: LexError) -> Self {
|
||||
match err {
|
||||
LexError::StringTooLong(max) => {
|
||||
Self::LiteralTooLarge("Length of string literal".to_string(), max)
|
||||
}
|
||||
_ => Self::BadInput(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error when parsing a script.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
|
||||
pub struct ParseError(pub Box<ParseErrorType>, pub Position);
|
||||
|
||||
impl Error for ParseError {}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)?;
|
||||
|
||||
// Do not write any position if None
|
||||
if !self.1.is_none() {
|
||||
write!(f, " ({})", self.1)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseErrorType> for Box<EvalAltResult> {
|
||||
#[inline(always)]
|
||||
fn from(err: ParseErrorType) -> Self {
|
||||
Box::new(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseErrorType> for EvalAltResult {
|
||||
#[inline(always)]
|
||||
fn from(err: ParseErrorType) -> Self {
|
||||
EvalAltResult::ErrorParsing(err, Position::NONE)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for Box<EvalAltResult> {
|
||||
#[inline(always)]
|
||||
fn from(err: ParseError) -> Self {
|
||||
Box::new(err.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for EvalAltResult {
|
||||
#[inline(always)]
|
||||
fn from(err: ParseError) -> Self {
|
||||
EvalAltResult::ErrorParsing(*err.0, err.1)
|
||||
}
|
||||
}
|
634
src/types/scope.rs
Normal file
634
src/types/scope.rs
Normal file
@@ -0,0 +1,634 @@
|
||||
//! Module that defines the [`Scope`] type representing a function call-stack scope.
|
||||
|
||||
use super::dynamic::{AccessMode, Variant};
|
||||
use crate::{Dynamic, Identifier, StaticVec};
|
||||
use std::iter::FromIterator;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
use std::{borrow::Cow, iter::Extend};
|
||||
|
||||
/// Keep a number of entries inline (since [`Dynamic`] is usually small enough).
|
||||
const SCOPE_ENTRIES_INLINED: usize = 8;
|
||||
|
||||
/// Type containing information about the current scope.
|
||||
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
|
||||
///
|
||||
/// # Thread Safety
|
||||
///
|
||||
/// Currently, [`Scope`] is neither [`Send`] nor [`Sync`].
|
||||
/// Turn on the `sync` feature to make it [`Send`] `+` [`Sync`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
/// use rhai::{Engine, Scope};
|
||||
///
|
||||
/// let engine = Engine::new();
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("z", 40_i64);
|
||||
///
|
||||
/// engine.eval_with_scope::<()>(&mut my_scope, "let x = z + 1; z = 0;")?;
|
||||
///
|
||||
/// assert_eq!(engine.eval_with_scope::<i64>(&mut my_scope, "x + 1")?, 42);
|
||||
///
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 41);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("z").expect("z should exist"), 0);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// When searching for entries, newly-added entries are found before similarly-named but older entries,
|
||||
/// allowing for automatic _shadowing_.
|
||||
//
|
||||
// # Implementation Notes
|
||||
//
|
||||
// [`Scope`] is implemented as two [`Vec`]'s of exactly the same length. Variables data (name, type, etc.)
|
||||
// is manually split into two equal-length arrays. That's because variable names take up the most space,
|
||||
// with [`Cow<str>`][Cow] being four words long, but in the vast majority of cases the name is NOT used to
|
||||
// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
|
||||
//
|
||||
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
||||
#[derive(Debug, Clone, Hash, Default)]
|
||||
pub struct Scope<'a> {
|
||||
/// Current value of the entry.
|
||||
values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
|
||||
/// (Name, aliases) of the entry.
|
||||
names: smallvec::SmallVec<
|
||||
[(Cow<'a, str>, Option<Box<StaticVec<Identifier>>>); SCOPE_ENTRIES_INLINED],
|
||||
>,
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for Scope<'a> {
|
||||
type Item = (Cow<'a, str>, Dynamic);
|
||||
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Box::new(
|
||||
self.values
|
||||
.into_iter()
|
||||
.zip(self.names.into_iter())
|
||||
.map(|(value, (name, _))| (name, value)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
/// Create a new [`Scope`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
/// Empty the [`Scope`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert!(my_scope.contains("x"));
|
||||
/// assert_eq!(my_scope.len(), 1);
|
||||
/// assert!(!my_scope.is_empty());
|
||||
///
|
||||
/// my_scope.clear();
|
||||
/// assert!(!my_scope.contains("x"));
|
||||
/// assert_eq!(my_scope.len(), 0);
|
||||
/// assert!(my_scope.is_empty());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
self.names.clear();
|
||||
self.values.clear();
|
||||
self
|
||||
}
|
||||
/// Get the number of entries inside the [`Scope`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
/// assert_eq!(my_scope.len(), 0);
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.len(), 1);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn len(&self) -> usize {
|
||||
self.values.len()
|
||||
}
|
||||
/// Is the [`Scope`] empty?
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
/// assert!(my_scope.is_empty());
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert!(!my_scope.is_empty());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.is_empty()
|
||||
}
|
||||
/// Add (push) a new entry to the [`Scope`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn push(
|
||||
&mut self,
|
||||
name: impl Into<Cow<'a, str>>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, AccessMode::ReadWrite, Dynamic::from(value))
|
||||
}
|
||||
/// Add (push) a new [`Dynamic`] entry to the [`Scope`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Dynamic, Scope};
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push_dynamic("x", Dynamic::from(42_i64));
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn push_dynamic(&mut self, name: impl Into<Cow<'a, str>>, value: Dynamic) -> &mut Self {
|
||||
self.push_dynamic_value(name, value.access_mode(), value)
|
||||
}
|
||||
/// Add (push) a new constant to the [`Scope`].
|
||||
///
|
||||
/// Constants are immutable and cannot be assigned to. Their values never change.
|
||||
/// Constants propagation is a technique used to optimize an [`AST`][crate::AST].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push_constant("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn push_constant(
|
||||
&mut self,
|
||||
name: impl Into<Cow<'a, str>>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, AccessMode::ReadOnly, Dynamic::from(value))
|
||||
}
|
||||
/// Add (push) a new constant with a [`Dynamic`] value to the Scope.
|
||||
///
|
||||
/// Constants are immutable and cannot be assigned to. Their values never change.
|
||||
/// Constants propagation is a technique used to optimize an [`AST`][crate::AST].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Dynamic, Scope};
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push_constant_dynamic("x", Dynamic::from(42_i64));
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn push_constant_dynamic(
|
||||
&mut self,
|
||||
name: impl Into<Cow<'a, str>>,
|
||||
value: Dynamic,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, AccessMode::ReadOnly, value)
|
||||
}
|
||||
/// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`].
|
||||
#[inline]
|
||||
pub(crate) fn push_dynamic_value(
|
||||
&mut self,
|
||||
name: impl Into<Cow<'a, str>>,
|
||||
access: AccessMode,
|
||||
mut value: Dynamic,
|
||||
) -> &mut Self {
|
||||
self.names.push((name.into(), None));
|
||||
value.set_access_mode(access);
|
||||
self.values.push(value);
|
||||
self
|
||||
}
|
||||
/// Truncate (rewind) the [`Scope`] to a previous size.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// my_scope.push("y", 123_i64);
|
||||
/// assert!(my_scope.contains("x"));
|
||||
/// assert!(my_scope.contains("y"));
|
||||
/// assert_eq!(my_scope.len(), 2);
|
||||
///
|
||||
/// my_scope.rewind(1);
|
||||
/// assert!(my_scope.contains("x"));
|
||||
/// assert!(!my_scope.contains("y"));
|
||||
/// assert_eq!(my_scope.len(), 1);
|
||||
///
|
||||
/// my_scope.rewind(0);
|
||||
/// assert!(!my_scope.contains("x"));
|
||||
/// assert!(!my_scope.contains("y"));
|
||||
/// assert_eq!(my_scope.len(), 0);
|
||||
/// assert!(my_scope.is_empty());
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
||||
self.names.truncate(size);
|
||||
self.values.truncate(size);
|
||||
self
|
||||
}
|
||||
/// Does the [`Scope`] contain the entry?
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert!(my_scope.contains("x"));
|
||||
/// assert!(!my_scope.contains("y"));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn contains(&self, name: &str) -> bool {
|
||||
self.names
|
||||
.iter()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.any(|(key, _)| name == key.as_ref())
|
||||
}
|
||||
/// Find an entry in the [`Scope`], starting from the last.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessMode)> {
|
||||
self.names
|
||||
.iter()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.enumerate()
|
||||
.find_map(|(index, (key, _))| {
|
||||
if name == key.as_ref() {
|
||||
let index = self.len() - 1 - index;
|
||||
Some((index, self.values[index].access_mode()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
/// Get the value of an entry in the [`Scope`], starting from the last.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_value<T: Variant + Clone>(&self, name: &str) -> Option<T> {
|
||||
self.names
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find(|(_, (key, _))| name == key.as_ref())
|
||||
.and_then(|(index, _)| {
|
||||
self.values[self.len() - 1 - index]
|
||||
.flatten_clone()
|
||||
.try_cast()
|
||||
})
|
||||
}
|
||||
/// Check if the named entry in the [`Scope`] is constant.
|
||||
///
|
||||
/// Search starts backwards from the last, stopping at the first entry matching the specified name.
|
||||
///
|
||||
/// Returns [`None`] if no entry matching the specified name is found.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push_constant("x", 42_i64);
|
||||
/// assert_eq!(my_scope.is_constant("x"), Some(true));
|
||||
/// assert_eq!(my_scope.is_constant("y"), None);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_constant(&self, name: &str) -> Option<bool> {
|
||||
self.get_index(name).and_then(|(_, access)| match access {
|
||||
AccessMode::ReadWrite => None,
|
||||
AccessMode::ReadOnly => Some(true),
|
||||
})
|
||||
}
|
||||
/// Update the value of the named entry in the [`Scope`] if it already exists and is not constant.
|
||||
/// Push a new entry with the value into the [`Scope`] if the name doesn't exist or if the
|
||||
/// existing entry is constant.
|
||||
///
|
||||
/// Search starts backwards from the last, and only the first entry matching the specified name is updated.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.set_or_push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
/// assert_eq!(my_scope.len(), 1);
|
||||
///
|
||||
/// my_scope.set_or_push("x", 0_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 0);
|
||||
/// assert_eq!(my_scope.len(), 1);
|
||||
///
|
||||
/// my_scope.set_or_push("y", 123_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("y").expect("y should exist"), 123);
|
||||
/// assert_eq!(my_scope.len(), 2);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn set_or_push(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Cow<'a, str>>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
match self.get_index(name.as_ref()) {
|
||||
None | Some((_, AccessMode::ReadOnly)) => {
|
||||
self.push(name, value);
|
||||
}
|
||||
Some((index, AccessMode::ReadWrite)) => {
|
||||
let value_ref = self.values.get_mut(index).expect("valid index");
|
||||
*value_ref = Dynamic::from(value);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Update the value of the named entry in the [`Scope`].
|
||||
///
|
||||
/// Search starts backwards from the last, and only the first entry matching the specified name is updated.
|
||||
/// If no entry matching the specified name is found, a new one is added.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when trying to update the value of a constant.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
///
|
||||
/// my_scope.set_value("x", 0_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 0);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn set_value(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Cow<'a, str>>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
match self.get_index(name.as_ref()) {
|
||||
None => {
|
||||
self.push(name, value);
|
||||
}
|
||||
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name.as_ref()),
|
||||
Some((index, AccessMode::ReadWrite)) => {
|
||||
let value_ref = self.values.get_mut(index).expect("valid index");
|
||||
*value_ref = Dynamic::from(value);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Get a mutable reference to an entry in the [`Scope`].
|
||||
///
|
||||
/// If the entry by the specified name is not found, of if it is read-only,
|
||||
/// [`None`] is returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Scope;
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
|
||||
///
|
||||
/// let ptr = my_scope.get_mut("x").expect("x should exist");
|
||||
/// *ptr = 123_i64.into();
|
||||
///
|
||||
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 123);
|
||||
///
|
||||
/// my_scope.push_constant("Z", 1_i64);
|
||||
/// assert!(my_scope.get_mut("Z").is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn get_mut(&mut self, name: &str) -> Option<&mut Dynamic> {
|
||||
self.get_index(name)
|
||||
.and_then(move |(index, access)| match access {
|
||||
AccessMode::ReadWrite => Some(self.get_mut_by_index(index)),
|
||||
AccessMode::ReadOnly => None,
|
||||
})
|
||||
}
|
||||
/// Get a mutable reference to an entry in the [`Scope`] based on the index.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the index is out of bounds.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn get_mut_by_index(&mut self, index: usize) -> &mut Dynamic {
|
||||
self.values.get_mut(index).expect("valid index")
|
||||
}
|
||||
/// 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]
|
||||
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
||||
let (_, aliases) = self.names.get_mut(index).expect("valid index");
|
||||
match aliases {
|
||||
None => {
|
||||
let mut list = StaticVec::new();
|
||||
list.push(alias);
|
||||
*aliases = Some(list.into());
|
||||
}
|
||||
Some(aliases) if !aliases.iter().any(|a| a == &alias) => aliases.push(alias),
|
||||
Some(_) => (),
|
||||
}
|
||||
self
|
||||
}
|
||||
/// Clone the [`Scope`], keeping only the last instances of each variable name.
|
||||
/// Shadowed variables are omitted in the copy.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub(crate) fn clone_visible(&self) -> Self {
|
||||
let mut entries = Self::new();
|
||||
|
||||
self.names
|
||||
.iter()
|
||||
.rev()
|
||||
.enumerate()
|
||||
.for_each(|(index, (name, alias))| {
|
||||
if !entries.names.iter().any(|(key, _)| key == name) {
|
||||
entries.names.push((name.clone(), alias.clone()));
|
||||
entries
|
||||
.values
|
||||
.push(self.values[self.len() - 1 - index].clone());
|
||||
}
|
||||
});
|
||||
|
||||
entries
|
||||
}
|
||||
/// Get an iterator to entries in the [`Scope`].
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn into_iter(
|
||||
self,
|
||||
) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<Identifier>)> {
|
||||
self.names
|
||||
.into_iter()
|
||||
.zip(self.values.into_iter())
|
||||
.map(|((name, alias), value)| {
|
||||
(name, value, alias.map(|a| a.to_vec()).unwrap_or_default())
|
||||
})
|
||||
}
|
||||
/// Get an iterator to entries in the [`Scope`].
|
||||
/// Shared values are flatten-cloned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::{Dynamic, Scope};
|
||||
///
|
||||
/// let mut my_scope = Scope::new();
|
||||
///
|
||||
/// my_scope.push("x", 42_i64);
|
||||
/// my_scope.push_constant("foo", "hello");
|
||||
///
|
||||
/// let mut iter = my_scope.iter();
|
||||
///
|
||||
/// let (name, is_constant, value) = iter.next().expect("value should exist");
|
||||
/// assert_eq!(name, "x");
|
||||
/// assert!(!is_constant);
|
||||
/// assert_eq!(value.cast::<i64>(), 42);
|
||||
///
|
||||
/// let (name, is_constant, value) = iter.next().expect("value should exist");
|
||||
/// assert_eq!(name, "foo");
|
||||
/// assert!(is_constant);
|
||||
/// assert_eq!(value.cast::<String>(), "hello");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&str, bool, Dynamic)> {
|
||||
self.iter_raw()
|
||||
.map(|(name, constant, value)| (name, constant, value.flatten_clone()))
|
||||
}
|
||||
/// Get an iterator to entries in the [`Scope`].
|
||||
/// Shared values are not expanded.
|
||||
#[inline]
|
||||
pub fn iter_raw(&self) -> impl Iterator<Item = (&str, bool, &Dynamic)> {
|
||||
self.names
|
||||
.iter()
|
||||
.zip(self.values.iter())
|
||||
.map(|((name, _), value)| (name.as_ref(), value.is_read_only(), value))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Into<Cow<'a, str>>> Extend<(K, Dynamic)> for Scope<'a> {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, value)| {
|
||||
self.push_dynamic_value(name, AccessMode::ReadWrite, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Into<Cow<'a, str>>> FromIterator<(K, Dynamic)> for Scope<'a> {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = (K, Dynamic)>>(iter: T) -> Self {
|
||||
let mut scope = Self::new();
|
||||
scope.extend(iter);
|
||||
scope
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Into<Cow<'a, str>>> Extend<(K, bool, Dynamic)> for Scope<'a> {
|
||||
#[inline]
|
||||
fn extend<T: IntoIterator<Item = (K, bool, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, is_constant, value)| {
|
||||
self.push_dynamic_value(
|
||||
name,
|
||||
if is_constant {
|
||||
AccessMode::ReadOnly
|
||||
} else {
|
||||
AccessMode::ReadWrite
|
||||
},
|
||||
value,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, K: Into<Cow<'a, str>>> FromIterator<(K, bool, Dynamic)> for Scope<'a> {
|
||||
#[inline]
|
||||
fn from_iter<T: IntoIterator<Item = (K, bool, Dynamic)>>(iter: T) -> Self {
|
||||
let mut scope = Self::new();
|
||||
scope.extend(iter);
|
||||
scope
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user