Restructure code base.

This commit is contained in:
Stephen Chung
2021-11-13 22:36:23 +08:00
parent 38884ede46
commit 64b889fb95
36 changed files with 154 additions and 140 deletions

2127
src/types/dynamic.rs Normal file

File diff suppressed because it is too large Load Diff

455
src/types/error.rs Normal file
View 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
View 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)
}
}

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