commit
e98b578ee6
25
CHANGELOG.md
25
CHANGELOG.md
@ -1,6 +1,31 @@
|
||||
Rhai Release Notes
|
||||
==================
|
||||
|
||||
Version 1.0.0
|
||||
=============
|
||||
|
||||
The official version `1.0`.
|
||||
|
||||
Almost the same version as `0.20.3` but with deprecated API's removed.
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Fixed infinite loop in certain script optimizations.
|
||||
|
||||
Breaking changes
|
||||
----------------
|
||||
|
||||
* All deprecated API's (e.g. the `RegisterFn` and `RegisterResultFn` traits) are removed.
|
||||
* `Module::set_id` is split into `Module::set_id` and `Module::clear_id` pair.
|
||||
|
||||
Enhancements
|
||||
------------
|
||||
|
||||
* New methods `is_odd`, `is_even` for integers, and `is_zero` for all numbers.
|
||||
* `From<BTreeSet>` and `From<HashSet>` are added for `Dynamic`, which create object maps with `()` values.
|
||||
|
||||
|
||||
Version 0.20.3
|
||||
==============
|
||||
|
||||
|
@ -3,7 +3,7 @@ members = [".", "codegen"]
|
||||
|
||||
[package]
|
||||
name = "rhai"
|
||||
version = "0.20.3"
|
||||
version = "1.0.0"
|
||||
edition = "2018"
|
||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||
description = "Embedded scripting for Rust"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rhai_codegen"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
edition = "2018"
|
||||
authors = ["jhwgh1968", "Stephen Chung"]
|
||||
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
|
||||
|
83
src/ast.rs
83
src/ast.rs
@ -251,20 +251,20 @@ impl AST {
|
||||
pub fn source(&self) -> Option<&str> {
|
||||
self.source.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
/// Clone the source, if any.
|
||||
/// Get a reference to the source.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn clone_source(&self) -> Option<Identifier> {
|
||||
self.source.clone()
|
||||
pub(crate) fn source_raw(&self) -> Option<&Identifier> {
|
||||
self.source.as_ref()
|
||||
}
|
||||
/// Set the source.
|
||||
#[inline(always)]
|
||||
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
||||
let source = Some(source.into());
|
||||
let source = source.into();
|
||||
Shared::get_mut(&mut self.functions)
|
||||
.as_mut()
|
||||
.map(|m| m.set_id(source.clone()));
|
||||
self.source = source;
|
||||
self.source = Some(source);
|
||||
self
|
||||
}
|
||||
/// Clear the source.
|
||||
@ -911,7 +911,7 @@ impl StmtBlock {
|
||||
/// Get the position of this statements block.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn position(&self) -> Position {
|
||||
pub const fn position(&self) -> Position {
|
||||
self.1
|
||||
}
|
||||
/// Get the statements of this statements block.
|
||||
@ -1044,7 +1044,7 @@ impl Stmt {
|
||||
/// Is this statement [`Noop`][Stmt::Noop]?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_noop(&self) -> bool {
|
||||
pub const fn is_noop(&self) -> bool {
|
||||
match self {
|
||||
Self::Noop(_) => true,
|
||||
_ => false,
|
||||
@ -1052,7 +1052,7 @@ impl Stmt {
|
||||
}
|
||||
/// Get the [position][Position] of this statement.
|
||||
#[must_use]
|
||||
pub fn position(&self) -> Position {
|
||||
pub const fn position(&self) -> Position {
|
||||
match self {
|
||||
Self::Noop(pos)
|
||||
| Self::Continue(pos)
|
||||
@ -1117,7 +1117,7 @@ impl Stmt {
|
||||
}
|
||||
/// Does this statement return a value?
|
||||
#[must_use]
|
||||
pub fn returns_value(&self) -> bool {
|
||||
pub const fn returns_value(&self) -> bool {
|
||||
match self {
|
||||
Self::If(_, _, _)
|
||||
| Self::Switch(_, _, _)
|
||||
@ -1142,12 +1142,12 @@ impl Stmt {
|
||||
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(_) => unreachable!("Stmt::Share should not be parsed"),
|
||||
Self::Share(_) => false,
|
||||
}
|
||||
}
|
||||
/// Is this statement self-terminated (i.e. no need for a semicolon terminator)?
|
||||
#[must_use]
|
||||
pub fn is_self_terminated(&self) -> bool {
|
||||
pub const fn is_self_terminated(&self) -> bool {
|
||||
match self {
|
||||
Self::If(_, _, _)
|
||||
| Self::Switch(_, _, _)
|
||||
@ -1173,7 +1173,7 @@ impl Stmt {
|
||||
Self::Import(_, _, _) | Self::Export(_, _) => false,
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Self::Share(_) => unreachable!("Stmt::Share should not be parsed"),
|
||||
Self::Share(_) => false,
|
||||
}
|
||||
}
|
||||
/// Is this statement _pure_?
|
||||
@ -1247,7 +1247,7 @@ impl Stmt {
|
||||
/// All statements following this statement will essentially be dead code.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_control_flow_break(&self) -> bool {
|
||||
pub const fn is_control_flow_break(&self) -> bool {
|
||||
match self {
|
||||
Self::Return(_, _, _) | Self::Break(_) | Self::Continue(_) => true,
|
||||
_ => false,
|
||||
@ -1512,7 +1512,7 @@ impl FnCallHashes {
|
||||
/// Create a [`FnCallHashes`] with only the native Rust hash.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_native(hash: u64) -> Self {
|
||||
pub const fn from_native(hash: u64) -> Self {
|
||||
Self {
|
||||
script: None,
|
||||
native: hash,
|
||||
@ -1521,7 +1521,7 @@ impl FnCallHashes {
|
||||
/// Create a [`FnCallHashes`] with both native Rust and script function hashes set to the same value.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_script(hash: u64) -> Self {
|
||||
pub const fn from_script(hash: u64) -> Self {
|
||||
Self {
|
||||
script: Some(hash),
|
||||
native: hash,
|
||||
@ -1530,7 +1530,7 @@ impl FnCallHashes {
|
||||
/// Create a [`FnCallHashes`] with both native Rust and script function hashes.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn from_script_and_native(script: u64, native: u64) -> Self {
|
||||
pub const fn from_script_and_native(script: u64, native: u64) -> Self {
|
||||
Self {
|
||||
script: Some(script),
|
||||
native,
|
||||
@ -1539,7 +1539,7 @@ impl FnCallHashes {
|
||||
/// Is this [`FnCallHashes`] native Rust only?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_native_only(&self) -> bool {
|
||||
pub const fn is_native_only(&self) -> bool {
|
||||
self.script.is_none()
|
||||
}
|
||||
}
|
||||
@ -1570,7 +1570,7 @@ impl FnCallExpr {
|
||||
/// Does this function call contain a qualified namespace?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_qualified(&self) -> bool {
|
||||
pub const fn is_qualified(&self) -> bool {
|
||||
self.namespace.is_some()
|
||||
}
|
||||
/// Convert this into a [`FnCall`][Expr::FnCall].
|
||||
@ -1683,6 +1683,7 @@ impl<F: Float> FloatWrapper<F> {
|
||||
/// Minimum floating-point number for natural display before switching to scientific notation.
|
||||
pub const MIN_NATURAL_FLOAT_FOR_DISPLAY: f32 = 0.0000000000001;
|
||||
|
||||
/// Create a new [`FloatWrapper`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new(value: F) -> Self {
|
||||
@ -1692,6 +1693,7 @@ impl<F: Float> FloatWrapper<F> {
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl FloatWrapper<FLOAT> {
|
||||
/// Create a new [`FloatWrapper`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) const fn const_new(value: FLOAT) -> Self {
|
||||
@ -1725,7 +1727,7 @@ pub enum Expr {
|
||||
/// [String][ImmutableString] constant.
|
||||
StringConstant(ImmutableString, Position),
|
||||
/// An interpolated [string][ImmutableString].
|
||||
InterpolatedString(Box<StaticVec<Expr>>),
|
||||
InterpolatedString(Box<StaticVec<Expr>>, Position),
|
||||
/// [ expr, ... ]
|
||||
Array(Box<StaticVec<Expr>>, Position),
|
||||
/// #{ name:expr, ... }
|
||||
@ -1796,7 +1798,7 @@ impl fmt::Debug for Expr {
|
||||
Self::StringConstant(value, _) => write!(f, "{:?}", value),
|
||||
Self::Unit(_) => f.write_str("()"),
|
||||
|
||||
Self::InterpolatedString(x) => {
|
||||
Self::InterpolatedString(x, _) => {
|
||||
f.write_str("InterpolatedString")?;
|
||||
return f.debug_list().entries(x.iter()).finish();
|
||||
}
|
||||
@ -1922,13 +1924,13 @@ impl Expr {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(f, _, _) => Self::FloatConstant(f, pos),
|
||||
|
||||
_ => Self::DynamicConstant(Box::new(value), pos),
|
||||
_ => Self::DynamicConstant(value.into(), pos),
|
||||
}
|
||||
}
|
||||
/// Is the expression a simple variable access?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool {
|
||||
pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool {
|
||||
match self {
|
||||
Self::Variable(_, _, x) => !non_qualified || x.1.is_none(),
|
||||
_ => false,
|
||||
@ -1946,7 +1948,7 @@ impl Expr {
|
||||
/// Get the [position][Position] of the expression.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn position(&self) -> Position {
|
||||
pub const fn position(&self) -> Position {
|
||||
match self {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Self::FloatConstant(_, pos) => *pos,
|
||||
@ -1962,14 +1964,8 @@ impl Expr {
|
||||
| Self::Variable(_, pos, _)
|
||||
| Self::Stack(_, pos)
|
||||
| Self::FnCall(_, pos)
|
||||
| Self::Custom(_, pos) => *pos,
|
||||
|
||||
Self::InterpolatedString(x) => x
|
||||
.first()
|
||||
.expect(
|
||||
"never fails because an interpolated string always contains at least one item",
|
||||
)
|
||||
.position(),
|
||||
| Self::Custom(_, pos)
|
||||
| Self::InterpolatedString(_, pos) => *pos,
|
||||
|
||||
Self::Property(x) => (x.2).1,
|
||||
Self::Stmt(x) => x.1,
|
||||
@ -2001,13 +1997,8 @@ impl Expr {
|
||||
| Self::Variable(_, pos, _)
|
||||
| Self::Stack(_, pos)
|
||||
| Self::FnCall(_, pos)
|
||||
| Self::Custom(_, pos) => *pos = new_pos,
|
||||
|
||||
Self::InterpolatedString(x) => {
|
||||
x.first_mut()
|
||||
.expect("never fails because an interpolated string always contains at least one item")
|
||||
.set_position(new_pos);
|
||||
}
|
||||
| Self::Custom(_, pos)
|
||||
| Self::InterpolatedString(_, pos) => *pos = new_pos,
|
||||
|
||||
Self::Property(x) => (x.2).1 = new_pos,
|
||||
Self::Stmt(x) => x.1 = new_pos,
|
||||
@ -2022,7 +2013,7 @@ impl Expr {
|
||||
#[must_use]
|
||||
pub fn is_pure(&self) -> bool {
|
||||
match self {
|
||||
Self::InterpolatedString(x) | Self::Array(x, _) => x.iter().all(Self::is_pure),
|
||||
Self::InterpolatedString(x, _) | Self::Array(x, _) => x.iter().all(Self::is_pure),
|
||||
|
||||
Self::Map(x, _) => x.0.iter().map(|(_, v)| v).all(Self::is_pure),
|
||||
|
||||
@ -2038,7 +2029,7 @@ impl Expr {
|
||||
/// Is the expression the unit `()` literal?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_unit(&self) -> bool {
|
||||
pub const fn is_unit(&self) -> bool {
|
||||
match self {
|
||||
Self::Unit(_) => true,
|
||||
_ => false,
|
||||
@ -2060,7 +2051,7 @@ impl Expr {
|
||||
| Self::Unit(_)
|
||||
| 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),
|
||||
|
||||
Self::Map(x, _) => x.0.iter().map(|(_, expr)| expr).all(Self::is_constant),
|
||||
|
||||
@ -2070,7 +2061,7 @@ impl Expr {
|
||||
/// Is a particular [token][Token] allowed as a postfix operator to this expression?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_valid_postfix(&self, token: &Token) -> bool {
|
||||
pub const fn is_valid_postfix(&self, token: &Token) -> bool {
|
||||
match token {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Token::Period => return true,
|
||||
@ -2090,7 +2081,7 @@ impl Expr {
|
||||
|
||||
Self::IntegerConstant(_, _)
|
||||
| Self::StringConstant(_, _)
|
||||
| Self::InterpolatedString(_)
|
||||
| Self::InterpolatedString(_, _)
|
||||
| Self::FnCall(_, _)
|
||||
| Self::Stmt(_)
|
||||
| Self::Dot(_, _)
|
||||
@ -2120,7 +2111,7 @@ impl Expr {
|
||||
|
||||
Self::Custom(_, _) => false,
|
||||
|
||||
Self::Stack(_, _) => unreachable!("Expr::Stack should not occur naturally"),
|
||||
Self::Stack(_, _) => false,
|
||||
}
|
||||
}
|
||||
/// Recursively walk this expression.
|
||||
@ -2145,7 +2136,7 @@ impl Expr {
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::InterpolatedString(x) | Self::Array(x, _) => {
|
||||
Self::InterpolatedString(x, _) | Self::Array(x, _) => {
|
||||
for e in x.as_ref() {
|
||||
if !e.walk(path, on_node) {
|
||||
return false;
|
||||
@ -2215,7 +2206,7 @@ mod tests {
|
||||
96
|
||||
}
|
||||
);
|
||||
assert_eq!(size_of::<Scope>(), 160);
|
||||
assert_eq!(size_of::<Scope>(), 464);
|
||||
assert_eq!(size_of::<LexError>(), 56);
|
||||
assert_eq!(
|
||||
size_of::<ParseError>(),
|
||||
|
@ -14,14 +14,21 @@ use std::any::TypeId;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
pub const MARKER_EXPR: &str = "$expr$";
|
||||
pub const MARKER_BLOCK: &str = "$block$";
|
||||
pub const MARKER_IDENT: &str = "$ident$";
|
||||
pub const MARKER_STRING: &str = "$string$";
|
||||
pub const MARKER_INT: &str = "$int$";
|
||||
/// Special marker for matching an expression.
|
||||
pub const CUSTOM_SYNTAX_MARKER_EXPR: &str = "$expr$";
|
||||
/// Special marker for matching a statements block.
|
||||
pub const CUSTOM_SYNTAX_MARKER_BLOCK: &str = "$block$";
|
||||
/// Special marker for matching an identifier.
|
||||
pub const CUSTOM_SYNTAX_MARKER_IDENT: &str = "$ident$";
|
||||
/// Special marker for matching a string literal.
|
||||
pub const CUSTOM_SYNTAX_MARKER_STRING: &str = "$string$";
|
||||
/// Special marker for matching an integer number.
|
||||
pub const CUSTOM_SYNTAX_MARKER_INT: &str = "$int$";
|
||||
/// Special marker for matching a floating-point number.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const MARKER_FLOAT: &str = "$float$";
|
||||
pub const MARKER_BOOL: &str = "$bool$";
|
||||
pub const CUSTOM_SYNTAX_MARKER_FLOAT: &str = "$float$";
|
||||
/// Special marker for matching a boolean value.
|
||||
pub const CUSTOM_SYNTAX_MARKER_BOOL: &str = "$bool$";
|
||||
|
||||
/// A general expression evaluation trait object.
|
||||
#[cfg(not(feature = "sync"))]
|
||||
@ -60,13 +67,13 @@ impl Expression<'_> {
|
||||
/// Get the expression.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn expr(&self) -> &Expr {
|
||||
pub(crate) const fn expr(&self) -> &Expr {
|
||||
&self.0
|
||||
}
|
||||
/// Get the position of this expression.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn position(&self) -> Position {
|
||||
pub const fn position(&self) -> Position {
|
||||
self.0.position()
|
||||
}
|
||||
/// Get the value of this expression if it is a literal constant.
|
||||
@ -120,7 +127,7 @@ impl Expression<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl EvalContext<'_, '_, '_, '_, '_, '_, '_> {
|
||||
impl EvalContext<'_, '_, '_, '_, '_, '_, '_, '_> {
|
||||
/// Evaluate an [expression tree][Expression].
|
||||
///
|
||||
/// # WARNING - Low Level API
|
||||
@ -185,7 +192,7 @@ impl Engine {
|
||||
for s in keywords {
|
||||
let s = s.as_ref().trim();
|
||||
|
||||
// skip empty keywords
|
||||
// Skip empty keywords
|
||||
if s.is_empty() {
|
||||
continue;
|
||||
}
|
||||
@ -194,15 +201,19 @@ impl Engine {
|
||||
|
||||
let seg = match s {
|
||||
// Markers not in first position
|
||||
MARKER_IDENT | MARKER_EXPR | MARKER_BLOCK | MARKER_BOOL | MARKER_INT
|
||||
| MARKER_STRING
|
||||
CUSTOM_SYNTAX_MARKER_IDENT
|
||||
| CUSTOM_SYNTAX_MARKER_EXPR
|
||||
| CUSTOM_SYNTAX_MARKER_BLOCK
|
||||
| CUSTOM_SYNTAX_MARKER_BOOL
|
||||
| CUSTOM_SYNTAX_MARKER_INT
|
||||
| CUSTOM_SYNTAX_MARKER_STRING
|
||||
if !segments.is_empty() =>
|
||||
{
|
||||
s.into()
|
||||
}
|
||||
// Markers not in first position
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||
CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(),
|
||||
// Standard or reserved keyword/symbol not in first position
|
||||
s if !segments.is_empty() && token.is_some() => {
|
||||
// Make it a custom keyword/symbol if it is disabled or reserved
|
||||
@ -307,11 +318,12 @@ impl Engine {
|
||||
) -> &mut Self {
|
||||
self.custom_syntax.insert(
|
||||
key.into(),
|
||||
Box::new(CustomSyntax {
|
||||
CustomSyntax {
|
||||
parse: Box::new(parse),
|
||||
func: (Box::new(func) as Box<FnCustomSyntaxEval>).into(),
|
||||
scope_changed,
|
||||
}),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
152
src/dynamic.rs
152
src/dynamic.rs
@ -33,6 +33,7 @@ use fmt::Debug;
|
||||
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
|
||||
use instant::Instant;
|
||||
|
||||
/// The message: never fails because the type was checked
|
||||
const CHECKED: &str = "never fails because the type was checked";
|
||||
|
||||
mod private {
|
||||
@ -164,7 +165,7 @@ pub type Tag = i32;
|
||||
pub type Tag = i16;
|
||||
|
||||
/// Default tag value for [`Dynamic`].
|
||||
const DEFAULT_TAG: Tag = 0;
|
||||
const DEFAULT_TAG_VALUE: Tag = 0;
|
||||
|
||||
/// Dynamic type containing any value.
|
||||
pub struct Dynamic(pub(crate) Union);
|
||||
@ -711,8 +712,6 @@ impl fmt::Debug for Dynamic {
|
||||
let _value_any = (***value).as_any();
|
||||
let _type_id = _value_any.type_id();
|
||||
|
||||
const CHECKED: &str = "never fails because the type was checked";
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
#[cfg(not(feature = "only_i64"))]
|
||||
if _type_id == TypeId::of::<u8>() {
|
||||
@ -819,28 +818,28 @@ impl Default for Dynamic {
|
||||
|
||||
impl Dynamic {
|
||||
/// A [`Dynamic`] containing a `()`.
|
||||
pub const UNIT: Dynamic = Self(Union::Unit((), DEFAULT_TAG, ReadWrite));
|
||||
pub const UNIT: Dynamic = Self(Union::Unit((), DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing a `true`.
|
||||
pub const TRUE: Dynamic = Self(Union::Bool(true, DEFAULT_TAG, ReadWrite));
|
||||
pub const TRUE: Dynamic = Self(Union::Bool(true, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing a [`false`].
|
||||
pub const FALSE: Dynamic = Self(Union::Bool(false, DEFAULT_TAG, ReadWrite));
|
||||
pub const FALSE: Dynamic = Self(Union::Bool(false, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing the integer zero.
|
||||
pub const ZERO: Dynamic = Self(Union::Int(0, DEFAULT_TAG, ReadWrite));
|
||||
pub const ZERO: Dynamic = Self(Union::Int(0, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing the integer one.
|
||||
pub const ONE: Dynamic = Self(Union::Int(1, DEFAULT_TAG, ReadWrite));
|
||||
pub const ONE: Dynamic = Self(Union::Int(1, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing the integer two.
|
||||
pub const TWO: Dynamic = Self(Union::Int(2, DEFAULT_TAG, ReadWrite));
|
||||
pub const TWO: Dynamic = Self(Union::Int(2, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing the integer ten.
|
||||
pub const TEN: Dynamic = Self(Union::Int(10, DEFAULT_TAG, ReadWrite));
|
||||
pub const TEN: Dynamic = Self(Union::Int(10, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing the integer negative one.
|
||||
pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, DEFAULT_TAG, ReadWrite));
|
||||
pub const NEGATIVE_ONE: Dynamic = Self(Union::Int(-1, DEFAULT_TAG_VALUE, ReadWrite));
|
||||
/// A [`Dynamic`] containing `0.0`.
|
||||
///
|
||||
/// Not available under `no_float`.
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_ZERO: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::const_new(0.0),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
/// A [`Dynamic`] containing `1.0`.
|
||||
@ -849,7 +848,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_ONE: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::const_new(1.0),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
/// A [`Dynamic`] containing `2.0`.
|
||||
@ -858,7 +857,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_TWO: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::const_new(2.0),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
/// A [`Dynamic`] containing `10.0`.
|
||||
@ -867,7 +866,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_TEN: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::const_new(10.0),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
/// A [`Dynamic`] containing the `-1.0`.
|
||||
@ -876,7 +875,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
pub const FLOAT_NEGATIVE_ONE: Dynamic = Self(Union::Float(
|
||||
FloatWrapper::const_new(-1.0),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
));
|
||||
|
||||
@ -1043,8 +1042,6 @@ impl Dynamic {
|
||||
pub fn from<T: Variant + Clone>(mut value: T) -> Self {
|
||||
// Coded this way in order to maximally leverage potentials for dead-code removal.
|
||||
|
||||
const CHECKED: &str = "never fails because the type was checked";
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<Dynamic>() {
|
||||
return unsafe_try_cast::<_, Dynamic>(value).ok().expect(CHECKED);
|
||||
}
|
||||
@ -1125,7 +1122,7 @@ impl Dynamic {
|
||||
|
||||
Self(Union::Variant(
|
||||
Box::new(Box::new(value)),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
))
|
||||
}
|
||||
@ -1153,7 +1150,7 @@ impl Dynamic {
|
||||
Union::Shared(_, _, _) => self,
|
||||
_ => Self(Union::Shared(
|
||||
crate::Locked::new(self).into(),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
_access,
|
||||
)),
|
||||
}
|
||||
@ -1825,23 +1822,6 @@ impl Dynamic {
|
||||
/// Convert the [`Dynamic`] into a [`String`] and return it.
|
||||
/// If there are other references to the same string, a cloned copy is returned.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated and will be removed in the future.
|
||||
/// Use [`as_string`][Dynamic::as_string] instead.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[deprecated(
|
||||
since = "0.20.3",
|
||||
note = "this method is deprecated and will be removed in the future"
|
||||
)]
|
||||
pub fn take_string(self) -> Result<String, &'static str> {
|
||||
self.as_string()
|
||||
}
|
||||
/// Convert the [`Dynamic`] into a [`String`] and return it.
|
||||
/// If there are other references to the same string, a cloned copy is returned.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn as_string(self) -> Result<String, &'static str> {
|
||||
@ -1849,22 +1829,6 @@ impl Dynamic {
|
||||
}
|
||||
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
///
|
||||
/// # Deprecated
|
||||
///
|
||||
/// This method is deprecated and will be removed in the future.
|
||||
/// Use [`as_immutable_string`][Dynamic::as_immutable_string] instead.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
#[deprecated(
|
||||
since = "0.20.3",
|
||||
note = "this method is deprecated and will be removed in the future"
|
||||
)]
|
||||
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||
self.as_immutable_string()
|
||||
}
|
||||
/// Convert the [`Dynamic`] into an [`ImmutableString`] and return it.
|
||||
/// Returns the name of the actual type if the cast fails.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn as_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||
@ -1890,56 +1854,52 @@ impl Dynamic {
|
||||
impl From<()> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: ()) -> Self {
|
||||
Self(Union::Unit(value, DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Unit(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
impl From<bool> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: bool) -> Self {
|
||||
Self(Union::Bool(value, DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Bool(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
impl From<INT> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: INT) -> Self {
|
||||
Self(Union::Int(value, DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Int(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl From<FLOAT> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: FLOAT) -> Self {
|
||||
Self(Union::Float(value.into(), DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Float(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl From<FloatWrapper<FLOAT>> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: FloatWrapper<FLOAT>) -> Self {
|
||||
Self(Union::Float(value, DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Float(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "decimal")]
|
||||
impl From<Decimal> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: Decimal) -> Self {
|
||||
Self(Union::Decimal(
|
||||
Box::new(value.into()),
|
||||
DEFAULT_TAG,
|
||||
ReadWrite,
|
||||
))
|
||||
Self(Union::Decimal(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
impl From<char> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: char) -> Self {
|
||||
Self(Union::Char(value, DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Char(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
impl<S: Into<ImmutableString>> From<S> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: S) -> Self {
|
||||
Self(Union::Str(value.into(), DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Str(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
impl From<&ImmutableString> for Dynamic {
|
||||
@ -1948,19 +1908,12 @@ impl From<&ImmutableString> for Dynamic {
|
||||
value.clone().into()
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
impl From<&crate::Identifier> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: &crate::Identifier) -> Self {
|
||||
value.to_string().into()
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
impl Dynamic {
|
||||
/// Create a [`Dynamic`] from an [`Array`].
|
||||
#[inline(always)]
|
||||
pub(crate) fn from_array(array: Array) -> Self {
|
||||
Self(Union::Array(Box::new(array), DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Array(array.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -1969,7 +1922,7 @@ impl<T: Variant + Clone> From<Vec<T>> for Dynamic {
|
||||
fn from(value: Vec<T>) -> Self {
|
||||
Self(Union::Array(
|
||||
Box::new(value.into_iter().map(Dynamic::from).collect()),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
))
|
||||
}
|
||||
@ -1980,7 +1933,7 @@ impl<T: Variant + Clone> From<&[T]> for Dynamic {
|
||||
fn from(value: &[T]) -> Self {
|
||||
Self(Union::Array(
|
||||
Box::new(value.iter().cloned().map(Dynamic::from).collect()),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
))
|
||||
}
|
||||
@ -1991,7 +1944,7 @@ impl<T: Variant + Clone> std::iter::FromIterator<T> for Dynamic {
|
||||
fn from_iter<X: IntoIterator<Item = T>>(iter: X) -> Self {
|
||||
Self(Union::Array(
|
||||
Box::new(iter.into_iter().map(Dynamic::from).collect()),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
))
|
||||
}
|
||||
@ -2001,7 +1954,7 @@ impl Dynamic {
|
||||
/// Create a [`Dynamic`] from a [`Map`].
|
||||
#[inline(always)]
|
||||
pub(crate) fn from_map(map: Map) -> Self {
|
||||
Self(Union::Map(Box::new(map), DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Map(map.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -2018,7 +1971,24 @@ impl<K: Into<crate::Identifier>, T: Variant + Clone> From<std::collections::Hash
|
||||
.map(|(k, v)| (k.into(), Dynamic::from(v)))
|
||||
.collect(),
|
||||
),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
impl<K: Into<crate::Identifier>> From<std::collections::HashSet<K>> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: std::collections::HashSet<K>) -> Self {
|
||||
Self(Union::Map(
|
||||
Box::new(
|
||||
value
|
||||
.into_iter()
|
||||
.map(|k| (k.into(), Dynamic::UNIT))
|
||||
.collect(),
|
||||
),
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
))
|
||||
}
|
||||
@ -2036,7 +2006,23 @@ impl<K: Into<crate::Identifier>, T: Variant + Clone> From<std::collections::BTre
|
||||
.map(|(k, v)| (k.into(), Dynamic::from(v)))
|
||||
.collect(),
|
||||
),
|
||||
DEFAULT_TAG,
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
impl<K: Into<crate::Identifier>> From<std::collections::BTreeSet<K>> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: std::collections::BTreeSet<K>) -> Self {
|
||||
Self(Union::Map(
|
||||
Box::new(
|
||||
value
|
||||
.into_iter()
|
||||
.map(|k| (k.into(), Dynamic::UNIT))
|
||||
.collect(),
|
||||
),
|
||||
DEFAULT_TAG_VALUE,
|
||||
ReadWrite,
|
||||
))
|
||||
}
|
||||
@ -2044,26 +2030,26 @@ impl<K: Into<crate::Identifier>, T: Variant + Clone> From<std::collections::BTre
|
||||
impl From<FnPtr> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: FnPtr) -> Self {
|
||||
Self(Union::FnPtr(Box::new(value), DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::FnPtr(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
impl From<Box<FnPtr>> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: Box<FnPtr>) -> Self {
|
||||
Self(Union::FnPtr(value, DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::FnPtr(value, DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
impl From<Instant> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: Instant) -> Self {
|
||||
Self(Union::TimeStamp(Box::new(value), DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::TimeStamp(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
impl From<crate::Shared<crate::Locked<Dynamic>>> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: crate::Shared<crate::Locked<Self>>) -> Self {
|
||||
Self(Union::Shared(value.into(), DEFAULT_TAG, ReadWrite))
|
||||
Self(Union::Shared(value.into(), DEFAULT_TAG_VALUE, ReadWrite))
|
||||
}
|
||||
}
|
||||
|
261
src/engine.rs
261
src/engine.rs
@ -274,50 +274,34 @@ enum ChainArgument {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Property(Position),
|
||||
/// Arguments to a dot method call.
|
||||
/// Wrapped values are the arguments plus the [position][Position] of the first argument.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
MethodCallArgs(StaticVec<Dynamic>, Position),
|
||||
/// Index value.
|
||||
/// Index value and [position][Position].
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
IndexValue(Dynamic, Position),
|
||||
}
|
||||
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
impl ChainArgument {
|
||||
/// Return the `Dynamic` value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if not `ChainArgument::IndexValue`.
|
||||
/// Return the index value.
|
||||
#[inline(always)]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[must_use]
|
||||
pub fn as_index_value(self) -> Dynamic {
|
||||
pub fn as_index_value(self) -> Option<Dynamic> {
|
||||
match self {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Self::Property(_) | Self::MethodCallArgs(_, _) => {
|
||||
panic!("expecting ChainArgument::IndexValue")
|
||||
}
|
||||
Self::IndexValue(value, _) => value,
|
||||
Self::IndexValue(value, _) => Some(value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
/// Return the `StaticVec<Dynamic>` value.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if not `ChainArgument::MethodCallArgs`.
|
||||
/// Return the list of method call arguments.
|
||||
#[inline(always)]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[must_use]
|
||||
pub fn as_fn_call_args(self) -> (StaticVec<Dynamic>, Position) {
|
||||
pub fn as_fn_call_args(self) -> Option<(StaticVec<Dynamic>, Position)> {
|
||||
match self {
|
||||
Self::Property(_) => {
|
||||
panic!("expecting ChainArgument::MethodCallArgs")
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Self::IndexValue(_, _) => {
|
||||
panic!("expecting ChainArgument::MethodCallArgs")
|
||||
}
|
||||
Self::MethodCallArgs(values, pos) => (values, pos),
|
||||
Self::MethodCallArgs(values, pos) => Some((values, pos)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -631,7 +615,7 @@ pub struct State {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
pub resolver: Option<Shared<crate::module::resolvers::StaticModuleResolver>>,
|
||||
/// Function resolution cache and free list.
|
||||
fn_resolution_caches: (StaticVec<FnResolutionCache>, Vec<FnResolutionCache>),
|
||||
fn_resolution_caches: StaticVec<FnResolutionCache>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
@ -645,11 +629,11 @@ impl State {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn fn_resolution_cache_mut(&mut self) -> &mut FnResolutionCache {
|
||||
if self.fn_resolution_caches.0.is_empty() {
|
||||
if self.fn_resolution_caches.is_empty() {
|
||||
// Push a new function resolution cache if the stack is empty
|
||||
self.fn_resolution_caches.0.push(BTreeMap::new());
|
||||
self.fn_resolution_caches.push(Default::default());
|
||||
}
|
||||
self.fn_resolution_caches.0.last_mut().expect(
|
||||
self.fn_resolution_caches.last_mut().expect(
|
||||
"never fails because there is at least one function resolution cache by this point",
|
||||
)
|
||||
}
|
||||
@ -657,9 +641,7 @@ impl State {
|
||||
#[allow(dead_code)]
|
||||
#[inline(always)]
|
||||
pub fn push_fn_resolution_cache(&mut self) {
|
||||
self.fn_resolution_caches
|
||||
.0
|
||||
.push(self.fn_resolution_caches.1.pop().unwrap_or_default());
|
||||
self.fn_resolution_caches.push(Default::default());
|
||||
}
|
||||
/// Remove the current function resolution cache from the stack and make the last one current.
|
||||
///
|
||||
@ -668,13 +650,9 @@ impl State {
|
||||
/// Panics if there are no more function resolution cache in the stack.
|
||||
#[inline(always)]
|
||||
pub fn pop_fn_resolution_cache(&mut self) {
|
||||
let mut cache = self
|
||||
.fn_resolution_caches
|
||||
.0
|
||||
self.fn_resolution_caches
|
||||
.pop()
|
||||
.expect("there should be at least one function resolution cache");
|
||||
cache.clear();
|
||||
self.fn_resolution_caches.1.push(cache);
|
||||
}
|
||||
}
|
||||
|
||||
@ -724,23 +702,44 @@ pub struct Limits {
|
||||
pub max_map_size: Option<NonZeroUsize>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
impl Default for Limits {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||
max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH),
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH),
|
||||
max_operations: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
max_modules: usize::MAX,
|
||||
max_string_size: None,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
max_array_size: None,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
max_map_size: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Context of a script evaluation process.
|
||||
#[derive(Debug)]
|
||||
pub struct EvalContext<'a, 'x, 'px, 'm, 's, 't, 'pt> {
|
||||
pub struct EvalContext<'a, 'x, 'px, 'm, 's, 'b, 't, 'pt> {
|
||||
pub(crate) engine: &'a Engine,
|
||||
pub(crate) scope: &'x mut Scope<'px>,
|
||||
pub(crate) mods: &'m mut Imports,
|
||||
pub(crate) state: &'s mut State,
|
||||
pub(crate) lib: &'a [&'a Module],
|
||||
pub(crate) lib: &'b [&'b Module],
|
||||
pub(crate) this_ptr: &'t mut Option<&'pt mut Dynamic>,
|
||||
pub(crate) level: usize,
|
||||
}
|
||||
|
||||
impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
|
||||
impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, '_> {
|
||||
/// The current [`Engine`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn engine(&self) -> &Engine {
|
||||
pub const fn engine(&self) -> &Engine {
|
||||
self.engine
|
||||
}
|
||||
/// The current source.
|
||||
@ -752,7 +751,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
|
||||
/// The current [`Scope`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn scope(&self) -> &Scope {
|
||||
pub const fn scope(&self) -> &Scope<'px> {
|
||||
self.scope
|
||||
}
|
||||
/// Mutable reference to the current [`Scope`].
|
||||
@ -774,7 +773,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn imports(&self) -> &Imports {
|
||||
pub const fn imports(&self) -> &Imports {
|
||||
self.mods
|
||||
}
|
||||
/// Get an iterator over the namespaces containing definition of all script-defined functions.
|
||||
@ -788,7 +787,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn namespaces(&self) -> &[&Module] {
|
||||
pub const fn namespaces(&self) -> &[&Module] {
|
||||
self.lib
|
||||
}
|
||||
/// The current bound `this` pointer, if any.
|
||||
@ -800,7 +799,7 @@ impl<'x, 'px> EvalContext<'_, 'x, 'px, '_, '_, '_, '_> {
|
||||
/// The current nesting level of function calls.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn call_level(&self) -> usize {
|
||||
pub const fn call_level(&self) -> usize {
|
||||
self.level
|
||||
}
|
||||
}
|
||||
@ -856,9 +855,9 @@ pub struct Engine {
|
||||
pub(crate) resolve_var: Option<OnVarCallback>,
|
||||
|
||||
/// Callback closure for implementing the `print` command.
|
||||
pub(crate) print: OnPrintCallback,
|
||||
pub(crate) print: Option<OnPrintCallback>,
|
||||
/// Callback closure for implementing the `debug` command.
|
||||
pub(crate) debug: OnDebugCallback,
|
||||
pub(crate) debug: Option<OnDebugCallback>,
|
||||
/// Callback closure for progress reporting.
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
pub(crate) progress: Option<crate::fn_native::OnProgressCallback>,
|
||||
@ -909,86 +908,50 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool {
|
||||
fn_name.starts_with(FN_ANONYMOUS)
|
||||
}
|
||||
|
||||
/// Print to stdout
|
||||
/// Print to `stdout`
|
||||
#[inline(always)]
|
||||
fn default_print(_s: &str) {
|
||||
#[allow(unused_variables)]
|
||||
fn print_to_stdout(s: &str) {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
println!("{}", _s);
|
||||
println!("{}", s);
|
||||
}
|
||||
|
||||
/// Debug to stdout
|
||||
/// Debug to `stdout`
|
||||
#[inline(always)]
|
||||
fn default_debug(_s: &str, _source: Option<&str>, _pos: Position) {
|
||||
#[allow(unused_variables)]
|
||||
fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
if let Some(source) = _source {
|
||||
println!("{}{:?} | {}", source, _pos, _s);
|
||||
} else if _pos.is_none() {
|
||||
println!("{}", _s);
|
||||
if let Some(source) = source {
|
||||
println!("{}{:?} | {}", source, pos, s);
|
||||
} else if pos.is_none() {
|
||||
println!("{}", s);
|
||||
} else {
|
||||
println!("{:?} | {}", _pos, _s);
|
||||
println!("{:?} | {}", pos, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
/// Create a new [`Engine`]
|
||||
/// Create a new [`Engine`].
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
// Create the new scripting Engine
|
||||
let mut engine = Self {
|
||||
global_namespace: Default::default(),
|
||||
global_modules: Default::default(),
|
||||
global_sub_modules: Default::default(),
|
||||
let mut engine = Self::new_raw();
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
module_resolver: Some(Box::new(crate::module::resolvers::FileModuleResolver::new())),
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(any(feature = "no_std", target_arch = "wasm32",))]
|
||||
module_resolver: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
|
||||
{
|
||||
engine.module_resolver =
|
||||
Some(Box::new(crate::module::resolvers::FileModuleResolver::new()));
|
||||
}
|
||||
|
||||
type_names: Default::default(),
|
||||
empty_string: Default::default(),
|
||||
disabled_symbols: Default::default(),
|
||||
custom_keywords: Default::default(),
|
||||
custom_syntax: Default::default(),
|
||||
// default print/debug implementations
|
||||
engine.print = Some(Box::new(print_to_stdout));
|
||||
engine.debug = Some(Box::new(debug_to_stdout));
|
||||
|
||||
// variable resolver
|
||||
resolve_var: None,
|
||||
|
||||
// default print/debug implementations
|
||||
print: Box::new(default_print),
|
||||
debug: Box::new(default_debug),
|
||||
|
||||
// progress callback
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
progress: None,
|
||||
|
||||
// optimization level
|
||||
optimization_level: Default::default(),
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
limits: Limits {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||
max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH),
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH),
|
||||
max_operations: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
max_modules: usize::MAX,
|
||||
max_string_size: None,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
max_array_size: None,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
max_map_size: None,
|
||||
},
|
||||
};
|
||||
|
||||
engine.global_namespace.internal = true;
|
||||
engine.register_global_module(StandardPackage::new().as_shared_module());
|
||||
|
||||
engine
|
||||
@ -1016,8 +979,8 @@ impl Engine {
|
||||
|
||||
resolve_var: None,
|
||||
|
||||
print: Box::new(|_| {}),
|
||||
debug: Box::new(|_, _, _| {}),
|
||||
print: None,
|
||||
debug: None,
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
progress: None,
|
||||
@ -1025,21 +988,7 @@ impl Engine {
|
||||
optimization_level: Default::default(),
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
limits: Limits {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
max_call_stack_depth: MAX_CALL_STACK_DEPTH,
|
||||
max_expr_depth: NonZeroUsize::new(MAX_EXPR_DEPTH),
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
max_function_expr_depth: NonZeroUsize::new(MAX_FUNCTION_EXPR_DEPTH),
|
||||
max_operations: None,
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
max_modules: usize::MAX,
|
||||
max_string_size: None,
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
max_array_size: None,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
max_map_size: None,
|
||||
},
|
||||
limits: Default::default(),
|
||||
};
|
||||
|
||||
engine.global_namespace.internal = true;
|
||||
@ -1243,12 +1192,14 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
ChainType::Index => {
|
||||
let pos = rhs.position();
|
||||
let idx_val = idx_val
|
||||
.as_index_value()
|
||||
.expect("never fails because `chain_type` is `ChainType::Index`");
|
||||
|
||||
match rhs {
|
||||
// xxx[idx].expr... | xxx[idx][expr]...
|
||||
Expr::Dot(x, x_pos) | Expr::Index(x, x_pos) => {
|
||||
let idx_pos = x.lhs.position();
|
||||
let idx_val = idx_val.as_index_value();
|
||||
let obj_ptr = &mut self.get_indexed_mut(
|
||||
mods, state, lib, target, idx_val, idx_pos, false, true, level,
|
||||
)?;
|
||||
@ -1264,7 +1215,6 @@ impl Engine {
|
||||
_ if new_val.is_some() => {
|
||||
let ((new_val, new_pos), (op_info, op_pos)) =
|
||||
new_val.expect("never fails because `new_val` is `Some`");
|
||||
let idx_val = idx_val.as_index_value();
|
||||
let mut idx_val_for_setter = idx_val.clone();
|
||||
|
||||
let try_setter = match self.get_indexed_mut(
|
||||
@ -1306,13 +1256,9 @@ impl Engine {
|
||||
Ok((Dynamic::UNIT, true))
|
||||
}
|
||||
// xxx[rhs]
|
||||
_ => {
|
||||
let idx_val = idx_val.as_index_value();
|
||||
self.get_indexed_mut(
|
||||
mods, state, lib, target, idx_val, pos, false, true, level,
|
||||
)
|
||||
.map(|v| (v.take_or_clone(), false))
|
||||
}
|
||||
_ => self
|
||||
.get_indexed_mut(mods, state, lib, target, idx_val, pos, false, true, level)
|
||||
.map(|v| (v.take_or_clone(), false)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1322,9 +1268,11 @@ impl Engine {
|
||||
// xxx.fn_name(arg_expr_list)
|
||||
Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
|
||||
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
||||
let args = &mut idx_val.as_fn_call_args();
|
||||
let call_args = &mut idx_val
|
||||
.as_fn_call_args()
|
||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
||||
self.make_method_call(
|
||||
mods, state, lib, name, *hashes, target, args, *pos, level,
|
||||
mods, state, lib, name, *hashes, target, call_args, *pos, level,
|
||||
)
|
||||
}
|
||||
// xxx.fn_name(...) = ???
|
||||
@ -1485,9 +1433,11 @@ impl Engine {
|
||||
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
||||
Expr::FnCall(ref x, pos) if !x.is_qualified() => {
|
||||
let FnCallExpr { name, hashes, .. } = x.as_ref();
|
||||
let args = &mut idx_val.as_fn_call_args();
|
||||
let call_args = &mut idx_val
|
||||
.as_fn_call_args()
|
||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
||||
let (val, _) = self.make_method_call(
|
||||
mods, state, lib, name, *hashes, target, args, pos, level,
|
||||
mods, state, lib, name, *hashes, target, call_args, pos, level,
|
||||
)?;
|
||||
val.into()
|
||||
}
|
||||
@ -1604,7 +1554,9 @@ impl Engine {
|
||||
Expr::FnCall(ref f, pos) if !f.is_qualified() => {
|
||||
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
let args = &mut idx_val.as_fn_call_args();
|
||||
let args = &mut idx_val
|
||||
.as_fn_call_args()
|
||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
||||
let (mut val, _) = self.make_method_call(
|
||||
mods, state, lib, name, *hashes, target, args, pos, level,
|
||||
)?;
|
||||
@ -1723,17 +1675,19 @@ impl Engine {
|
||||
args, constants, ..
|
||||
} = x.as_ref();
|
||||
let mut arg_values = StaticVec::with_capacity(args.len());
|
||||
let mut first_arg_pos = Position::NONE;
|
||||
|
||||
for index in 0..args.len() {
|
||||
let (value, _) = self.get_arg_value(
|
||||
let (value, pos) = self.get_arg_value(
|
||||
scope, mods, state, lib, this_ptr, level, args, constants, index,
|
||||
)?;
|
||||
arg_values.push(value.flatten());
|
||||
if index == 0 {
|
||||
first_arg_pos = pos
|
||||
}
|
||||
}
|
||||
|
||||
let pos = x.args.get(0).map(Expr::position).unwrap_or_default();
|
||||
|
||||
idx_values.push((arg_values, pos).into());
|
||||
idx_values.push((arg_values, first_arg_pos).into());
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
|
||||
@ -1765,17 +1719,19 @@ impl Engine {
|
||||
args, constants, ..
|
||||
} = x.as_ref();
|
||||
let mut arg_values = StaticVec::with_capacity(args.len());
|
||||
let mut first_arg_pos = Position::NONE;
|
||||
|
||||
for index in 0..args.len() {
|
||||
let (value, _) = self.get_arg_value(
|
||||
let (value, pos) = self.get_arg_value(
|
||||
scope, mods, state, lib, this_ptr, level, args, constants, index,
|
||||
)?;
|
||||
arg_values.push(value.flatten());
|
||||
if index == 0 {
|
||||
first_arg_pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
let pos = x.args.get(0).map(Expr::position).unwrap_or_default();
|
||||
|
||||
(arg_values, pos).into()
|
||||
(arg_values, first_arg_pos).into()
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
|
||||
@ -2045,8 +2001,8 @@ impl Engine {
|
||||
}
|
||||
|
||||
// `... ${...} ...`
|
||||
Expr::InterpolatedString(x) => {
|
||||
let mut pos = expr.position();
|
||||
Expr::InterpolatedString(x, pos) => {
|
||||
let mut pos = *pos;
|
||||
let mut result: Dynamic = self.empty_string.clone().into();
|
||||
|
||||
for expr in x.iter() {
|
||||
@ -2761,10 +2717,9 @@ impl Engine {
|
||||
|
||||
err_map.insert("message".into(), err.to_string().into());
|
||||
|
||||
state
|
||||
.source
|
||||
.as_ref()
|
||||
.map(|source| err_map.insert("source".into(), source.into()));
|
||||
if let Some(ref source) = state.source {
|
||||
err_map.insert("source".into(), source.as_str().into());
|
||||
}
|
||||
|
||||
if err_pos.is_none() {
|
||||
// No position info
|
||||
|
@ -903,14 +903,6 @@ impl Engine {
|
||||
self.global_modules.insert(0, module);
|
||||
self
|
||||
}
|
||||
/// Register a shared [`Module`] into the global namespace of [`Engine`].
|
||||
/// This function is deprecated and will be removed in the future.
|
||||
/// Use [`register_global_module`][Engine::register_global_module] instead.
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.19.9", note = "use `register_global_module` instead")]
|
||||
pub fn load_package(&mut self, module: impl Into<Shared<Module>>) -> &mut Self {
|
||||
self.register_global_module(module.into())
|
||||
}
|
||||
/// Register a shared [`Module`] as a static module namespace with the [`Engine`].
|
||||
///
|
||||
/// Functions marked [`FnNamespace::Global`] and type iterators are exposed to scripts without
|
||||
@ -999,19 +991,6 @@ impl Engine {
|
||||
self
|
||||
}
|
||||
|
||||
/// Register a shared [`Module`] as a static module namespace with the [`Engine`].
|
||||
/// This function is deprecated and will be removed in the future.
|
||||
/// Use [`register_static_module`][Engine::register_static_module] instead.
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[deprecated(since = "0.19.9", note = "use `register_static_module` instead")]
|
||||
pub fn register_module(
|
||||
&mut self,
|
||||
name: impl AsRef<str> + Into<Identifier>,
|
||||
module: impl Into<Shared<Module>>,
|
||||
) -> &mut Self {
|
||||
self.register_static_module(name, module.into())
|
||||
}
|
||||
/// Compile a string into an [`AST`], which can be used later for evaluation.
|
||||
///
|
||||
/// # Example
|
||||
@ -1335,7 +1314,8 @@ impl Engine {
|
||||
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
|
||||
}
|
||||
/// Parse a JSON string into an [object map][`Map`].
|
||||
/// This is a light-weight alternative to using, say, [`serde_json`] to deserialize the JSON.
|
||||
/// This is a light-weight alternative to using, say,
|
||||
/// [`serde_json`](https://crates.io/crates/serde_json) to deserialize the JSON.
|
||||
///
|
||||
/// Not available under `no_object`.
|
||||
///
|
||||
@ -1770,7 +1750,7 @@ impl Engine {
|
||||
level: usize,
|
||||
) -> RhaiResult {
|
||||
let mut state: State = Default::default();
|
||||
state.source = ast.clone_source();
|
||||
state.source = ast.source_raw().cloned();
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
state.resolver = ast.resolver();
|
||||
@ -1852,7 +1832,7 @@ impl Engine {
|
||||
) -> Result<(), Box<EvalAltResult>> {
|
||||
let mods = &mut Default::default();
|
||||
let mut state: State = Default::default();
|
||||
state.source = ast.clone_source();
|
||||
state.source = ast.source_raw().cloned();
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
{
|
||||
state.resolver = ast.resolver();
|
||||
@ -2084,7 +2064,12 @@ impl Engine {
|
||||
.lib()
|
||||
.iter_fn()
|
||||
.filter(|f| f.func.is_script())
|
||||
.map(|f| f.func.get_fn_def().clone())
|
||||
.map(|f| {
|
||||
f.func
|
||||
.get_script_fn_def()
|
||||
.expect("never fails because the function is scripted")
|
||||
.clone()
|
||||
})
|
||||
.collect();
|
||||
|
||||
#[cfg(feature = "no_function")]
|
||||
@ -2234,7 +2219,7 @@ impl Engine {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn on_print(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
|
||||
self.print = Box::new(callback);
|
||||
self.print = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
/// Override default action of `debug` (print to stdout using [`println!`])
|
||||
@ -2273,7 +2258,7 @@ impl Engine {
|
||||
&mut self,
|
||||
callback: impl Fn(&str, Option<&str>, Position) + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
self.debug = Box::new(callback);
|
||||
self.debug = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_optimize"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn optimization_level(&self) -> crate::OptimizationLevel {
|
||||
pub const fn optimization_level(&self) -> crate::OptimizationLevel {
|
||||
self.optimization_level
|
||||
}
|
||||
/// Set the maximum levels of function calls allowed for a script in order to avoid
|
||||
@ -50,7 +50,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn max_call_levels(&self) -> usize {
|
||||
pub const fn max_call_levels(&self) -> usize {
|
||||
self.limits.max_call_stack_depth
|
||||
}
|
||||
/// Set the maximum number of operations allowed for a script to run to avoid
|
||||
@ -69,8 +69,12 @@ impl Engine {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn max_operations(&self) -> u64 {
|
||||
self.limits.max_operations.map_or(0, NonZeroU64::get)
|
||||
pub const fn max_operations(&self) -> u64 {
|
||||
if let Some(n) = self.limits.max_operations {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
/// Set the maximum number of imported [modules][crate::Module] allowed for a script.
|
||||
///
|
||||
@ -89,7 +93,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn max_modules(&self) -> usize {
|
||||
pub const fn max_modules(&self) -> usize {
|
||||
self.limits.max_modules
|
||||
}
|
||||
/// Set the depth limits for expressions (0 for unlimited).
|
||||
@ -115,8 +119,12 @@ impl Engine {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn max_expr_depth(&self) -> usize {
|
||||
self.limits.max_expr_depth.map_or(0, NonZeroUsize::get)
|
||||
pub const fn max_expr_depth(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_expr_depth {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
/// The depth limit for expressions in functions (0 for unlimited).
|
||||
///
|
||||
@ -125,10 +133,12 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn max_function_expr_depth(&self) -> usize {
|
||||
self.limits
|
||||
.max_function_expr_depth
|
||||
.map_or(0, NonZeroUsize::get)
|
||||
pub const fn max_function_expr_depth(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_function_expr_depth {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
/// Set the maximum length of [strings][crate::ImmutableString] (0 for unlimited).
|
||||
///
|
||||
@ -145,8 +155,12 @@ impl Engine {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn max_string_size(&self) -> usize {
|
||||
self.limits.max_string_size.map_or(0, NonZeroUsize::get)
|
||||
pub const fn max_string_size(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_string_size {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
/// Set the maximum length of [arrays][crate::Array] (0 for unlimited).
|
||||
///
|
||||
@ -165,8 +179,12 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn max_array_size(&self) -> usize {
|
||||
self.limits.max_array_size.map_or(0, NonZeroUsize::get)
|
||||
pub const fn max_array_size(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_array_size {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
/// Set the maximum size of [object maps][crate::Map] (0 for unlimited).
|
||||
///
|
||||
@ -185,8 +203,12 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn max_map_size(&self) -> usize {
|
||||
self.limits.max_map_size.map_or(0, NonZeroUsize::get)
|
||||
pub const fn max_map_size(&self) -> usize {
|
||||
if let Some(n) = self.limits.max_map_size {
|
||||
n.get()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
/// Set the module resolution service used by the [`Engine`].
|
||||
///
|
||||
|
117
src/error.rs
117
src/error.rs
@ -93,55 +93,13 @@ pub enum EvalAltResult {
|
||||
Return(Dynamic, Position),
|
||||
}
|
||||
|
||||
impl EvalAltResult {
|
||||
#[must_use]
|
||||
pub(crate) fn desc(&self) -> &str {
|
||||
match self {
|
||||
#[allow(deprecated)]
|
||||
Self::ErrorSystem(_, s) => s.description(),
|
||||
Self::ErrorParsing(p, _) => p.desc(),
|
||||
Self::ErrorInFunctionCall(_, _, _, _) => "Error in called function",
|
||||
Self::ErrorInModule(_, _, _) => "Error in module",
|
||||
Self::ErrorFunctionNotFound(_, _) => "Function not found",
|
||||
Self::ErrorUnboundThis(_) => "'this' is not bound",
|
||||
Self::ErrorMismatchDataType(_, _, _) => "Data type is incorrect",
|
||||
Self::ErrorIndexingType(_, _) => "No indexer of the appropriate types defined",
|
||||
Self::ErrorArrayBounds(0, _, _) => "Empty array has nothing to access",
|
||||
Self::ErrorArrayBounds(_, _, _) => "Array index out of bounds",
|
||||
Self::ErrorStringBounds(0, _, _) => "Empty string has nothing to index",
|
||||
Self::ErrorStringBounds(_, _, _) => "String index out of bounds",
|
||||
Self::ErrorBitFieldBounds(_, _, _) => "Bit-field index out of bounds",
|
||||
Self::ErrorFor(_) => "For loop expects a type with an iterator defined",
|
||||
Self::ErrorVariableNotFound(_, _) => "Variable not found",
|
||||
Self::ErrorModuleNotFound(_, _) => "Module not found",
|
||||
Self::ErrorDataRace(_, _) => "Data race detected when accessing variable",
|
||||
Self::ErrorAssignmentToConstant(_, _) => "Cannot modify a constant",
|
||||
Self::ErrorMismatchOutputType(_, _, _) => "Output type is incorrect",
|
||||
Self::ErrorDotExpr(_, _) => "Malformed dot expression",
|
||||
Self::ErrorArithmetic(_, _) => "Arithmetic error",
|
||||
Self::ErrorTooManyOperations(_) => "Too many operations",
|
||||
Self::ErrorTooManyModules(_) => "Too many modules imported",
|
||||
Self::ErrorStackOverflow(_) => "Stack overflow",
|
||||
Self::ErrorDataTooLarge(_, _) => "Data size exceeds maximum limit",
|
||||
Self::ErrorTerminated(_, _) => "Script terminated.",
|
||||
Self::ErrorRuntime(_, _) => "Runtime error",
|
||||
Self::LoopBreak(true, _) => "Break statement not inside a loop",
|
||||
Self::LoopBreak(false, _) => "Continue statement not inside a loop",
|
||||
Self::Return(_, _) => "[Not Error] Function returns value",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for EvalAltResult {}
|
||||
|
||||
impl fmt::Display for EvalAltResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let desc = self.desc();
|
||||
let pos = self.position();
|
||||
|
||||
match self {
|
||||
Self::ErrorSystem(s, _) if s.is_empty() => f.write_str(desc)?,
|
||||
Self::ErrorSystem(s, _) => write!(f, "{}: {}", s, desc)?,
|
||||
Self::ErrorSystem(s, err) if s.is_empty() => write!(f, "{}", err)?,
|
||||
Self::ErrorSystem(s, err) => write!(f, "{}: {}", s, err)?,
|
||||
|
||||
Self::ErrorParsing(p, _) => write!(f, "Syntax error: {}", p)?,
|
||||
|
||||
@ -164,32 +122,35 @@ impl fmt::Display for EvalAltResult {
|
||||
}
|
||||
Self::ErrorInModule(s, err, _) => write!(f, "Error in module '{}': {}", s, err)?,
|
||||
|
||||
Self::ErrorFunctionNotFound(s, _)
|
||||
| Self::ErrorVariableNotFound(s, _)
|
||||
| Self::ErrorDataRace(s, _) => write!(f, "{}: {}", desc, s)?,
|
||||
|
||||
Self::ErrorModuleNotFound(s, _) => write!(f, "{}: '{}'", desc, s)?,
|
||||
|
||||
Self::ErrorFunctionNotFound(s, _) => write!(f, "Function not found: {}", s)?,
|
||||
Self::ErrorVariableNotFound(s, _) => write!(f, "Variable not found: {}", s)?,
|
||||
Self::ErrorModuleNotFound(s, _) => write!(f, "Module not found: '{}'", s)?,
|
||||
Self::ErrorDataRace(s, _) => {
|
||||
write!(f, "Data race detected when accessing variable: {}", s)?
|
||||
}
|
||||
Self::ErrorDotExpr(s, _) if !s.is_empty() => f.write_str(s)?,
|
||||
|
||||
Self::ErrorIndexingType(s, _) => write!(f, "Indexer not registered for '{}'", s)?,
|
||||
|
||||
Self::ErrorUnboundThis(_)
|
||||
| Self::ErrorFor(_)
|
||||
| Self::ErrorDotExpr(_, _)
|
||||
| Self::ErrorTooManyOperations(_)
|
||||
| Self::ErrorTooManyModules(_)
|
||||
| Self::ErrorStackOverflow(_)
|
||||
| Self::ErrorTerminated(_, _) => f.write_str(desc)?,
|
||||
Self::ErrorUnboundThis(_) => f.write_str("'this' is not bound")?,
|
||||
Self::ErrorFor(_) => f.write_str("For loop expects a type with an iterator defined")?,
|
||||
Self::ErrorDotExpr(_, _) => f.write_str("Malformed dot expression")?,
|
||||
Self::ErrorTooManyOperations(_) => f.write_str("Too many operations")?,
|
||||
Self::ErrorTooManyModules(_) => f.write_str("Too many modules imported")?,
|
||||
Self::ErrorStackOverflow(_) => f.write_str("Stack overflow")?,
|
||||
Self::ErrorTerminated(_, _) => f.write_str("Script terminated")?,
|
||||
|
||||
Self::ErrorRuntime(d, _) if d.is::<ImmutableString>() => {
|
||||
let s = &*d
|
||||
.read_lock::<ImmutableString>()
|
||||
.expect("never fails because the type was checked");
|
||||
write!(f, "{}: {}", desc, if s.is_empty() { desc } else { s })?
|
||||
|
||||
if s.is_empty() {
|
||||
f.write_str("Runtime error")?
|
||||
} else {
|
||||
write!(f, "Runtime error: {}", s)?
|
||||
}
|
||||
}
|
||||
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str(desc)?,
|
||||
Self::ErrorRuntime(d, _) => write!(f, "{}: {}", desc, d)?,
|
||||
Self::ErrorRuntime(d, _) if d.is::<()>() => f.write_str("Runtime error")?,
|
||||
Self::ErrorRuntime(d, _) => write!(f, "Runtime error: {}", d)?,
|
||||
|
||||
Self::ErrorAssignmentToConstant(s, _) => write!(f, "Cannot modify constant {}", s)?,
|
||||
Self::ErrorMismatchOutputType(s, r, _) => {
|
||||
@ -206,8 +167,10 @@ impl fmt::Display for EvalAltResult {
|
||||
}
|
||||
Self::ErrorArithmetic(s, _) => f.write_str(s)?,
|
||||
|
||||
Self::LoopBreak(_, _) => f.write_str(desc)?,
|
||||
Self::Return(_, _) => f.write_str(desc)?,
|
||||
Self::LoopBreak(true, _) => f.write_str("Break statement not inside a loop")?,
|
||||
Self::LoopBreak(false, _) => f.write_str("Continue statement not inside a loop")?,
|
||||
|
||||
Self::Return(_, _) => f.write_str("NOT AN ERROR - Function returns value")?,
|
||||
|
||||
Self::ErrorArrayBounds(0, index, _) => {
|
||||
write!(f, "Array index {} out of bounds: array is empty", index)?
|
||||
@ -244,8 +207,8 @@ impl fmt::Display for EvalAltResult {
|
||||
}
|
||||
|
||||
// Do not write any position if None
|
||||
if !pos.is_none() {
|
||||
write!(f, " ({})", pos)?;
|
||||
if !self.position().is_none() {
|
||||
write!(f, " ({})", self.position())?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -274,19 +237,15 @@ impl EvalAltResult {
|
||||
///
|
||||
/// [`LoopBreak`][EvalAltResult::LoopBreak] and [`Return`][EvalAltResult::Return] are pseudo errors.
|
||||
#[must_use]
|
||||
pub fn is_pseudo_error(&self) -> bool {
|
||||
pub const fn is_pseudo_error(&self) -> bool {
|
||||
match self {
|
||||
Self::LoopBreak(_, _) | Self::Return(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
/// Can this error be caught?
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return].
|
||||
#[must_use]
|
||||
pub fn is_catchable(&self) -> bool {
|
||||
pub const fn is_catchable(&self) -> bool {
|
||||
match self {
|
||||
Self::ErrorSystem(_, _) => false,
|
||||
Self::ErrorParsing(_, _) => false,
|
||||
@ -316,17 +275,12 @@ impl EvalAltResult {
|
||||
| Self::ErrorDataTooLarge(_, _)
|
||||
| Self::ErrorTerminated(_, _) => false,
|
||||
|
||||
Self::LoopBreak(_, _) => panic!("EvalAltResult::LoopBreak should not occur naturally"),
|
||||
Self::Return(_, _) => panic!("EvalAltResult::Return should not occur naturally"),
|
||||
Self::LoopBreak(_, _) | Self::Return(_, _) => false,
|
||||
}
|
||||
}
|
||||
/// Is this error a system exception?
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics when [`LoopBreak`][EvalAltResult::LoopBreak] or [`Return`][EvalAltResult::Return].
|
||||
#[must_use]
|
||||
pub fn is_system_exception(&self) -> bool {
|
||||
pub const fn is_system_exception(&self) -> bool {
|
||||
match self {
|
||||
Self::ErrorSystem(_, _) => true,
|
||||
Self::ErrorParsing(_, _) => true,
|
||||
@ -338,9 +292,6 @@ impl EvalAltResult {
|
||||
|
||||
Self::ErrorTerminated(_, _) => true,
|
||||
|
||||
Self::LoopBreak(_, _) => panic!("EvalAltResult::LoopBreak should not occur naturally"),
|
||||
Self::Return(_, _) => panic!("EvalAltResult::Return should not occur naturally"),
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -414,7 +365,7 @@ impl EvalAltResult {
|
||||
}
|
||||
/// Get the [position][Position] of this error.
|
||||
#[must_use]
|
||||
pub fn position(&self) -> Position {
|
||||
pub const fn position(&self) -> Position {
|
||||
match self {
|
||||
Self::ErrorSystem(_, _) => Position::NONE,
|
||||
|
||||
|
@ -43,12 +43,16 @@ impl fmt::Display for LexError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::UnexpectedInput(s) => write!(f, "Unexpected '{}'", s),
|
||||
Self::MalformedEscapeSequence(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||
Self::MalformedNumber(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||
Self::MalformedChar(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||
Self::MalformedIdentifier(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||
Self::UnterminatedString => f.write_str(self.desc()),
|
||||
Self::StringTooLong(max) => write!(f, "{} ({})", self.desc(), max),
|
||||
Self::MalformedEscapeSequence(s) => write!(f, "Invalid escape sequence: '{}'", s),
|
||||
Self::MalformedNumber(s) => write!(f, "Invalid number: '{}'", s),
|
||||
Self::MalformedChar(s) => write!(f, "Invalid character: '{}'", s),
|
||||
Self::MalformedIdentifier(s) => write!(f, "Variable name is not proper: '{}'", s),
|
||||
Self::UnterminatedString => f.write_str("Open string is not terminated"),
|
||||
Self::StringTooLong(max) => write!(
|
||||
f,
|
||||
"Length of string literal exceeds the maximum limit ({})",
|
||||
max
|
||||
),
|
||||
Self::ImproperSymbol(s, d) if d.is_empty() => {
|
||||
write!(f, "Invalid symbol encountered: '{}'", s)
|
||||
}
|
||||
@ -58,19 +62,6 @@ impl fmt::Display for LexError {
|
||||
}
|
||||
|
||||
impl LexError {
|
||||
#[must_use]
|
||||
pub(crate) fn desc(&self) -> &str {
|
||||
match self {
|
||||
Self::UnexpectedInput(_) => "Unexpected character encountered",
|
||||
Self::UnterminatedString => "Open string is not terminated",
|
||||
Self::StringTooLong(_) => "Length of string literal exceeds the maximum limit",
|
||||
Self::MalformedEscapeSequence(_) => "Invalid escape sequence",
|
||||
Self::MalformedNumber(_) => "Invalid number",
|
||||
Self::MalformedChar(_) => "Invalid character",
|
||||
Self::MalformedIdentifier(_) => "Variable name is not proper",
|
||||
Self::ImproperSymbol(_, _) => "Invalid symbol encountered",
|
||||
}
|
||||
}
|
||||
/// Convert a [`LexError`] into a [`ParseError`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
@ -192,44 +183,7 @@ impl ParseErrorType {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn into_err(self, pos: Position) -> ParseError {
|
||||
ParseError(Box::new(self), pos)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn desc(&self) -> &str {
|
||||
match self {
|
||||
Self::UnexpectedEOF => "Script is incomplete",
|
||||
Self::BadInput(err) => err.desc(),
|
||||
Self::UnknownOperator(_) => "Unknown operator",
|
||||
Self::MissingToken(_, _) => "Expecting a certain token that is missing",
|
||||
Self::MissingSymbol(_) => "Expecting a certain symbol that is missing",
|
||||
Self::MalformedCallExpr(_) => "Invalid expression in function call arguments",
|
||||
Self::MalformedIndexExpr(_) => "Invalid index in indexing expression",
|
||||
Self::MalformedInExpr(_) => "Invalid 'in' expression",
|
||||
Self::MalformedCapture(_) => "Invalid capturing",
|
||||
Self::DuplicatedProperty(_) => "Duplicated property in object map literal",
|
||||
Self::DuplicatedSwitchCase => "Duplicated switch case",
|
||||
Self::DuplicatedVariable(_) => "Duplicated variable name",
|
||||
Self::WrongSwitchDefaultCase => "Default switch case is not the last",
|
||||
Self::WrongSwitchCaseCondition => "Default switch case cannot have condition",
|
||||
Self::PropertyExpected => "Expecting name of a property",
|
||||
Self::VariableExpected => "Expecting name of a variable",
|
||||
Self::Reserved(_) => "Invalid use of reserved keyword",
|
||||
Self::ExprExpected(_) => "Expecting an expression",
|
||||
Self::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||
Self::FnDuplicatedDefinition(_, _) => "Function already exists",
|
||||
Self::FnMissingName => "Expecting function name in function declaration",
|
||||
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||
Self::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
||||
Self::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||
Self::WrongDocComment => "Doc-comment must be followed immediately by a function definition",
|
||||
Self::WrongExport => "Export statement can only appear at global level",
|
||||
Self::AssignmentToConstant(_) => "Cannot assign to a constant value",
|
||||
Self::AssignmentToInvalidLHS(_) => "Expression cannot be assigned to",
|
||||
Self::ExprTooDeep => "Expression exceeds maximum complexity",
|
||||
Self::LiteralTooLarge(_, _) => "Literal exceeds maximum limit",
|
||||
Self::LoopBreak => "Break statement should only be used inside a loop"
|
||||
}
|
||||
ParseError(self.into(), pos)
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,12 +192,14 @@ impl fmt::Display for ParseErrorType {
|
||||
match self {
|
||||
Self::BadInput(err) => write!(f, "{}", err),
|
||||
|
||||
Self::MalformedCallExpr(s) => f.write_str(if s.is_empty() { self.desc() } else { s }),
|
||||
Self::UnknownOperator(s) => write!(f, "{}: '{}'", self.desc(), s),
|
||||
Self::UnknownOperator(s) => write!(f, "Unknown operator: '{}'", s),
|
||||
|
||||
Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => {
|
||||
f.write_str(if s.is_empty() { self.desc() } else { s })
|
||||
}
|
||||
Self::MalformedCallExpr(s) if s.is_empty() => f.write_str("Invalid expression in function call arguments"),
|
||||
Self::MalformedIndexExpr(s) if s.is_empty() => f.write_str("Invalid index in indexing expression"),
|
||||
Self::MalformedInExpr(s) if s.is_empty() => f.write_str("Invalid 'in' expression"),
|
||||
Self::MalformedCapture(s) if s.is_empty() => f.write_str("Invalid capturing"),
|
||||
|
||||
Self::MalformedCallExpr(s) | Self::MalformedIndexExpr(s) | Self::MalformedInExpr(s) | Self::MalformedCapture(s) => f.write_str(s),
|
||||
|
||||
Self::FnDuplicatedDefinition(s, n) => {
|
||||
write!(f, "Function '{}' with ", s)?;
|
||||
@ -253,43 +209,39 @@ impl fmt::Display for ParseErrorType {
|
||||
_ => write!(f, "{} parameters already exists", n),
|
||||
}
|
||||
}
|
||||
Self::DuplicatedProperty(s) => {
|
||||
write!(f, "Duplicated property '{}' for object map literal", s)
|
||||
}
|
||||
Self::DuplicatedSwitchCase => f.write_str(self.desc()),
|
||||
|
||||
Self::FnMissingBody(s) if s.is_empty() => f.write_str("Expecting body statement block for anonymous function"),
|
||||
Self::FnMissingBody(s) => write!(f, "Expecting body statement block for function '{}'", s),
|
||||
|
||||
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s),
|
||||
Self::FnDuplicatedParam(s, arg) => write!(f, "Duplicated parameter '{}' for function '{}'", arg, s),
|
||||
Self::DuplicatedProperty(s) => write!(f, "Duplicated property '{}' for object map literal", s),
|
||||
Self::DuplicatedSwitchCase => f.write_str("Duplicated switch case"),
|
||||
Self::DuplicatedVariable(s) => write!(f, "Duplicated variable name '{}'", s),
|
||||
|
||||
Self::ExprExpected(s) => write!(f, "Expecting {} expression", s),
|
||||
|
||||
Self::FnMissingParams(s) => write!(f, "Expecting parameters for function '{}'", s),
|
||||
|
||||
Self::FnMissingBody(s) if s.is_empty() => {
|
||||
f.write_str("Expecting body statement block for anonymous function")
|
||||
}
|
||||
Self::FnMissingBody(s) => {
|
||||
write!(f, "Expecting body statement block for function '{}'", s)
|
||||
}
|
||||
|
||||
Self::FnDuplicatedParam(s, arg) => {
|
||||
write!(f, "Duplicated parameter '{}' for function '{}'", arg, s)
|
||||
}
|
||||
|
||||
Self::MissingToken(token, s) => write!(f, "Expecting '{}' {}", token, s),
|
||||
Self::MissingSymbol(s) => f.write_str(s),
|
||||
|
||||
Self::AssignmentToConstant(s) if s.is_empty() => f.write_str(self.desc()),
|
||||
Self::AssignmentToConstant(s) if s.is_empty() => f.write_str("Cannot assign to a constant value"),
|
||||
Self::AssignmentToConstant(s) => write!(f, "Cannot assign to constant '{}'", s),
|
||||
|
||||
Self::AssignmentToInvalidLHS(s) if s.is_empty() => f.write_str(self.desc()),
|
||||
Self::AssignmentToInvalidLHS(s) if s.is_empty() => f.write_str("Expression cannot be assigned to"),
|
||||
Self::AssignmentToInvalidLHS(s) => f.write_str(s),
|
||||
|
||||
Self::LiteralTooLarge(typ, max) => {
|
||||
write!(f, "{} exceeds the maximum limit ({})", typ, max)
|
||||
}
|
||||
|
||||
Self::LiteralTooLarge(typ, max) => write!(f, "{} exceeds the maximum limit ({})", typ, max),
|
||||
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
||||
|
||||
_ => f.write_str(self.desc()),
|
||||
Self::UnexpectedEOF => f.write_str("Script is incomplete"),
|
||||
Self::WrongSwitchDefaultCase => f.write_str("Default switch case is not the last"),
|
||||
Self::WrongSwitchCaseCondition => f.write_str("Default switch case cannot have condition"),
|
||||
Self::PropertyExpected => f.write_str("Expecting name of a property"),
|
||||
Self::VariableExpected => f.write_str("Expecting name of a variable"),
|
||||
Self::WrongFnDefinition => f.write_str("Function definitions must be at global level and cannot be inside a block or another function"),
|
||||
Self::FnMissingName => f.write_str("Expecting function name in function declaration"),
|
||||
Self::WrongDocComment => f.write_str("Doc-comment must be followed immediately by a function definition"),
|
||||
Self::WrongExport => f.write_str("Export statement can only appear at global level"),
|
||||
Self::ExprTooDeep => f.write_str("Expression exceeds maximum complexity"),
|
||||
Self::LoopBreak => f.write_str("Break statement should only be used inside a loop"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use crate::dynamic::Variant;
|
||||
use crate::{Dynamic, StaticVec};
|
||||
use crate::Dynamic;
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
|
||||
@ -70,13 +70,10 @@ macro_rules! impl_args {
|
||||
impl<$($p: Variant + Clone),*> FuncArgs for ($($p,)*)
|
||||
{
|
||||
#[inline(always)]
|
||||
#[allow(unused_variables)]
|
||||
fn parse<CONTAINER: Extend<Dynamic>>(self, container: &mut CONTAINER) {
|
||||
let ($($p,)*) = self;
|
||||
|
||||
let mut _v = StaticVec::new();
|
||||
$(_v.push($p.into_dynamic());)*
|
||||
|
||||
container.extend(_v.into_iter());
|
||||
$(container.extend(Some($p.into_dynamic()));)*
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ use crate::FLOAT;
|
||||
#[cfg(feature = "decimal")]
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
/// The message: never fails because this is built-in code and the type is already checked
|
||||
const BUILTIN: &str = "never fails because this is built-in code and the type is already checked";
|
||||
|
||||
/// Is the type a numeric type?
|
||||
|
@ -302,9 +302,13 @@ impl Engine {
|
||||
|
||||
let result = if func.is_plugin_fn() {
|
||||
func.get_plugin_fn()
|
||||
.expect("never fails because the function is a plugin")
|
||||
.call((self, name, source, mods, lib).into(), args)
|
||||
} else {
|
||||
func.get_native_fn()((self, name, source, mods, lib).into(), args)
|
||||
let func = func
|
||||
.get_native_fn()
|
||||
.expect("never fails because the function is native");
|
||||
func((self, name, source, mods, lib).into(), args)
|
||||
};
|
||||
|
||||
// Restore the original reference
|
||||
@ -315,25 +319,33 @@ impl Engine {
|
||||
// See if the function match print/debug (which requires special processing)
|
||||
return Ok(match name {
|
||||
KEYWORD_PRINT => {
|
||||
let text = result.as_immutable_string().map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||
typ.into(),
|
||||
pos,
|
||||
)
|
||||
})?;
|
||||
((self.print)(&text).into(), false)
|
||||
if let Some(ref print) = self.print {
|
||||
let text = result.as_immutable_string().map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||
typ.into(),
|
||||
pos,
|
||||
)
|
||||
})?;
|
||||
(print(&text).into(), false)
|
||||
} else {
|
||||
(Dynamic::UNIT, false)
|
||||
}
|
||||
}
|
||||
KEYWORD_DEBUG => {
|
||||
let text = result.as_immutable_string().map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||
typ.into(),
|
||||
pos,
|
||||
)
|
||||
})?;
|
||||
let source = state.source.as_ref().map(|s| s.as_str());
|
||||
((self.debug)(&text, source, pos).into(), false)
|
||||
if let Some(ref debug) = self.debug {
|
||||
let text = result.as_immutable_string().map_err(|typ| {
|
||||
EvalAltResult::ErrorMismatchOutputType(
|
||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||
typ.into(),
|
||||
pos,
|
||||
)
|
||||
})?;
|
||||
let source = state.source.as_ref().map(|s| s.as_str());
|
||||
(debug(&text, source, pos).into(), false)
|
||||
} else {
|
||||
(Dynamic::UNIT, false)
|
||||
}
|
||||
}
|
||||
_ => (result, func.is_method()),
|
||||
});
|
||||
@ -681,7 +693,9 @@ impl Engine {
|
||||
// Script function call
|
||||
assert!(func.is_script());
|
||||
|
||||
let func = func.get_fn_def();
|
||||
let func = func
|
||||
.get_script_fn_def()
|
||||
.expect("never fails because the function is scripted");
|
||||
|
||||
if func.body.is_empty() {
|
||||
return Ok((Dynamic::UNIT, false));
|
||||
@ -937,7 +951,7 @@ impl Engine {
|
||||
fn_ptr.clone()
|
||||
} else {
|
||||
FnPtr::new_unchecked(
|
||||
fn_ptr.get_fn_name().clone(),
|
||||
fn_ptr.fn_name_raw().clone(),
|
||||
fn_ptr
|
||||
.curry()
|
||||
.iter()
|
||||
@ -967,7 +981,7 @@ impl Engine {
|
||||
if let Some(val) = map.get(fn_name) {
|
||||
if let Some(fn_ptr) = val.read_lock::<FnPtr>() {
|
||||
// Remap the function name
|
||||
_redirected = fn_ptr.get_fn_name().clone();
|
||||
_redirected = fn_ptr.fn_name_raw().clone();
|
||||
fn_name = &_redirected;
|
||||
// Add curried arguments
|
||||
if fn_ptr.is_curried() {
|
||||
@ -1192,7 +1206,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
return result.map_err(|err| {
|
||||
Box::new(EvalAltResult::ErrorInFunctionCall(
|
||||
EvalAltResult::ErrorInFunctionCall(
|
||||
KEYWORD_EVAL.to_string(),
|
||||
state
|
||||
.source
|
||||
@ -1201,7 +1215,8 @@ impl Engine {
|
||||
.unwrap_or_default(),
|
||||
err,
|
||||
pos,
|
||||
))
|
||||
)
|
||||
.into()
|
||||
});
|
||||
}
|
||||
|
||||
@ -1384,7 +1399,9 @@ impl Engine {
|
||||
match func {
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Some(f) if f.is_script() => {
|
||||
let fn_def = f.get_fn_def();
|
||||
let fn_def = f
|
||||
.get_script_fn_def()
|
||||
.expect("never fails because the function is scripted");
|
||||
|
||||
if fn_def.body.is_empty() {
|
||||
Ok(Dynamic::UNIT)
|
||||
@ -1408,12 +1425,16 @@ impl Engine {
|
||||
|
||||
Some(f) if f.is_plugin_fn() => f
|
||||
.get_plugin_fn()
|
||||
.expect("never fails because the function is a plugin")
|
||||
.clone()
|
||||
.call((self, fn_name, module.id(), &*mods, lib).into(), &mut args)
|
||||
.map_err(|err| err.fill_position(pos)),
|
||||
|
||||
Some(f) if f.is_native() => {
|
||||
f.get_native_fn()((self, fn_name, module.id(), &*mods, lib).into(), &mut args)
|
||||
let func = f
|
||||
.get_native_fn()
|
||||
.expect("never fails because the function is native");
|
||||
func((self, fn_name, module.id(), &*mods, lib).into(), &mut args)
|
||||
.map_err(|err| err.fill_position(pos))
|
||||
}
|
||||
|
||||
|
@ -52,6 +52,33 @@ pub fn get_hasher() -> ahash::AHasher {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Calculate a [`u64`] hash key from a namespace-qualified variable name.
|
||||
///
|
||||
/// Module names are passed in via `&str` references from an iterator.
|
||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn calc_qualified_var_hash<'a>(
|
||||
modules: impl Iterator<Item = &'a str>,
|
||||
var_name: impl AsRef<str>,
|
||||
) -> u64 {
|
||||
let s = &mut get_hasher();
|
||||
|
||||
// We always skip the first module
|
||||
let mut len = 0;
|
||||
modules
|
||||
.inspect(|_| len += 1)
|
||||
.skip(1)
|
||||
.for_each(|m| m.hash(s));
|
||||
len.hash(s);
|
||||
var_name.as_ref().hash(s);
|
||||
s.finish()
|
||||
}
|
||||
|
||||
/// Calculate a [`u64`] hash key from a namespace-qualified function name
|
||||
/// and the number of parameters, but no parameter types.
|
||||
///
|
||||
@ -108,6 +135,6 @@ pub fn calc_fn_params_hash(params: impl Iterator<Item = TypeId>) -> u64 {
|
||||
/// Combine two [`u64`] hashes by taking the XOR of them.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
|
||||
pub(crate) const fn combine_hashes(a: u64, b: u64) -> u64 {
|
||||
a ^ b
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
/// Create a new [`NativeCallContext`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self {
|
||||
pub const fn new(engine: &'a Engine, fn_name: &'a str, lib: &'a [&Module]) -> Self {
|
||||
Self {
|
||||
engine,
|
||||
fn_name,
|
||||
@ -106,17 +106,17 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn new_with_all_fields(
|
||||
pub const fn new_with_all_fields(
|
||||
engine: &'a Engine,
|
||||
fn_name: &'a str,
|
||||
source: &'a Option<&str>,
|
||||
source: Option<&'a str>,
|
||||
imports: &'a Imports,
|
||||
lib: &'a [&Module],
|
||||
) -> Self {
|
||||
Self {
|
||||
engine,
|
||||
fn_name,
|
||||
source: source.clone(),
|
||||
source,
|
||||
mods: Some(imports),
|
||||
lib,
|
||||
}
|
||||
@ -124,19 +124,19 @@ impl<'a> NativeCallContext<'a> {
|
||||
/// The current [`Engine`].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn engine(&self) -> &Engine {
|
||||
pub const fn engine(&self) -> &Engine {
|
||||
self.engine
|
||||
}
|
||||
/// Name of the function called.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn fn_name(&self) -> &str {
|
||||
pub const fn fn_name(&self) -> &str {
|
||||
self.fn_name
|
||||
}
|
||||
/// The current source.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn source(&self) -> Option<&str> {
|
||||
pub const fn source(&self) -> Option<&str> {
|
||||
self.source
|
||||
}
|
||||
/// Get an iterator over the current set of modules imported via `import` statements.
|
||||
@ -166,7 +166,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn imports(&self) -> Option<&Imports> {
|
||||
pub const fn imports(&self) -> Option<&Imports> {
|
||||
self.mods
|
||||
}
|
||||
/// Get an iterator over the namespaces containing definitions of all script-defined functions.
|
||||
@ -180,7 +180,7 @@ impl<'a> NativeCallContext<'a> {
|
||||
#[cfg(feature = "internals")]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn namespaces(&self) -> &[&Module] {
|
||||
pub const fn namespaces(&self) -> &[&Module] {
|
||||
self.lib
|
||||
}
|
||||
/// Call a function inside the call context.
|
||||
@ -457,74 +457,51 @@ impl CallableFunction {
|
||||
}
|
||||
}
|
||||
/// Get a shared reference to a native Rust function.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the [`CallableFunction`] is not [`Pure`][CallableFunction::Pure] or
|
||||
/// [`Method`][CallableFunction::Method].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get_native_fn(&self) -> &Shared<FnAny> {
|
||||
pub fn get_native_fn(&self) -> Option<&Shared<FnAny>> {
|
||||
match self {
|
||||
Self::Pure(f) | Self::Method(f) => f,
|
||||
Self::Iterator(_) | Self::Plugin(_) => panic!("function should be native"),
|
||||
Self::Pure(f) | Self::Method(f) => Some(f),
|
||||
Self::Iterator(_) | Self::Plugin(_) => None,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(_) => panic!("function should be native"),
|
||||
Self::Script(_) => None,
|
||||
}
|
||||
}
|
||||
/// Get a shared reference to a script-defined function definition.
|
||||
///
|
||||
/// Not available under `no_function`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the [`CallableFunction`] is not [`Script`][CallableFunction::Script].
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get_fn_def(&self) -> &Shared<crate::ast::ScriptFnDef> {
|
||||
pub const fn get_script_fn_def(&self) -> Option<&Shared<crate::ast::ScriptFnDef>> {
|
||||
match self {
|
||||
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => {
|
||||
panic!("function should be scripted")
|
||||
}
|
||||
Self::Script(f) => f,
|
||||
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => None,
|
||||
Self::Script(f) => Some(f),
|
||||
}
|
||||
}
|
||||
/// Get a reference to an iterator function.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the [`CallableFunction`] is not [`Iterator`][CallableFunction::Iterator].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get_iter_fn(&self) -> IteratorFn {
|
||||
pub fn get_iter_fn(&self) -> Option<IteratorFn> {
|
||||
match self {
|
||||
Self::Iterator(f) => *f,
|
||||
Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => {
|
||||
panic!("function should an iterator")
|
||||
}
|
||||
Self::Iterator(f) => Some(*f),
|
||||
Self::Pure(_) | Self::Method(_) | Self::Plugin(_) => None,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(_) => panic!("function should be an iterator"),
|
||||
Self::Script(_) => None,
|
||||
}
|
||||
}
|
||||
/// Get a shared reference to a plugin function.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the [`CallableFunction`] is not [`Plugin`][CallableFunction::Plugin].
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn get_plugin_fn<'s>(&'s self) -> &Shared<FnPlugin> {
|
||||
pub fn get_plugin_fn<'s>(&'s self) -> Option<&Shared<FnPlugin>> {
|
||||
match self {
|
||||
Self::Plugin(f) => f,
|
||||
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => {
|
||||
panic!("function should a plugin")
|
||||
}
|
||||
Self::Plugin(f) => Some(f),
|
||||
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => None,
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
Self::Script(_) => panic!("function should a plugin"),
|
||||
Self::Script(_) => None,
|
||||
}
|
||||
}
|
||||
/// Create a new [`CallableFunction::Pure`].
|
||||
|
@ -33,12 +33,12 @@ impl FnPtr {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn fn_name(&self) -> &str {
|
||||
self.get_fn_name().as_ref()
|
||||
self.fn_name_raw().as_ref()
|
||||
}
|
||||
/// Get the name of the function.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn get_fn_name(&self) -> &Identifier {
|
||||
pub(crate) const fn fn_name_raw(&self) -> &Identifier {
|
||||
&self.0
|
||||
}
|
||||
/// Get the underlying data of the function pointer.
|
||||
|
@ -106,12 +106,25 @@ impl From<String> for ImmutableString {
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
impl From<&SmartString> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: &SmartString) -> Self {
|
||||
Self(Into::<SmartString>::into(value.as_str()).into())
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_smartstring"))]
|
||||
impl From<SmartString> for ImmutableString {
|
||||
#[inline(always)]
|
||||
fn from(value: SmartString) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
impl From<&ImmutableString> for SmartString {
|
||||
#[inline(always)]
|
||||
fn from(value: &ImmutableString) -> Self {
|
||||
value.as_str().into()
|
||||
}
|
||||
}
|
||||
impl From<ImmutableString> for SmartString {
|
||||
#[inline(always)]
|
||||
fn from(mut value: ImmutableString) -> Self {
|
||||
|
21
src/lib.rs
21
src/lib.rs
@ -141,22 +141,6 @@ pub use module::{FnNamespace, Module};
|
||||
pub use scope::Scope;
|
||||
pub use token::Position;
|
||||
|
||||
/// A trait to enable registering Rust functions.
|
||||
/// This trait is no longer needed and will be removed in the future.
|
||||
#[deprecated(
|
||||
since = "0.19.15",
|
||||
note = "this trait is no longer needed and will be removed in the future"
|
||||
)]
|
||||
pub trait RegisterFn {}
|
||||
|
||||
/// A trait to enable registering Rust functions.
|
||||
/// This trait is no longer needed and will be removed in the future.
|
||||
#[deprecated(
|
||||
since = "0.19.15",
|
||||
note = "this trait is no longer needed and will be removed in the future"
|
||||
)]
|
||||
pub trait RegisterResultFn {}
|
||||
|
||||
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most
|
||||
/// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline.
|
||||
#[cfg(not(feature = "internals"))]
|
||||
@ -188,7 +172,8 @@ pub use fn_native::Shared;
|
||||
use fn_native::Locked;
|
||||
|
||||
pub(crate) use fn_hash::{
|
||||
calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes,
|
||||
calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, calc_qualified_var_hash,
|
||||
combine_hashes,
|
||||
};
|
||||
|
||||
pub use rhai_codegen::*;
|
||||
@ -207,7 +192,7 @@ pub use ast::ScriptFnMetadata;
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
pub type Array = Vec<Dynamic>;
|
||||
|
||||
/// Hash map of [`Dynamic`] values with [`ImmutableString`] keys.
|
||||
/// Hash map of [`Dynamic`] values with [`SmartString`](https://crates.io/crates/smartstring) keys.
|
||||
/// Not available under `no_object`.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
pub type Map = std::collections::BTreeMap<Identifier, Dynamic>;
|
||||
|
@ -235,8 +235,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_var("answer", 42_i64);
|
||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||
@ -265,10 +264,9 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_id(Some("hello"));
|
||||
/// module.set_id("hello");
|
||||
/// assert_eq!(module.id(), Some("hello"));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
@ -278,19 +276,9 @@ impl Module {
|
||||
}
|
||||
|
||||
/// Get the ID of the [`Module`] as an [`Identifier`], if any.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_id(Some("hello"));
|
||||
/// assert_eq!(module.id_raw().map(|s| s.as_str()), Some("hello"));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn id_raw(&self) -> Option<&Identifier> {
|
||||
pub(crate) const fn id_raw(&self) -> Option<&Identifier> {
|
||||
self.id.as_ref()
|
||||
}
|
||||
|
||||
@ -299,15 +287,31 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_id(Some("hello"));
|
||||
/// module.set_id("hello");
|
||||
/// assert_eq!(module.id(), Some("hello"));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_id<S: Into<Identifier>>(&mut self, id: Option<S>) -> &mut Self {
|
||||
self.id = id.map(|s| s.into());
|
||||
pub fn set_id(&mut self, id: impl Into<Identifier>) -> &mut Self {
|
||||
self.id = Some(id.into());
|
||||
self
|
||||
}
|
||||
/// Clear the ID of the [`Module`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_id("hello");
|
||||
/// assert_eq!(module.id(), Some("hello"));
|
||||
/// module.clear_id();
|
||||
/// assert_eq!(module.id(), None);
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn clear_id(&mut self) -> &mut Self {
|
||||
self.id = None;
|
||||
self
|
||||
}
|
||||
|
||||
@ -316,8 +320,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let module = Module::new();
|
||||
/// assert!(module.is_empty());
|
||||
/// ```
|
||||
@ -340,8 +343,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// assert!(module.is_indexed());
|
||||
///
|
||||
@ -356,7 +358,7 @@ impl Module {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_indexed(&self) -> bool {
|
||||
pub const fn is_indexed(&self) -> bool {
|
||||
self.indexed
|
||||
}
|
||||
|
||||
@ -380,8 +382,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_var("answer", 42_i64);
|
||||
/// assert!(module.contains_var("answer"));
|
||||
@ -397,8 +398,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_var("answer", 42_i64);
|
||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||
@ -414,8 +414,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_var("answer", 42_i64);
|
||||
/// assert_eq!(module.get_var("answer").unwrap().cast::<i64>(), 42);
|
||||
@ -433,8 +432,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// module.set_var("answer", 42_i64);
|
||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||
@ -449,7 +447,7 @@ impl Module {
|
||||
let value = Dynamic::from(value);
|
||||
|
||||
if self.indexed {
|
||||
let hash_var = crate::calc_qualified_fn_hash(once(""), &ident, 0);
|
||||
let hash_var = crate::calc_qualified_var_hash(once(""), &ident);
|
||||
self.all_variables.insert(hash_var, value.clone());
|
||||
}
|
||||
self.variables.insert(ident, value);
|
||||
@ -481,7 +479,7 @@ impl Module {
|
||||
param_names.push("Dynamic".into());
|
||||
self.functions.insert(
|
||||
hash_script,
|
||||
Box::new(FuncInfo {
|
||||
FuncInfo {
|
||||
name: fn_def.name.clone(),
|
||||
namespace: FnNamespace::Internal,
|
||||
access: fn_def.access,
|
||||
@ -490,7 +488,8 @@ impl Module {
|
||||
#[cfg(feature = "metadata")]
|
||||
param_names,
|
||||
func: Into::<CallableFunction>::into(fn_def).into(),
|
||||
}),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
self.indexed = false;
|
||||
self.contains_indexed_global_functions = false;
|
||||
@ -510,7 +509,7 @@ impl Module {
|
||||
self.functions
|
||||
.values()
|
||||
.find(|f| f.params == num_params && f.name == name)
|
||||
.map(|f| f.func.get_fn_def())
|
||||
.and_then(|f| f.func.get_script_fn_def())
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the underlying [`BTreeMap`] of sub-modules.
|
||||
@ -539,8 +538,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// let sub_module = Module::new();
|
||||
/// module.set_sub_module("question", sub_module);
|
||||
@ -557,8 +555,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// let sub_module = Module::new();
|
||||
/// module.set_sub_module("question", sub_module);
|
||||
@ -577,8 +574,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// let sub_module = Module::new();
|
||||
/// module.set_sub_module("question", sub_module);
|
||||
@ -603,8 +599,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_native_fn("calc", || Ok(42_i64));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
@ -715,7 +710,7 @@ impl Module {
|
||||
|
||||
self.functions.insert(
|
||||
hash_fn,
|
||||
Box::new(FuncInfo {
|
||||
FuncInfo {
|
||||
name: self.identifiers.get(name),
|
||||
namespace,
|
||||
access,
|
||||
@ -724,7 +719,8 @@ impl Module {
|
||||
#[cfg(feature = "metadata")]
|
||||
param_names,
|
||||
func: func.into(),
|
||||
}),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
self.indexed = false;
|
||||
@ -845,8 +841,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_native_fn("calc", || Ok(42_i64));
|
||||
/// assert!(module.contains_fn(hash));
|
||||
@ -880,8 +875,7 @@ impl Module {
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rhai::Module;
|
||||
///
|
||||
/// # use rhai::Module;
|
||||
/// let mut module = Module::new();
|
||||
/// let hash = module.set_getter_fn("value", |x: &mut i64| { Ok(*x) });
|
||||
/// assert!(module.contains_fn(hash));
|
||||
@ -1349,7 +1343,9 @@ impl Module {
|
||||
f.access,
|
||||
f.name.as_str(),
|
||||
f.params,
|
||||
f.func.get_fn_def(),
|
||||
f.func
|
||||
.get_script_fn_def()
|
||||
.expect("never fails because the function is scripted"),
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -1474,13 +1470,23 @@ impl Module {
|
||||
.filter(|f| f.func.is_script())
|
||||
.for_each(|f| {
|
||||
// Encapsulate AST environment
|
||||
let mut func = f.func.get_fn_def().as_ref().clone();
|
||||
let mut func = f
|
||||
.func
|
||||
.get_script_fn_def()
|
||||
.expect("never fails because the function is scripted")
|
||||
.as_ref()
|
||||
.clone();
|
||||
func.lib = Some(ast.shared_lib());
|
||||
func.mods = func_mods.clone();
|
||||
module.set_script_fn(func);
|
||||
});
|
||||
|
||||
module.set_id(ast.clone_source());
|
||||
if let Some(s) = ast.source_raw() {
|
||||
module.set_id(s.clone());
|
||||
} else {
|
||||
module.clear_id();
|
||||
}
|
||||
|
||||
module.build_index();
|
||||
|
||||
Ok(module)
|
||||
@ -1523,7 +1529,7 @@ impl Module {
|
||||
|
||||
// Index all variables
|
||||
module.variables.iter().for_each(|(var_name, value)| {
|
||||
let hash_var = crate::calc_qualified_fn_hash(path.iter().map(|&v| v), var_name, 0);
|
||||
let hash_var = crate::calc_qualified_var_hash(path.iter().map(|&v| v), var_name);
|
||||
variables.insert(hash_var, value.clone());
|
||||
});
|
||||
|
||||
@ -1728,7 +1734,7 @@ impl NamespaceRef {
|
||||
/// Get the [`Scope`][crate::Scope] index offset.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub(crate) fn index(&self) -> Option<NonZeroUsize> {
|
||||
pub(crate) const fn index(&self) -> Option<NonZeroUsize> {
|
||||
self.index
|
||||
}
|
||||
/// Set the [`Scope`][crate::Scope] index offset.
|
||||
|
@ -345,17 +345,19 @@ impl ModuleResolver for FileModuleResolver {
|
||||
let file_path = self.get_file_path(path, source_path);
|
||||
|
||||
// Load the script file and compile it
|
||||
match engine.compile_file(file_path).map_err(|err| match *err {
|
||||
EvalAltResult::ErrorSystem(_, err) if err.is::<IoError>() => {
|
||||
Box::new(EvalAltResult::ErrorModuleNotFound(path.to_string(), pos))
|
||||
}
|
||||
_ => Box::new(EvalAltResult::ErrorInModule(path.to_string(), err, pos)),
|
||||
}) {
|
||||
Ok(mut ast) => {
|
||||
ast.set_source(path);
|
||||
Some(Ok(ast))
|
||||
}
|
||||
err => Some(err),
|
||||
}
|
||||
Some(
|
||||
engine
|
||||
.compile_file(file_path)
|
||||
.map(|mut ast| {
|
||||
ast.set_source(path);
|
||||
ast
|
||||
})
|
||||
.map_err(|err| match *err {
|
||||
EvalAltResult::ErrorSystem(_, err) if err.is::<IoError>() => {
|
||||
EvalAltResult::ErrorModuleNotFound(path.to_string(), pos).into()
|
||||
}
|
||||
_ => EvalAltResult::ErrorInModule(path.to_string(), err, pos).into(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -61,14 +61,14 @@ struct State<'a> {
|
||||
impl<'a> State<'a> {
|
||||
/// Create a new State.
|
||||
#[inline(always)]
|
||||
pub fn new(
|
||||
pub const fn new(
|
||||
engine: &'a Engine,
|
||||
lib: &'a [&'a Module],
|
||||
optimization_level: OptimizationLevel,
|
||||
) -> Self {
|
||||
Self {
|
||||
changed: false,
|
||||
variables: vec![],
|
||||
variables: Vec::new(),
|
||||
propagate_constants: true,
|
||||
engine,
|
||||
lib,
|
||||
@ -87,7 +87,7 @@ impl<'a> State<'a> {
|
||||
}
|
||||
/// Is the [`AST`] dirty (i.e. changed)?
|
||||
#[inline(always)]
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
pub const fn is_dirty(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
/// Prune the list of constants back to a specified size.
|
||||
@ -297,8 +297,14 @@ fn optimize_stmt_block(
|
||||
Stmt::Noop(pos)
|
||||
};
|
||||
}
|
||||
// { ...; stmt; noop } -> done
|
||||
[.., ref second_last_stmt, Stmt::Noop(_)]
|
||||
if second_last_stmt.returns_value() => {}
|
||||
if second_last_stmt.returns_value() =>
|
||||
{
|
||||
break
|
||||
}
|
||||
// { ...; stmt_that_returns; pure_non_value_stmt } -> { ...; stmt_that_returns; noop }
|
||||
// { ...; stmt; pure_non_value_stmt } -> { ...; stmt }
|
||||
[.., ref second_last_stmt, ref last_stmt]
|
||||
if !last_stmt.returns_value() && is_pure(last_stmt) =>
|
||||
{
|
||||
@ -413,7 +419,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
||||
|
||||
*stmt = if preserve_result {
|
||||
// -> { expr, Noop }
|
||||
Stmt::Block(Box::new([Stmt::Expr(expr), Stmt::Noop(pos)]), pos)
|
||||
Stmt::Block([Stmt::Expr(expr), Stmt::Noop(pos)].into(), pos)
|
||||
} else {
|
||||
// -> expr
|
||||
Stmt::Expr(expr)
|
||||
@ -787,17 +793,17 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
||||
// ``
|
||||
Expr::InterpolatedString(x) if x.is_empty() => {
|
||||
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
||||
state.set_dirty();
|
||||
*expr = Expr::StringConstant(state.engine.empty_string.clone(), Position::NONE);
|
||||
*expr = Expr::StringConstant(state.engine.empty_string.clone(), *pos);
|
||||
}
|
||||
// `...`
|
||||
Expr::InterpolatedString(x) if x.len() == 1 && matches!(x[0], Expr::StringConstant(_, _)) => {
|
||||
Expr::InterpolatedString(x, _) if x.len() == 1 && matches!(x[0], Expr::StringConstant(_, _)) => {
|
||||
state.set_dirty();
|
||||
*expr = mem::take(&mut x[0]);
|
||||
}
|
||||
// `... ${ ... } ...`
|
||||
Expr::InterpolatedString(x) => {
|
||||
Expr::InterpolatedString(x, _) => {
|
||||
let mut n = 0;
|
||||
|
||||
// Merge consecutive strings
|
||||
@ -843,7 +849,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Array(_, _) if expr.is_constant() => {
|
||||
state.set_dirty();
|
||||
*expr = Expr::DynamicConstant(Box::new(expr.get_literal_value().unwrap()), expr.position());
|
||||
*expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position());
|
||||
}
|
||||
// [ items .. ]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -852,7 +858,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Map(_, _) if expr.is_constant() => {
|
||||
state.set_dirty();
|
||||
*expr = Expr::DynamicConstant(Box::new(expr.get_literal_value().unwrap()), expr.position());
|
||||
*expr = Expr::DynamicConstant(expr.get_literal_value().unwrap().into(), expr.position());
|
||||
}
|
||||
// #{ key:value, .. }
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
|
@ -121,6 +121,15 @@ macro_rules! gen_arithmetic_functions {
|
||||
pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type {
|
||||
x ^ y
|
||||
}
|
||||
pub fn is_zero(x: $arg_type) -> bool {
|
||||
x == 0
|
||||
}
|
||||
pub fn is_odd(x: $arg_type) -> bool {
|
||||
x % 2 != 0
|
||||
}
|
||||
pub fn is_even(x: $arg_type) -> bool {
|
||||
x % 2 == 0
|
||||
}
|
||||
}
|
||||
})* }
|
||||
}
|
||||
@ -174,6 +183,7 @@ macro_rules! reg_functions {
|
||||
}
|
||||
|
||||
def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||
combine_with_exported_module!(lib, "int", int_functions);
|
||||
reg_functions!(lib += signed_basic; INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
@ -201,6 +211,19 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, {
|
||||
combine_with_exported_module!(lib, "decimal", decimal_functions);
|
||||
});
|
||||
|
||||
#[export_module]
|
||||
mod int_functions {
|
||||
pub fn is_zero(x: INT) -> bool {
|
||||
x == 0
|
||||
}
|
||||
pub fn is_odd(x: INT) -> bool {
|
||||
x % 2 != 0
|
||||
}
|
||||
pub fn is_even(x: INT) -> bool {
|
||||
x % 2 == 0
|
||||
}
|
||||
}
|
||||
|
||||
gen_arithmetic_functions!(arith_basic => INT);
|
||||
|
||||
#[cfg(not(feature = "only_i32"))]
|
||||
@ -315,6 +338,9 @@ mod f32_functions {
|
||||
1
|
||||
}
|
||||
}
|
||||
pub fn is_zero(x: f32) -> bool {
|
||||
x == 0.0
|
||||
}
|
||||
#[rhai_fn(name = "**", return_raw)]
|
||||
pub fn pow_f_i(x: f32, y: INT) -> Result<f32, Box<EvalAltResult>> {
|
||||
if cfg!(not(feature = "unchecked")) && y > (i32::MAX as INT) {
|
||||
@ -420,6 +446,9 @@ mod f64_functions {
|
||||
1
|
||||
}
|
||||
}
|
||||
pub fn is_zero(x: f64) -> bool {
|
||||
x == 0.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
@ -511,4 +540,7 @@ pub mod decimal_functions {
|
||||
1
|
||||
}
|
||||
}
|
||||
pub fn is_zero(x: Decimal) -> bool {
|
||||
x.is_zero()
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
|
||||
mod fn_ptr_functions {
|
||||
#[rhai_fn(name = "name", get = "name", pure)]
|
||||
pub fn name(f: &mut FnPtr) -> ImmutableString {
|
||||
f.get_fn_name().as_str().into()
|
||||
f.fn_name_raw().into()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
|
@ -27,10 +27,11 @@ where
|
||||
return EvalAltResult::ErrorInFunctionCall(
|
||||
"range".to_string(),
|
||||
Default::default(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic(
|
||||
EvalAltResult::ErrorArithmetic(
|
||||
"step value cannot be zero".to_string(),
|
||||
crate::Position::NONE,
|
||||
)),
|
||||
)
|
||||
.into(),
|
||||
crate::Position::NONE,
|
||||
)
|
||||
.into();
|
||||
@ -314,7 +315,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if step == 0.0 {
|
||||
return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)),
|
||||
EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(),
|
||||
crate::Position::NONE,
|
||||
).into();
|
||||
}
|
||||
@ -376,7 +377,7 @@ def_package!(crate:BasicIteratorPackage:"Basic range iterators.", lib, {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
if step.is_zero() {
|
||||
return EvalAltResult::ErrorInFunctionCall("range".to_string(), "".to_string(),
|
||||
Box::new(EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE)),
|
||||
EvalAltResult::ErrorArithmetic("step value cannot be zero".to_string(), crate::Position::NONE).into(),
|
||||
crate::Position::NONE,
|
||||
).into();
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ mod core_functions {
|
||||
#[rhai_fn(name = "set_tag", set = "tag", return_raw)]
|
||||
pub fn set_tag(value: &mut Dynamic, tag: INT) -> Result<(), Box<EvalAltResult>> {
|
||||
if tag < Tag::MIN as INT {
|
||||
Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"{} is too small to fit into a tag (must be between {} and {})",
|
||||
tag,
|
||||
@ -22,9 +22,10 @@ mod core_functions {
|
||||
Tag::MAX
|
||||
),
|
||||
Position::NONE,
|
||||
)))
|
||||
)
|
||||
.into()
|
||||
} else if tag > Tag::MAX as INT {
|
||||
Err(Box::new(EvalAltResult::ErrorArithmetic(
|
||||
EvalAltResult::ErrorArithmetic(
|
||||
format!(
|
||||
"{} is too large to fit into a tag (must be between {} and {})",
|
||||
tag,
|
||||
@ -32,7 +33,8 @@ mod core_functions {
|
||||
Tag::MAX
|
||||
),
|
||||
Position::NONE,
|
||||
)))
|
||||
)
|
||||
.into()
|
||||
} else {
|
||||
value.set_tag(tag as Tag);
|
||||
Ok(())
|
||||
|
@ -40,15 +40,6 @@ pub trait Package {
|
||||
/// Retrieve the generic package library from this package.
|
||||
#[must_use]
|
||||
fn as_shared_module(&self) -> Shared<Module>;
|
||||
|
||||
/// Retrieve the generic package library from this package.
|
||||
/// This method is deprecated and will be removed in the future.
|
||||
/// Use [`as_shared_module`][Package::as_shared_module] instead.
|
||||
#[must_use]
|
||||
#[deprecated(since = "0.19.9", note = "use `as_shared_module` instead")]
|
||||
fn get(&self) -> Shared<Module> {
|
||||
self.as_shared_module()
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro that makes it easy to define a _package_ (which is basically a shared [module][Module])
|
||||
|
154
src/parse.rs
154
src/parse.rs
@ -5,20 +5,20 @@ use crate::ast::{
|
||||
ScriptFnDef, Stmt, StmtBlock,
|
||||
};
|
||||
use crate::custom_syntax::{
|
||||
CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING,
|
||||
CustomSyntax, CUSTOM_SYNTAX_MARKER_BLOCK, CUSTOM_SYNTAX_MARKER_BOOL, CUSTOM_SYNTAX_MARKER_EXPR,
|
||||
CUSTOM_SYNTAX_MARKER_IDENT, CUSTOM_SYNTAX_MARKER_INT, CUSTOM_SYNTAX_MARKER_STRING,
|
||||
};
|
||||
use crate::dynamic::{AccessMode, Union};
|
||||
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
|
||||
use crate::fn_hash::get_hasher;
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
||||
|
||||
use crate::fn_hash::get_hasher;
|
||||
use crate::token::{
|
||||
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
|
||||
};
|
||||
use crate::{
|
||||
calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError,
|
||||
ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
||||
calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, Identifier,
|
||||
LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
|
||||
};
|
||||
#[cfg(feature = "no_std")]
|
||||
use std::prelude::v1::*;
|
||||
@ -29,7 +29,7 @@ use std::{
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
use crate::{custom_syntax::MARKER_FLOAT, FLOAT};
|
||||
use crate::{custom_syntax::CUSTOM_SYNTAX_MARKER_FLOAT, FLOAT};
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
use crate::FnAccess;
|
||||
@ -38,6 +38,10 @@ type PERR = ParseErrorType;
|
||||
|
||||
type FunctionsLib = BTreeMap<u64, Shared<ScriptFnDef>>;
|
||||
|
||||
/// Invalid variable name that acts as a search barrier in a [`Scope`].
|
||||
const SCOPE_SEARCH_BARRIER_MARKER: &str = "$BARRIER$";
|
||||
|
||||
/// The message: never fails because `TokenStream` never ends
|
||||
const NEVER_ENDS: &str = "never fails because `TokenStream` never ends";
|
||||
|
||||
/// A factory of identifiers from text strings.
|
||||
@ -138,7 +142,7 @@ impl<'e> ParseState<'e> {
|
||||
///
|
||||
/// Return `None` when the variable name is not found in the `stack`.
|
||||
#[inline(always)]
|
||||
fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
||||
pub fn access_var(&mut self, name: &str, _pos: Position) -> Option<NonZeroUsize> {
|
||||
let mut barrier = false;
|
||||
|
||||
let index = self
|
||||
@ -147,8 +151,8 @@ impl<'e> ParseState<'e> {
|
||||
.rev()
|
||||
.enumerate()
|
||||
.find(|(_, (n, _))| {
|
||||
if n.is_empty() {
|
||||
// Do not go beyond empty variable names
|
||||
if n == SCOPE_SEARCH_BARRIER_MARKER {
|
||||
// Do not go beyond the barrier
|
||||
barrier = true;
|
||||
false
|
||||
} else {
|
||||
@ -230,7 +234,7 @@ impl ParseSettings {
|
||||
/// Create a new `ParseSettings` with one higher expression level.
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn level_up(&self) -> Self {
|
||||
pub const fn level_up(&self) -> Self {
|
||||
Self {
|
||||
level: self.level + 1,
|
||||
..*self
|
||||
@ -244,8 +248,10 @@ impl ParseSettings {
|
||||
&self,
|
||||
limit: Option<NonZeroUsize>,
|
||||
) -> Result<(), ParseError> {
|
||||
if limit.map(|limit| self.level > limit.get()).unwrap_or(false) {
|
||||
return Err(PERR::ExprTooDeep.into_err(self.pos));
|
||||
if let Some(limit) = limit {
|
||||
if self.level > limit.get() {
|
||||
return Err(PERR::ExprTooDeep.into_err(self.pos));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -508,7 +514,7 @@ fn parse_index_chain(
|
||||
Expr::IntegerConstant(_, _)
|
||||
| Expr::Array(_, _)
|
||||
| Expr::StringConstant(_, _)
|
||||
| Expr::InterpolatedString(_) => (),
|
||||
| Expr::InterpolatedString(_, _) => (),
|
||||
|
||||
Expr::Map(_, _) => {
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
@ -540,10 +546,10 @@ fn parse_index_chain(
|
||||
},
|
||||
|
||||
// lhs[string]
|
||||
Expr::StringConstant(_, _) | Expr::InterpolatedString(_) => match lhs {
|
||||
Expr::StringConstant(_, _) | Expr::InterpolatedString(_, _) => match lhs {
|
||||
Expr::Map(_, _) => (),
|
||||
|
||||
Expr::Array(_, _) | Expr::StringConstant(_, _) | Expr::InterpolatedString(_) => {
|
||||
Expr::Array(_, _) | Expr::StringConstant(_, _) | Expr::InterpolatedString(_, _) => {
|
||||
return Err(PERR::MalformedIndexExpr(
|
||||
"Array or string expects numeric index, not a string".into(),
|
||||
)
|
||||
@ -628,13 +634,13 @@ fn parse_index_chain(
|
||||
parse_index_chain(input, state, lib, idx_expr, settings.level_up())?;
|
||||
// Indexing binds to right
|
||||
Ok(Expr::Index(
|
||||
Box::new(BinaryExpr { lhs, rhs: idx_expr }),
|
||||
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
||||
prev_pos,
|
||||
))
|
||||
}
|
||||
// Otherwise terminate the indexing chain
|
||||
_ => Ok(Expr::Index(
|
||||
Box::new(BinaryExpr { lhs, rhs: idx_expr }),
|
||||
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
||||
settings.pos,
|
||||
)),
|
||||
}
|
||||
@ -834,7 +840,7 @@ fn parse_map_literal(
|
||||
|
||||
map.shrink_to_fit();
|
||||
|
||||
Ok(Expr::Map(Box::new((map, template)), settings.pos))
|
||||
Ok(Expr::Map((map, template).into(), settings.pos))
|
||||
}
|
||||
|
||||
/// Parse a switch expression.
|
||||
@ -944,7 +950,7 @@ fn parse_switch(
|
||||
let need_comma = !stmt.is_self_terminated();
|
||||
|
||||
def_stmt = if let Some(hash) = hash {
|
||||
table.insert(hash, Box::new((condition, stmt.into())));
|
||||
table.insert(hash, (condition, stmt.into()).into());
|
||||
None
|
||||
} else {
|
||||
Some(stmt.into())
|
||||
@ -973,12 +979,11 @@ fn parse_switch(
|
||||
}
|
||||
}
|
||||
|
||||
let def_stmt_block = def_stmt.unwrap_or_else(|| Stmt::Noop(Position::NONE).into());
|
||||
|
||||
Ok(Stmt::Switch(
|
||||
item,
|
||||
Box::new((
|
||||
table,
|
||||
def_stmt.unwrap_or_else(|| Stmt::Noop(Position::NONE).into()),
|
||||
)),
|
||||
(table, def_stmt_block).into(),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@ -1126,7 +1131,7 @@ fn parse_primary(
|
||||
}
|
||||
|
||||
segments.shrink_to_fit();
|
||||
Expr::InterpolatedString(segments.into())
|
||||
Expr::InterpolatedString(segments.into(), settings.pos)
|
||||
}
|
||||
|
||||
// Array literal
|
||||
@ -1155,7 +1160,7 @@ fn parse_primary(
|
||||
Expr::Variable(
|
||||
None,
|
||||
settings.pos,
|
||||
Box::new((None, None, state.get_identifier(s))),
|
||||
(None, None, state.get_identifier(s)).into(),
|
||||
)
|
||||
}
|
||||
// Namespace qualification
|
||||
@ -1169,7 +1174,7 @@ fn parse_primary(
|
||||
Expr::Variable(
|
||||
None,
|
||||
settings.pos,
|
||||
Box::new((None, None, state.get_identifier(s))),
|
||||
(None, None, state.get_identifier(s)).into(),
|
||||
)
|
||||
}
|
||||
// Normal variable access
|
||||
@ -1185,7 +1190,7 @@ fn parse_primary(
|
||||
Expr::Variable(
|
||||
short_index,
|
||||
settings.pos,
|
||||
Box::new((index, None, state.get_identifier(s))),
|
||||
(index, None, state.get_identifier(s)).into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1203,13 +1208,13 @@ fn parse_primary(
|
||||
Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable(
|
||||
None,
|
||||
settings.pos,
|
||||
Box::new((None, None, state.get_identifier(s))),
|
||||
(None, None, state.get_identifier(s)).into(),
|
||||
),
|
||||
// Access to `this` as a variable is OK within a function scope
|
||||
_ if s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable(
|
||||
None,
|
||||
settings.pos,
|
||||
Box::new((None, None, state.get_identifier(s))),
|
||||
(None, None, state.get_identifier(s)).into(),
|
||||
),
|
||||
// Cannot access to `this` as a variable not in a function scope
|
||||
_ if s == KEYWORD_THIS => {
|
||||
@ -1300,7 +1305,7 @@ fn parse_primary(
|
||||
Expr::Variable(
|
||||
None,
|
||||
pos2,
|
||||
Box::new((None, namespace, state.get_identifier(id2))),
|
||||
(None, namespace, state.get_identifier(id2)).into(),
|
||||
)
|
||||
}
|
||||
// Indexing
|
||||
@ -1348,7 +1353,7 @@ fn parse_primary(
|
||||
}
|
||||
.map(|x| match x {
|
||||
(_, Some((namespace, hash)), name) => {
|
||||
*hash = calc_qualified_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0);
|
||||
*hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name);
|
||||
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
namespace.set_index(state.find_module(&namespace[0].name));
|
||||
@ -1493,7 +1498,7 @@ fn make_assignment_stmt<'a>(
|
||||
}
|
||||
// var (non-indexed) = rhs
|
||||
Expr::Variable(None, _, ref x) if x.0.is_none() => {
|
||||
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||
Ok(Stmt::Assignment((lhs, op_info, rhs).into(), op_pos))
|
||||
}
|
||||
// var (indexed) = rhs
|
||||
Expr::Variable(i, var_pos, ref x) => {
|
||||
@ -1509,9 +1514,7 @@ fn make_assignment_stmt<'a>(
|
||||
|n| n.get() as usize,
|
||||
);
|
||||
match state.stack[state.stack.len() - index].1 {
|
||||
AccessMode::ReadWrite => {
|
||||
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||
}
|
||||
AccessMode::ReadWrite => Ok(Stmt::Assignment((lhs, op_info, rhs).into(), op_pos)),
|
||||
// Constant values cannot be assigned to
|
||||
AccessMode::ReadOnly => {
|
||||
Err(PERR::AssignmentToConstant(name.to_string()).into_err(var_pos))
|
||||
@ -1524,7 +1527,7 @@ fn make_assignment_stmt<'a>(
|
||||
None => match x.lhs {
|
||||
// var[???] = rhs, var.??? = rhs
|
||||
Expr::Variable(_, _, _) => {
|
||||
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||
Ok(Stmt::Assignment((lhs, op_info, rhs).into(), op_pos))
|
||||
}
|
||||
// expr[???] = rhs, expr.??? = rhs
|
||||
ref expr => {
|
||||
@ -1601,7 +1604,7 @@ fn make_dot_expr(
|
||||
(state.get_identifier(ident).into(), var_pos),
|
||||
)));
|
||||
|
||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
}
|
||||
// lhs.module::id - syntax error
|
||||
(_, Expr::Variable(_, _, x)) if x.1.is_some() => {
|
||||
@ -1609,20 +1612,19 @@ fn make_dot_expr(
|
||||
.into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos))
|
||||
}
|
||||
// lhs.prop
|
||||
(lhs, prop @ Expr::Property(_)) => {
|
||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs: prop }), op_pos)
|
||||
}
|
||||
(lhs, prop @ Expr::Property(_)) => Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), op_pos),
|
||||
// lhs.dot_lhs.dot_rhs
|
||||
(lhs, Expr::Dot(x, pos)) => match x.lhs {
|
||||
Expr::Variable(_, _, _) | Expr::Property(_) => {
|
||||
let rhs = Expr::Dot(
|
||||
Box::new(BinaryExpr {
|
||||
BinaryExpr {
|
||||
lhs: x.lhs.into_property(state),
|
||||
rhs: x.rhs,
|
||||
}),
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
);
|
||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
}
|
||||
Expr::FnCall(mut func, func_pos) => {
|
||||
// Recalculate hash
|
||||
@ -1632,26 +1634,28 @@ fn make_dot_expr(
|
||||
);
|
||||
|
||||
let rhs = Expr::Dot(
|
||||
Box::new(BinaryExpr {
|
||||
BinaryExpr {
|
||||
lhs: Expr::FnCall(func, func_pos),
|
||||
rhs: x.rhs,
|
||||
}),
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
);
|
||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
}
|
||||
_ => unreachable!("invalid dot expression: {:?}", x.lhs),
|
||||
},
|
||||
// lhs.idx_lhs[idx_rhs]
|
||||
(lhs, Expr::Index(x, pos)) => {
|
||||
let rhs = Expr::Index(
|
||||
Box::new(BinaryExpr {
|
||||
BinaryExpr {
|
||||
lhs: x.lhs.into_property(state),
|
||||
rhs: x.rhs,
|
||||
}),
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
);
|
||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
}
|
||||
// lhs.nnn::func(...)
|
||||
(_, Expr::FnCall(x, _)) if x.is_qualified() => {
|
||||
@ -1687,7 +1691,7 @@ fn make_dot_expr(
|
||||
calc_fn_hash(&func.name, func.args.len() + 1),
|
||||
);
|
||||
let rhs = Expr::FnCall(func, func_pos);
|
||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
}
|
||||
// lhs.rhs
|
||||
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
|
||||
@ -1811,10 +1815,11 @@ fn parse_binary_op(
|
||||
.pop()
|
||||
.expect("never fails because `||` has two arguments");
|
||||
Expr::Or(
|
||||
Box::new(BinaryExpr {
|
||||
BinaryExpr {
|
||||
lhs: current_lhs,
|
||||
rhs,
|
||||
}),
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
@ -1826,10 +1831,11 @@ fn parse_binary_op(
|
||||
.pop()
|
||||
.expect("never fails because `&&` has two arguments");
|
||||
Expr::And(
|
||||
Box::new(BinaryExpr {
|
||||
BinaryExpr {
|
||||
lhs: current_lhs,
|
||||
rhs,
|
||||
}),
|
||||
}
|
||||
.into(),
|
||||
pos,
|
||||
)
|
||||
}
|
||||
@ -1894,7 +1900,7 @@ fn parse_custom_syntax(
|
||||
// Add an empty variable name to the stack.
|
||||
// Empty variable names act as a barrier so earlier variables will not be matched.
|
||||
// Variable searches stop at the first empty variable name.
|
||||
let empty = state.get_identifier("");
|
||||
let empty = state.get_identifier(SCOPE_SEARCH_BARRIER_MARKER);
|
||||
state.stack.push((empty, AccessMode::ReadWrite));
|
||||
}
|
||||
|
||||
@ -1915,32 +1921,32 @@ fn parse_custom_syntax(
|
||||
};
|
||||
|
||||
match required_token.as_str() {
|
||||
MARKER_IDENT => {
|
||||
CUSTOM_SYNTAX_MARKER_IDENT => {
|
||||
let (name, pos) = parse_var_name(input)?;
|
||||
let name = state.get_identifier(name);
|
||||
segments.push(name.clone().into());
|
||||
tokens.push(state.get_identifier(MARKER_IDENT));
|
||||
keywords.push(Expr::Variable(None, pos, Box::new((None, None, name))));
|
||||
tokens.push(state.get_identifier(CUSTOM_SYNTAX_MARKER_IDENT));
|
||||
keywords.push(Expr::Variable(None, pos, (None, None, name).into()));
|
||||
}
|
||||
MARKER_EXPR => {
|
||||
CUSTOM_SYNTAX_MARKER_EXPR => {
|
||||
keywords.push(parse_expr(input, state, lib, settings)?);
|
||||
let keyword = state.get_identifier(MARKER_EXPR);
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_EXPR);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
}
|
||||
MARKER_BLOCK => match parse_block(input, state, lib, settings)? {
|
||||
CUSTOM_SYNTAX_MARKER_BLOCK => match parse_block(input, state, lib, settings)? {
|
||||
block @ Stmt::Block(_, _) => {
|
||||
keywords.push(Expr::Stmt(Box::new(block.into())));
|
||||
let keyword = state.get_identifier(MARKER_BLOCK);
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_BLOCK);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
}
|
||||
stmt => unreachable!("expecting Stmt::Block, but gets {:?}", stmt),
|
||||
},
|
||||
MARKER_BOOL => match input.next().expect(NEVER_ENDS) {
|
||||
CUSTOM_SYNTAX_MARKER_BOOL => match input.next().expect(NEVER_ENDS) {
|
||||
(b @ Token::True, pos) | (b @ Token::False, pos) => {
|
||||
keywords.push(Expr::BoolConstant(b == Token::True, pos));
|
||||
let keyword = state.get_identifier(MARKER_BOOL);
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_BOOL);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
}
|
||||
@ -1951,10 +1957,10 @@ fn parse_custom_syntax(
|
||||
)
|
||||
}
|
||||
},
|
||||
MARKER_INT => match input.next().expect(NEVER_ENDS) {
|
||||
CUSTOM_SYNTAX_MARKER_INT => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::IntegerConstant(i), pos) => {
|
||||
keywords.push(Expr::IntegerConstant(i, pos));
|
||||
let keyword = state.get_identifier(MARKER_INT);
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_INT);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
}
|
||||
@ -1966,10 +1972,10 @@ fn parse_custom_syntax(
|
||||
}
|
||||
},
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
MARKER_FLOAT => match input.next().expect(NEVER_ENDS) {
|
||||
CUSTOM_SYNTAX_MARKER_FLOAT => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::FloatConstant(f), pos) => {
|
||||
keywords.push(Expr::FloatConstant(f, pos));
|
||||
let keyword = state.get_identifier(MARKER_FLOAT);
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_FLOAT);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
}
|
||||
@ -1980,10 +1986,10 @@ fn parse_custom_syntax(
|
||||
.into_err(pos))
|
||||
}
|
||||
},
|
||||
MARKER_STRING => match input.next().expect(NEVER_ENDS) {
|
||||
CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) {
|
||||
(Token::StringConstant(s), pos) => {
|
||||
keywords.push(Expr::StringConstant(state.get_identifier(s).into(), pos));
|
||||
let keyword = state.get_identifier(MARKER_STRING);
|
||||
let keyword = state.get_identifier(CUSTOM_SYNTAX_MARKER_STRING);
|
||||
segments.push(keyword.clone().into());
|
||||
tokens.push(keyword);
|
||||
}
|
||||
@ -2138,7 +2144,7 @@ fn parse_if(
|
||||
|
||||
Ok(Stmt::If(
|
||||
guard,
|
||||
Box::new((if_body.into(), else_body.into())),
|
||||
(if_body.into(), else_body.into()).into(),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@ -2829,7 +2835,7 @@ fn parse_try_catch(
|
||||
let catch_body = parse_block(input, state, lib, settings.level_up())?;
|
||||
|
||||
Ok(Stmt::TryCatch(
|
||||
Box::new((body.into(), var_def, catch_body.into())),
|
||||
(body.into(), var_def, catch_body.into()).into(),
|
||||
settings.pos,
|
||||
))
|
||||
}
|
||||
@ -2960,7 +2966,7 @@ fn make_curry_from_externals(
|
||||
externals
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|x| Expr::Variable(None, Position::NONE, Box::new((None, None, x)))),
|
||||
.map(|x| Expr::Variable(None, Position::NONE, (None, None, x).into())),
|
||||
);
|
||||
|
||||
let expr = FnCallExpr {
|
||||
@ -2979,7 +2985,7 @@ fn make_curry_from_externals(
|
||||
let mut statements = StaticVec::with_capacity(externals.len() + 1);
|
||||
statements.extend(externals.into_iter().map(Stmt::Share));
|
||||
statements.push(Stmt::Expr(expr));
|
||||
Expr::Stmt(Box::new(StmtBlock::new(statements, pos)))
|
||||
Expr::Stmt(StmtBlock::new(statements, pos).into())
|
||||
}
|
||||
|
||||
/// Parse an anonymous function definition.
|
||||
|
36
src/scope.rs
36
src/scope.rs
@ -7,7 +7,7 @@ use std::prelude::v1::*;
|
||||
use std::{borrow::Cow, iter::Extend};
|
||||
|
||||
/// Keep a number of entries inline (since [`Dynamic`] is usually small enough).
|
||||
const SCOPE_SIZE: usize = 8;
|
||||
const SCOPE_ENTRIES_INLINED: usize = 8;
|
||||
|
||||
/// Type containing information about the current scope.
|
||||
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
|
||||
@ -49,14 +49,14 @@ const SCOPE_SIZE: usize = 8;
|
||||
// look up a variable. Variable lookup is usually via direct indexing, by-passing the name altogether.
|
||||
//
|
||||
// Since [`Dynamic`] is reasonably small, packing it tightly improves cache locality when variables are accessed.
|
||||
//
|
||||
// The alias is `Box`'ed because it occurs infrequently.
|
||||
#[derive(Debug, Clone, Hash)]
|
||||
pub struct Scope<'a> {
|
||||
/// Current value of the entry.
|
||||
values: smallvec::SmallVec<[Dynamic; SCOPE_SIZE]>,
|
||||
values: smallvec::SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
|
||||
/// (Name, aliases) of the entry.
|
||||
names: Vec<(Cow<'a, str>, Option<Box<StaticVec<Identifier>>>)>,
|
||||
names: smallvec::SmallVec<
|
||||
[(Cow<'a, str>, Option<Box<StaticVec<Identifier>>>); SCOPE_ENTRIES_INLINED],
|
||||
>,
|
||||
}
|
||||
|
||||
impl Default for Scope<'_> {
|
||||
@ -161,7 +161,7 @@ impl<'a> Scope<'a> {
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.values.len() == 0
|
||||
self.values.is_empty()
|
||||
}
|
||||
/// Add (push) a new entry to the [`Scope`].
|
||||
///
|
||||
@ -253,7 +253,7 @@ impl<'a> Scope<'a> {
|
||||
access: AccessMode,
|
||||
mut value: Dynamic,
|
||||
) -> &mut Self {
|
||||
self.names.push((name.into(), None));
|
||||
self.names.push((name.into(), Default::default()));
|
||||
value.set_access_mode(access);
|
||||
self.values.push(value.into());
|
||||
self
|
||||
@ -436,20 +436,18 @@ impl<'a> Scope<'a> {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
#[inline(always)]
|
||||
pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
||||
let entry = self
|
||||
let (_, aliases) = self
|
||||
.names
|
||||
.get_mut(index)
|
||||
.expect("never fails unless the index is out of bounds");
|
||||
if entry.1.is_none() {
|
||||
// Initialize the alias list if it is empty.
|
||||
entry.1 = Some(Default::default());
|
||||
}
|
||||
let list = entry
|
||||
.1
|
||||
.as_mut()
|
||||
.expect("never fails because the list is initialized");
|
||||
if !list.iter().any(|a| a == &alias) {
|
||||
list.push(alias);
|
||||
match aliases {
|
||||
None => {
|
||||
let mut list = StaticVec::new();
|
||||
list.push(alias);
|
||||
*aliases = Some(list.into());
|
||||
}
|
||||
Some(aliases) if !aliases.iter().any(|a| a == &alias) => aliases.push(alias),
|
||||
Some(_) => (),
|
||||
}
|
||||
self
|
||||
}
|
||||
@ -534,7 +532,7 @@ impl<'a, K: Into<Cow<'a, str>>> Extend<(K, Dynamic)> for Scope<'a> {
|
||||
#[inline(always)]
|
||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, value)| {
|
||||
self.names.push((name.into(), None));
|
||||
self.names.push((name.into(), Default::default()));
|
||||
self.values.push(value);
|
||||
});
|
||||
}
|
||||
|
@ -146,7 +146,11 @@ impl From<&crate::module::FuncInfo> for FnMetadata {
|
||||
}
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
{
|
||||
info.func.get_fn_def().comments.to_vec()
|
||||
info.func
|
||||
.get_script_fn_def()
|
||||
.expect("never fails because the function is scripted")
|
||||
.comments
|
||||
.to_vec()
|
||||
}
|
||||
} else {
|
||||
Default::default()
|
||||
|
56
src/token.rs
56
src/token.rs
@ -41,7 +41,7 @@ pub type TokenizerControl = Rc<Cell<TokenizerControlBlock>>;
|
||||
type LERR = LexError;
|
||||
|
||||
/// Separator character for numbers.
|
||||
const NUM_SEP: char = '_';
|
||||
const NUMBER_SEPARATOR: char = '_';
|
||||
|
||||
/// A stream of tokens.
|
||||
pub type TokenStream<'a> = Peekable<TokenIterator<'a>>;
|
||||
@ -110,7 +110,7 @@ impl Position {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return Some(self.line as usize);
|
||||
#[cfg(feature = "no_position")]
|
||||
unreachable!();
|
||||
unreachable!("there is no Position");
|
||||
}
|
||||
}
|
||||
/// Get the character position (1-based), or [`None`] if at beginning of a line.
|
||||
@ -128,7 +128,7 @@ impl Position {
|
||||
};
|
||||
|
||||
#[cfg(feature = "no_position")]
|
||||
unreachable!();
|
||||
unreachable!("there is no Position");
|
||||
}
|
||||
}
|
||||
/// Advance by one character position.
|
||||
@ -175,7 +175,7 @@ impl Position {
|
||||
/// Is this [`Position`] at the beginning of a line?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_beginning_of_line(self) -> bool {
|
||||
pub const fn is_beginning_of_line(self) -> bool {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return self.pos == 0 && !self.is_none();
|
||||
#[cfg(feature = "no_position")]
|
||||
@ -184,9 +184,9 @@ impl Position {
|
||||
/// Is there no [`Position`]?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_none(self) -> bool {
|
||||
pub const fn is_none(self) -> bool {
|
||||
#[cfg(not(feature = "no_position"))]
|
||||
return self == Self::NONE;
|
||||
return self.line == 0 && self.pos == 0;
|
||||
#[cfg(feature = "no_position")]
|
||||
return true;
|
||||
}
|
||||
@ -467,12 +467,8 @@ pub enum Token {
|
||||
|
||||
impl Token {
|
||||
/// Get the syntax of the token if it is a keyword.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the token is not a keyword.
|
||||
#[must_use]
|
||||
pub fn keyword_syntax(&self) -> &'static str {
|
||||
pub const fn keyword_syntax(&self) -> &'static str {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -556,7 +552,7 @@ impl Token {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
As => "as",
|
||||
|
||||
t => unreachable!("{:?} is not a keyword", t),
|
||||
_ => "ERROR: NOT A KEYWORD",
|
||||
}
|
||||
}
|
||||
|
||||
@ -589,7 +585,7 @@ impl Token {
|
||||
/// Is this token an op-assignment operator?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn is_op_assignment(&self) -> bool {
|
||||
pub const fn is_op_assignment(&self) -> bool {
|
||||
match self {
|
||||
Self::PlusAssign
|
||||
| Self::MinusAssign
|
||||
@ -608,7 +604,7 @@ impl Token {
|
||||
|
||||
/// Get the corresponding operator of the token if it is an op-assignment operator.
|
||||
#[must_use]
|
||||
pub fn map_op_assignment(&self) -> Option<Self> {
|
||||
pub const fn map_op_assignment(&self) -> Option<Self> {
|
||||
Some(match self {
|
||||
Self::PlusAssign => Self::Plus,
|
||||
Self::MinusAssign => Self::Minus,
|
||||
@ -628,7 +624,7 @@ impl Token {
|
||||
/// Has this token a corresponding op-assignment operator?
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn has_op_assignment(&self) -> bool {
|
||||
pub const fn has_op_assignment(&self) -> bool {
|
||||
match self {
|
||||
Self::Plus
|
||||
| Self::Minus
|
||||
@ -647,7 +643,7 @@ impl Token {
|
||||
|
||||
/// Get the corresponding op-assignment operator of the token.
|
||||
#[must_use]
|
||||
pub fn make_op_assignment(&self) -> Option<Self> {
|
||||
pub const fn make_op_assignment(&self) -> Option<Self> {
|
||||
Some(match self {
|
||||
Self::Plus => Self::PlusAssign,
|
||||
Self::Minus => Self::MinusAssign,
|
||||
@ -777,7 +773,7 @@ impl Token {
|
||||
// Is this token [`EOF`][Token::EOF]?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_eof(&self) -> bool {
|
||||
pub const fn is_eof(&self) -> bool {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -789,7 +785,7 @@ impl Token {
|
||||
// If another operator is after these, it's probably an unary operator
|
||||
// (not sure about `fn` name).
|
||||
#[must_use]
|
||||
pub fn is_next_unary(&self) -> bool {
|
||||
pub const fn is_next_unary(&self) -> bool {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -850,7 +846,7 @@ impl Token {
|
||||
|
||||
/// Get the precedence number of the token.
|
||||
#[must_use]
|
||||
pub fn precedence(&self) -> Option<Precedence> {
|
||||
pub const fn precedence(&self) -> Option<Precedence> {
|
||||
use Token::*;
|
||||
|
||||
Precedence::new(match self {
|
||||
@ -885,7 +881,7 @@ impl Token {
|
||||
|
||||
/// Does an expression bind to the right (instead of left)?
|
||||
#[must_use]
|
||||
pub fn is_bind_right(&self) -> bool {
|
||||
pub const fn is_bind_right(&self) -> bool {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -906,7 +902,7 @@ impl Token {
|
||||
|
||||
/// Is this token a standard symbol used in the language?
|
||||
#[must_use]
|
||||
pub fn is_symbol(&self) -> bool {
|
||||
pub const fn is_symbol(&self) -> bool {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -924,7 +920,7 @@ impl Token {
|
||||
|
||||
/// Is this token an active standard keyword?
|
||||
#[must_use]
|
||||
pub fn is_keyword(&self) -> bool {
|
||||
pub const fn is_keyword(&self) -> bool {
|
||||
use Token::*;
|
||||
|
||||
match self {
|
||||
@ -944,7 +940,7 @@ impl Token {
|
||||
/// Is this token a reserved symbol?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_reserved(&self) -> bool {
|
||||
pub const fn is_reserved(&self) -> bool {
|
||||
match self {
|
||||
Self::Reserved(_) => true,
|
||||
_ => false,
|
||||
@ -964,7 +960,7 @@ impl Token {
|
||||
/// Is this token a custom keyword?
|
||||
#[inline(always)]
|
||||
#[must_use]
|
||||
pub fn is_custom(&self) -> bool {
|
||||
pub const fn is_custom(&self) -> bool {
|
||||
match self {
|
||||
Self::Custom(_) => true,
|
||||
_ => false,
|
||||
@ -1435,7 +1431,7 @@ fn get_next_token_inner(
|
||||
|
||||
while let Some(next_char) = stream.peek_next() {
|
||||
match next_char {
|
||||
ch if valid(ch) || ch == NUM_SEP => {
|
||||
ch if valid(ch) || ch == NUMBER_SEPARATOR => {
|
||||
result.push(next_char);
|
||||
eat_next(stream, pos);
|
||||
}
|
||||
@ -1536,8 +1532,11 @@ fn get_next_token_inner(
|
||||
// Parse number
|
||||
return Some((
|
||||
if let Some(radix) = radix_base {
|
||||
let out: String =
|
||||
result.iter().skip(2).filter(|&&c| c != NUM_SEP).collect();
|
||||
let out: String = result
|
||||
.iter()
|
||||
.skip(2)
|
||||
.filter(|&&c| c != NUMBER_SEPARATOR)
|
||||
.collect();
|
||||
|
||||
INT::from_str_radix(&out, radix)
|
||||
.map(Token::IntegerConstant)
|
||||
@ -1545,7 +1544,8 @@ fn get_next_token_inner(
|
||||
Token::LexError(LERR::MalformedNumber(result.into_iter().collect()))
|
||||
})
|
||||
} else {
|
||||
let out: String = result.iter().filter(|&&c| c != NUM_SEP).collect();
|
||||
let out: String =
|
||||
result.iter().filter(|&&c| c != NUMBER_SEPARATOR).collect();
|
||||
let num = INT::from_str(&out).map(Token::IntegerConstant);
|
||||
|
||||
// If integer parsing is unnecessary, try float instead
|
||||
|
@ -2,6 +2,26 @@
|
||||
|
||||
use rhai::{Engine, EvalAltResult, OptimizationLevel, INT};
|
||||
|
||||
#[test]
|
||||
fn test_optimizer() -> Result<(), Box<EvalAltResult>> {
|
||||
let mut engine = Engine::new();
|
||||
engine.set_optimization_level(OptimizationLevel::Full);
|
||||
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
assert_eq!(
|
||||
engine.eval::<INT>(
|
||||
"
|
||||
fn foo(x) { print(x); return; }
|
||||
fn foo2(x) { if x > 0 {} return; }
|
||||
42
|
||||
"
|
||||
)?,
|
||||
42
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
|
||||
fn run_test(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseError, ParseErrorType, Position, INT};
|
||||
use rhai::{Dynamic, Engine, EvalAltResult, LexError, ParseErrorType, Position, INT};
|
||||
|
||||
#[test]
|
||||
fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
||||
@ -130,13 +130,9 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
|
||||
1 => Ok(Some("$ident$".into())),
|
||||
2 => match stream[1].as_str() {
|
||||
"world" | "kitty" => Ok(None),
|
||||
s => Err(ParseError(
|
||||
Box::new(ParseErrorType::BadInput(LexError::ImproperSymbol(
|
||||
s.to_string(),
|
||||
Default::default(),
|
||||
))),
|
||||
Position::NONE,
|
||||
)),
|
||||
s => Err(LexError::ImproperSymbol(s.to_string(), Default::default())
|
||||
.into_err(Position::NONE)
|
||||
.into()),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user