Remove hashing of the entire script by making Expr and Stmt Hash.
This commit is contained in:
parent
e2a47b2a65
commit
cf9d35166d
176
src/ast.rs
176
src/ast.rs
@ -8,14 +8,13 @@ use crate::stdlib::{
|
|||||||
boxed::Box,
|
boxed::Box,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt,
|
fmt,
|
||||||
hash::Hash,
|
hash::{Hash, Hasher},
|
||||||
num::{NonZeroU64, NonZeroUsize},
|
num::{NonZeroU64, NonZeroUsize},
|
||||||
ops::{Add, AddAssign},
|
ops::{Add, AddAssign, Deref, DerefMut},
|
||||||
string::String,
|
string::String,
|
||||||
vec,
|
vec,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use crate::syntax::FnCustomSyntaxEval;
|
|
||||||
use crate::token::Token;
|
use crate::token::Token;
|
||||||
use crate::utils::StraightHasherBuilder;
|
use crate::utils::StraightHasherBuilder;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -492,11 +491,7 @@ impl AST {
|
|||||||
(true, true) => vec![],
|
(true, true) => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let source = if other.source.is_some() {
|
let source = other.source.clone().or_else(|| self.source.clone());
|
||||||
other.source.clone()
|
|
||||||
} else {
|
|
||||||
self.source.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut functions = functions.as_ref().clone();
|
let mut functions = functions.as_ref().clone();
|
||||||
functions.merge_filtered(&other.functions, &mut filter);
|
functions.merge_filtered(&other.functions, &mut filter);
|
||||||
@ -696,24 +691,68 @@ pub enum ReturnType {
|
|||||||
Exception,
|
Exception,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type that wraps a [`HashMap`]`<u64, Stmt>` for the `switch` statement and implements [`Hash`].
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SwitchHashWrapper(HashMap<u64, Stmt, StraightHasherBuilder>);
|
||||||
|
|
||||||
|
impl From<HashMap<u64, Stmt, StraightHasherBuilder>> for SwitchHashWrapper {
|
||||||
|
fn from(value: HashMap<u64, Stmt, StraightHasherBuilder>) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsRef<HashMap<u64, Stmt, StraightHasherBuilder>> for SwitchHashWrapper {
|
||||||
|
fn as_ref(&self) -> &HashMap<u64, Stmt, StraightHasherBuilder> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl AsMut<HashMap<u64, Stmt, StraightHasherBuilder>> for SwitchHashWrapper {
|
||||||
|
fn as_mut(&mut self) -> &mut HashMap<u64, Stmt, StraightHasherBuilder> {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Deref for SwitchHashWrapper {
|
||||||
|
type Target = HashMap<u64, Stmt, StraightHasherBuilder>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for SwitchHashWrapper {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl fmt::Debug for SwitchHashWrapper {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Hash for SwitchHashWrapper {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
let mut keys: Vec<_> = self.0.keys().collect();
|
||||||
|
keys.sort();
|
||||||
|
|
||||||
|
keys.into_iter().for_each(|key| {
|
||||||
|
key.hash(state);
|
||||||
|
self.0.get(&key).unwrap().hash(state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ A statement.
|
/// _(INTERNALS)_ A statement.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// # WARNING
|
/// # WARNING
|
||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
/// No-op.
|
/// No-op.
|
||||||
Noop(Position),
|
Noop(Position),
|
||||||
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
||||||
If(Expr, Box<(Stmt, Option<Stmt>)>, Position),
|
If(Expr, Box<(Stmt, Option<Stmt>)>, Position),
|
||||||
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
||||||
Switch(
|
Switch(Expr, Box<(SwitchHashWrapper, Option<Stmt>)>, Position),
|
||||||
Expr,
|
|
||||||
Box<(HashMap<u64, Stmt, StraightHasherBuilder>, Option<Stmt>)>,
|
|
||||||
Position,
|
|
||||||
),
|
|
||||||
/// `while` expr `{` stmt `}`
|
/// `while` expr `{` stmt `}`
|
||||||
While(Expr, Box<Stmt>, Position),
|
While(Expr, Box<Stmt>, Position),
|
||||||
/// `do` `{` stmt `}` `while`|`until` expr
|
/// `do` `{` stmt `}` `while`|`until` expr
|
||||||
@ -899,10 +938,8 @@ impl Stmt {
|
|||||||
/// # WARNING
|
/// # WARNING
|
||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct CustomExpr {
|
pub struct CustomExpr {
|
||||||
/// Implementation function.
|
|
||||||
pub func: Shared<FnCustomSyntaxEval>,
|
|
||||||
/// List of keywords.
|
/// List of keywords.
|
||||||
pub keywords: StaticVec<Expr>,
|
pub keywords: StaticVec<Expr>,
|
||||||
/// List of tokens actually parsed.
|
/// List of tokens actually parsed.
|
||||||
@ -926,7 +963,7 @@ impl fmt::Debug for CustomExpr {
|
|||||||
/// # WARNING
|
/// # WARNING
|
||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub struct BinaryExpr {
|
pub struct BinaryExpr {
|
||||||
/// LHS expression.
|
/// LHS expression.
|
||||||
pub lhs: Expr,
|
pub lhs: Expr,
|
||||||
@ -940,7 +977,7 @@ pub struct BinaryExpr {
|
|||||||
/// # WARNING
|
/// # WARNING
|
||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default, Hash)]
|
||||||
pub struct FnCallExpr {
|
pub struct FnCallExpr {
|
||||||
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
|
/// Pre-calculated hash for a script-defined function of the same name and number of parameters.
|
||||||
/// None if native Rust only.
|
/// None if native Rust only.
|
||||||
@ -959,13 +996,83 @@ pub struct FnCallExpr {
|
|||||||
pub args: StaticVec<Expr>,
|
pub args: StaticVec<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type that wraps a [`FLOAT`] and implements [`Hash`].
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct FloatWrapper(FLOAT);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl Hash for FloatWrapper {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.to_le_bytes().hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl AsRef<FLOAT> for FloatWrapper {
|
||||||
|
fn as_ref(&self) -> &FLOAT {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl AsMut<FLOAT> for FloatWrapper {
|
||||||
|
fn as_mut(&mut self) -> &mut FLOAT {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl Deref for FloatWrapper {
|
||||||
|
type Target = FLOAT;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl DerefMut for FloatWrapper {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl fmt::Debug for FloatWrapper {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl fmt::Display for FloatWrapper {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl From<FLOAT> for FloatWrapper {
|
||||||
|
fn from(value: FLOAT) -> Self {
|
||||||
|
Self::new(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl FloatWrapper {
|
||||||
|
pub const fn new(value: FLOAT) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ An expression sub-tree.
|
/// _(INTERNALS)_ An expression sub-tree.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// # WARNING
|
/// # WARNING
|
||||||
///
|
///
|
||||||
/// This type is volatile and may change.
|
/// This type is volatile and may change.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
/// Dynamic constant.
|
/// Dynamic constant.
|
||||||
/// Used to hold either an [`Array`] or [`Map`] literal for quick cloning.
|
/// Used to hold either an [`Array`] or [`Map`] literal for quick cloning.
|
||||||
@ -977,7 +1084,7 @@ pub enum Expr {
|
|||||||
IntegerConstant(INT, Position),
|
IntegerConstant(INT, Position),
|
||||||
/// Floating-point constant.
|
/// Floating-point constant.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
FloatConstant(FLOAT, Position),
|
FloatConstant(FloatWrapper, Position),
|
||||||
/// Character constant.
|
/// Character constant.
|
||||||
CharConstant(char, Position),
|
CharConstant(char, Position),
|
||||||
/// [String][ImmutableString] constant.
|
/// [String][ImmutableString] constant.
|
||||||
@ -1250,19 +1357,20 @@ mod tests {
|
|||||||
/// This test is to make sure no code changes increase the sizes of critical data structures.
|
/// This test is to make sure no code changes increase the sizes of critical data structures.
|
||||||
#[test]
|
#[test]
|
||||||
fn check_struct_sizes() {
|
fn check_struct_sizes() {
|
||||||
use std::mem::size_of;
|
use crate::stdlib::mem::size_of;
|
||||||
|
use crate::*;
|
||||||
|
|
||||||
assert_eq!(size_of::<crate::Dynamic>(), 16);
|
assert_eq!(size_of::<Dynamic>(), 16);
|
||||||
assert_eq!(size_of::<Option<crate::Dynamic>>(), 16);
|
assert_eq!(size_of::<Option<Dynamic>>(), 16);
|
||||||
assert_eq!(size_of::<crate::Position>(), 4);
|
assert_eq!(size_of::<Position>(), 4);
|
||||||
assert_eq!(size_of::<crate::ast::Expr>(), 16);
|
assert_eq!(size_of::<ast::Expr>(), 16);
|
||||||
assert_eq!(size_of::<Option<crate::ast::Expr>>(), 16);
|
assert_eq!(size_of::<Option<ast::Expr>>(), 16);
|
||||||
assert_eq!(size_of::<crate::ast::Stmt>(), 32);
|
assert_eq!(size_of::<ast::Stmt>(), 32);
|
||||||
assert_eq!(size_of::<Option<crate::ast::Stmt>>(), 32);
|
assert_eq!(size_of::<Option<ast::Stmt>>(), 32);
|
||||||
assert_eq!(size_of::<crate::FnPtr>(), 32);
|
assert_eq!(size_of::<FnPtr>(), 32);
|
||||||
assert_eq!(size_of::<crate::Scope>(), 48);
|
assert_eq!(size_of::<Scope>(), 48);
|
||||||
assert_eq!(size_of::<crate::LexError>(), 56);
|
assert_eq!(size_of::<LexError>(), 56);
|
||||||
assert_eq!(size_of::<crate::ParseError>(), 16);
|
assert_eq!(size_of::<ParseError>(), 16);
|
||||||
assert_eq!(size_of::<crate::EvalAltResult>(), 72);
|
assert_eq!(size_of::<EvalAltResult>(), 72);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,13 @@ use crate::stdlib::{
|
|||||||
boxed::Box,
|
boxed::Box,
|
||||||
fmt,
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
string::String,
|
string::String,
|
||||||
};
|
};
|
||||||
use crate::{FnPtr, ImmutableString, INT};
|
use crate::{FnPtr, ImmutableString, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::{ast::FloatWrapper, FLOAT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
@ -155,7 +154,7 @@ pub enum Union {
|
|||||||
Char(char, AccessMode),
|
Char(char, AccessMode),
|
||||||
Int(INT, AccessMode),
|
Int(INT, AccessMode),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Float(FLOAT, AccessMode),
|
Float(FloatWrapper, AccessMode),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Array(Box<Array>, AccessMode),
|
Array(Box<Array>, AccessMode),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -362,8 +361,6 @@ impl Dynamic {
|
|||||||
|
|
||||||
impl Hash for Dynamic {
|
impl Hash for Dynamic {
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
mem::discriminant(self).hash(state);
|
|
||||||
|
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Unit(_, _) => ().hash(state),
|
Union::Unit(_, _) => ().hash(state),
|
||||||
Union::Bool(value, _) => value.hash(state),
|
Union::Bool(value, _) => value.hash(state),
|
||||||
@ -371,7 +368,7 @@ impl Hash for Dynamic {
|
|||||||
Union::Char(ch, _) => ch.hash(state),
|
Union::Char(ch, _) => ch.hash(state),
|
||||||
Union::Int(i, _) => i.hash(state),
|
Union::Int(i, _) => i.hash(state),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(f, _) => f.to_le_bytes().hash(state),
|
Union::Float(f, _) => f.hash(state),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(a, _) => (**a).hash(state),
|
Union::Array(a, _) => (**a).hash(state),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -559,13 +556,16 @@ impl Dynamic {
|
|||||||
pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, AccessMode::ReadWrite));
|
pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, AccessMode::ReadWrite));
|
||||||
/// A [`Dynamic`] containing the floating-point zero.
|
/// A [`Dynamic`] containing the floating-point zero.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_ZERO: Dynamic = Self(Union::Float(0.0, AccessMode::ReadWrite));
|
pub const FLOAT_ZERO: Dynamic =
|
||||||
|
Self(Union::Float(FloatWrapper::new(0.0), AccessMode::ReadWrite));
|
||||||
/// A [`Dynamic`] containing the floating-point one.
|
/// A [`Dynamic`] containing the floating-point one.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_ONE: Dynamic = Self(Union::Float(1.0, AccessMode::ReadWrite));
|
pub const FLOAT_ONE: Dynamic =
|
||||||
|
Self(Union::Float(FloatWrapper::new(1.0), AccessMode::ReadWrite));
|
||||||
/// A [`Dynamic`] containing the floating-point negative one.
|
/// A [`Dynamic`] containing the floating-point negative one.
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(-1.0, AccessMode::ReadWrite));
|
pub const FLOAT_NEGATIVE_ONE: Dynamic =
|
||||||
|
Self(Union::Float(FloatWrapper::new(-1.0), AccessMode::ReadWrite));
|
||||||
|
|
||||||
/// Get the [`AccessMode`] for this [`Dynamic`].
|
/// Get the [`AccessMode`] for this [`Dynamic`].
|
||||||
pub(crate) fn access_mode(&self) -> AccessMode {
|
pub(crate) fn access_mode(&self) -> AccessMode {
|
||||||
@ -836,7 +836,7 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Float(value, _) => unsafe_try_cast(value),
|
Union::Float(value, _) => unsafe_try_cast(*value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1105,7 +1105,7 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Float(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Float(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1194,7 +1194,7 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Float(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Float(value, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1277,7 +1277,7 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Float(n, _) => Ok(n),
|
Union::Float(n, _) => Ok(*n),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
@ -1380,6 +1380,13 @@ impl From<INT> for Dynamic {
|
|||||||
impl From<FLOAT> for Dynamic {
|
impl From<FLOAT> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: FLOAT) -> Self {
|
fn from(value: FLOAT) -> Self {
|
||||||
|
Self(Union::Float(value.into(), AccessMode::ReadWrite))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
impl From<FloatWrapper> for Dynamic {
|
||||||
|
#[inline(always)]
|
||||||
|
fn from(value: FloatWrapper) -> Self {
|
||||||
Self(Union::Float(value, AccessMode::ReadWrite))
|
Self(Union::Float(value, AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1923,6 +1923,10 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect::<StaticVec<_>>();
|
.collect::<StaticVec<_>>();
|
||||||
|
let custom_def = self
|
||||||
|
.custom_syntax
|
||||||
|
.get(custom.tokens.first().unwrap())
|
||||||
|
.unwrap();
|
||||||
let mut context = EvalContext {
|
let mut context = EvalContext {
|
||||||
engine: self,
|
engine: self,
|
||||||
scope,
|
scope,
|
||||||
@ -1932,7 +1936,7 @@ impl Engine {
|
|||||||
this_ptr,
|
this_ptr,
|
||||||
level,
|
level,
|
||||||
};
|
};
|
||||||
(custom.func)(&mut context, &expressions)
|
(custom_def.func)(&mut context, &expressions)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
_ => unreachable!("expression cannot be evaluated: {:?}", expr),
|
||||||
@ -2072,11 +2076,7 @@ impl Engine {
|
|||||||
let args = &mut [lhs_ptr_inner, &mut rhs_val];
|
let args = &mut [lhs_ptr_inner, &mut rhs_val];
|
||||||
|
|
||||||
// Overriding exact implementation
|
// Overriding exact implementation
|
||||||
let source = if source.is_none() {
|
let source = source.or_else(|| state.source.as_ref());
|
||||||
state.source.as_ref()
|
|
||||||
} else {
|
|
||||||
source
|
|
||||||
};
|
|
||||||
if func.is_plugin_fn() {
|
if func.is_plugin_fn() {
|
||||||
func.get_plugin_fn()
|
func.get_plugin_fn()
|
||||||
.call((self, source, &*mods, lib).into(), args)?;
|
.call((self, source, &*mods, lib).into(), args)?;
|
||||||
@ -2130,14 +2130,13 @@ impl Engine {
|
|||||||
&mut rhs_val,
|
&mut rhs_val,
|
||||||
];
|
];
|
||||||
|
|
||||||
let result = self
|
Some(
|
||||||
.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, op, None, args, false, false, false, *op_pos, None,
|
mods, state, lib, op, None, args, false, false, false, *op_pos, None,
|
||||||
None, level,
|
None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)?;
|
.map(|(v, _)| (v, rhs_expr.position()))?,
|
||||||
|
)
|
||||||
Some((result, rhs_expr.position()))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||||
|
@ -8,11 +8,9 @@ use crate::stdlib::{
|
|||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
format,
|
format,
|
||||||
hash::{Hash, Hasher},
|
|
||||||
string::String,
|
string::String,
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use crate::utils::get_hasher;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
|
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext,
|
||||||
ParseError, Position, Shared, AST,
|
ParseError, Position, Shared, AST,
|
||||||
@ -24,13 +22,6 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
/// Calculate a unique hash for a script.
|
|
||||||
fn calc_hash_for_scripts<'a>(scripts: impl IntoIterator<Item = &'a &'a str>) -> u64 {
|
|
||||||
let s = &mut get_hasher();
|
|
||||||
scripts.into_iter().for_each(|&script| script.hash(s));
|
|
||||||
s.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Engine public API
|
/// Engine public API
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// Register a function of the [`Engine`].
|
/// Register a function of the [`Engine`].
|
||||||
@ -960,9 +951,8 @@ impl Engine {
|
|||||||
scripts: &[&str],
|
scripts: &[&str],
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let hash = calc_hash_for_scripts(scripts);
|
|
||||||
let stream = self.lex(scripts);
|
let stream = self.lex(scripts);
|
||||||
self.parse(hash, &mut stream.peekable(), scope, optimization_level)
|
self.parse(&mut stream.peekable(), scope, optimization_level)
|
||||||
}
|
}
|
||||||
/// Read the contents of a file into a string.
|
/// Read the contents of a file into a string.
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -1123,8 +1113,6 @@ impl Engine {
|
|||||||
.into());
|
.into());
|
||||||
};
|
};
|
||||||
|
|
||||||
let hash = calc_hash_for_scripts(&scripts);
|
|
||||||
|
|
||||||
let stream = self.lex_with_map(
|
let stream = self.lex_with_map(
|
||||||
&scripts,
|
&scripts,
|
||||||
if has_null {
|
if has_null {
|
||||||
@ -1138,12 +1126,8 @@ impl Engine {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast = self.parse_global_expr(
|
let ast =
|
||||||
hash,
|
self.parse_global_expr(&mut stream.peekable(), &scope, OptimizationLevel::None)?;
|
||||||
&mut stream.peekable(),
|
|
||||||
&scope,
|
|
||||||
OptimizationLevel::None,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Handle null - map to ()
|
// Handle null - map to ()
|
||||||
if has_null {
|
if has_null {
|
||||||
@ -1222,11 +1206,10 @@ impl Engine {
|
|||||||
script: &str,
|
script: &str,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let hash = calc_hash_for_scripts(&scripts);
|
|
||||||
let stream = self.lex(&scripts);
|
let stream = self.lex(&scripts);
|
||||||
|
|
||||||
let mut peekable = stream.peekable();
|
let mut peekable = stream.peekable();
|
||||||
self.parse_global_expr(hash, &mut peekable, scope, self.optimization_level)
|
self.parse_global_expr(&mut peekable, scope, self.optimization_level)
|
||||||
}
|
}
|
||||||
/// Evaluate a script file.
|
/// Evaluate a script file.
|
||||||
///
|
///
|
||||||
@ -1384,12 +1367,10 @@ impl Engine {
|
|||||||
script: &str,
|
script: &str,
|
||||||
) -> Result<T, Box<EvalAltResult>> {
|
) -> Result<T, Box<EvalAltResult>> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let hash = calc_hash_for_scripts(&scripts);
|
|
||||||
let stream = self.lex(&scripts);
|
let stream = self.lex(&scripts);
|
||||||
|
|
||||||
// No need to optimize a lone expression
|
// No need to optimize a lone expression
|
||||||
let ast =
|
let ast = self.parse_global_expr(&mut stream.peekable(), scope, OptimizationLevel::None)?;
|
||||||
self.parse_global_expr(hash, &mut stream.peekable(), scope, OptimizationLevel::None)?;
|
|
||||||
|
|
||||||
self.eval_ast_with_scope(scope, &ast)
|
self.eval_ast_with_scope(scope, &ast)
|
||||||
}
|
}
|
||||||
@ -1522,9 +1503,8 @@ impl Engine {
|
|||||||
script: &str,
|
script: &str,
|
||||||
) -> Result<(), Box<EvalAltResult>> {
|
) -> Result<(), Box<EvalAltResult>> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let hash = calc_hash_for_scripts(&scripts);
|
|
||||||
let stream = self.lex(&scripts);
|
let stream = self.lex(&scripts);
|
||||||
let ast = self.parse(hash, &mut stream.peekable(), scope, self.optimization_level)?;
|
let ast = self.parse(&mut stream.peekable(), scope, self.optimization_level)?;
|
||||||
self.consume_ast_with_scope(scope, &ast)
|
self.consume_ast_with_scope(scope, &ast)
|
||||||
}
|
}
|
||||||
/// Evaluate an AST, but throw away the result and only return error (if any).
|
/// Evaluate an AST, but throw away the result and only return error (if any).
|
||||||
|
@ -183,7 +183,10 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni
|
|||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated = "this type is volatile and may change"]
|
#[deprecated = "this type is volatile and may change"]
|
||||||
pub use ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt};
|
pub use ast::{
|
||||||
|
BinaryExpr, CustomExpr, Expr, FloatWrapper, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt,
|
||||||
|
SwitchHashWrapper,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[deprecated = "this type is volatile and may change"]
|
#[deprecated = "this type is volatile and may change"]
|
||||||
|
@ -41,8 +41,6 @@ type FunctionsLib = HashMap<NonZeroU64, ScriptFnDef, StraightHasherBuilder>;
|
|||||||
struct ParseState<'e> {
|
struct ParseState<'e> {
|
||||||
/// Reference to the scripting [`Engine`].
|
/// Reference to the scripting [`Engine`].
|
||||||
engine: &'e Engine,
|
engine: &'e Engine,
|
||||||
/// Hash that uniquely identifies a script.
|
|
||||||
script_hash: u64,
|
|
||||||
/// Interned strings.
|
/// Interned strings.
|
||||||
strings: HashMap<String, ImmutableString>,
|
strings: HashMap<String, ImmutableString>,
|
||||||
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
@ -75,7 +73,6 @@ impl<'e> ParseState<'e> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &'e Engine,
|
engine: &'e Engine,
|
||||||
script_hash: u64,
|
|
||||||
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
|
#[cfg(not(feature = "unchecked"))] max_expr_depth: usize,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -83,7 +80,6 @@ impl<'e> ParseState<'e> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
script_hash,
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
max_expr_depth,
|
max_expr_depth,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -919,7 +915,7 @@ fn parse_switch(
|
|||||||
|
|
||||||
Ok(Stmt::Switch(
|
Ok(Stmt::Switch(
|
||||||
item,
|
item,
|
||||||
Box::new((final_table, def_stmt)),
|
Box::new((final_table.into(), def_stmt)),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -956,7 +952,7 @@ fn parse_primary(
|
|||||||
},
|
},
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Token::FloatConstant(x) => {
|
Token::FloatConstant(x) => {
|
||||||
let x = *x;
|
let x = (*x).into();
|
||||||
input.next().unwrap();
|
input.next().unwrap();
|
||||||
Expr::FloatConstant(x, settings.pos)
|
Expr::FloatConstant(x, settings.pos)
|
||||||
}
|
}
|
||||||
@ -986,7 +982,6 @@ fn parse_primary(
|
|||||||
Token::Pipe | Token::Or if settings.allow_anonymous_fn => {
|
Token::Pipe | Token::Or if settings.allow_anonymous_fn => {
|
||||||
let mut new_state = ParseState::new(
|
let mut new_state = ParseState::new(
|
||||||
state.engine,
|
state.engine,
|
||||||
state.script_hash,
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
state.max_function_expr_depth,
|
state.max_function_expr_depth,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -1284,7 +1279,7 @@ fn parse_unary(
|
|||||||
.map(|i| Expr::IntegerConstant(i, pos))
|
.map(|i| Expr::IntegerConstant(i, pos))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
return Some(Expr::FloatConstant(-(num as FLOAT), pos));
|
return Some(Expr::FloatConstant((-(num as FLOAT)).into(), pos));
|
||||||
#[cfg(feature = "no_float")]
|
#[cfg(feature = "no_float")]
|
||||||
return None;
|
return None;
|
||||||
})
|
})
|
||||||
@ -1292,7 +1287,7 @@ fn parse_unary(
|
|||||||
|
|
||||||
// Negative float
|
// Negative float
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Expr::FloatConstant(x, pos) => Ok(Expr::FloatConstant(-x, pos)),
|
Expr::FloatConstant(x, pos) => Ok(Expr::FloatConstant((-(*x)).into(), pos)),
|
||||||
|
|
||||||
// Call negative function
|
// Call negative function
|
||||||
expr => {
|
expr => {
|
||||||
@ -1998,14 +1993,7 @@ fn parse_custom_syntax(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Expr::Custom(
|
Ok(Expr::Custom(Box::new(CustomExpr { keywords, tokens }), pos))
|
||||||
Box::new(CustomExpr {
|
|
||||||
keywords,
|
|
||||||
func: syntax.func.clone(),
|
|
||||||
tokens,
|
|
||||||
}),
|
|
||||||
pos,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an expression.
|
/// Parse an expression.
|
||||||
@ -2600,7 +2588,6 @@ fn parse_stmt(
|
|||||||
(Token::Fn, pos) => {
|
(Token::Fn, pos) => {
|
||||||
let mut new_state = ParseState::new(
|
let mut new_state = ParseState::new(
|
||||||
state.engine,
|
state.engine,
|
||||||
state.script_hash,
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
state.max_function_expr_depth,
|
state.max_function_expr_depth,
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -3009,10 +2996,10 @@ fn parse_anon_fn(
|
|||||||
params.into_iter().map(|(v, _)| v).collect()
|
params.into_iter().map(|(v, _)| v).collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create unique function name by hashing the script hash plus the position
|
// Create unique function name by hashing the script body plus the parameters.
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
state.script_hash.hash(hasher);
|
params.iter().for_each(|p| p.as_str().hash(hasher));
|
||||||
settings.pos.hash(hasher);
|
body.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
let fn_name: ImmutableString = format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash).into();
|
let fn_name: ImmutableString = format!("{}{:016x}", crate::engine::FN_ANONYMOUS, hash).into();
|
||||||
@ -3045,7 +3032,6 @@ fn parse_anon_fn(
|
|||||||
impl Engine {
|
impl Engine {
|
||||||
pub(crate) fn parse_global_expr(
|
pub(crate) fn parse_global_expr(
|
||||||
&self,
|
&self,
|
||||||
script_hash: u64,
|
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
@ -3053,7 +3039,6 @@ impl Engine {
|
|||||||
let mut functions = Default::default();
|
let mut functions = Default::default();
|
||||||
let mut state = ParseState::new(
|
let mut state = ParseState::new(
|
||||||
self,
|
self,
|
||||||
script_hash,
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.max_expr_depth(),
|
self.max_expr_depth(),
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -3095,14 +3080,12 @@ impl Engine {
|
|||||||
/// Parse the global level statements.
|
/// Parse the global level statements.
|
||||||
fn parse_global_level(
|
fn parse_global_level(
|
||||||
&self,
|
&self,
|
||||||
script_hash: u64,
|
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
|
) -> Result<(Vec<Stmt>, Vec<ScriptFnDef>), ParseError> {
|
||||||
let mut statements = Vec::with_capacity(16);
|
let mut statements = Vec::with_capacity(16);
|
||||||
let mut functions = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder);
|
let mut functions = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder);
|
||||||
let mut state = ParseState::new(
|
let mut state = ParseState::new(
|
||||||
self,
|
self,
|
||||||
script_hash,
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.max_expr_depth(),
|
self.max_expr_depth(),
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -3165,12 +3148,11 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn parse(
|
pub(crate) fn parse(
|
||||||
&self,
|
&self,
|
||||||
script_hash: u64,
|
|
||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
scope: &Scope,
|
scope: &Scope,
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> Result<AST, ParseError> {
|
) -> Result<AST, ParseError> {
|
||||||
let (statements, lib) = self.parse_global_level(script_hash, input)?;
|
let (statements, lib) = self.parse_global_level(input)?;
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
// Optimize AST
|
// Optimize AST
|
||||||
|
Loading…
x
Reference in New Issue
Block a user