Merge pull request #536 from schungx/master
Scope::set_alias and new sleep function.
This commit is contained in:
commit
b861d6a8fa
@ -17,17 +17,15 @@ Bug fixes
|
|||||||
* Exporting a variable that contains a local function pointer (including anonymous function or closure) now raises a runtime error.
|
* Exporting a variable that contains a local function pointer (including anonymous function or closure) now raises a runtime error.
|
||||||
* Full optimization is now skipped for method calls.
|
* Full optimization is now skipped for method calls.
|
||||||
|
|
||||||
Breaking changes
|
|
||||||
----------------
|
|
||||||
|
|
||||||
* `ScriptFnMetadata` fields `params` and `comments` are changed to boxed slices instead of `Vec`. In the vast majority of cases this should not cause code breakage.
|
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* Variable definitions are optimized so that shadowed variables are reused as much as possible to reduce memory consumption.
|
* Variable definitions are optimized so that shadowed variables are reused as much as possible to reduce memory consumption.
|
||||||
* `FnAccess` and `FnNamespace` now implement `Ord` and `PartialOrd`.
|
* `FnAccess` and `FnNamespace` now implement `Ord` and `PartialOrd`.
|
||||||
* The `event_handler_map` example is enhanced to prevent shadowing of the state object map.
|
* The `event_handler_map` example is enhanced to prevent shadowing of the state object map.
|
||||||
|
* Separation of constants in function calls is removed as its performance benefit is dubious.
|
||||||
|
* A function `sleep` is added to block the current thread by a specified number of seconds.
|
||||||
|
* `Scope::set_alias` is added to export a variable under a particular alias name.
|
||||||
|
|
||||||
|
|
||||||
Version 1.5.0
|
Version 1.5.0
|
||||||
|
@ -72,8 +72,8 @@ impl Expression<'_> {
|
|||||||
pub fn get_string_value(&self) -> Option<&str> {
|
pub fn get_string_value(&self) -> Option<&str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::Variable(.., x) if x.1.is_some() => None,
|
Expr::Variable(x, ..) if !x.1.is_empty() => None,
|
||||||
Expr::Variable(.., x) => Some(x.2.as_str()),
|
Expr::Variable(x, ..) => Some(x.3.as_str()),
|
||||||
Expr::StringConstant(x, ..) => Some(x.as_str()),
|
Expr::StringConstant(x, ..) => Some(x.as_str()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -102,8 +102,8 @@ impl Expression<'_> {
|
|||||||
|
|
||||||
Expr::CharConstant(x, ..) => reify!(*x => Option<T>),
|
Expr::CharConstant(x, ..) => reify!(*x => Option<T>),
|
||||||
Expr::StringConstant(x, ..) => reify!(x.clone() => Option<T>),
|
Expr::StringConstant(x, ..) => reify!(x.clone() => Option<T>),
|
||||||
Expr::Variable(.., x) => {
|
Expr::Variable(x, ..) => {
|
||||||
let x: ImmutableString = x.2.clone().into();
|
let x: ImmutableString = x.3.clone().into();
|
||||||
reify!(x => Option<T>)
|
reify!(x => Option<T>)
|
||||||
}
|
}
|
||||||
Expr::BoolConstant(x, ..) => reify!(*x => Option<T>),
|
Expr::BoolConstant(x, ..) => reify!(*x => Option<T>),
|
||||||
|
@ -293,7 +293,7 @@ impl Engine {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Register an type iterator for an iterable type with the [`Engine`].
|
/// Register an type iterator for an iterable type with the [`Engine`].
|
||||||
/// This is an advanced feature.
|
/// This is an advanced API.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_iterator<T>(&mut self) -> &mut Self
|
pub fn register_iterator<T>(&mut self) -> &mut Self
|
||||||
where
|
where
|
||||||
|
@ -169,24 +169,13 @@ impl FnCallHashes {
|
|||||||
pub struct FnCallExpr {
|
pub struct FnCallExpr {
|
||||||
/// Namespace of the function, if any.
|
/// Namespace of the function, if any.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub namespace: Option<crate::module::Namespace>,
|
pub namespace: super::Namespace,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
/// Pre-calculated hashes.
|
/// Pre-calculated hashes.
|
||||||
pub hashes: FnCallHashes,
|
pub hashes: FnCallHashes,
|
||||||
/// List of function call argument expressions.
|
/// List of function call argument expressions.
|
||||||
pub args: StaticVec<Expr>,
|
pub args: StaticVec<Expr>,
|
||||||
/// List of function call arguments that are constants.
|
|
||||||
///
|
|
||||||
/// Any arguments in `args` that is [`Expr::Stack`] indexes into this
|
|
||||||
/// array to find the constant for use as its argument value.
|
|
||||||
///
|
|
||||||
/// # Notes
|
|
||||||
///
|
|
||||||
/// Constant arguments are very common in function calls, and keeping each constant in
|
|
||||||
/// an [`Expr::DynamicConstant`] involves an additional allocation. Keeping the constant
|
|
||||||
/// values in an inlined array avoids these extra allocations.
|
|
||||||
pub constants: StaticVec<Dynamic>,
|
|
||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture_parent_scope: bool,
|
pub capture_parent_scope: bool,
|
||||||
/// [Position] of the function name.
|
/// [Position] of the function name.
|
||||||
@ -197,8 +186,8 @@ impl fmt::Debug for FnCallExpr {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let mut ff = f.debug_struct("FnCallExpr");
|
let mut ff = f.debug_struct("FnCallExpr");
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some(ref ns) = self.namespace {
|
if !self.namespace.is_empty() {
|
||||||
ff.field("namespace", ns);
|
ff.field("namespace", &self.namespace);
|
||||||
}
|
}
|
||||||
if self.capture_parent_scope {
|
if self.capture_parent_scope {
|
||||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||||
@ -206,9 +195,6 @@ impl fmt::Debug for FnCallExpr {
|
|||||||
ff.field("hash", &self.hashes)
|
ff.field("hash", &self.hashes)
|
||||||
.field("name", &self.name)
|
.field("name", &self.name)
|
||||||
.field("args", &self.args);
|
.field("args", &self.args);
|
||||||
if !self.constants.is_empty() {
|
|
||||||
ff.field("constants", &self.constants);
|
|
||||||
}
|
|
||||||
ff.field("pos", &self.pos);
|
ff.field("pos", &self.pos);
|
||||||
ff.finish()
|
ff.finish()
|
||||||
}
|
}
|
||||||
@ -220,9 +206,9 @@ impl FnCallExpr {
|
|||||||
/// Always `false` under `no_module`.
|
/// Always `false` under `no_module`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn is_qualified(&self) -> bool {
|
pub fn is_qualified(&self) -> bool {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
return self.namespace.is_some();
|
return !self.namespace.is_empty();
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -386,21 +372,17 @@ pub enum Expr {
|
|||||||
),
|
),
|
||||||
/// ()
|
/// ()
|
||||||
Unit(Position),
|
Unit(Position),
|
||||||
/// Variable access - optional short index, position, (optional index, optional (hash, modules), variable name)
|
/// Variable access - (optional long index, namespace, namespace hash, variable name), optional short index, position
|
||||||
///
|
///
|
||||||
/// The short index is [`u8`] which is used when the index is <= 255, which should be the vast
|
/// The short index is [`u8`] which is used when the index is <= 255, which should be the vast
|
||||||
/// majority of cases (unless there are more than 255 variables defined!).
|
/// majority of cases (unless there are more than 255 variables defined!).
|
||||||
/// This is to avoid reading a pointer redirection during each variable access.
|
/// This is to avoid reading a pointer redirection during each variable access.
|
||||||
Variable(
|
Variable(
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
Box<(Option<NonZeroUsize>, super::Namespace, u64, Identifier)>,
|
||||||
|
#[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, (), u64, Identifier)>,
|
||||||
Option<NonZeroU8>,
|
Option<NonZeroU8>,
|
||||||
Position,
|
Position,
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
Box<(
|
|
||||||
Option<NonZeroUsize>,
|
|
||||||
Option<(crate::module::Namespace, u64)>,
|
|
||||||
Identifier,
|
|
||||||
)>,
|
|
||||||
#[cfg(feature = "no_module")] Box<(Option<NonZeroUsize>, (), Identifier)>,
|
|
||||||
),
|
),
|
||||||
/// Property access - ((getter, hash), (setter, hash), prop)
|
/// Property access - ((getter, hash), (setter, hash), prop)
|
||||||
Property(
|
Property(
|
||||||
@ -409,12 +391,6 @@ pub enum Expr {
|
|||||||
),
|
),
|
||||||
/// xxx `.` method `(` expr `,` ... `)`
|
/// xxx `.` method `(` expr `,` ... `)`
|
||||||
MethodCall(Box<FnCallExpr>, Position),
|
MethodCall(Box<FnCallExpr>, Position),
|
||||||
/// Stack slot for function calls. See [`FnCallExpr`] for more details.
|
|
||||||
///
|
|
||||||
/// This variant does not map to any language structure. It is used in function calls with
|
|
||||||
/// constant arguments where the `usize` number indexes into an array containing a list of
|
|
||||||
/// constant arguments for the function call.
|
|
||||||
Stack(usize, Position),
|
|
||||||
/// { [statement][Stmt] ... }
|
/// { [statement][Stmt] ... }
|
||||||
Stmt(Box<StmtBlock>),
|
Stmt(Box<StmtBlock>),
|
||||||
/// func `(` expr `,` ... `)`
|
/// func `(` expr `,` ... `)`
|
||||||
@ -471,18 +447,18 @@ impl fmt::Debug for Expr {
|
|||||||
.entries(x.0.iter().map(|(k, v)| (k, v)))
|
.entries(x.0.iter().map(|(k, v)| (k, v)))
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
Self::Variable(i, _, x) => {
|
Self::Variable(x, i, ..) => {
|
||||||
f.write_str("Variable(")?;
|
f.write_str("Variable(")?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some((ref namespace, ..)) = x.1 {
|
if !x.1.is_empty() {
|
||||||
write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())?;
|
write!(f, "{}{}", x.1, Token::DoubleColon.literal_syntax())?;
|
||||||
let pos = namespace.position();
|
let pos = x.1.position();
|
||||||
if !pos.is_none() {
|
if !pos.is_none() {
|
||||||
display_pos = format!(" @ {:?}", pos);
|
display_pos = format!(" @ {:?}", pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.write_str(&x.2)?;
|
f.write_str(&x.3)?;
|
||||||
if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
|
if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) {
|
||||||
write!(f, " #{}", n)?;
|
write!(f, " #{}", n)?;
|
||||||
}
|
}
|
||||||
@ -490,7 +466,6 @@ impl fmt::Debug for Expr {
|
|||||||
}
|
}
|
||||||
Self::Property(x, ..) => write!(f, "Property({})", x.2),
|
Self::Property(x, ..) => write!(f, "Property({})", x.2),
|
||||||
Self::MethodCall(x, ..) => f.debug_tuple("MethodCall").field(x).finish(),
|
Self::MethodCall(x, ..) => f.debug_tuple("MethodCall").field(x).finish(),
|
||||||
Self::Stack(x, ..) => write!(f, "ConstantArg[{}]", x),
|
|
||||||
Self::Stmt(x) => {
|
Self::Stmt(x) => {
|
||||||
let pos = x.span();
|
let pos = x.span();
|
||||||
if !pos.is_none() {
|
if !pos.is_none() {
|
||||||
@ -642,11 +617,10 @@ impl Expr {
|
|||||||
Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
|
Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall(
|
||||||
FnCallExpr {
|
FnCallExpr {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace: None,
|
namespace: super::Namespace::NONE,
|
||||||
name: KEYWORD_FN_PTR.into(),
|
name: KEYWORD_FN_PTR.into(),
|
||||||
hashes: calc_fn_hash(f.fn_name(), 1).into(),
|
hashes: calc_fn_hash(f.fn_name(), 1).into(),
|
||||||
args: once(Self::Stack(0, pos)).collect(),
|
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||||
constants: once(f.fn_name().into()).collect(),
|
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
pos,
|
pos,
|
||||||
}
|
}
|
||||||
@ -662,12 +636,12 @@ impl Expr {
|
|||||||
/// `non_qualified` is ignored under `no_module`.
|
/// `non_qualified` is ignored under `no_module`.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool {
|
pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool {
|
||||||
let _non_qualified = non_qualified;
|
let _non_qualified = non_qualified;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Self::Variable(.., x) if _non_qualified && x.1.is_some() => false,
|
Self::Variable(x, ..) if _non_qualified && !x.1.is_empty() => false,
|
||||||
Self::Variable(..) => true,
|
Self::Variable(..) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -682,8 +656,8 @@ impl Expr {
|
|||||||
|
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Self::Variable(.., x) if _non_qualified && x.1.is_some() => None,
|
Self::Variable(x, ..) if _non_qualified && !x.1.is_empty() => None,
|
||||||
Self::Variable(.., x) => Some(x.2.as_str()),
|
Self::Variable(x, ..) => Some(x.3.as_str()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -703,8 +677,7 @@ impl Expr {
|
|||||||
| Self::StringConstant(.., pos)
|
| Self::StringConstant(.., pos)
|
||||||
| Self::Array(.., pos)
|
| Self::Array(.., pos)
|
||||||
| Self::Map(.., pos)
|
| Self::Map(.., pos)
|
||||||
| Self::Variable(.., pos, _)
|
| Self::Variable(.., pos)
|
||||||
| Self::Stack(.., pos)
|
|
||||||
| Self::And(.., pos)
|
| Self::And(.., pos)
|
||||||
| Self::Or(.., pos)
|
| Self::Or(.., pos)
|
||||||
| Self::Index(.., pos)
|
| Self::Index(.., pos)
|
||||||
@ -725,9 +698,9 @@ impl Expr {
|
|||||||
pub fn start_position(&self) -> Position {
|
pub fn start_position(&self) -> Position {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Self::Variable(.., x) => {
|
Self::Variable(x, ..) => {
|
||||||
if let Some((ref namespace, ..)) = x.1 {
|
if !x.1.is_empty() {
|
||||||
namespace.position()
|
x.1.position()
|
||||||
} else {
|
} else {
|
||||||
self.position()
|
self.position()
|
||||||
}
|
}
|
||||||
@ -758,8 +731,7 @@ impl Expr {
|
|||||||
| Self::Or(.., pos)
|
| Self::Or(.., pos)
|
||||||
| Self::Dot(.., pos)
|
| Self::Dot(.., pos)
|
||||||
| Self::Index(.., pos)
|
| Self::Index(.., pos)
|
||||||
| Self::Variable(.., pos, _)
|
| Self::Variable(.., pos)
|
||||||
| Self::Stack(.., pos)
|
|
||||||
| Self::FnCall(.., pos)
|
| Self::FnCall(.., pos)
|
||||||
| Self::MethodCall(.., pos)
|
| Self::MethodCall(.., pos)
|
||||||
| Self::Custom(.., pos)
|
| Self::Custom(.., pos)
|
||||||
@ -786,7 +758,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::Stmt(x) => x.iter().all(Stmt::is_pure),
|
Self::Stmt(x) => x.iter().all(Stmt::is_pure),
|
||||||
|
|
||||||
Self::Variable(..) | Self::Stack(..) => true,
|
Self::Variable(..) => true,
|
||||||
|
|
||||||
_ => self.is_constant(),
|
_ => self.is_constant(),
|
||||||
}
|
}
|
||||||
@ -810,8 +782,7 @@ impl Expr {
|
|||||||
| Self::IntegerConstant(..)
|
| Self::IntegerConstant(..)
|
||||||
| Self::CharConstant(..)
|
| Self::CharConstant(..)
|
||||||
| Self::StringConstant(..)
|
| Self::StringConstant(..)
|
||||||
| Self::Unit(..)
|
| Self::Unit(..) => true,
|
||||||
| Self::Stack(..) => true,
|
|
||||||
|
|
||||||
Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_constant),
|
Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_constant),
|
||||||
|
|
||||||
@ -872,8 +843,6 @@ impl Expr {
|
|||||||
Token::LeftParen => true,
|
Token::LeftParen => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
},
|
},
|
||||||
|
|
||||||
Self::Stack(..) => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Recursively walk this expression.
|
/// Recursively walk this expression.
|
||||||
|
@ -18,14 +18,14 @@ bitflags! {
|
|||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
pub struct ASTFlags: u8 {
|
pub struct ASTFlags: u8 {
|
||||||
/// No options for the [`AST`][crate::AST] node.
|
/// No options for the [`AST`][crate::AST] node.
|
||||||
const NONE = 0b0000_0000;
|
const NONE = 0b_0000_0000;
|
||||||
/// The [`AST`][crate::AST] node is read-only.
|
/// The [`AST`][crate::AST] node is read-only.
|
||||||
const CONSTANT = 0b0000_0001;
|
const CONSTANT = 0b_0000_0001;
|
||||||
/// The [`AST`][crate::AST] node is exposed to the outside (i.e. public).
|
/// The [`AST`][crate::AST] node is exposed to the outside (i.e. public).
|
||||||
const EXPORTED = 0b0000_0010;
|
const EXPORTED = 0b_0000_0010;
|
||||||
/// The [`AST`][crate::AST] node is negated (i.e. whatever information is the opposite).
|
/// The [`AST`][crate::AST] node is negated (i.e. whatever information is the opposite).
|
||||||
const NEGATED = 0b0000_0100;
|
const NEGATED = 0b_0000_0100;
|
||||||
/// The [`AST`][crate::AST] node breaks out of normal control flow.
|
/// The [`AST`][crate::AST] node breaks out of normal control flow.
|
||||||
const BREAK = 0b0000_1000;
|
const BREAK = 0b_0000_1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,11 @@
|
|||||||
use crate::{Identifier, Position};
|
use crate::{Identifier, Position};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{fmt, hash::Hash};
|
use std::{
|
||||||
|
fmt,
|
||||||
|
hash::Hash,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
};
|
||||||
|
|
||||||
/// _(internals)_ An identifier containing a name and a [position][Position].
|
/// _(internals)_ An identifier containing a name and a [position][Position].
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -29,7 +33,30 @@ impl AsRef<str> for Ident {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for Ident {
|
||||||
|
type Target = Identifier;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Ident {
|
||||||
|
#[inline(always)]
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Ident {
|
impl Ident {
|
||||||
|
/// An empty [`Ident`].
|
||||||
|
pub const EMPTY: Self = Self {
|
||||||
|
name: Identifier::new_const(),
|
||||||
|
pos: Position::NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get the name of the identifier as a string slice.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_str(&self) -> &str {
|
pub fn as_str(&self) -> &str {
|
||||||
self.name.as_str()
|
self.name.as_str()
|
||||||
|
@ -4,6 +4,7 @@ pub mod ast;
|
|||||||
pub mod expr;
|
pub mod expr;
|
||||||
pub mod flags;
|
pub mod flags;
|
||||||
pub mod ident;
|
pub mod ident;
|
||||||
|
pub mod namespace;
|
||||||
pub mod script_fn;
|
pub mod script_fn;
|
||||||
pub mod stmt;
|
pub mod stmt;
|
||||||
|
|
||||||
@ -12,6 +13,8 @@ pub use expr::{BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes};
|
|||||||
pub use flags::{ASTFlags, FnAccess};
|
pub use flags::{ASTFlags, FnAccess};
|
||||||
pub use ident::Ident;
|
pub use ident::Ident;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
pub use namespace::Namespace;
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use script_fn::EncapsulatedEnviron;
|
pub use script_fn::EncapsulatedEnviron;
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
@ -30,6 +30,10 @@ pub struct Namespace {
|
|||||||
|
|
||||||
impl fmt::Debug for Namespace {
|
impl fmt::Debug for Namespace {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.is_empty() {
|
||||||
|
return f.write_str("NONE");
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(index) = self.index {
|
if let Some(index) = self.index {
|
||||||
write!(f, "{} -> ", index)?;
|
write!(f, "{} -> ", index)?;
|
||||||
}
|
}
|
||||||
@ -38,7 +42,7 @@ impl fmt::Debug for Namespace {
|
|||||||
&self
|
&self
|
||||||
.path
|
.path
|
||||||
.iter()
|
.iter()
|
||||||
.map(|Ident { name, .. }| name.as_str())
|
.map(|m| m.as_str())
|
||||||
.collect::<StaticVec<_>>()
|
.collect::<StaticVec<_>>()
|
||||||
.join(Token::DoubleColon.literal_syntax()),
|
.join(Token::DoubleColon.literal_syntax()),
|
||||||
)
|
)
|
||||||
@ -47,11 +51,15 @@ impl fmt::Debug for Namespace {
|
|||||||
|
|
||||||
impl fmt::Display for Namespace {
|
impl fmt::Display for Namespace {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
f.write_str(
|
f.write_str(
|
||||||
&self
|
&self
|
||||||
.path
|
.path
|
||||||
.iter()
|
.iter()
|
||||||
.map(|Ident { name, .. }| name.as_str())
|
.map(|m| m.as_str())
|
||||||
.collect::<StaticVec<_>>()
|
.collect::<StaticVec<_>>()
|
||||||
.join(Token::DoubleColon.literal_syntax()),
|
.join(Token::DoubleColon.literal_syntax()),
|
||||||
)
|
)
|
||||||
@ -94,14 +102,20 @@ impl From<StaticVec<Ident>> for Namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Namespace {
|
impl Namespace {
|
||||||
|
/// Constant for no namespace.
|
||||||
|
pub const NONE: Self = Self {
|
||||||
|
index: None,
|
||||||
|
path: StaticVec::new_const(),
|
||||||
|
};
|
||||||
|
|
||||||
/// Create a new [`Namespace`].
|
/// Create a new [`Namespace`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn new() -> Self {
|
pub fn new(root: impl Into<Ident>) -> Self {
|
||||||
Self {
|
let mut path = StaticVec::new_const();
|
||||||
index: None,
|
path.push(root.into());
|
||||||
path: StaticVec::new_const(),
|
|
||||||
}
|
Self { index: None, path }
|
||||||
}
|
}
|
||||||
/// Get the [`Scope`][crate::Scope] index offset.
|
/// Get the [`Scope`][crate::Scope] index offset.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
@ -46,7 +46,7 @@ pub struct ScriptFnDef {
|
|||||||
/// _(metadata)_ Function doc-comments (if any).
|
/// _(metadata)_ Function doc-comments (if any).
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub comments: Option<Box<[Box<str>]>>,
|
pub comments: Box<[Box<str>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ScriptFnDef {
|
impl fmt::Display for ScriptFnDef {
|
||||||
@ -79,7 +79,7 @@ pub struct ScriptFnMetadata<'a> {
|
|||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: &'a str,
|
pub name: &'a str,
|
||||||
/// Function parameters (if any).
|
/// Function parameters (if any).
|
||||||
pub params: Box<[&'a str]>,
|
pub params: Vec<&'a str>,
|
||||||
/// Function access mode.
|
/// Function access mode.
|
||||||
pub access: FnAccess,
|
pub access: FnAccess,
|
||||||
/// _(metadata)_ Function doc-comments (if any).
|
/// _(metadata)_ Function doc-comments (if any).
|
||||||
@ -93,7 +93,7 @@ pub struct ScriptFnMetadata<'a> {
|
|||||||
/// corresponding doc-comment leader: `///` or `/**`.
|
/// corresponding doc-comment leader: `///` or `/**`.
|
||||||
/// Function access mode.
|
/// Function access mode.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub comments: Box<[&'a str]>,
|
pub comments: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ScriptFnMetadata<'_> {
|
impl fmt::Display for ScriptFnMetadata<'_> {
|
||||||
@ -120,23 +120,10 @@ impl<'a> From<&'a ScriptFnDef> for ScriptFnMetadata<'a> {
|
|||||||
fn from(value: &'a ScriptFnDef) -> Self {
|
fn from(value: &'a ScriptFnDef) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: &value.name,
|
name: &value.name,
|
||||||
params: value
|
params: value.params.iter().map(|s| s.as_str()).collect(),
|
||||||
.params
|
|
||||||
.iter()
|
|
||||||
.map(|s| s.as_str())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_boxed_slice(),
|
|
||||||
access: value.access,
|
access: value.access,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: value.comments.as_ref().map_or_else(
|
comments: value.comments.iter().map(Box::as_ref).collect(),
|
||||||
|| Vec::new().into_boxed_slice(),
|
|
||||||
|v| {
|
|
||||||
v.iter()
|
|
||||||
.map(Box::as_ref)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_boxed_slice()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,7 +127,7 @@ pub struct TryCatchBlock {
|
|||||||
/// `try` block.
|
/// `try` block.
|
||||||
pub try_block: StmtBlock,
|
pub try_block: StmtBlock,
|
||||||
/// `catch` variable, if any.
|
/// `catch` variable, if any.
|
||||||
pub catch_var: Option<Ident>,
|
pub catch_var: Ident,
|
||||||
/// `catch` block.
|
/// `catch` block.
|
||||||
pub catch_block: StmtBlock,
|
pub catch_block: StmtBlock,
|
||||||
}
|
}
|
||||||
@ -346,7 +346,7 @@ pub enum Stmt {
|
|||||||
/// * [`NEGATED`][ASTFlags::NEGATED] = `until`
|
/// * [`NEGATED`][ASTFlags::NEGATED] = `until`
|
||||||
Do(Box<(Expr, StmtBlock)>, ASTFlags, Position),
|
Do(Box<(Expr, StmtBlock)>, ASTFlags, Position),
|
||||||
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
|
/// `for` `(` id `,` counter `)` `in` expr `{` stmt `}`
|
||||||
For(Box<(Ident, Option<Ident>, Expr, StmtBlock)>, Position),
|
For(Box<(Ident, Ident, Expr, StmtBlock)>, Position),
|
||||||
/// \[`export`\] `let`|`const` id `=` expr
|
/// \[`export`\] `let`|`const` id `=` expr
|
||||||
///
|
///
|
||||||
/// ### Flags
|
/// ### Flags
|
||||||
@ -385,7 +385,7 @@ pub enum Stmt {
|
|||||||
///
|
///
|
||||||
/// Not available under `no_module`.
|
/// Not available under `no_module`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Import(Box<(Expr, Option<Ident>)>, Position),
|
Import(Box<(Expr, Ident)>, Position),
|
||||||
/// `export` var `as` alias
|
/// `export` var `as` alias
|
||||||
///
|
///
|
||||||
/// Not available under `no_module`.
|
/// Not available under `no_module`.
|
||||||
@ -621,7 +621,7 @@ impl Stmt {
|
|||||||
/// upper block.
|
/// upper block.
|
||||||
///
|
///
|
||||||
/// Currently only variable definitions (i.e. `let` and `const`), `import`/`export` statements,
|
/// Currently only variable definitions (i.e. `let` and `const`), `import`/`export` statements,
|
||||||
/// and `eval` calls (which may in turn call define variables) fall under this category.
|
/// and `eval` calls (which may in turn define variables) fall under this category.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_block_dependent(&self) -> bool {
|
pub fn is_block_dependent(&self) -> bool {
|
||||||
|
@ -45,7 +45,7 @@ pub enum ChainArgument {
|
|||||||
/// Since many dotted function calls have no arguments (e.g. `string.len()`), it is better to
|
/// Since many dotted function calls have no arguments (e.g. `string.len()`), it is better to
|
||||||
/// reduce the size of [`ChainArgument`] by using a boxed slice.
|
/// reduce the size of [`ChainArgument`] by using a boxed slice.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
MethodCallArgs(Option<Box<[Dynamic]>>, Position),
|
MethodCallArgs(Box<[Dynamic]>, Position),
|
||||||
/// Index value and [position][Position].
|
/// Index value and [position][Position].
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
IndexValue(Dynamic, Position),
|
IndexValue(Dynamic, Position),
|
||||||
@ -73,8 +73,10 @@ impl ChainArgument {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn into_fn_call_args(self) -> (crate::FnArgsVec<Dynamic>, Position) {
|
pub fn into_fn_call_args(self) -> (crate::FnArgsVec<Dynamic>, Position) {
|
||||||
match self {
|
match self {
|
||||||
Self::MethodCallArgs(None, pos) => (crate::FnArgsVec::new_const(), pos),
|
Self::MethodCallArgs(values, pos) if values.is_empty() => {
|
||||||
Self::MethodCallArgs(Some(mut values), pos) => {
|
(crate::FnArgsVec::new_const(), pos)
|
||||||
|
}
|
||||||
|
Self::MethodCallArgs(mut values, pos) => {
|
||||||
(values.iter_mut().map(std::mem::take).collect(), pos)
|
(values.iter_mut().map(std::mem::take).collect(), pos)
|
||||||
}
|
}
|
||||||
x => unreachable!("ChainArgument::MethodCallArgs expected but gets {:?}", x),
|
x => unreachable!("ChainArgument::MethodCallArgs expected but gets {:?}", x),
|
||||||
@ -100,9 +102,9 @@ impl ChainArgument {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_fn_call_args(values: crate::FnArgsVec<Dynamic>, pos: Position) -> Self {
|
pub fn from_fn_call_args(values: crate::FnArgsVec<Dynamic>, pos: Position) -> Self {
|
||||||
if values.is_empty() {
|
if values.is_empty() {
|
||||||
Self::MethodCallArgs(None, pos)
|
Self::MethodCallArgs(Box::default(), pos)
|
||||||
} else {
|
} else {
|
||||||
Self::MethodCallArgs(Some(values.into_vec().into()), pos)
|
Self::MethodCallArgs(values.into_boxed_slice(), pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create an [`IndexValue`][ChainArgument::IndexValue].
|
/// Create an [`IndexValue`][ChainArgument::IndexValue].
|
||||||
@ -620,7 +622,7 @@ impl Engine {
|
|||||||
|
|
||||||
match lhs {
|
match lhs {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(.., var_pos, x) => {
|
Expr::Variable(x, .., var_pos) => {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?;
|
self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?;
|
||||||
|
|
||||||
@ -631,7 +633,7 @@ impl Engine {
|
|||||||
self.search_namespace(scope, global, state, lib, this_ptr, lhs, level)?;
|
self.search_namespace(scope, global, state, lib, this_ptr, lhs, level)?;
|
||||||
|
|
||||||
let obj_ptr = &mut target;
|
let obj_ptr = &mut target;
|
||||||
let root = (x.2.as_str(), *var_pos);
|
let root = (x.3.as_str(), *var_pos);
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
global, state, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values,
|
global, state, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values,
|
||||||
@ -684,16 +686,13 @@ impl Engine {
|
|||||||
Expr::MethodCall(x, ..)
|
Expr::MethodCall(x, ..)
|
||||||
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||||
{
|
{
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr { args, .. } = x.as_ref();
|
||||||
args, constants, ..
|
|
||||||
} = x.as_ref();
|
|
||||||
|
|
||||||
let (values, pos) = args.iter().try_fold(
|
let (values, pos) = args.iter().try_fold(
|
||||||
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|
||||||
|(mut values, mut pos), expr| {
|
|(mut values, mut pos), expr| {
|
||||||
let (value, arg_pos) = self.get_arg_value(
|
let (value, arg_pos) =
|
||||||
scope, global, state, lib, this_ptr, expr, constants, level,
|
self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)?;
|
||||||
)?;
|
|
||||||
if values.is_empty() {
|
if values.is_empty() {
|
||||||
pos = arg_pos;
|
pos = arg_pos;
|
||||||
}
|
}
|
||||||
@ -732,15 +731,13 @@ impl Engine {
|
|||||||
Expr::MethodCall(x, ..)
|
Expr::MethodCall(x, ..)
|
||||||
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||||
{
|
{
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr { args, .. } = x.as_ref();
|
||||||
args, constants, ..
|
|
||||||
} = x.as_ref();
|
|
||||||
|
|
||||||
let (values, pos) = args.iter().try_fold(
|
let (values, pos) = args.iter().try_fold(
|
||||||
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|
(crate::FnArgsVec::with_capacity(args.len()), Position::NONE),
|
||||||
|(mut values, mut pos), expr| {
|
|(mut values, mut pos), expr| {
|
||||||
let (value, arg_pos) = self.get_arg_value(
|
let (value, arg_pos) = self.get_arg_value(
|
||||||
scope, global, state, lib, this_ptr, expr, constants, level,
|
scope, global, state, lib, this_ptr, expr, level,
|
||||||
)?;
|
)?;
|
||||||
if values.is_empty() {
|
if values.is_empty() {
|
||||||
pos = arg_pos
|
pos = arg_pos
|
||||||
@ -925,22 +922,20 @@ impl Engine {
|
|||||||
if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() =>
|
if idx.is::<crate::ExclusiveRange>() || idx.is::<crate::InclusiveRange>() =>
|
||||||
{
|
{
|
||||||
// val_int[range]
|
// val_int[range]
|
||||||
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
|
|
||||||
|
|
||||||
let (shift, mask) = if let Some(range) = idx.read_lock::<crate::ExclusiveRange>() {
|
let (shift, mask) = if let Some(range) = idx.read_lock::<crate::ExclusiveRange>() {
|
||||||
let start = range.start;
|
let start = range.start;
|
||||||
let end = range.end;
|
let end = range.end;
|
||||||
|
|
||||||
let start = super::calc_index(BITS, start, false, || {
|
let start = super::calc_index(crate::INT_BITS, start, false, || {
|
||||||
ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into()
|
ERR::ErrorBitFieldBounds(crate::INT_BITS, start, idx_pos).into()
|
||||||
})?;
|
})?;
|
||||||
let end = super::calc_index(BITS, end, false, || {
|
let end = super::calc_index(crate::INT_BITS, end, false, || {
|
||||||
ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into()
|
ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if end <= start {
|
if end <= start {
|
||||||
(0, 0)
|
(0, 0)
|
||||||
} else if end == BITS && start == 0 {
|
} else if end == crate::INT_BITS && start == 0 {
|
||||||
// -1 = all bits set
|
// -1 = all bits set
|
||||||
(0, -1)
|
(0, -1)
|
||||||
} else {
|
} else {
|
||||||
@ -956,16 +951,16 @@ impl Engine {
|
|||||||
let start = *range.start();
|
let start = *range.start();
|
||||||
let end = *range.end();
|
let end = *range.end();
|
||||||
|
|
||||||
let start = super::calc_index(BITS, start, false, || {
|
let start = super::calc_index(crate::INT_BITS, start, false, || {
|
||||||
ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into()
|
ERR::ErrorBitFieldBounds(crate::INT_BITS, start, idx_pos).into()
|
||||||
})?;
|
})?;
|
||||||
let end = super::calc_index(BITS, end, false, || {
|
let end = super::calc_index(crate::INT_BITS, end, false, || {
|
||||||
ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into()
|
ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if end < start {
|
if end < start {
|
||||||
(0, 0)
|
(0, 0)
|
||||||
} else if end == BITS - 1 && start == 0 {
|
} else if end == crate::INT_BITS - 1 && start == 0 {
|
||||||
// -1 = all bits set
|
// -1 = all bits set
|
||||||
(0, -1)
|
(0, -1)
|
||||||
} else {
|
} else {
|
||||||
@ -998,10 +993,8 @@ impl Engine {
|
|||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, idx_pos))?;
|
||||||
|
|
||||||
const BITS: usize = std::mem::size_of::<crate::INT>() * 8;
|
let bit = super::calc_index(crate::INT_BITS, index, true, || {
|
||||||
|
ERR::ErrorBitFieldBounds(crate::INT_BITS, index, idx_pos).into()
|
||||||
let bit = super::calc_index(BITS, index, true, || {
|
|
||||||
ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into()
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let bit_value = (*value & (1 << bit)) != 0;
|
let bit_value = (*value & (1 << bit)) != 0;
|
||||||
|
@ -18,8 +18,10 @@ impl Engine {
|
|||||||
&self,
|
&self,
|
||||||
global: &GlobalRuntimeState,
|
global: &GlobalRuntimeState,
|
||||||
state: &mut EvalState,
|
state: &mut EvalState,
|
||||||
namespace: &crate::module::Namespace,
|
namespace: &crate::ast::Namespace,
|
||||||
) -> Option<crate::Shared<Module>> {
|
) -> Option<crate::Shared<Module>> {
|
||||||
|
assert!(!namespace.is_empty());
|
||||||
|
|
||||||
let root = namespace.root();
|
let root = namespace.root();
|
||||||
|
|
||||||
// Qualified - check if the root module is directly indexed
|
// Qualified - check if the root module is directly indexed
|
||||||
@ -53,23 +55,23 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResultOf<(Target<'s>, Position)> {
|
) -> RhaiResultOf<(Target<'s>, Position)> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Variable(Some(_), ..) => {
|
Expr::Variable(_, Some(_), _) => {
|
||||||
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
|
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
|
||||||
}
|
}
|
||||||
Expr::Variable(None, _var_pos, v) => match v.as_ref() {
|
Expr::Variable(v, None, _var_pos) => match v.as_ref() {
|
||||||
// Normal variable access
|
// Normal variable access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(_, None, _) => {
|
(_, ns, ..) if ns.is_empty() => {
|
||||||
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
|
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
(_, (), _) => {
|
(_, (), ..) => {
|
||||||
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
|
self.search_scope_only(scope, global, state, lib, this_ptr, expr, level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Qualified variable access
|
// Qualified variable access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(_, Some((namespace, hash_var)), var_name) => {
|
(_, namespace, hash_var, var_name) => {
|
||||||
// foo:bar::baz::VARIABLE
|
// foo:bar::baz::VARIABLE
|
||||||
if let Some(module) = self.search_imports(global, state, namespace) {
|
if let Some(module) = self.search_imports(global, state, namespace) {
|
||||||
return if let Some(mut target) = module.get_qualified_var(*hash_var) {
|
return if let Some(mut target) = module.get_qualified_var(*hash_var) {
|
||||||
@ -139,7 +141,7 @@ impl Engine {
|
|||||||
|
|
||||||
let (index, var_pos) = match expr {
|
let (index, var_pos) = match expr {
|
||||||
// Check if the variable is `this`
|
// Check if the variable is `this`
|
||||||
Expr::Variable(None, pos, v) if v.0.is_none() && v.2 == KEYWORD_THIS => {
|
Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => {
|
||||||
return if let Some(val) = this_ptr {
|
return if let Some(val) = this_ptr {
|
||||||
Ok(((*val).into(), *pos))
|
Ok(((*val).into(), *pos))
|
||||||
} else {
|
} else {
|
||||||
@ -147,8 +149,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ if state.always_search_scope => (0, expr.start_position()),
|
_ if state.always_search_scope => (0, expr.start_position()),
|
||||||
Expr::Variable(Some(i), pos, ..) => (i.get() as usize, *pos),
|
Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos),
|
||||||
Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
|
Expr::Variable(v, None, pos) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos),
|
||||||
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
_ => unreachable!("Expr::Variable expected but gets {:?}", expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -212,18 +214,16 @@ impl Engine {
|
|||||||
capture_parent_scope: capture,
|
capture_parent_scope: capture,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
constants,
|
|
||||||
..
|
..
|
||||||
} = expr;
|
} = expr;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some(namespace) = namespace.as_ref() {
|
if !namespace.is_empty() {
|
||||||
// Qualified function call
|
// Qualified function call
|
||||||
let hash = hashes.native;
|
let hash = hashes.native;
|
||||||
|
|
||||||
return self.make_qualified_function_call(
|
return self.make_qualified_function_call(
|
||||||
scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos,
|
scope, global, state, lib, this_ptr, namespace, name, args, hash, pos, level,
|
||||||
level,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,8 +234,8 @@ impl Engine {
|
|||||||
);
|
);
|
||||||
|
|
||||||
self.make_function_call(
|
self.make_function_call(
|
||||||
scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes,
|
scope, global, state, lib, this_ptr, name, first_arg, args, *hashes, *capture, pos,
|
||||||
*capture, pos, level,
|
level,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,14 +282,14 @@ impl Engine {
|
|||||||
// Then variable access.
|
// Then variable access.
|
||||||
// We shouldn't do this for too many variants because, soon or later, the added comparisons
|
// We shouldn't do this for too many variants because, soon or later, the added comparisons
|
||||||
// will cost more than the mis-predicted `match` branch.
|
// will cost more than the mis-predicted `match` branch.
|
||||||
if let Expr::Variable(index, var_pos, x) = expr {
|
if let Expr::Variable(x, index, var_pos) = expr {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?;
|
self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
self.inc_operations(&mut global.num_operations, expr.position())?;
|
self.inc_operations(&mut global.num_operations, expr.position())?;
|
||||||
|
|
||||||
return if index.is_none() && x.0.is_none() && x.2 == KEYWORD_THIS {
|
return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
|
||||||
this_ptr
|
this_ptr
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -398,8 +398,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let mut sizes = (0, 0, 0);
|
let mut sizes = (0, 0, 0);
|
||||||
|
|
||||||
for (crate::ast::Ident { name, .. }, value_expr) in x.0.iter() {
|
for (key, value_expr) in x.0.iter() {
|
||||||
let key = name.as_str();
|
|
||||||
let value = match self
|
let value = match self
|
||||||
.eval_expr(scope, global, state, lib, this_ptr, value_expr, level)
|
.eval_expr(scope, global, state, lib, this_ptr, value_expr, level)
|
||||||
{
|
{
|
||||||
@ -413,7 +412,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
let delta = Self::calc_data_sizes(&value, true);
|
let delta = Self::calc_data_sizes(&value, true);
|
||||||
|
|
||||||
*map.get_mut(key).unwrap() = value;
|
*map.get_mut(key.as_str()).unwrap() = value;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
if self.has_data_size_limit() {
|
if self.has_data_size_limit() {
|
||||||
|
@ -569,7 +569,7 @@ impl Engine {
|
|||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(x, ..) => {
|
Stmt::For(x, ..) => {
|
||||||
let (Ident { name: var_name, .. }, counter, expr, statements) = x.as_ref();
|
let (var_name, counter, expr, statements) = x.as_ref();
|
||||||
|
|
||||||
let iter_result = self
|
let iter_result = self
|
||||||
.eval_expr(scope, global, state, lib, this_ptr, expr, level)
|
.eval_expr(scope, global, state, lib, this_ptr, expr, level)
|
||||||
@ -600,14 +600,14 @@ impl Engine {
|
|||||||
if let Some(func) = func {
|
if let Some(func) = func {
|
||||||
// Add the loop variables
|
// Add the loop variables
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
let counter_index = if let Some(counter) = counter {
|
let counter_index = if !counter.is_empty() {
|
||||||
scope.push(counter.name.clone(), 0 as INT);
|
scope.push(counter.name.clone(), 0 as INT);
|
||||||
scope.len() - 1
|
scope.len() - 1
|
||||||
} else {
|
} else {
|
||||||
usize::MAX
|
usize::MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
scope.push(var_name.clone(), ());
|
scope.push(var_name.name.clone(), ());
|
||||||
let index = scope.len() - 1;
|
let index = scope.len() - 1;
|
||||||
|
|
||||||
let mut loop_result = Ok(Dynamic::UNIT);
|
let mut loop_result = Ok(Dynamic::UNIT);
|
||||||
@ -619,7 +619,7 @@ impl Engine {
|
|||||||
if x > INT::MAX as usize {
|
if x > INT::MAX as usize {
|
||||||
loop_result = Err(ERR::ErrorArithmetic(
|
loop_result = Err(ERR::ErrorArithmetic(
|
||||||
format!("for-loop counter overflow: {}", x),
|
format!("for-loop counter overflow: {}", x),
|
||||||
counter.as_ref().expect("`Some`").pos,
|
counter.pos,
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
break;
|
break;
|
||||||
@ -707,7 +707,10 @@ impl Engine {
|
|||||||
Stmt::TryCatch(x, ..) => {
|
Stmt::TryCatch(x, ..) => {
|
||||||
let TryCatchBlock {
|
let TryCatchBlock {
|
||||||
try_block,
|
try_block,
|
||||||
catch_var,
|
catch_var:
|
||||||
|
Ident {
|
||||||
|
name: catch_var, ..
|
||||||
|
},
|
||||||
catch_block,
|
catch_block,
|
||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
|
|
||||||
@ -757,9 +760,9 @@ impl Engine {
|
|||||||
|
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
|
|
||||||
catch_var
|
if !catch_var.is_empty() {
|
||||||
.as_ref()
|
scope.push(catch_var.clone(), err_value);
|
||||||
.map(|Ident { name, .. }| scope.push(name.clone(), err_value));
|
}
|
||||||
|
|
||||||
let result = self.eval_stmt_block(
|
let result = self.eval_stmt_block(
|
||||||
scope,
|
scope,
|
||||||
@ -808,12 +811,12 @@ impl Engine {
|
|||||||
Stmt::Return(None, .., pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()),
|
Stmt::Return(None, .., pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()),
|
||||||
|
|
||||||
// Let/const statement - shadowing disallowed
|
// Let/const statement - shadowing disallowed
|
||||||
Stmt::Var(x, .., pos) if !self.allow_shadowing() && scope.contains(&x.0.name) => {
|
Stmt::Var(x, .., pos) if !self.allow_shadowing() && scope.contains(&x.0) => {
|
||||||
Err(ERR::ErrorVariableExists(x.0.name.to_string(), *pos).into())
|
Err(ERR::ErrorVariableExists(x.0.to_string(), *pos).into())
|
||||||
}
|
}
|
||||||
// Let/const statement
|
// Let/const statement
|
||||||
Stmt::Var(x, options, pos) => {
|
Stmt::Var(x, options, pos) => {
|
||||||
let (Ident { name: var_name, .. }, expr, index) = x.as_ref();
|
let (var_name, expr, index) = x.as_ref();
|
||||||
|
|
||||||
let access = if options.contains(ASTFlags::CONSTANT) {
|
let access = if options.contains(ASTFlags::CONSTANT) {
|
||||||
AccessMode::ReadOnly
|
AccessMode::ReadOnly
|
||||||
@ -879,7 +882,7 @@ impl Engine {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
crate::func::locked_write(global.constants.as_ref().unwrap())
|
crate::func::locked_write(global.constants.as_ref().unwrap())
|
||||||
.insert(var_name.clone(), value.clone());
|
.insert(var_name.name.clone(), value.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
if export {
|
if export {
|
||||||
@ -897,12 +900,12 @@ impl Engine {
|
|||||||
value.set_access_mode(access);
|
value.set_access_mode(access);
|
||||||
*scope.get_mut_by_index(scope.len() - index.get()) = value;
|
*scope.get_mut_by_index(scope.len() - index.get()) = value;
|
||||||
} else {
|
} else {
|
||||||
scope.push_entry(var_name.clone(), access, value);
|
scope.push_entry(var_name.name.clone(), access, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some(alias) = _alias {
|
if let Some(alias) = _alias {
|
||||||
scope.add_entry_alias(scope.len() - 1, alias.clone());
|
scope.add_alias_by_index(scope.len() - 1, alias.name.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
@ -958,14 +961,14 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Ok(module) = module_result {
|
if let Ok(module) = module_result {
|
||||||
if let Some(name) = export.as_ref().map(|x| x.name.clone()) {
|
if !export.is_empty() {
|
||||||
if !module.is_indexed() {
|
if !module.is_indexed() {
|
||||||
// Index the module (making a clone copy if necessary) if it is not indexed
|
// Index the module (making a clone copy if necessary) if it is not indexed
|
||||||
let mut module = crate::func::native::shared_take_or_clone(module);
|
let mut m = crate::func::native::shared_take_or_clone(module);
|
||||||
module.build_index();
|
m.build_index();
|
||||||
global.push_import(name, module);
|
global.push_import(export.name.clone(), m);
|
||||||
} else {
|
} else {
|
||||||
global.push_import(name, module);
|
global.push_import(export.name.clone(), module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -983,13 +986,11 @@ impl Engine {
|
|||||||
// Export statement
|
// Export statement
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Export(x, ..) => {
|
Stmt::Export(x, ..) => {
|
||||||
let (Ident { name, pos, .. }, Ident { name: alias, .. }) = x.as_ref();
|
let (Ident { name, pos, .. }, alias) = x.as_ref();
|
||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
if let Some((index, ..)) = scope.get_index(name) {
|
if let Some((index, ..)) = scope.get_index(name) {
|
||||||
scope.add_entry_alias(
|
let alias = if alias.is_empty() { name } else { alias }.clone();
|
||||||
index,
|
scope.add_alias_by_index(index, alias);
|
||||||
if alias.is_empty() { name } else { alias }.clone(),
|
|
||||||
);
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
} else {
|
} else {
|
||||||
Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into())
|
Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into())
|
||||||
|
@ -151,14 +151,14 @@ impl Engine {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
fn gen_call_signature(
|
fn gen_call_signature(
|
||||||
&self,
|
&self,
|
||||||
#[cfg(not(feature = "no_module"))] namespace: Option<&crate::module::Namespace>,
|
#[cfg(not(feature = "no_module"))] namespace: &crate::ast::Namespace,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args: &[&mut Dynamic],
|
args: &[&mut Dynamic],
|
||||||
) -> String {
|
) -> String {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let (ns, sep) = (
|
let (ns, sep) = (
|
||||||
namespace.map_or_else(|| String::new(), |ns| ns.to_string()),
|
namespace.to_string(),
|
||||||
if namespace.is_some() {
|
if !namespace.is_empty() {
|
||||||
crate::tokenizer::Token::DoubleColon.literal_syntax()
|
crate::tokenizer::Token::DoubleColon.literal_syntax()
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
@ -576,7 +576,7 @@ impl Engine {
|
|||||||
_ => Err(ERR::ErrorFunctionNotFound(
|
_ => Err(ERR::ErrorFunctionNotFound(
|
||||||
self.gen_call_signature(
|
self.gen_call_signature(
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
None,
|
&crate::ast::Namespace::NONE,
|
||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
),
|
),
|
||||||
@ -958,35 +958,30 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
arg_expr: &Expr,
|
arg_expr: &Expr,
|
||||||
constants: &[Dynamic],
|
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResultOf<(Dynamic, Position)> {
|
) -> RhaiResultOf<(Dynamic, Position)> {
|
||||||
Ok((
|
#[cfg(feature = "debugging")]
|
||||||
if let Expr::Stack(slot, ..) = arg_expr {
|
if self.debugger.is_some() {
|
||||||
|
if let Some(value) = arg_expr.get_literal_value() {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?;
|
self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?;
|
||||||
constants[*slot].clone()
|
return Ok((value, arg_expr.start_position()));
|
||||||
} else if let Some(value) = arg_expr.get_literal_value() {
|
}
|
||||||
#[cfg(feature = "debugging")]
|
}
|
||||||
self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?;
|
|
||||||
value
|
|
||||||
} else {
|
|
||||||
// Do not match function exit for arguments
|
|
||||||
#[cfg(feature = "debugging")]
|
|
||||||
let reset_debugger = global.debugger.clear_status_if(|status| {
|
|
||||||
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
|
||||||
});
|
|
||||||
|
|
||||||
let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level);
|
// Do not match function exit for arguments
|
||||||
|
#[cfg(feature = "debugging")]
|
||||||
|
let reset_debugger = global.debugger.clear_status_if(|status| {
|
||||||
|
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
||||||
|
});
|
||||||
|
|
||||||
// Restore function exit status
|
let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level);
|
||||||
#[cfg(feature = "debugging")]
|
|
||||||
global.debugger.reset_status(reset_debugger);
|
|
||||||
|
|
||||||
result?
|
// Restore function exit status
|
||||||
},
|
#[cfg(feature = "debugging")]
|
||||||
arg_expr.start_position(),
|
global.debugger.reset_status(reset_debugger);
|
||||||
))
|
|
||||||
|
Ok((result?, arg_expr.start_position()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function in normal function-call style.
|
/// Call a function in normal function-call style.
|
||||||
@ -1000,7 +995,6 @@ impl Engine {
|
|||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
first_arg: Option<&Expr>,
|
first_arg: Option<&Expr>,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
constants: &[Dynamic],
|
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
@ -1019,7 +1013,7 @@ impl Engine {
|
|||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
|
||||||
|
|
||||||
if !arg_value.is::<FnPtr>() {
|
if !arg_value.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
@ -1054,7 +1048,7 @@ impl Engine {
|
|||||||
KEYWORD_FN_PTR if total_args == 1 => {
|
KEYWORD_FN_PTR if total_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
|
||||||
|
|
||||||
// Fn - only in function call style
|
// Fn - only in function call style
|
||||||
return arg_value
|
return arg_value
|
||||||
@ -1068,8 +1062,8 @@ impl Engine {
|
|||||||
// Handle curry()
|
// Handle curry()
|
||||||
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
||||||
let first = first_arg.unwrap();
|
let first = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) = self
|
let (arg_value, arg_pos) =
|
||||||
.get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?;
|
||||||
|
|
||||||
if !arg_value.is::<FnPtr>() {
|
if !arg_value.is::<FnPtr>() {
|
||||||
return Err(self.make_type_mismatch_err::<FnPtr>(
|
return Err(self.make_type_mismatch_err::<FnPtr>(
|
||||||
@ -1082,9 +1076,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Append the new curried arguments to the existing list.
|
// Append the new curried arguments to the existing list.
|
||||||
let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| {
|
let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| {
|
||||||
let (value, ..) = self.get_arg_value(
|
let (value, ..) =
|
||||||
scope, global, state, lib, this_ptr, expr, constants, level,
|
self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)?;
|
||||||
)?;
|
|
||||||
curried.push(value);
|
curried.push(value);
|
||||||
Ok::<_, RhaiError>(curried)
|
Ok::<_, RhaiError>(curried)
|
||||||
})?;
|
})?;
|
||||||
@ -1097,7 +1090,7 @@ impl Engine {
|
|||||||
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, ..) =
|
let (arg_value, ..) =
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
|
||||||
return Ok(arg_value.is_shared().into());
|
return Ok(arg_value.is_shared().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1105,16 +1098,15 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
||||||
let first = first_arg.unwrap();
|
let first = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) = self
|
let (arg_value, arg_pos) =
|
||||||
.get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?;
|
||||||
|
|
||||||
let fn_name = arg_value
|
let fn_name = arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
|
|
||||||
let (arg_value, arg_pos) = self.get_arg_value(
|
let (arg_value, arg_pos) =
|
||||||
scope, global, state, lib, this_ptr, &a_expr[0], constants, level,
|
self.get_arg_value(scope, global, state, lib, this_ptr, &a_expr[0], level)?;
|
||||||
)?;
|
|
||||||
|
|
||||||
let num_params = arg_value
|
let num_params = arg_value
|
||||||
.as_int()
|
.as_int()
|
||||||
@ -1133,7 +1125,7 @@ impl Engine {
|
|||||||
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
|
||||||
let var_name = arg_value
|
let var_name = arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
@ -1146,7 +1138,7 @@ impl Engine {
|
|||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, pos) =
|
let (arg_value, pos) =
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?;
|
self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?;
|
||||||
let script = &arg_value
|
let script = &arg_value
|
||||||
.into_immutable_string()
|
.into_immutable_string()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, pos))?;
|
||||||
@ -1195,7 +1187,7 @@ impl Engine {
|
|||||||
.map(|&v| v)
|
.map(|&v| v)
|
||||||
.chain(a_expr.iter())
|
.chain(a_expr.iter())
|
||||||
.try_for_each(|expr| {
|
.try_for_each(|expr| {
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
|
self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
|
||||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
args.extend(curry.iter_mut());
|
args.extend(curry.iter_mut());
|
||||||
@ -1227,7 +1219,7 @@ impl Engine {
|
|||||||
|
|
||||||
// func(x, ...) -> x.func(...)
|
// func(x, ...) -> x.func(...)
|
||||||
a_expr.iter().try_for_each(|expr| {
|
a_expr.iter().try_for_each(|expr| {
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
|
self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
|
||||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -1262,10 +1254,8 @@ impl Engine {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(a_expr.iter())
|
.chain(a_expr.iter())
|
||||||
.try_for_each(|expr| {
|
.try_for_each(|expr| {
|
||||||
self.get_arg_value(
|
self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
|
||||||
scope, global, state, lib, this_ptr, expr, constants, level,
|
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||||
)
|
|
||||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
|
||||||
})?;
|
})?;
|
||||||
args.extend(curry.iter_mut());
|
args.extend(curry.iter_mut());
|
||||||
args.extend(arg_values.iter_mut());
|
args.extend(arg_values.iter_mut());
|
||||||
@ -1287,10 +1277,9 @@ impl Engine {
|
|||||||
state: &mut EvalState,
|
state: &mut EvalState,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
namespace: &crate::module::Namespace,
|
namespace: &crate::ast::Namespace,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
constants: &[Dynamic],
|
|
||||||
hash: u64,
|
hash: u64,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -1313,7 +1302,7 @@ impl Engine {
|
|||||||
arg_values.push(Dynamic::UNIT);
|
arg_values.push(Dynamic::UNIT);
|
||||||
|
|
||||||
args_expr.iter().skip(1).try_for_each(|expr| {
|
args_expr.iter().skip(1).try_for_each(|expr| {
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
|
self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
|
||||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -1344,7 +1333,7 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
// func(..., ...) or func(mod::x, ...)
|
// func(..., ...) or func(mod::x, ...)
|
||||||
args_expr.iter().try_for_each(|expr| {
|
args_expr.iter().try_for_each(|expr| {
|
||||||
self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level)
|
self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)
|
||||||
.map(|(value, ..)| arg_values.push(value.flatten()))
|
.map(|(value, ..)| arg_values.push(value.flatten()))
|
||||||
})?;
|
})?;
|
||||||
args.extend(arg_values.iter_mut());
|
args.extend(arg_values.iter_mut());
|
||||||
@ -1417,7 +1406,7 @@ impl Engine {
|
|||||||
Some(f) => unreachable!("unknown function type: {:?}", f),
|
Some(f) => unreachable!("unknown function type: {:?}", f),
|
||||||
|
|
||||||
None => Err(ERR::ErrorFunctionNotFound(
|
None => Err(ERR::ErrorFunctionNotFound(
|
||||||
self.gen_call_signature(Some(namespace), fn_name, &args),
|
self.gen_call_signature(namespace, fn_name, &args),
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()),
|
.into()),
|
||||||
|
24
src/lib.rs
24
src/lib.rs
@ -121,6 +121,16 @@ type UNSIGNED_INT = u64;
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
type UNSIGNED_INT = u32;
|
type UNSIGNED_INT = u32;
|
||||||
|
|
||||||
|
/// Number of bits in [`INT`].
|
||||||
|
///
|
||||||
|
/// It is 64 unless the `only_i32` feature is enabled when it will be 32.
|
||||||
|
const INT_BITS: usize = std::mem::size_of::<INT>() * 8;
|
||||||
|
|
||||||
|
/// Number of bytes that make up an [`INT`].
|
||||||
|
///
|
||||||
|
/// It is 8 unless the `only_i32` feature is enabled when it will be 4.
|
||||||
|
const INT_BYTES: usize = std::mem::size_of::<INT>();
|
||||||
|
|
||||||
/// The system floating-point type. It is defined as [`f64`].
|
/// The system floating-point type. It is defined as [`f64`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_float`.
|
/// Not available under `no_float`.
|
||||||
@ -140,6 +150,12 @@ pub type FLOAT = f64;
|
|||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
pub type FLOAT = f32;
|
pub type FLOAT = f32;
|
||||||
|
|
||||||
|
/// Number of bytes that make up a [`FLOAT`].
|
||||||
|
///
|
||||||
|
/// It is 8 unless the `f32_float` feature is enabled when it will be 4.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
const FLOAT_BYTES: usize = std::mem::size_of::<FLOAT>();
|
||||||
|
|
||||||
/// An exclusive integer range.
|
/// An exclusive integer range.
|
||||||
type ExclusiveRange = std::ops::Range<INT>;
|
type ExclusiveRange = std::ops::Range<INT>;
|
||||||
|
|
||||||
@ -264,6 +280,10 @@ pub use ast::{
|
|||||||
FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock,
|
FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
pub use ast::Namespace;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -279,10 +299,6 @@ pub use eval::{EvalState, GlobalRuntimeState};
|
|||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub use func::call::{FnResolutionCache, FnResolutionCacheEntry};
|
pub use func::call::{FnResolutionCache, FnResolutionCacheEntry};
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
pub use module::Namespace;
|
|
||||||
|
|
||||||
/// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a
|
/// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a
|
||||||
/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
|
/// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored.
|
||||||
///
|
///
|
||||||
|
@ -51,7 +51,7 @@ pub struct FnMetadata {
|
|||||||
pub return_type: Identifier,
|
pub return_type: Identifier,
|
||||||
/// Comments.
|
/// Comments.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
pub comments: Option<Box<[Box<str>]>>,
|
pub comments: Box<[Box<str>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for FnMetadata {
|
impl PartialOrd for FnMetadata {
|
||||||
@ -597,7 +597,7 @@ impl Module {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
return_type: "".into(),
|
return_type: "".into(),
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: None,
|
comments: Box::default(),
|
||||||
},
|
},
|
||||||
func: Into::<CallableFunction>::into(fn_def).into(),
|
func: Into::<CallableFunction>::into(fn_def).into(),
|
||||||
param_types: StaticVec::new_const(),
|
param_types: StaticVec::new_const(),
|
||||||
@ -816,7 +816,7 @@ impl Module {
|
|||||||
|
|
||||||
if !comments.is_empty() {
|
if !comments.is_empty() {
|
||||||
let f = self.functions.get_mut(&hash_fn).unwrap();
|
let f = self.functions.get_mut(&hash_fn).unwrap();
|
||||||
f.metadata.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect());
|
f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -923,7 +923,7 @@ impl Module {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
return_type: return_type_name,
|
return_type: return_type_name,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: None,
|
comments: Box::default(),
|
||||||
},
|
},
|
||||||
func: func.into(),
|
func: func.into(),
|
||||||
param_types,
|
param_types,
|
||||||
@ -981,7 +981,7 @@ impl Module {
|
|||||||
|
|
||||||
if !comments.is_empty() {
|
if !comments.is_empty() {
|
||||||
let f = self.functions.get_mut(&hash).unwrap();
|
let f = self.functions.get_mut(&hash).unwrap();
|
||||||
f.metadata.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect());
|
f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect();
|
||||||
}
|
}
|
||||||
|
|
||||||
hash
|
hash
|
||||||
@ -2039,14 +2039,9 @@ impl Module {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod namespace;
|
|
||||||
|
|
||||||
/// Module containing all built-in [module resolvers][ModuleResolver].
|
/// Module containing all built-in [module resolvers][ModuleResolver].
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub mod resolvers;
|
pub mod resolvers;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
pub use namespace::Namespace;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub use resolvers::ModuleResolver;
|
pub use resolvers::ModuleResolver;
|
||||||
|
@ -10,7 +10,7 @@ use crate::tokenizer::{Span, Token};
|
|||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope,
|
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope,
|
||||||
StaticVec, AST, INT,
|
StaticVec, AST, INT, INT_BITS,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -259,7 +259,7 @@ fn optimize_stmt_block(
|
|||||||
|
|
||||||
if x.1.is_constant() {
|
if x.1.is_constant() {
|
||||||
state.push_var(
|
state.push_var(
|
||||||
x.0.name.as_str(),
|
x.0.as_str(),
|
||||||
AccessMode::ReadOnly,
|
AccessMode::ReadOnly,
|
||||||
x.1.get_literal_value(),
|
x.1.get_literal_value(),
|
||||||
);
|
);
|
||||||
@ -267,7 +267,7 @@ fn optimize_stmt_block(
|
|||||||
} else {
|
} else {
|
||||||
// Add variables into the state
|
// Add variables into the state
|
||||||
optimize_expr(&mut x.1, state, false);
|
optimize_expr(&mut x.1, state, false);
|
||||||
state.push_var(x.0.name.as_str(), AccessMode::ReadWrite, None);
|
state.push_var(x.0.as_str(), AccessMode::ReadWrite, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
@ -438,15 +438,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
Expr::FnCall(ref mut x2, ..) => {
|
Expr::FnCall(ref mut x2, ..) => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
x.0 = Some(OpAssignment::new_from_base(&x2.name));
|
x.0 = Some(OpAssignment::new_from_base(&x2.name));
|
||||||
|
x.1.rhs = mem::take(&mut x2.args[1]);
|
||||||
let value = mem::take(&mut x2.args[1]);
|
|
||||||
|
|
||||||
if let Expr::Stack(slot, pos) = value {
|
|
||||||
x.1.rhs =
|
|
||||||
Expr::from_dynamic(mem::take(x2.constants.get_mut(slot).unwrap()), pos);
|
|
||||||
} else {
|
|
||||||
x.1.rhs = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr),
|
ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr),
|
||||||
}
|
}
|
||||||
@ -903,7 +895,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name == prop)
|
*expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.as_str() == prop)
|
||||||
.map(|(.., mut expr)| { expr.set_position(*pos); expr })
|
.map(|(.., mut expr)| { expr.set_position(*pos); expr })
|
||||||
.unwrap_or_else(|| Expr::Unit(*pos));
|
.unwrap_or_else(|| Expr::Unit(*pos));
|
||||||
}
|
}
|
||||||
@ -943,21 +935,21 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name.as_str() == s.as_str())
|
*expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.as_str() == s.as_str())
|
||||||
.map(|(.., mut expr)| { expr.set_position(*pos); expr })
|
.map(|(.., mut expr)| { expr.set_position(*pos); expr })
|
||||||
.unwrap_or_else(|| Expr::Unit(*pos));
|
.unwrap_or_else(|| Expr::Unit(*pos));
|
||||||
}
|
}
|
||||||
// int[int]
|
// int[int]
|
||||||
(Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < (std::mem::size_of_val(n) * 8) => {
|
(Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < INT_BITS => {
|
||||||
// Bit-field literal indexing - get the bit
|
// Bit-field literal indexing - get the bit
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::BoolConstant((*n & (1 << (*i as usize))) != 0, *pos);
|
*expr = Expr::BoolConstant((*n & (1 << (*i as usize))) != 0, *pos);
|
||||||
}
|
}
|
||||||
// int[-int]
|
// int[-int]
|
||||||
(Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i < 0 && i.checked_abs().map(|i| i as usize <= (std::mem::size_of_val(n) * 8)).unwrap_or(false) => {
|
(Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i < 0 && i.checked_abs().map(|i| i as usize <= INT_BITS).unwrap_or(false) => {
|
||||||
// Bit-field literal indexing - get the bit
|
// Bit-field literal indexing - get the bit
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
*expr = Expr::BoolConstant((*n & (1 << (std::mem::size_of_val(n) * 8 - i.abs() as usize))) != 0, *pos);
|
*expr = Expr::BoolConstant((*n & (1 << (INT_BITS - i.abs() as usize))) != 0, *pos);
|
||||||
}
|
}
|
||||||
// string[int]
|
// string[int]
|
||||||
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < s.chars().count() => {
|
(Expr::StringConstant(s, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < s.chars().count() => {
|
||||||
@ -1063,7 +1055,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
&& x.args[0].is_constant()
|
&& x.args[0].is_constant()
|
||||||
=> {
|
=> {
|
||||||
let fn_name = match x.args[0] {
|
let fn_name = match x.args[0] {
|
||||||
Expr::Stack(slot, ..) => x.constants[slot].clone(),
|
|
||||||
Expr::StringConstant(ref s, ..) => s.clone().into(),
|
Expr::StringConstant(ref s, ..) => s.clone().into(),
|
||||||
_ => Dynamic::UNIT
|
_ => Dynamic::UNIT
|
||||||
};
|
};
|
||||||
@ -1077,7 +1068,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do not call some special keywords
|
// Do not call some special keywords
|
||||||
Expr::FnCall(x, ..) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => {
|
Expr::FnCall(x, ..) if DONT_EVAL_KEYWORDS.contains(&x.name.as_str()) => {
|
||||||
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1086,13 +1077,9 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
if !x.is_qualified() // Non-qualified
|
if !x.is_qualified() // Non-qualified
|
||||||
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
&& state.optimization_level == OptimizationLevel::Simple // simple optimizations
|
||||||
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
&& x.args.iter().all(Expr::is_constant) // all arguments are constants
|
||||||
//&& !is_valid_identifier(x.name.chars()) // cannot be scripted
|
//&& !is_valid_identifier(x.chars()) // cannot be scripted
|
||||||
=> {
|
=> {
|
||||||
let arg_values = &mut x.args.iter().map(|e| match e {
|
let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>();
|
||||||
Expr::Stack(slot, ..) => x.constants[*slot].clone(),
|
|
||||||
_ => e.get_literal_value().unwrap()
|
|
||||||
}).collect::<StaticVec<_>>();
|
|
||||||
|
|
||||||
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect();
|
||||||
|
|
||||||
match x.name.as_str() {
|
match x.name.as_str() {
|
||||||
@ -1109,14 +1096,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
}
|
}
|
||||||
// Overloaded operators can override built-in.
|
// Overloaded operators can override built-in.
|
||||||
_ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, arg_types.as_ref()) => {
|
_ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, arg_types.as_ref()) => {
|
||||||
if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|f| {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let lib = state.lib;
|
let lib = state.lib;
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let lib = &[];
|
let lib = &[];
|
||||||
|
|
||||||
let context = (state.engine, x.name.as_str(), lib).into();
|
let context = (state.engine, &x.name, lib).into();
|
||||||
let (first, second) = arg_values.split_first_mut().unwrap();
|
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||||
(f)(context, &mut [ first, &mut second[0] ]).ok()
|
(f)(context, &mut [ first, &mut second[0] ]).ok()
|
||||||
}) {
|
}) {
|
||||||
@ -1131,14 +1118,21 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
x.args.iter_mut().for_each(|a| optimize_expr(a, state, false));
|
||||||
|
|
||||||
// Move constant arguments
|
// Move constant arguments
|
||||||
let constants = &mut x.constants;
|
for arg in x.args.iter_mut() {
|
||||||
x.args.iter_mut().for_each(|arg| {
|
match arg {
|
||||||
if let Some(value) = arg.get_literal_value() {
|
Expr::DynamicConstant(..) | Expr::Unit(..)
|
||||||
state.set_dirty();
|
| Expr::StringConstant(..) | Expr::CharConstant(..)
|
||||||
constants.push(value);
|
| Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (),
|
||||||
*arg = Expr::Stack(constants.len()-1, arg.start_position());
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Expr:: FloatConstant(..) => (),
|
||||||
|
|
||||||
|
_ => if let Some(value) = arg.get_literal_value() {
|
||||||
|
state.set_dirty();
|
||||||
|
*arg = Expr::DynamicConstant(value.into(), arg.start_position());
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eagerly call functions
|
// Eagerly call functions
|
||||||
@ -1154,10 +1148,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
let has_script_fn = false;
|
let has_script_fn = false;
|
||||||
|
|
||||||
if !has_script_fn {
|
if !has_script_fn {
|
||||||
let arg_values = &mut x.args.iter().map(|e| match e {
|
let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>();
|
||||||
Expr::Stack(slot, ..) => x.constants[*slot].clone(),
|
|
||||||
_ => e.get_literal_value().unwrap()
|
|
||||||
}).collect::<StaticVec<_>>();
|
|
||||||
|
|
||||||
let result = match x.name.as_str() {
|
let result = match x.name.as_str() {
|
||||||
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
|
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),
|
||||||
@ -1181,19 +1172,27 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) {
|
|||||||
optimize_expr(arg, state, false);
|
optimize_expr(arg, state, false);
|
||||||
|
|
||||||
// Move constant arguments
|
// Move constant arguments
|
||||||
if let Some(value) = arg.get_literal_value() {
|
match arg {
|
||||||
state.set_dirty();
|
Expr::DynamicConstant(..) | Expr::Unit(..)
|
||||||
x.constants.push(value);
|
| Expr::StringConstant(..) | Expr::CharConstant(..)
|
||||||
*arg = Expr::Stack(x.constants.len()-1, arg.start_position());
|
| Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Expr:: FloatConstant(..) => (),
|
||||||
|
|
||||||
|
_ => if let Some(value) = arg.get_literal_value() {
|
||||||
|
state.set_dirty();
|
||||||
|
*arg = Expr::DynamicConstant(value.into(), arg.start_position());
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::Variable(.., x) if x.1.is_some() => (),
|
Expr::Variable(x, ..) if !x.1.is_empty() => (),
|
||||||
Expr::Variable(.., pos, x) if state.find_constant(&x.2).is_some() => {
|
Expr::Variable(x, .., pos) if state.find_constant(&x.3).is_some() => {
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
*expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos);
|
*expr = Expr::from_dynamic(state.find_constant(&x.3).unwrap().clone(), *pos);
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1278,7 +1277,7 @@ pub fn optimize_into_ast(
|
|||||||
environ: None,
|
environ: None,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: None,
|
comments: Box::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::eval::calc_index;
|
use crate::eval::calc_index;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, UNSIGNED_INT,
|
def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, INT_BITS,
|
||||||
|
UNSIGNED_INT,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -17,8 +18,6 @@ def_package! {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod bit_field_functions {
|
mod bit_field_functions {
|
||||||
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
|
||||||
|
|
||||||
/// Return `true` if the specified `bit` in the number is set.
|
/// Return `true` if the specified `bit` in the number is set.
|
||||||
///
|
///
|
||||||
/// If `bit` < 0, position counts from the MSB (Most Significant Bit).
|
/// If `bit` < 0, position counts from the MSB (Most Significant Bit).
|
||||||
@ -36,8 +35,8 @@ mod bit_field_functions {
|
|||||||
/// ```
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn get_bit(value: INT, bit: INT) -> RhaiResultOf<bool> {
|
pub fn get_bit(value: INT, bit: INT) -> RhaiResultOf<bool> {
|
||||||
let bit = calc_index(BITS, bit, true, || {
|
let bit = calc_index(INT_BITS, bit, true, || {
|
||||||
ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into()
|
ERR::ErrorBitFieldBounds(INT_BITS, bit, Position::NONE).into()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok((value & (1 << bit)) != 0)
|
Ok((value & (1 << bit)) != 0)
|
||||||
@ -66,8 +65,8 @@ mod bit_field_functions {
|
|||||||
/// ```
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
#[rhai_fn(return_raw)]
|
||||||
pub fn set_bit(value: &mut INT, bit: INT, new_value: bool) -> RhaiResultOf<()> {
|
pub fn set_bit(value: &mut INT, bit: INT, new_value: bool) -> RhaiResultOf<()> {
|
||||||
let bit = calc_index(BITS, bit, true, || {
|
let bit = calc_index(INT_BITS, bit, true, || {
|
||||||
ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into()
|
ERR::ErrorBitFieldBounds(INT_BITS, bit, Position::NONE).into()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let mask = 1 << bit;
|
let mask = 1 << bit;
|
||||||
@ -128,17 +127,17 @@ mod bit_field_functions {
|
|||||||
return Ok(0);
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
let bit = calc_index(BITS, start, true, || {
|
let bit = calc_index(INT_BITS, start, true, || {
|
||||||
ERR::ErrorBitFieldBounds(BITS, start, Position::NONE).into()
|
ERR::ErrorBitFieldBounds(INT_BITS, start, Position::NONE).into()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let bits = if bit + bits as usize > BITS {
|
let bits = if bit + bits as usize > INT_BITS {
|
||||||
BITS - bit
|
INT_BITS - bit
|
||||||
} else {
|
} else {
|
||||||
bits as usize
|
bits as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
if bit == 0 && bits == BITS {
|
if bit == 0 && bits == INT_BITS {
|
||||||
return Ok(value);
|
return Ok(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,17 +213,17 @@ mod bit_field_functions {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let bit = calc_index(BITS, bit, true, || {
|
let bit = calc_index(INT_BITS, bit, true, || {
|
||||||
ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into()
|
ERR::ErrorBitFieldBounds(INT_BITS, bit, Position::NONE).into()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let bits = if bit + bits as usize > BITS {
|
let bits = if bit + bits as usize > INT_BITS {
|
||||||
BITS - bit
|
INT_BITS - bit
|
||||||
} else {
|
} else {
|
||||||
bits as usize
|
bits as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
if bit == 0 && bits == BITS {
|
if bit == 0 && bits == INT_BITS {
|
||||||
*value = new_value;
|
*value = new_value;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
@ -4,19 +4,14 @@ use crate::eval::{calc_index, calc_offset_len};
|
|||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
|
def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position,
|
||||||
RhaiResultOf, INT,
|
RhaiResultOf, INT, INT_BYTES,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::TypeId, mem};
|
use std::{any::TypeId, mem};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::{FLOAT, FLOAT_BYTES};
|
||||||
|
|
||||||
const INT_BYTES: usize = mem::size_of::<INT>();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
const FLOAT_BYTES: usize = mem::size_of::<FLOAT>();
|
|
||||||
|
|
||||||
def_package! {
|
def_package! {
|
||||||
/// Package of basic BLOB utilities.
|
/// Package of basic BLOB utilities.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::eval::calc_index;
|
use crate::eval::calc_index;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT};
|
use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT, INT_BITS};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -117,18 +117,16 @@ impl<T> FusedIterator for StepRange<T> where
|
|||||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||||
struct BitRange(INT, INT, usize);
|
struct BitRange(INT, INT, usize);
|
||||||
|
|
||||||
const BITS: usize = std::mem::size_of::<INT>() * 8;
|
|
||||||
|
|
||||||
impl BitRange {
|
impl BitRange {
|
||||||
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
|
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
|
||||||
let from = calc_index(BITS, from, true, || {
|
let from = calc_index(INT_BITS, from, true, || {
|
||||||
crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into()
|
crate::ERR::ErrorBitFieldBounds(INT_BITS, from, Position::NONE).into()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let len = if len < 0 {
|
let len = if len < 0 {
|
||||||
0
|
0
|
||||||
} else if from + (len as usize) > BITS {
|
} else if from + (len as usize) > INT_BITS {
|
||||||
BITS - from
|
INT_BITS - from
|
||||||
} else {
|
} else {
|
||||||
len as usize
|
len as usize
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,9 @@ use crate::{Dynamic, RhaiResultOf, ERR, INT};
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
use crate::FLOAT;
|
||||||
|
|
||||||
def_package! {
|
def_package! {
|
||||||
/// Package of core language features.
|
/// Package of core language features.
|
||||||
pub LanguageCorePackage(lib) {
|
pub LanguageCorePackage(lib) {
|
||||||
@ -21,10 +24,32 @@ def_package! {
|
|||||||
|
|
||||||
#[export_module]
|
#[export_module]
|
||||||
mod core_functions {
|
mod core_functions {
|
||||||
|
/// Return the _tag_ of a `Dynamic` value.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = "hello, world!";
|
||||||
|
///
|
||||||
|
/// x.tag = 42;
|
||||||
|
///
|
||||||
|
/// print(x.tag); // prints 42
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "tag", get = "tag", pure)]
|
#[rhai_fn(name = "tag", get = "tag", pure)]
|
||||||
pub fn get_tag(value: &mut Dynamic) -> INT {
|
pub fn get_tag(value: &mut Dynamic) -> INT {
|
||||||
value.tag() as INT
|
value.tag() as INT
|
||||||
}
|
}
|
||||||
|
/// Set the _tag_ of a `Dynamic` value.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = "hello, world!";
|
||||||
|
///
|
||||||
|
/// x.tag = 42;
|
||||||
|
///
|
||||||
|
/// print(x.tag); // prints 42
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "set_tag", set = "tag", return_raw)]
|
#[rhai_fn(name = "set_tag", set = "tag", return_raw)]
|
||||||
pub fn set_tag(value: &mut Dynamic, tag: INT) -> RhaiResultOf<()> {
|
pub fn set_tag(value: &mut Dynamic, tag: INT) -> RhaiResultOf<()> {
|
||||||
if tag < Tag::MIN as INT {
|
if tag < Tag::MIN as INT {
|
||||||
@ -54,6 +79,30 @@ mod core_functions {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Block the current thread for a particular number of `seconds`.
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[rhai_fn(name = "sleep")]
|
||||||
|
pub fn sleep_float(seconds: FLOAT) {
|
||||||
|
if seconds <= 0.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "f32_float"))]
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs_f64(seconds));
|
||||||
|
#[cfg(feature = "f32_float")]
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs_f32(seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Block the current thread for a particular number of `seconds`.
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
pub fn sleep(seconds: INT) {
|
||||||
|
if seconds <= 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(seconds as u64));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -92,7 +141,7 @@ fn collect_fn_metadata(
|
|||||||
// Create a metadata record for a function.
|
// Create a metadata record for a function.
|
||||||
fn make_metadata(
|
fn make_metadata(
|
||||||
dict: &BTreeSet<Identifier>,
|
dict: &BTreeSet<Identifier>,
|
||||||
#[cfg(not(feature = "no_module"))] namespace: Option<Identifier>,
|
#[cfg(not(feature = "no_module"))] namespace: Identifier,
|
||||||
func: &ScriptFnDef,
|
func: &ScriptFnDef,
|
||||||
) -> Map {
|
) -> Map {
|
||||||
const DICT: &str = "key exists";
|
const DICT: &str = "key exists";
|
||||||
@ -100,8 +149,8 @@ fn collect_fn_metadata(
|
|||||||
let mut map = Map::new();
|
let mut map = Map::new();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some(ns) = namespace {
|
if !namespace.is_empty() {
|
||||||
map.insert(dict.get("namespace").expect(DICT).clone(), ns.into());
|
map.insert(dict.get("namespace").expect(DICT).clone(), namespace.into());
|
||||||
}
|
}
|
||||||
map.insert(
|
map.insert(
|
||||||
dict.get("name").expect(DICT).clone(),
|
dict.get("name").expect(DICT).clone(),
|
||||||
@ -157,7 +206,7 @@ fn collect_fn_metadata(
|
|||||||
make_metadata(
|
make_metadata(
|
||||||
&dict,
|
&dict,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
None,
|
Identifier::new_const(),
|
||||||
f,
|
f,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@ -174,7 +223,7 @@ fn collect_fn_metadata(
|
|||||||
make_metadata(
|
make_metadata(
|
||||||
&dict,
|
&dict,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
None,
|
Identifier::new_const(),
|
||||||
f,
|
f,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@ -192,7 +241,7 @@ fn collect_fn_metadata(
|
|||||||
make_metadata(
|
make_metadata(
|
||||||
&dict,
|
&dict,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
None,
|
Identifier::new_const(),
|
||||||
f,
|
f,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@ -219,9 +268,7 @@ fn collect_fn_metadata(
|
|||||||
module
|
module
|
||||||
.iter_script_fn()
|
.iter_script_fn()
|
||||||
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
|
.filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f))
|
||||||
.for_each(|(.., f)| {
|
.for_each(|(.., f)| list.push(make_metadata(dict, namespace.clone(), f).into()));
|
||||||
list.push(make_metadata(dict, Some(namespace.clone()), f).into())
|
|
||||||
});
|
|
||||||
for (ns, m) in module.iter_sub_modules() {
|
for (ns, m) in module.iter_sub_modules() {
|
||||||
let ns = format!(
|
let ns = format!(
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
|
@ -31,6 +31,16 @@ mod time_functions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the number of seconds between the current system time and the timestamp.
|
/// Return the number of seconds between the current system time and the timestamp.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let now = timestamp();
|
||||||
|
///
|
||||||
|
/// sleep(10.0); // sleep for 10 seconds
|
||||||
|
///
|
||||||
|
/// print(now.elapsed); // prints 10.???
|
||||||
|
/// ```
|
||||||
#[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
|
#[rhai_fn(name = "elapsed", get = "elapsed", return_raw)]
|
||||||
pub fn elapsed(timestamp: Instant) -> RhaiResult {
|
pub fn elapsed(timestamp: Instant) -> RhaiResult {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
203
src/parser.rs
203
src/parser.rs
@ -135,7 +135,7 @@ impl<'e> ParseState<'e> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if self.allow_capture {
|
if self.allow_capture {
|
||||||
if index == 0 && !self.external_vars.iter().any(|v| v.name == name) {
|
if index == 0 && !self.external_vars.iter().any(|v| v.as_str() == name) {
|
||||||
self.external_vars.push(crate::ast::Ident {
|
self.external_vars.push(crate::ast::Ident {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
pos: _pos,
|
pos: _pos,
|
||||||
@ -266,9 +266,9 @@ impl Expr {
|
|||||||
fn into_property(self, state: &mut ParseState) -> Self {
|
fn into_property(self, state: &mut ParseState) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Self::Variable(.., x) if x.1.is_some() => unreachable!("qualified property"),
|
Self::Variable(x, ..) if !x.1.is_empty() => unreachable!("qualified property"),
|
||||||
Self::Variable(.., pos, x) => {
|
Self::Variable(x, .., pos) => {
|
||||||
let ident = x.2;
|
let ident = x.3;
|
||||||
let getter = state.get_identifier(crate::engine::FN_GET, &ident);
|
let getter = state.get_identifier(crate::engine::FN_GET, &ident);
|
||||||
let hash_get = calc_fn_hash(&getter, 1);
|
let hash_get = calc_fn_hash(&getter, 1);
|
||||||
let setter = state.get_identifier(crate::engine::FN_SET, &ident);
|
let setter = state.get_identifier(crate::engine::FN_SET, &ident);
|
||||||
@ -456,7 +456,7 @@ impl Engine {
|
|||||||
lib: &mut FnLib,
|
lib: &mut FnLib,
|
||||||
id: Identifier,
|
id: Identifier,
|
||||||
capture_parent_scope: bool,
|
capture_parent_scope: bool,
|
||||||
#[cfg(not(feature = "no_module"))] namespace: Option<crate::module::Namespace>,
|
#[cfg(not(feature = "no_module"))] namespace: crate::ast::Namespace,
|
||||||
settings: ParseSettings,
|
settings: ParseSettings,
|
||||||
) -> ParseResult<Expr> {
|
) -> ParseResult<Expr> {
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
@ -484,7 +484,7 @@ impl Engine {
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let hash = if let Some(namespace) = namespace.as_mut() {
|
let hash = if !namespace.is_empty() {
|
||||||
let index = state.find_module(namespace.root());
|
let index = state.find_module(namespace.root());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -499,7 +499,7 @@ impl Engine {
|
|||||||
|
|
||||||
namespace.set_index(index);
|
namespace.set_index(index);
|
||||||
|
|
||||||
crate::calc_qualified_fn_hash(namespace.iter().map(|m| m.name.as_str()), &id, 0)
|
crate::calc_qualified_fn_hash(namespace.iter().map(|m| m.as_str()), &id, 0)
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(&id, 0)
|
calc_fn_hash(&id, 0)
|
||||||
};
|
};
|
||||||
@ -521,7 +521,6 @@ impl Engine {
|
|||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
constants: StaticVec::new_const(),
|
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(settings.pos));
|
.into_fn_call_expr(settings.pos));
|
||||||
@ -545,7 +544,7 @@ impl Engine {
|
|||||||
eat_token(input, Token::RightParen);
|
eat_token(input, Token::RightParen);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let hash = if let Some(namespace) = namespace.as_mut() {
|
let hash = if !namespace.is_empty() {
|
||||||
let index = state.find_module(namespace.root());
|
let index = state.find_module(namespace.root());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -561,7 +560,7 @@ impl Engine {
|
|||||||
namespace.set_index(index);
|
namespace.set_index(index);
|
||||||
|
|
||||||
crate::calc_qualified_fn_hash(
|
crate::calc_qualified_fn_hash(
|
||||||
namespace.iter().map(|m| m.name.as_str()),
|
namespace.iter().map(|m| m.as_str()),
|
||||||
&id,
|
&id,
|
||||||
args.len(),
|
args.len(),
|
||||||
)
|
)
|
||||||
@ -586,7 +585,6 @@ impl Engine {
|
|||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
args,
|
args,
|
||||||
constants: StaticVec::new_const(),
|
|
||||||
pos: settings.pos,
|
pos: settings.pos,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(settings.pos));
|
.into_fn_call_expr(settings.pos));
|
||||||
@ -902,7 +900,7 @@ impl Engine {
|
|||||||
|
|
||||||
let (name, pos) = match input.next().expect(NEVER_ENDS) {
|
let (name, pos) = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::Identifier(s), pos) | (Token::StringConstant(s), pos) => {
|
(Token::Identifier(s), pos) | (Token::StringConstant(s), pos) => {
|
||||||
if map.iter().any(|(p, ..)| p.name == &*s) {
|
if map.iter().any(|(p, ..)| **p == s) {
|
||||||
return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos));
|
return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos));
|
||||||
}
|
}
|
||||||
(s, pos)
|
(s, pos)
|
||||||
@ -1389,9 +1387,9 @@ impl Engine {
|
|||||||
// Identifier
|
// Identifier
|
||||||
Token::Identifier(..) => {
|
Token::Identifier(..) => {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let none = None;
|
let ns = crate::ast::Namespace::NONE;
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
let none = ();
|
let ns = ();
|
||||||
|
|
||||||
let s = match input.next().expect(NEVER_ENDS) {
|
let s = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::Identifier(s), ..) => s,
|
(Token::Identifier(s), ..) => s,
|
||||||
@ -1407,9 +1405,9 @@ impl Engine {
|
|||||||
state.allow_capture = true;
|
state.allow_capture = true;
|
||||||
}
|
}
|
||||||
Expr::Variable(
|
Expr::Variable(
|
||||||
|
(None, ns, 0, state.get_identifier("", s)).into(),
|
||||||
None,
|
None,
|
||||||
settings.pos,
|
settings.pos,
|
||||||
(None, none, state.get_identifier("", s)).into(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Namespace qualification
|
// Namespace qualification
|
||||||
@ -1421,9 +1419,9 @@ impl Engine {
|
|||||||
state.allow_capture = true;
|
state.allow_capture = true;
|
||||||
}
|
}
|
||||||
Expr::Variable(
|
Expr::Variable(
|
||||||
|
(None, ns, 0, state.get_identifier("", s)).into(),
|
||||||
None,
|
None,
|
||||||
settings.pos,
|
settings.pos,
|
||||||
(None, none, state.get_identifier("", s)).into(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Normal variable access
|
// Normal variable access
|
||||||
@ -1444,9 +1442,9 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
Expr::Variable(
|
Expr::Variable(
|
||||||
|
(index, ns, 0, state.get_identifier("", s)).into(),
|
||||||
short_index,
|
short_index,
|
||||||
settings.pos,
|
settings.pos,
|
||||||
(index, none, state.get_identifier("", s)).into(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1455,9 +1453,9 @@ impl Engine {
|
|||||||
// Reserved keyword or symbol
|
// Reserved keyword or symbol
|
||||||
Token::Reserved(..) => {
|
Token::Reserved(..) => {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let none = None;
|
let ns = crate::ast::Namespace::NONE;
|
||||||
#[cfg(feature = "no_module")]
|
#[cfg(feature = "no_module")]
|
||||||
let none = ();
|
let ns = ();
|
||||||
|
|
||||||
let s = match input.next().expect(NEVER_ENDS) {
|
let s = match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::Reserved(s), ..) => s,
|
(Token::Reserved(s), ..) => s,
|
||||||
@ -1467,16 +1465,16 @@ impl Engine {
|
|||||||
match input.peek().expect(NEVER_ENDS).0 {
|
match input.peek().expect(NEVER_ENDS).0 {
|
||||||
// Function call is allowed to have reserved keyword
|
// Function call is allowed to have reserved keyword
|
||||||
Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable(
|
Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable(
|
||||||
|
(None, ns, 0, state.get_identifier("", s)).into(),
|
||||||
None,
|
None,
|
||||||
settings.pos,
|
settings.pos,
|
||||||
(None, none, state.get_identifier("", s)).into(),
|
|
||||||
),
|
),
|
||||||
// Access to `this` as a variable is OK within a function scope
|
// Access to `this` as a variable is OK within a function scope
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
_ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable(
|
_ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable(
|
||||||
|
(None, ns, 0, state.get_identifier("", s)).into(),
|
||||||
None,
|
None,
|
||||||
settings.pos,
|
settings.pos,
|
||||||
(None, none, state.get_identifier("", s)).into(),
|
|
||||||
),
|
),
|
||||||
// Cannot access to `this` as a variable not in a function scope
|
// Cannot access to `this` as a variable not in a function scope
|
||||||
_ if &*s == KEYWORD_THIS => {
|
_ if &*s == KEYWORD_THIS => {
|
||||||
@ -1529,7 +1527,7 @@ impl Engine {
|
|||||||
lhs = match (lhs, tail_token) {
|
lhs = match (lhs, tail_token) {
|
||||||
// Qualified function call with !
|
// Qualified function call with !
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(Expr::Variable(.., x), Token::Bang) if x.1.is_some() => {
|
(Expr::Variable(x, ..), Token::Bang) if !x.1.is_empty() => {
|
||||||
return if !match_token(input, Token::LeftParen).0 {
|
return if !match_token(input, Token::LeftParen).0 {
|
||||||
Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string())
|
Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string())
|
||||||
.into_err(tail_pos))
|
.into_err(tail_pos))
|
||||||
@ -1542,7 +1540,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Function call with !
|
// Function call with !
|
||||||
(Expr::Variable(.., pos, x), Token::Bang) => {
|
(Expr::Variable(x, .., pos), Token::Bang) => {
|
||||||
match match_token(input, Token::LeftParen) {
|
match match_token(input, Token::LeftParen) {
|
||||||
(false, pos) => {
|
(false, pos) => {
|
||||||
return Err(PERR::MissingToken(
|
return Err(PERR::MissingToken(
|
||||||
@ -1554,10 +1552,8 @@ impl Engine {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let (.., _ns, name) = *x;
|
let (.., _ns, _, name) = *x;
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
let _ns = _ns.map(|(ns, ..)| ns);
|
|
||||||
self.parse_fn_call(
|
self.parse_fn_call(
|
||||||
input,
|
input,
|
||||||
state,
|
state,
|
||||||
@ -1570,10 +1566,8 @@ impl Engine {
|
|||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
// Function call
|
// Function call
|
||||||
(Expr::Variable(.., pos, x), Token::LeftParen) => {
|
(Expr::Variable(x, .., pos), Token::LeftParen) => {
|
||||||
let (.., _ns, name) = *x;
|
let (.., _ns, _, name) = *x;
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
let _ns = _ns.map(|(ns, ..)| ns);
|
|
||||||
settings.pos = pos;
|
settings.pos = pos;
|
||||||
self.parse_fn_call(
|
self.parse_fn_call(
|
||||||
input,
|
input,
|
||||||
@ -1588,23 +1582,17 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// module access
|
// module access
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(Expr::Variable(.., pos, x), Token::DoubleColon) => {
|
(Expr::Variable(x, .., pos), Token::DoubleColon) => {
|
||||||
let (id2, pos2) = parse_var_name(input)?;
|
let (id2, pos2) = parse_var_name(input)?;
|
||||||
let (.., mut namespace, name) = *x;
|
let (.., mut namespace, _, name) = *x;
|
||||||
let var_name_def = Ident { name, pos };
|
let var_name_def = Ident { name, pos };
|
||||||
|
|
||||||
if let Some((ref mut namespace, ..)) = namespace {
|
namespace.push(var_name_def);
|
||||||
namespace.push(var_name_def);
|
|
||||||
} else {
|
|
||||||
let mut ns = crate::module::Namespace::new();
|
|
||||||
ns.push(var_name_def);
|
|
||||||
namespace = Some((ns, 42));
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Variable(
|
Expr::Variable(
|
||||||
|
(None, namespace, 0, state.get_identifier("", id2)).into(),
|
||||||
None,
|
None,
|
||||||
pos2,
|
pos2,
|
||||||
(None, namespace, state.get_identifier("", id2)).into(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Indexing
|
// Indexing
|
||||||
@ -1643,33 +1631,35 @@ impl Engine {
|
|||||||
// Cache the hash key for namespace-qualified variables
|
// Cache the hash key for namespace-qualified variables
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let namespaced_variable = match lhs {
|
let namespaced_variable = match lhs {
|
||||||
Expr::Variable(.., ref mut x) if x.1.is_some() => Some(x.as_mut()),
|
Expr::Variable(ref mut x, ..) if !x.1.is_empty() => Some(x.as_mut()),
|
||||||
Expr::Index(ref mut x, ..) | Expr::Dot(ref mut x, ..) => match x.lhs {
|
Expr::Index(ref mut x, ..) | Expr::Dot(ref mut x, ..) => match x.lhs {
|
||||||
Expr::Variable(.., ref mut x) if x.1.is_some() => Some(x.as_mut()),
|
Expr::Variable(ref mut x, ..) if !x.1.is_empty() => Some(x.as_mut()),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if let Some((.., Some((namespace, hash)), name)) = namespaced_variable {
|
if let Some((.., namespace, hash, name)) = namespaced_variable {
|
||||||
*hash = crate::calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name);
|
if !namespace.is_empty() {
|
||||||
|
*hash = crate::calc_qualified_var_hash(namespace.iter().map(|v| v.as_str()), name);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
let index = state.find_module(namespace.root());
|
let index = state.find_module(namespace.root());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let relax = settings.is_function_scope;
|
let relax = settings.is_function_scope;
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let relax = false;
|
let relax = false;
|
||||||
|
|
||||||
if !relax && settings.options.strict_var && index.is_none() {
|
if !relax && settings.options.strict_var && index.is_none() {
|
||||||
return Err(PERR::ModuleUndefined(namespace.root().to_string())
|
return Err(PERR::ModuleUndefined(namespace.root().to_string())
|
||||||
.into_err(namespace.position()));
|
.into_err(namespace.position()));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace.set_index(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace.set_index(index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1828,13 +1818,13 @@ impl Engine {
|
|||||||
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.start_position()))
|
Err(PERR::AssignmentToConstant("".into()).into_err(lhs.start_position()))
|
||||||
}
|
}
|
||||||
// var (non-indexed) = rhs
|
// var (non-indexed) = rhs
|
||||||
Expr::Variable(None, _, ref x) if x.0.is_none() => Ok(Stmt::Assignment(
|
Expr::Variable(ref x, None, _) if x.0.is_none() => Ok(Stmt::Assignment(
|
||||||
(op_info, (lhs, rhs).into()).into(),
|
(op_info, (lhs, rhs).into()).into(),
|
||||||
op_pos,
|
op_pos,
|
||||||
)),
|
)),
|
||||||
// var (indexed) = rhs
|
// var (indexed) = rhs
|
||||||
Expr::Variable(i, var_pos, ref x) => {
|
Expr::Variable(ref x, i, var_pos) => {
|
||||||
let (index, _, name) = x.as_ref();
|
let (index, .., name) = x.as_ref();
|
||||||
let index = i.map_or_else(
|
let index = i.map_or_else(
|
||||||
|| index.expect("either long or short index is `None`").get(),
|
|| index.expect("either long or short index is `None`").get(),
|
||||||
|n| n.get() as usize,
|
|n| n.get() as usize,
|
||||||
@ -1939,8 +1929,8 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// lhs.module::id - syntax error
|
// lhs.module::id - syntax error
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(.., Expr::Variable(.., x)) if x.1.is_some() => {
|
(.., Expr::Variable(x, ..)) if !x.1.is_empty() => {
|
||||||
Err(PERR::PropertyExpected.into_err(x.1.expect("`Some`").0.position()))
|
Err(PERR::PropertyExpected.into_err(x.1.position()))
|
||||||
}
|
}
|
||||||
// lhs.id
|
// lhs.id
|
||||||
(lhs, var_expr @ Expr::Variable(..)) => {
|
(lhs, var_expr @ Expr::Variable(..)) => {
|
||||||
@ -1960,7 +1950,7 @@ impl Engine {
|
|||||||
// lhs.nnn::func(...) - syntax error
|
// lhs.nnn::func(...) - syntax error
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(.., Expr::FnCall(func, ..)) if func.is_qualified() => {
|
(.., Expr::FnCall(func, ..)) if func.is_qualified() => {
|
||||||
Err(PERR::PropertyExpected.into_err(func.namespace.expect("`Some`").position()))
|
Err(PERR::PropertyExpected.into_err(func.namespace.position()))
|
||||||
}
|
}
|
||||||
// lhs.Fn() or lhs.eval()
|
// lhs.Fn() or lhs.eval()
|
||||||
(.., Expr::FnCall(func, func_pos))
|
(.., Expr::FnCall(func, func_pos))
|
||||||
@ -2008,14 +1998,13 @@ impl Engine {
|
|||||||
match x.lhs {
|
match x.lhs {
|
||||||
// lhs.module::id.dot_rhs or lhs.module::id[idx_rhs] - syntax error
|
// lhs.module::id.dot_rhs or lhs.module::id[idx_rhs] - syntax error
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::Variable(.., x) if x.1.is_some() => {
|
Expr::Variable(x, ..) if !x.1.is_empty() => {
|
||||||
Err(PERR::PropertyExpected.into_err(x.1.expect("`Some`").0.position()))
|
Err(PERR::PropertyExpected.into_err(x.1.position()))
|
||||||
}
|
}
|
||||||
// lhs.module::func().dot_rhs or lhs.module::func()[idx_rhs] - syntax error
|
// lhs.module::func().dot_rhs or lhs.module::func()[idx_rhs] - syntax error
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Expr::FnCall(func, ..) if func.is_qualified() => {
|
Expr::FnCall(func, ..) if func.is_qualified() => {
|
||||||
Err(PERR::PropertyExpected
|
Err(PERR::PropertyExpected.into_err(func.namespace.position()))
|
||||||
.into_err(func.namespace.expect("`Some`").position()))
|
|
||||||
}
|
}
|
||||||
// lhs.id.dot_rhs or lhs.id[idx_rhs]
|
// lhs.id.dot_rhs or lhs.id[idx_rhs]
|
||||||
Expr::Variable(..) | Expr::Property(..) => {
|
Expr::Variable(..) | Expr::Property(..) => {
|
||||||
@ -2296,21 +2285,15 @@ impl Engine {
|
|||||||
CUSTOM_SYNTAX_MARKER_IDENT => {
|
CUSTOM_SYNTAX_MARKER_IDENT => {
|
||||||
let (name, pos) = parse_var_name(input)?;
|
let (name, pos) = parse_var_name(input)?;
|
||||||
let name = state.get_identifier("", name);
|
let name = state.get_identifier("", name);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
let ns = crate::ast::Namespace::NONE;
|
||||||
|
#[cfg(feature = "no_module")]
|
||||||
|
let ns = ();
|
||||||
|
|
||||||
segments.push(name.clone().into());
|
segments.push(name.clone().into());
|
||||||
tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_IDENT));
|
tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_IDENT));
|
||||||
inputs.push(Expr::Variable(
|
inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos));
|
||||||
None,
|
|
||||||
pos,
|
|
||||||
(
|
|
||||||
None,
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
None,
|
|
||||||
#[cfg(feature = "no_module")]
|
|
||||||
(),
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
CUSTOM_SYNTAX_MARKER_SYMBOL => {
|
CUSTOM_SYNTAX_MARKER_SYMBOL => {
|
||||||
let (symbol, pos) = parse_symbol(input)?;
|
let (symbol, pos) = parse_symbol(input)?;
|
||||||
@ -2613,11 +2596,11 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
.into_err(pos));
|
.into_err(pos));
|
||||||
}
|
}
|
||||||
(name, name_pos, Some(counter_name), Some(counter_pos))
|
(name, name_pos, counter_name, counter_pos)
|
||||||
} else {
|
} else {
|
||||||
// name
|
// name
|
||||||
let (name, name_pos) = parse_var_name(input)?;
|
let (name, name_pos) = parse_var_name(input)?;
|
||||||
(name, name_pos, None, None)
|
(name, name_pos, Identifier::new_const(), Position::NONE)
|
||||||
};
|
};
|
||||||
|
|
||||||
// for name in ...
|
// for name in ...
|
||||||
@ -2641,12 +2624,13 @@ impl Engine {
|
|||||||
|
|
||||||
let prev_stack_len = state.stack.len();
|
let prev_stack_len = state.stack.len();
|
||||||
|
|
||||||
let counter_var = counter_name.map(|name| {
|
if !counter_name.is_empty() {
|
||||||
let name = state.get_identifier("", name);
|
|
||||||
let pos = counter_pos.expect("`Some`");
|
|
||||||
state.stack.push(name.clone(), ());
|
state.stack.push(name.clone(), ());
|
||||||
Ident { name, pos }
|
}
|
||||||
});
|
let counter_var = Ident {
|
||||||
|
name: state.get_identifier("", counter_name),
|
||||||
|
pos: counter_pos,
|
||||||
|
};
|
||||||
|
|
||||||
let loop_var = state.get_identifier("", name);
|
let loop_var = state.get_identifier("", name);
|
||||||
state.stack.push(loop_var.clone(), ());
|
state.stack.push(loop_var.clone(), ());
|
||||||
@ -2788,7 +2772,7 @@ impl Engine {
|
|||||||
|
|
||||||
// import expr as ...
|
// import expr as ...
|
||||||
if !match_token(input, Token::As).0 {
|
if !match_token(input, Token::As).0 {
|
||||||
return Ok(Stmt::Import((expr, None).into(), settings.pos));
|
return Ok(Stmt::Import((expr, Ident::EMPTY).into(), settings.pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// import expr as name ...
|
// import expr as name ...
|
||||||
@ -2797,7 +2781,7 @@ impl Engine {
|
|||||||
state.imports.push(name.clone());
|
state.imports.push(name.clone());
|
||||||
|
|
||||||
Ok(Stmt::Import(
|
Ok(Stmt::Import(
|
||||||
(expr, Some(Ident { name, pos })).into(),
|
(expr, Ident { name, pos }).into(),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -3231,15 +3215,15 @@ impl Engine {
|
|||||||
|
|
||||||
let name = state.get_identifier("", name);
|
let name = state.get_identifier("", name);
|
||||||
state.stack.push(name.clone(), ());
|
state.stack.push(name.clone(), ());
|
||||||
Some(Ident { name, pos })
|
Ident { name, pos }
|
||||||
} else {
|
} else {
|
||||||
None
|
Ident::EMPTY
|
||||||
};
|
};
|
||||||
|
|
||||||
// try { try_block } catch ( var ) { catch_block }
|
// try { try_block } catch ( var ) { catch_block }
|
||||||
let catch_block = self.parse_block(input, state, lib, settings.level_up())?;
|
let catch_block = self.parse_block(input, state, lib, settings.level_up())?;
|
||||||
|
|
||||||
if catch_var.is_some() {
|
if !catch_var.is_empty() {
|
||||||
// Remove the error variable from the stack
|
// Remove the error variable from the stack
|
||||||
state.stack.rewind(state.stack.len() - 1);
|
state.stack.rewind(state.stack.len() - 1);
|
||||||
}
|
}
|
||||||
@ -3346,17 +3330,11 @@ impl Engine {
|
|||||||
environ: None,
|
environ: None,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: if comments.is_empty() {
|
comments: comments
|
||||||
None
|
.into_iter()
|
||||||
} else {
|
.map(|s| s.to_string().into_boxed_str())
|
||||||
Some(
|
.collect::<Vec<_>>()
|
||||||
comments
|
.into_boxed_slice(),
|
||||||
.into_iter()
|
|
||||||
.map(|s| s.to_string().into_boxed_str())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.into_boxed_slice(),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3384,19 +3362,12 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|crate::ast::Ident { name, pos }| {
|
.map(|crate::ast::Ident { name, pos }| {
|
||||||
Expr::Variable(
|
#[cfg(not(feature = "no_module"))]
|
||||||
None,
|
let ns = crate::ast::Namespace::NONE;
|
||||||
pos,
|
#[cfg(feature = "no_module")]
|
||||||
(
|
let ns = ();
|
||||||
None,
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
Expr::Variable((None, ns, 0, name).into(), None, pos)
|
||||||
None,
|
|
||||||
#[cfg(feature = "no_module")]
|
|
||||||
(),
|
|
||||||
name,
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -3518,7 +3489,7 @@ impl Engine {
|
|||||||
environ: None,
|
environ: None,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
comments: None,
|
comments: Box::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let fn_ptr = crate::FnPtr::new_unchecked(fn_name, StaticVec::new_const());
|
let fn_ptr = crate::FnPtr::new_unchecked(fn_name, StaticVec::new_const());
|
||||||
|
@ -145,13 +145,11 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
|||||||
.get_script_fn_def()
|
.get_script_fn_def()
|
||||||
.expect("script-defined function")
|
.expect("script-defined function")
|
||||||
.comments
|
.comments
|
||||||
.as_ref()
|
.iter()
|
||||||
.map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect())
|
.map(Box::as_ref)
|
||||||
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
info.metadata
|
info.metadata.comments.iter().map(Box::as_ref).collect()
|
||||||
.comments
|
|
||||||
.as_ref()
|
|
||||||
.map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect())
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -514,13 +514,37 @@ impl Scope<'_> {
|
|||||||
/// Panics if the index is out of bounds.
|
/// Panics if the index is out of bounds.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
pub(crate) fn add_alias_by_index(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
||||||
let aliases = self.aliases.get_mut(index).unwrap();
|
let aliases = self.aliases.get_mut(index).unwrap();
|
||||||
if aliases.is_empty() || !aliases.contains(&alias) {
|
if aliases.is_empty() || !aliases.contains(&alias) {
|
||||||
aliases.push(alias);
|
aliases.push(alias);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
/// Add an alias to a variable in the [`Scope`] so that it is exported under that name.
|
||||||
|
/// This is an advanced API.
|
||||||
|
///
|
||||||
|
/// If the alias is empty, then the variable is exported under its original name.
|
||||||
|
///
|
||||||
|
/// Multiple aliases can be added to any variable.
|
||||||
|
///
|
||||||
|
/// Only the last variable matching the name (and not other shadowed versions) is aliased by
|
||||||
|
/// this call.
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[inline]
|
||||||
|
pub fn set_alias(
|
||||||
|
&mut self,
|
||||||
|
name: impl AsRef<str> + Into<Identifier>,
|
||||||
|
alias: impl Into<Identifier>,
|
||||||
|
) {
|
||||||
|
if let Some((index, ..)) = self.get_index(name.as_ref()) {
|
||||||
|
let alias = match alias.into() {
|
||||||
|
x if x.is_empty() => name.into(),
|
||||||
|
x => x,
|
||||||
|
};
|
||||||
|
self.add_alias_by_index(index, alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Clone the [`Scope`], keeping only the last instances of each variable name.
|
/// Clone the [`Scope`], keeping only the last instances of each variable name.
|
||||||
/// Shadowed variables are omitted in the copy.
|
/// Shadowed variables are omitted in the copy.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -9,66 +9,64 @@ fn test_blobs() -> Result<(), Box<EvalAltResult>> {
|
|||||||
a.push(3);
|
a.push(3);
|
||||||
|
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
let mut orig_scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
orig_scope.push("x", a);
|
scope.push("x", a);
|
||||||
|
|
||||||
let mut scope = orig_scope.clone();
|
|
||||||
|
|
||||||
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[1]")?, 2);
|
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[1]")?, 2);
|
||||||
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[0]")?, 1);
|
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[0]")?, 1);
|
||||||
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[-1]")?, 3);
|
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[-1]")?, 3);
|
||||||
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[-3]")?, 1);
|
assert_eq!(engine.eval_with_scope::<INT>(&mut scope, "x[-3]")?, 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<INT>(&mut scope, "x += 4; x[3]")?,
|
engine.eval_with_scope::<INT>(&mut scope.clone(), "x += 4; x[3]")?,
|
||||||
4
|
4
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
{
|
{
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.push(4); x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x.push(4); x")?,
|
||||||
[1, 2, 3, 4]
|
[1, 2, 3, 4]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.insert(0, 4); x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x.insert(0, 4); x")?,
|
||||||
[4, 1, 2, 3]
|
[4, 1, 2, 3]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.insert(999, 4); x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x.insert(999, 4); x")?,
|
||||||
[1, 2, 3, 4]
|
[1, 2, 3, 4]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.insert(-2, 4); x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x.insert(-2, 4); x")?,
|
||||||
[1, 4, 2, 3]
|
[1, 4, 2, 3]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x.insert(-999, 4); x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x.insert(-999, 4); x")?,
|
||||||
[4, 1, 2, 3]
|
[4, 1, 2, 3]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<INT>(&mut orig_scope.clone(), "let z = [42]; x[z.len]")?,
|
engine.eval_with_scope::<INT>(&mut scope.clone(), "let z = [42]; x[z.len]")?,
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<INT>(&mut orig_scope.clone(), "let z = [2]; x[z[0]]")?,
|
engine.eval_with_scope::<INT>(&mut scope.clone(), "let z = [2]; x[z[0]]")?,
|
||||||
3
|
3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x += x; x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x += x; x")?,
|
||||||
[1, 2, 3, 1, 2, 3]
|
[1, 2, 3, 1, 2, 3]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x + x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x + x")?,
|
||||||
[1, 2, 3, 1, 2, 3]
|
[1, 2, 3, 1, 2, 3]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x += 999; x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x += 999; x")?,
|
||||||
[1, 2, 3, 0xe7]
|
[1, 2, 3, 0xe7]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval_with_scope::<Blob>(&mut orig_scope.clone(), "x[2] = 999; x")?,
|
engine.eval_with_scope::<Blob>(&mut scope.clone(), "x[2] = 999; x")?,
|
||||||
[1, 2, 0xe7]
|
[1, 2, 0xe7]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user