commit
e75709f627
@ -10,6 +10,7 @@ Bug fixes
|
|||||||
* `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications.
|
* `Engine::parse_json` now returns an error on unquoted keys to be consistent with JSON specifications.
|
||||||
* `import` statements inside `eval` no longer cause errors in subsequent code.
|
* `import` statements inside `eval` no longer cause errors in subsequent code.
|
||||||
* Functions marked `global` in `import`ed modules with no alias names now work properly.
|
* Functions marked `global` in `import`ed modules with no alias names now work properly.
|
||||||
|
* Incorrect loop optimizations that are too aggressive (e.g. unrolling a `do { ... } until true` with a `break` statement inside) and cause crashes are removed.
|
||||||
|
|
||||||
Speed Improvements
|
Speed Improvements
|
||||||
------------------
|
------------------
|
||||||
@ -19,6 +20,12 @@ Speed Improvements
|
|||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
### Loop expressions
|
||||||
|
|
||||||
|
* Loops (such as `loop`, `do`, `while` and `for`) can now act as _expressions_, with the `break` statement returning an optional value.
|
||||||
|
* Normal loops return `()` as the value.
|
||||||
|
* Loop expressions can be enabled/disabled via `Engine::set_allow_loop_expressions`
|
||||||
|
|
||||||
### Stable hashing
|
### Stable hashing
|
||||||
|
|
||||||
* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures.
|
* It is now possible to specify a fixed _seed_ for use with the `ahash` hasher, via an environment variable, in order to force stable (i.e. deterministic) hashes for function signatures.
|
||||||
|
@ -442,8 +442,8 @@ impl Module {
|
|||||||
|
|
||||||
if f.access != FnAccess::Private {
|
if f.access != FnAccess::Private {
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
let operator = def.engine.custom_keywords.contains_key(&f.name)
|
let operator = def.engine.custom_keywords.contains_key(f.name.as_str())
|
||||||
|| (!f.name.contains('$') && !is_valid_function_name(&f.name));
|
|| (!f.name.contains('$') && !is_valid_function_name(f.name.as_str()));
|
||||||
|
|
||||||
#[cfg(feature = "no_custom_syntax")]
|
#[cfg(feature = "no_custom_syntax")]
|
||||||
let operator = !f.name.contains('$') && !is_valid_function_name(&f.name);
|
let operator = !f.name.contains('$') && !is_valid_function_name(&f.name);
|
||||||
|
@ -217,7 +217,7 @@ impl Engine {
|
|||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let mut caches = Caches::new();
|
let mut caches = Caches::new();
|
||||||
global.source = ast.source_raw().clone();
|
global.source = ast.source_raw().cloned();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let orig_embedded_module_resolver = std::mem::replace(
|
let orig_embedded_module_resolver = std::mem::replace(
|
||||||
|
@ -12,23 +12,25 @@ bitflags! {
|
|||||||
const IF_EXPR = 0b_0000_0000_0001;
|
const IF_EXPR = 0b_0000_0000_0001;
|
||||||
/// Is `switch` expression allowed?
|
/// Is `switch` expression allowed?
|
||||||
const SWITCH_EXPR = 0b_0000_0000_0010;
|
const SWITCH_EXPR = 0b_0000_0000_0010;
|
||||||
|
/// Are loop expressions allowed?
|
||||||
|
const LOOP_EXPR = 0b_0000_0000_0100;
|
||||||
/// Is statement-expression allowed?
|
/// Is statement-expression allowed?
|
||||||
const STMT_EXPR = 0b_0000_0000_0100;
|
const STMT_EXPR = 0b_0000_0000_1000;
|
||||||
/// Is anonymous function allowed?
|
/// Is anonymous function allowed?
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
const ANON_FN = 0b_0000_0000_1000;
|
const ANON_FN = 0b_0000_0001_0000;
|
||||||
/// Is looping allowed?
|
/// Is looping allowed?
|
||||||
const LOOPING = 0b_0000_0001_0000;
|
const LOOPING = 0b_0000_0010_0000;
|
||||||
/// Is variables shadowing allowed?
|
/// Is variables shadowing allowed?
|
||||||
const SHADOW = 0b_0000_0010_0000;
|
const SHADOW = 0b_0000_0100_0000;
|
||||||
/// Strict variables mode?
|
/// Strict variables mode?
|
||||||
const STRICT_VAR = 0b_0000_0100_0000;
|
const STRICT_VAR = 0b_0000_1000_0000;
|
||||||
/// Raise error if an object map property does not exist?
|
/// Raise error if an object map property does not exist?
|
||||||
/// Returns `()` if `false`.
|
/// Returns `()` if `false`.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
const FAIL_ON_INVALID_MAP_PROPERTY = 0b_0000_1000_0000;
|
const FAIL_ON_INVALID_MAP_PROPERTY = 0b_0001_0000_0000;
|
||||||
/// Fast operators mode?
|
/// Fast operators mode?
|
||||||
const FAST_OPS = 0b_0001_0000_0000;
|
const FAST_OPS = 0b_0010_0000_0000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +83,18 @@ impl Engine {
|
|||||||
pub fn set_allow_switch_expression(&mut self, enable: bool) {
|
pub fn set_allow_switch_expression(&mut self, enable: bool) {
|
||||||
self.options.set(LangOptions::SWITCH_EXPR, enable);
|
self.options.set(LangOptions::SWITCH_EXPR, enable);
|
||||||
}
|
}
|
||||||
|
/// Are loop expressions allowed?
|
||||||
|
/// Default is `true`.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn allow_loop_expressions(&self) -> bool {
|
||||||
|
self.options.contains(LangOptions::LOOP_EXPR)
|
||||||
|
}
|
||||||
|
/// Set whether loop expressions are allowed.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn set_allow_loop_expressions(&mut self, enable: bool) {
|
||||||
|
self.options.set(LangOptions::LOOP_EXPR, enable);
|
||||||
|
}
|
||||||
/// Is statement-expression allowed?
|
/// Is statement-expression allowed?
|
||||||
/// Default is `true`.
|
/// Default is `true`.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -113,7 +113,7 @@ impl Engine {
|
|||||||
pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
|
pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
|
||||||
let caches = &mut Caches::new();
|
let caches = &mut Caches::new();
|
||||||
let global = &mut GlobalRuntimeState::new(self);
|
let global = &mut GlobalRuntimeState::new(self);
|
||||||
global.source = ast.source_raw().clone();
|
global.source = ast.source_raw().cloned();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Module defining the AST (abstract syntax tree).
|
//! Module defining the AST (abstract syntax tree).
|
||||||
|
|
||||||
use super::{ASTFlags, Expr, FnAccess, Stmt, StmtBlock, StmtBlockContainer};
|
use super::{ASTFlags, Expr, FnAccess, Stmt, StmtBlock, StmtBlockContainer};
|
||||||
use crate::{Dynamic, FnNamespace, Identifier, Position};
|
use crate::{Dynamic, FnNamespace, ImmutableString, Position};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -20,8 +20,7 @@ use std::{
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AST {
|
pub struct AST {
|
||||||
/// Source of the [`AST`].
|
/// Source of the [`AST`].
|
||||||
/// No source if string is empty.
|
source: Option<ImmutableString>,
|
||||||
source: Identifier,
|
|
||||||
/// [`AST`] documentation.
|
/// [`AST`] documentation.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
doc: crate::SmartString,
|
doc: crate::SmartString,
|
||||||
@ -78,7 +77,7 @@ impl AST {
|
|||||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: Identifier::new_const(),
|
source: None,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
doc: crate::SmartString::new_const(),
|
doc: crate::SmartString::new_const(),
|
||||||
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
||||||
@ -98,7 +97,7 @@ impl AST {
|
|||||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: Identifier::new_const(),
|
source: None,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
doc: crate::SmartString::new_const(),
|
doc: crate::SmartString::new_const(),
|
||||||
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
body: StmtBlock::new(statements, Position::NONE, Position::NONE),
|
||||||
@ -115,7 +114,7 @@ impl AST {
|
|||||||
pub(crate) fn new_with_source(
|
pub(crate) fn new_with_source(
|
||||||
statements: impl IntoIterator<Item = Stmt>,
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
||||||
source: impl Into<Identifier>,
|
source: impl Into<ImmutableString>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut ast = Self::new(
|
let mut ast = Self::new(
|
||||||
statements,
|
statements,
|
||||||
@ -133,7 +132,7 @@ impl AST {
|
|||||||
pub fn new_with_source(
|
pub fn new_with_source(
|
||||||
statements: impl IntoIterator<Item = Stmt>,
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
|
||||||
source: impl Into<Identifier>,
|
source: impl Into<ImmutableString>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut ast = Self::new(
|
let mut ast = Self::new(
|
||||||
statements,
|
statements,
|
||||||
@ -148,7 +147,7 @@ impl AST {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
source: Identifier::new_const(),
|
source: None,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
doc: crate::SmartString::new_const(),
|
doc: crate::SmartString::new_const(),
|
||||||
body: StmtBlock::NONE,
|
body: StmtBlock::NONE,
|
||||||
@ -159,36 +158,39 @@ impl AST {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the source, if any.
|
/// Get the source, if any.
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn source(&self) -> Option<&str> {
|
pub fn source(&self) -> Option<&str> {
|
||||||
if self.source.is_empty() {
|
self.source.as_ref().map(|s| s.as_str())
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.source.as_str())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// Get a reference to the source.
|
/// Get a reference to the source.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) const fn source_raw(&self) -> &Identifier {
|
pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> {
|
||||||
&self.source
|
self.source.as_ref()
|
||||||
}
|
}
|
||||||
/// Set the source.
|
/// Set the source.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set_source(&mut self, source: impl Into<Identifier>) -> &mut Self {
|
pub fn set_source(&mut self, source: impl Into<ImmutableString>) -> &mut Self {
|
||||||
let source = source.into();
|
let source = source.into();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::Shared::get_mut(&mut self.lib)
|
crate::Shared::get_mut(&mut self.lib)
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.map(|m| m.set_id(source.clone()));
|
.map(|m| m.set_id(source.clone()));
|
||||||
self.source = source;
|
|
||||||
|
if source.is_empty() {
|
||||||
|
self.source = None;
|
||||||
|
} else {
|
||||||
|
self.source = Some(source);
|
||||||
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Clear the source.
|
/// Clear the source.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_source(&mut self) -> &mut Self {
|
pub fn clear_source(&mut self) -> &mut Self {
|
||||||
self.source.clear();
|
self.source = None;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Get the documentation (if any).
|
/// Get the documentation (if any).
|
||||||
@ -559,18 +561,18 @@ impl AST {
|
|||||||
lib
|
lib
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut _ast = if other.source.is_empty() {
|
let mut _ast = if let Some(ref source) = other.source {
|
||||||
Self::new(
|
|
||||||
merged,
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
lib,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Self::new_with_source(
|
Self::new_with_source(
|
||||||
merged,
|
merged,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
lib,
|
lib,
|
||||||
other.source.clone(),
|
source.clone(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Self::new(
|
||||||
|
merged,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
lib,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -609,7 +609,7 @@ impl Expr {
|
|||||||
let mut s = SmartString::new_const();
|
let mut s = SmartString::new_const();
|
||||||
for segment in x.iter() {
|
for segment in x.iter() {
|
||||||
let v = segment.get_literal_value().unwrap();
|
let v = segment.get_literal_value().unwrap();
|
||||||
write!(&mut s, "{}", v).unwrap();
|
write!(&mut s, "{v}").unwrap();
|
||||||
}
|
}
|
||||||
s.into()
|
s.into()
|
||||||
}
|
}
|
||||||
@ -619,7 +619,7 @@ impl Expr {
|
|||||||
if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR =>
|
if !x.is_qualified() && x.args.len() == 1 && x.name == KEYWORD_FN_PTR =>
|
||||||
{
|
{
|
||||||
if let Self::StringConstant(ref s, ..) = x.args[0] {
|
if let Self::StringConstant(ref s, ..) = x.args[0] {
|
||||||
FnPtr::new(s).ok()?.into()
|
FnPtr::new(s.clone()).ok()?.into()
|
||||||
} else {
|
} else {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -581,14 +581,14 @@ pub enum Stmt {
|
|||||||
TryCatch(Box<TryCatchBlock>, Position),
|
TryCatch(Box<TryCatchBlock>, Position),
|
||||||
/// [expression][Expr]
|
/// [expression][Expr]
|
||||||
Expr(Box<Expr>),
|
Expr(Box<Expr>),
|
||||||
/// `continue`/`break`
|
/// `continue`/`break` expr
|
||||||
///
|
///
|
||||||
/// ### Flags
|
/// ### Flags
|
||||||
///
|
///
|
||||||
/// * [`NONE`][ASTFlags::NONE] = `continue`
|
/// * [`NONE`][ASTFlags::NONE] = `continue`
|
||||||
/// * [`BREAK`][ASTFlags::BREAK] = `break`
|
/// * [`BREAK`][ASTFlags::BREAK] = `break`
|
||||||
BreakLoop(ASTFlags, Position),
|
BreakLoop(Option<Box<Expr>>, ASTFlags, Position),
|
||||||
/// `return`/`throw`
|
/// `return`/`throw` expr
|
||||||
///
|
///
|
||||||
/// ### Flags
|
/// ### Flags
|
||||||
///
|
///
|
||||||
@ -605,19 +605,16 @@ pub enum Stmt {
|
|||||||
/// Not available under `no_module`.
|
/// Not available under `no_module`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Export(Box<(Ident, Ident)>, Position),
|
Export(Box<(Ident, Ident)>, Position),
|
||||||
/// Convert a variable to shared.
|
/// Convert a list of variables to shared.
|
||||||
///
|
///
|
||||||
/// Not available under `no_closure`.
|
/// Not available under `no_closure`.
|
||||||
///
|
///
|
||||||
/// # Notes
|
/// # Notes
|
||||||
///
|
///
|
||||||
/// This variant does not map to any language structure. It is currently only used only to
|
/// This variant does not map to any language structure. It is currently only used only to
|
||||||
/// convert a normal variable into a shared variable when the variable is _captured_ by a closure.
|
/// convert normal variables into shared variables when they are _captured_ by a closure.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Share(
|
Share(Box<crate::FnArgsVec<(crate::ImmutableString, Option<NonZeroUsize>, Position)>>),
|
||||||
Box<(crate::ImmutableString, Option<NonZeroUsize>)>,
|
|
||||||
Position,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Stmt {
|
impl Default for Stmt {
|
||||||
@ -684,7 +681,7 @@ impl Stmt {
|
|||||||
Self::Export(.., pos) => *pos,
|
Self::Export(.., pos) => *pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(.., pos) => *pos,
|
Self::Share(x) => x[0].2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Override the [position][Position] of this statement.
|
/// Override the [position][Position] of this statement.
|
||||||
@ -716,7 +713,7 @@ impl Stmt {
|
|||||||
Self::Export(.., pos) => *pos = new_pos,
|
Self::Export(.., pos) => *pos = new_pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(.., pos) => *pos = new_pos,
|
Self::Share(x) => x.iter_mut().for_each(|(_, _, pos)| *pos = new_pos),
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
|
@ -516,7 +516,7 @@ fn debug_callback(
|
|||||||
|
|
||||||
if range.contains(&n) {
|
if range.contains(&n) {
|
||||||
let bp = rhai::debugger::BreakPoint::AtPosition {
|
let bp = rhai::debugger::BreakPoint::AtPosition {
|
||||||
source: source.unwrap_or("").into(),
|
source: source.map(|s| s.into()),
|
||||||
pos: Position::new(n as u16, 0),
|
pos: Position::new(n as u16, 0),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
};
|
};
|
||||||
@ -546,7 +546,7 @@ fn debug_callback(
|
|||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
["break" | "b"] => {
|
["break" | "b"] => {
|
||||||
let bp = rhai::debugger::BreakPoint::AtPosition {
|
let bp = rhai::debugger::BreakPoint::AtPosition {
|
||||||
source: source.unwrap_or("").into(),
|
source: source.map(|s| s.into()),
|
||||||
pos,
|
pos,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
};
|
};
|
||||||
|
@ -309,13 +309,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register sample functions
|
// Register sample functions
|
||||||
engine
|
engine.register_global_module(exported_module!(sample_functions).into());
|
||||||
.register_global_module(exported_module!(sample_functions).into())
|
|
||||||
.register_get_set(
|
|
||||||
"test",
|
|
||||||
|x: &mut INT| *x % 2 == 0,
|
|
||||||
|x: &mut INT, y: bool| if y { *x *= 2 } else { *x /= 2 },
|
|
||||||
);
|
|
||||||
|
|
||||||
// Create scope
|
// Create scope
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
|
@ -239,8 +239,10 @@ impl Engine {
|
|||||||
engine.print = Box::new(|s| println!("{s}"));
|
engine.print = Box::new(|s| println!("{s}"));
|
||||||
engine.debug = Box::new(|s, source, pos| match (source, pos) {
|
engine.debug = Box::new(|s, source, pos| match (source, pos) {
|
||||||
(Some(source), crate::Position::NONE) => println!("{source} | {s}"),
|
(Some(source), crate::Position::NONE) => println!("{source} | {s}"),
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
(Some(source), pos) => println!("{source} @ {pos:?} | {s}"),
|
(Some(source), pos) => println!("{source} @ {pos:?} | {s}"),
|
||||||
(None, crate::Position::NONE) => println!("{s}"),
|
(None, crate::Position::NONE) => println!("{s}"),
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
(None, pos) => println!("{pos:?} | {s}"),
|
(None, pos) => println!("{pos:?} | {s}"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::func::{CallableFunction, StraightHashMap};
|
use crate::func::{CallableFunction, StraightHashMap};
|
||||||
use crate::types::BloomFilterU64;
|
use crate::types::BloomFilterU64;
|
||||||
use crate::{Identifier, StaticVec};
|
use crate::{ImmutableString, StaticVec};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -14,7 +14,7 @@ pub struct FnResolutionCacheEntry {
|
|||||||
/// Function.
|
/// Function.
|
||||||
pub func: CallableFunction,
|
pub func: CallableFunction,
|
||||||
/// Optional source.
|
/// Optional source.
|
||||||
pub source: Option<Box<Identifier>>,
|
pub source: Option<ImmutableString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ A function resolution cache with a bloom filter.
|
/// _(internals)_ A function resolution cache with a bloom filter.
|
||||||
|
@ -45,8 +45,8 @@ impl Engine {
|
|||||||
target: &mut Target,
|
target: &mut Target,
|
||||||
root: (&str, Position),
|
root: (&str, Position),
|
||||||
_parent: &Expr,
|
_parent: &Expr,
|
||||||
|
parent_options: ASTFlags,
|
||||||
rhs: &Expr,
|
rhs: &Expr,
|
||||||
_parent_options: ASTFlags,
|
|
||||||
idx_values: &mut FnArgsVec<Dynamic>,
|
idx_values: &mut FnArgsVec<Dynamic>,
|
||||||
chain_type: ChainType,
|
chain_type: ChainType,
|
||||||
level: usize,
|
level: usize,
|
||||||
@ -61,7 +61,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
ChainType::Indexing => {
|
ChainType::Indexing => {
|
||||||
// Check for existence with the null conditional operator
|
// Check for existence with the null conditional operator
|
||||||
if _parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
|
if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
|
||||||
return Ok((Dynamic::UNIT, false));
|
return Ok((Dynamic::UNIT, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ impl Engine {
|
|||||||
match rhs {
|
match rhs {
|
||||||
// xxx[idx].expr... | xxx[idx][expr]...
|
// xxx[idx].expr... | xxx[idx][expr]...
|
||||||
Expr::Dot(x, options, x_pos) | Expr::Index(x, options, x_pos)
|
Expr::Dot(x, options, x_pos) | Expr::Index(x, options, x_pos)
|
||||||
if !_parent_options.contains(ASTFlags::BREAK) =>
|
if !parent_options.contains(ASTFlags::BREAK) =>
|
||||||
{
|
{
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
||||||
@ -88,8 +88,8 @@ impl Engine {
|
|||||||
let obj_ptr = &mut obj;
|
let obj_ptr = &mut obj;
|
||||||
|
|
||||||
match self.eval_dot_index_chain_helper(
|
match self.eval_dot_index_chain_helper(
|
||||||
global, caches, lib, this_ptr, obj_ptr, root, rhs, &x.rhs,
|
global, caches, lib, this_ptr, obj_ptr, root, rhs, *options,
|
||||||
*options, idx_values, rhs_chain, level, new_val,
|
&x.rhs, idx_values, rhs_chain, level, new_val,
|
||||||
) {
|
) {
|
||||||
Ok((result, true)) if is_obj_temp_val => {
|
Ok((result, true)) if is_obj_temp_val => {
|
||||||
(Some(obj.take_or_clone()), (result, true))
|
(Some(obj.take_or_clone()), (result, true))
|
||||||
@ -190,7 +190,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
ChainType::Dotting => {
|
ChainType::Dotting => {
|
||||||
// Check for existence with the Elvis operator
|
// Check for existence with the Elvis operator
|
||||||
if _parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
|
if parent_options.contains(ASTFlags::NEGATED) && target.is::<()>() {
|
||||||
return Ok((Dynamic::UNIT, false));
|
return Ok((Dynamic::UNIT, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +407,7 @@ impl Engine {
|
|||||||
let rhs_chain = rhs.into();
|
let rhs_chain = rhs.into();
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
global, caches, lib, this_ptr, val_target, root, rhs, &x.rhs, *options,
|
global, caches, lib, this_ptr, val_target, root, rhs, *options, &x.rhs,
|
||||||
idx_values, rhs_chain, level, new_val,
|
idx_values, rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
@ -455,8 +455,8 @@ impl Engine {
|
|||||||
|
|
||||||
let (result, may_be_changed) = self
|
let (result, may_be_changed) = self
|
||||||
.eval_dot_index_chain_helper(
|
.eval_dot_index_chain_helper(
|
||||||
global, caches, lib, this_ptr, val, root, rhs, &x.rhs,
|
global, caches, lib, this_ptr, val, root, rhs, *options,
|
||||||
*options, idx_values, rhs_chain, level, new_val,
|
&x.rhs, idx_values, rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))?;
|
.map_err(|err| err.fill_position(*x_pos))?;
|
||||||
|
|
||||||
@ -525,8 +525,8 @@ impl Engine {
|
|||||||
let val = &mut val.into();
|
let val = &mut val.into();
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
global, caches, lib, this_ptr, val, root, rhs, &x.rhs,
|
global, caches, lib, this_ptr, val, root, rhs, *options,
|
||||||
*options, idx_values, rhs_chain, level, new_val,
|
&x.rhs, idx_values, rhs_chain, level, new_val,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(pos))
|
.map_err(|err| err.fill_position(pos))
|
||||||
}
|
}
|
||||||
@ -612,7 +612,7 @@ impl Engine {
|
|||||||
let root = (x.3.as_str(), *var_pos);
|
let root = (x.3.as_str(), *var_pos);
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
global, caches, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values,
|
global, caches, lib, &mut None, obj_ptr, root, expr, options, rhs, idx_values,
|
||||||
chain_type, level, new_val,
|
chain_type, level, new_val,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -627,7 +627,7 @@ impl Engine {
|
|||||||
let root = ("", expr.start_position());
|
let root = ("", expr.start_position());
|
||||||
|
|
||||||
self.eval_dot_index_chain_helper(
|
self.eval_dot_index_chain_helper(
|
||||||
global, caches, lib, this_ptr, obj_ptr, root, expr, rhs, options, idx_values,
|
global, caches, lib, this_ptr, obj_ptr, root, expr, options, rhs, idx_values,
|
||||||
chain_type, level, new_val,
|
chain_type, level, new_val,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -646,7 +646,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
parent_options: ASTFlags,
|
parent_options: ASTFlags,
|
||||||
_parent_chain_type: ChainType,
|
parent_chain_type: ChainType,
|
||||||
idx_values: &mut FnArgsVec<Dynamic>,
|
idx_values: &mut FnArgsVec<Dynamic>,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> RhaiResultOf<()> {
|
) -> RhaiResultOf<()> {
|
||||||
@ -655,7 +655,7 @@ impl Engine {
|
|||||||
match expr {
|
match expr {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::MethodCall(x, ..)
|
Expr::MethodCall(x, ..)
|
||||||
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
if parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||||
{
|
{
|
||||||
for arg_expr in &x.args {
|
for arg_expr in &x.args {
|
||||||
idx_values.push(
|
idx_values.push(
|
||||||
@ -666,12 +666,12 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::MethodCall(..) if _parent_chain_type == ChainType::Dotting => {
|
Expr::MethodCall(..) if parent_chain_type == ChainType::Dotting => {
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(..) if _parent_chain_type == ChainType::Dotting => (),
|
Expr::Property(..) if parent_chain_type == ChainType::Dotting => (),
|
||||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
Expr::Index(x, options, ..) | Expr::Dot(x, options, ..)
|
Expr::Index(x, options, ..) | Expr::Dot(x, options, ..)
|
||||||
@ -684,12 +684,12 @@ impl Engine {
|
|||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
match lhs {
|
match lhs {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(..) if _parent_chain_type == ChainType::Dotting => (),
|
Expr::Property(..) if parent_chain_type == ChainType::Dotting => (),
|
||||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::MethodCall(x, ..)
|
Expr::MethodCall(x, ..)
|
||||||
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
if parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||||
{
|
{
|
||||||
for arg_expr in &x.args {
|
for arg_expr in &x.args {
|
||||||
_arg_values.push(
|
_arg_values.push(
|
||||||
@ -702,15 +702,15 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::MethodCall(..) if _parent_chain_type == ChainType::Dotting => {
|
Expr::MethodCall(..) if parent_chain_type == ChainType::Dotting => {
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
expr if _parent_chain_type == ChainType::Dotting => {
|
expr if parent_chain_type == ChainType::Dotting => {
|
||||||
unreachable!("invalid dot expression: {:?}", expr);
|
unreachable!("invalid dot expression: {:?}", expr);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if _parent_chain_type == ChainType::Indexing => {
|
_ if parent_chain_type == ChainType::Indexing => {
|
||||||
_arg_values.push(
|
_arg_values.push(
|
||||||
self.eval_expr(scope, global, caches, lib, this_ptr, lhs, level)?
|
self.eval_expr(scope, global, caches, lib, this_ptr, lhs, level)?
|
||||||
.flatten(),
|
.flatten(),
|
||||||
@ -733,11 +733,11 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
_ if _parent_chain_type == ChainType::Dotting => {
|
_ if parent_chain_type == ChainType::Dotting => {
|
||||||
unreachable!("invalid dot expression: {:?}", expr);
|
unreachable!("invalid dot expression: {:?}", expr);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if _parent_chain_type == ChainType::Indexing => idx_values.push(
|
_ if parent_chain_type == ChainType::Indexing => idx_values.push(
|
||||||
self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)?
|
self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)?
|
||||||
.flatten(),
|
.flatten(),
|
||||||
),
|
),
|
||||||
|
@ -110,6 +110,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether the size of a [`Dynamic`] is within limits.
|
/// Check whether the size of a [`Dynamic`] is within limits.
|
||||||
|
#[inline]
|
||||||
pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> {
|
pub(crate) fn check_data_size(&self, value: &Dynamic, pos: Position) -> RhaiResultOf<()> {
|
||||||
// If no data size limits, just return
|
// If no data size limits, just return
|
||||||
if !self.has_data_size_limit() {
|
if !self.has_data_size_limit() {
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
use super::{EvalContext, GlobalRuntimeState};
|
use super::{EvalContext, GlobalRuntimeState};
|
||||||
use crate::ast::{ASTNode, Expr, Stmt};
|
use crate::ast::{ASTNode, Expr, Stmt};
|
||||||
use crate::{Dynamic, Engine, EvalAltResult, Identifier, Module, Position, RhaiResultOf, Scope};
|
use crate::{
|
||||||
|
Dynamic, Engine, EvalAltResult, ImmutableString, Module, Position, RhaiResultOf, Scope,
|
||||||
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{fmt, iter::repeat, mem};
|
use std::{fmt, iter::repeat, mem};
|
||||||
@ -100,12 +102,10 @@ pub enum BreakPoint {
|
|||||||
/// Break at a particular position under a particular source.
|
/// Break at a particular position under a particular source.
|
||||||
///
|
///
|
||||||
/// Not available under `no_position`.
|
/// Not available under `no_position`.
|
||||||
///
|
|
||||||
/// Source is empty if not available.
|
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
AtPosition {
|
AtPosition {
|
||||||
/// Source (empty if not available) of the break-point.
|
/// Source (empty if not available) of the break-point.
|
||||||
source: Identifier,
|
source: Option<ImmutableString>,
|
||||||
/// [Position] of the break-point.
|
/// [Position] of the break-point.
|
||||||
pos: Position,
|
pos: Position,
|
||||||
/// Is the break-point enabled?
|
/// Is the break-point enabled?
|
||||||
@ -114,14 +114,14 @@ pub enum BreakPoint {
|
|||||||
/// Break at a particular function call.
|
/// Break at a particular function call.
|
||||||
AtFunctionName {
|
AtFunctionName {
|
||||||
/// Function name.
|
/// Function name.
|
||||||
name: Identifier,
|
name: ImmutableString,
|
||||||
/// Is the break-point enabled?
|
/// Is the break-point enabled?
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
},
|
},
|
||||||
/// Break at a particular function call with a particular number of arguments.
|
/// Break at a particular function call with a particular number of arguments.
|
||||||
AtFunctionCall {
|
AtFunctionCall {
|
||||||
/// Function name.
|
/// Function name.
|
||||||
name: Identifier,
|
name: ImmutableString,
|
||||||
/// Number of arguments.
|
/// Number of arguments.
|
||||||
args: usize,
|
args: usize,
|
||||||
/// Is the break-point enabled?
|
/// Is the break-point enabled?
|
||||||
@ -133,7 +133,7 @@ pub enum BreakPoint {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
AtProperty {
|
AtProperty {
|
||||||
/// Property name.
|
/// Property name.
|
||||||
name: Identifier,
|
name: ImmutableString,
|
||||||
/// Is the break-point enabled?
|
/// Is the break-point enabled?
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
},
|
},
|
||||||
@ -148,7 +148,7 @@ impl fmt::Display for BreakPoint {
|
|||||||
pos,
|
pos,
|
||||||
enabled,
|
enabled,
|
||||||
} => {
|
} => {
|
||||||
if !source.is_empty() {
|
if let Some(ref source) = source {
|
||||||
write!(f, "{source} ")?;
|
write!(f, "{source} ")?;
|
||||||
}
|
}
|
||||||
write!(f, "@ {pos:?}")?;
|
write!(f, "@ {pos:?}")?;
|
||||||
@ -223,11 +223,11 @@ impl BreakPoint {
|
|||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Debug, Clone, Hash)]
|
||||||
pub struct CallStackFrame {
|
pub struct CallStackFrame {
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub fn_name: Identifier,
|
pub fn_name: ImmutableString,
|
||||||
/// Copies of function call arguments, if any.
|
/// Copies of function call arguments, if any.
|
||||||
pub args: crate::StaticVec<Dynamic>,
|
pub args: crate::StaticVec<Dynamic>,
|
||||||
/// Source of the function, empty if none.
|
/// Source of the function.
|
||||||
pub source: Identifier,
|
pub source: Option<ImmutableString>,
|
||||||
/// [Position][`Position`] of the function call.
|
/// [Position][`Position`] of the function call.
|
||||||
pub pos: Position,
|
pub pos: Position,
|
||||||
}
|
}
|
||||||
@ -243,8 +243,8 @@ impl fmt::Display for CallStackFrame {
|
|||||||
fp.finish()?;
|
fp.finish()?;
|
||||||
|
|
||||||
if !self.pos.is_none() {
|
if !self.pos.is_none() {
|
||||||
if !self.source.is_empty() {
|
if let Some(ref source) = self.source {
|
||||||
write!(f, ": {}", self.source)?;
|
write!(f, ": {source}")?;
|
||||||
}
|
}
|
||||||
write!(f, " @ {:?}", self.pos)?;
|
write!(f, " @ {:?}", self.pos)?;
|
||||||
}
|
}
|
||||||
@ -293,15 +293,15 @@ impl Debugger {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn push_call_stack_frame(
|
pub(crate) fn push_call_stack_frame(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: impl Into<Identifier>,
|
fn_name: ImmutableString,
|
||||||
args: crate::StaticVec<Dynamic>,
|
args: crate::StaticVec<Dynamic>,
|
||||||
source: impl Into<Identifier>,
|
source: Option<ImmutableString>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) {
|
) {
|
||||||
self.call_stack.push(CallStackFrame {
|
self.call_stack.push(CallStackFrame {
|
||||||
fn_name: fn_name.into(),
|
fn_name: fn_name.into(),
|
||||||
args,
|
args,
|
||||||
source: source.into(),
|
source,
|
||||||
pos,
|
pos,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -328,7 +328,7 @@ impl Debugger {
|
|||||||
}
|
}
|
||||||
/// Returns the first break-point triggered by a particular [`AST` Node][ASTNode].
|
/// Returns the first break-point triggered by a particular [`AST` Node][ASTNode].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_break_point(&self, src: &str, node: ASTNode) -> Option<usize> {
|
pub fn is_break_point(&self, src: Option<&str>, node: ASTNode) -> Option<usize> {
|
||||||
let _src = src;
|
let _src = src;
|
||||||
|
|
||||||
self.break_points()
|
self.break_points()
|
||||||
@ -340,11 +340,12 @@ impl Debugger {
|
|||||||
BreakPoint::AtPosition { pos, .. } if pos.is_none() => false,
|
BreakPoint::AtPosition { pos, .. } if pos.is_none() => false,
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
BreakPoint::AtPosition { source, pos, .. } if pos.is_beginning_of_line() => {
|
BreakPoint::AtPosition { source, pos, .. } if pos.is_beginning_of_line() => {
|
||||||
node.position().line().unwrap_or(0) == pos.line().unwrap() && _src == source
|
node.position().line().unwrap_or(0) == pos.line().unwrap()
|
||||||
|
&& _src == source.as_ref().map(|s| s.as_str())
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
BreakPoint::AtPosition { source, pos, .. } => {
|
BreakPoint::AtPosition { source, pos, .. } => {
|
||||||
node.position() == *pos && _src == source
|
node.position() == *pos && _src == source.as_ref().map(|s| s.as_str())
|
||||||
}
|
}
|
||||||
BreakPoint::AtFunctionName { name, .. } => match node {
|
BreakPoint::AtFunctionName { name, .. } => match node {
|
||||||
ASTNode::Expr(Expr::FnCall(x, ..)) | ASTNode::Stmt(Stmt::FnCall(x, ..)) => {
|
ASTNode::Expr(Expr::FnCall(x, ..)) | ASTNode::Stmt(Stmt::FnCall(x, ..)) => {
|
||||||
@ -487,7 +488,7 @@ impl Engine {
|
|||||||
|
|
||||||
let event = match event {
|
let event = match event {
|
||||||
Some(e) => e,
|
Some(e) => e,
|
||||||
None => match global.debugger.is_break_point(&global.source, node) {
|
None => match global.debugger.is_break_point(global.source(), node) {
|
||||||
Some(bp) => DebuggerEvent::BreakPoint(bp),
|
Some(bp) => DebuggerEvent::BreakPoint(bp),
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
},
|
},
|
||||||
@ -512,17 +513,12 @@ impl Engine {
|
|||||||
event: DebuggerEvent,
|
event: DebuggerEvent,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
||||||
let source = global.source.clone();
|
let src = global.source_raw().cloned();
|
||||||
let source = if source.is_empty() {
|
let src = src.as_ref().map(|s| s.as_str());
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(source.as_str())
|
|
||||||
};
|
|
||||||
|
|
||||||
let context = crate::EvalContext::new(self, scope, global, None, lib, this_ptr, level);
|
let context = crate::EvalContext::new(self, scope, global, None, lib, this_ptr, level);
|
||||||
|
|
||||||
if let Some((.., ref on_debugger)) = self.debugger {
|
if let Some((.., ref on_debugger)) = self.debugger {
|
||||||
let command = on_debugger(context, event, node, source, node.position())?;
|
let command = on_debugger(context, event, node, src, node.position())?;
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
DebuggerCommand::Continue => {
|
DebuggerCommand::Continue => {
|
||||||
|
@ -58,11 +58,7 @@ impl<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> EvalContext<'a, 's, 'ps, 'g, 'pg, '
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn source(&self) -> Option<&str> {
|
pub fn source(&self) -> Option<&str> {
|
||||||
if self.global.source.is_empty() {
|
self.global.source()
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.global.source.as_str())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
/// The current [`Scope`].
|
/// The current [`Scope`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Global runtime state.
|
//! Global runtime state.
|
||||||
|
|
||||||
use crate::{Dynamic, Engine, Identifier};
|
use crate::{Dynamic, Engine, ImmutableString};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{fmt, marker::PhantomData};
|
use std::{fmt, marker::PhantomData};
|
||||||
@ -9,7 +9,7 @@ use std::{fmt, marker::PhantomData};
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub type GlobalConstants =
|
pub type GlobalConstants =
|
||||||
crate::Shared<crate::Locked<std::collections::BTreeMap<crate::ImmutableString, Dynamic>>>;
|
crate::Shared<crate::Locked<std::collections::BTreeMap<ImmutableString, Dynamic>>>;
|
||||||
|
|
||||||
/// _(internals)_ Global runtime states.
|
/// _(internals)_ Global runtime states.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -25,14 +25,14 @@ pub type GlobalConstants =
|
|||||||
pub struct GlobalRuntimeState<'a> {
|
pub struct GlobalRuntimeState<'a> {
|
||||||
/// Names of imported [modules][crate::Module].
|
/// Names of imported [modules][crate::Module].
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
imports: crate::StaticVec<crate::ImmutableString>,
|
imports: crate::StaticVec<ImmutableString>,
|
||||||
/// Stack of imported [modules][crate::Module].
|
/// Stack of imported [modules][crate::Module].
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: crate::StaticVec<crate::Shared<crate::Module>>,
|
modules: crate::StaticVec<crate::Shared<crate::Module>>,
|
||||||
/// Source of the current context.
|
/// Source of the current context.
|
||||||
///
|
///
|
||||||
/// No source if the string is empty.
|
/// No source if the string is empty.
|
||||||
pub source: Identifier,
|
pub source: Option<ImmutableString>,
|
||||||
/// Number of operations performed.
|
/// Number of operations performed.
|
||||||
pub num_operations: u64,
|
pub num_operations: u64,
|
||||||
/// Number of modules loaded.
|
/// Number of modules loaded.
|
||||||
@ -84,7 +84,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
imports: crate::StaticVec::new_const(),
|
imports: crate::StaticVec::new_const(),
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
modules: crate::StaticVec::new_const(),
|
modules: crate::StaticVec::new_const(),
|
||||||
source: Identifier::new_const(),
|
source: None,
|
||||||
num_operations: 0,
|
num_operations: 0,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
num_modules_loaded: 0,
|
num_modules_loaded: 0,
|
||||||
@ -168,7 +168,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_import(
|
pub fn push_import(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<crate::ImmutableString>,
|
name: impl Into<ImmutableString>,
|
||||||
module: impl Into<crate::Shared<crate::Module>>,
|
module: impl Into<crate::Shared<crate::Module>>,
|
||||||
) {
|
) {
|
||||||
self.imports.push(name.into());
|
self.imports.push(name.into());
|
||||||
@ -202,7 +202,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn iter_imports_raw(
|
pub(crate) fn iter_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::Shared<crate::Module>)> {
|
) -> impl Iterator<Item = (&ImmutableString, &crate::Shared<crate::Module>)> {
|
||||||
self.imports.iter().zip(self.modules.iter()).rev()
|
self.imports.iter().zip(self.modules.iter()).rev()
|
||||||
}
|
}
|
||||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
|
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order.
|
||||||
@ -212,7 +212,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn scan_imports_raw(
|
pub fn scan_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::Shared<crate::Module>)> {
|
) -> impl Iterator<Item = (&ImmutableString, &crate::Shared<crate::Module>)> {
|
||||||
self.imports.iter().zip(self.modules.iter())
|
self.imports.iter().zip(self.modules.iter())
|
||||||
}
|
}
|
||||||
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
/// Can the particular function with [`Dynamic`] parameter(s) exist in the stack of
|
||||||
@ -247,11 +247,11 @@ impl GlobalRuntimeState<'_> {
|
|||||||
pub fn get_qualified_fn(
|
pub fn get_qualified_fn(
|
||||||
&self,
|
&self,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
) -> Option<(&crate::func::CallableFunction, Option<&str>)> {
|
) -> Option<(&crate::func::CallableFunction, Option<&ImmutableString>)> {
|
||||||
self.modules
|
self.modules
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id())))
|
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||||
}
|
}
|
||||||
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
|
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
|
||||||
/// globally-imported [modules][crate::Module]?
|
/// globally-imported [modules][crate::Module]?
|
||||||
@ -278,14 +278,16 @@ impl GlobalRuntimeState<'_> {
|
|||||||
.find_map(|m| m.get_qualified_iter(id))
|
.find_map(|m| m.get_qualified_iter(id))
|
||||||
}
|
}
|
||||||
/// Get the current source.
|
/// Get the current source.
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn source(&self) -> Option<&str> {
|
pub fn source(&self) -> Option<&str> {
|
||||||
if self.source.is_empty() {
|
self.source.as_ref().map(|s| s.as_str())
|
||||||
None
|
}
|
||||||
} else {
|
/// Get the current source.
|
||||||
Some(self.source.as_str())
|
#[inline(always)]
|
||||||
}
|
#[must_use]
|
||||||
|
pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> {
|
||||||
|
self.source.as_ref()
|
||||||
}
|
}
|
||||||
/// Get the pre-calculated index getter hash.
|
/// Get the pre-calculated index getter hash.
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
@ -317,10 +319,10 @@ impl GlobalRuntimeState<'_> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
impl IntoIterator for GlobalRuntimeState<'_> {
|
impl IntoIterator for GlobalRuntimeState<'_> {
|
||||||
type Item = (crate::ImmutableString, crate::Shared<crate::Module>);
|
type Item = (ImmutableString, crate::Shared<crate::Module>);
|
||||||
type IntoIter = std::iter::Rev<
|
type IntoIter = std::iter::Rev<
|
||||||
std::iter::Zip<
|
std::iter::Zip<
|
||||||
smallvec::IntoIter<[crate::ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>,
|
smallvec::IntoIter<[ImmutableString; crate::STATIC_VEC_INLINE_SIZE]>,
|
||||||
smallvec::IntoIter<[crate::Shared<crate::Module>; crate::STATIC_VEC_INLINE_SIZE]>,
|
smallvec::IntoIter<[crate::Shared<crate::Module>; crate::STATIC_VEC_INLINE_SIZE]>,
|
||||||
>,
|
>,
|
||||||
>;
|
>;
|
||||||
@ -332,10 +334,10 @@ impl IntoIterator for GlobalRuntimeState<'_> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> {
|
impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> {
|
||||||
type Item = (&'a crate::ImmutableString, &'a crate::Shared<crate::Module>);
|
type Item = (&'a ImmutableString, &'a crate::Shared<crate::Module>);
|
||||||
type IntoIter = std::iter::Rev<
|
type IntoIter = std::iter::Rev<
|
||||||
std::iter::Zip<
|
std::iter::Zip<
|
||||||
std::slice::Iter<'a, crate::ImmutableString>,
|
std::slice::Iter<'a, ImmutableString>,
|
||||||
std::slice::Iter<'a, crate::Shared<crate::Module>>,
|
std::slice::Iter<'a, crate::Shared<crate::Module>>,
|
||||||
>,
|
>,
|
||||||
>;
|
>;
|
||||||
@ -346,7 +348,7 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
impl<K: Into<crate::ImmutableString>, M: Into<crate::Shared<crate::Module>>> Extend<(K, M)>
|
impl<K: Into<ImmutableString>, M: Into<crate::Shared<crate::Module>>> Extend<(K, M)>
|
||||||
for GlobalRuntimeState<'_>
|
for GlobalRuntimeState<'_>
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -493,7 +493,7 @@ impl Engine {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
ERR::LoopBreak(false, ..) => (),
|
ERR::LoopBreak(false, ..) => (),
|
||||||
ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT),
|
ERR::LoopBreak(true, value, ..) => break Ok(value),
|
||||||
_ => break Err(err),
|
_ => break Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -524,7 +524,7 @@ impl Engine {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
ERR::LoopBreak(false, ..) => (),
|
ERR::LoopBreak(false, ..) => (),
|
||||||
ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT),
|
ERR::LoopBreak(true, value, ..) => break Ok(value),
|
||||||
_ => break Err(err),
|
_ => break Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -547,7 +547,7 @@ impl Engine {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
ERR::LoopBreak(false, ..) => continue,
|
ERR::LoopBreak(false, ..) => continue,
|
||||||
ERR::LoopBreak(true, ..) => break Ok(Dynamic::UNIT),
|
ERR::LoopBreak(true, value, ..) => break Ok(value),
|
||||||
_ => break Err(err),
|
_ => break Err(err),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -614,7 +614,7 @@ impl Engine {
|
|||||||
|
|
||||||
let loop_result = func(iter_obj)
|
let loop_result = func(iter_obj)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.try_for_each(|(x, iter_value)| {
|
.try_fold(Dynamic::UNIT, |_, (x, iter_value)| {
|
||||||
// Increment counter
|
// Increment counter
|
||||||
if counter_index < usize::MAX {
|
if counter_index < usize::MAX {
|
||||||
// As the variable increments from 0, this should always work
|
// As the variable increments from 0, this should always work
|
||||||
@ -644,26 +644,26 @@ impl Engine {
|
|||||||
self.track_operation(global, statements.position())?;
|
self.track_operation(global, statements.position())?;
|
||||||
|
|
||||||
if statements.is_empty() {
|
if statements.is_empty() {
|
||||||
return Ok(());
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.eval_stmt_block(
|
self.eval_stmt_block(
|
||||||
scope, global, caches, lib, this_ptr, statements, true, level,
|
scope, global, caches, lib, this_ptr, statements, true, level,
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| Dynamic::UNIT)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
ERR::LoopBreak(false, ..) => Ok(()),
|
ERR::LoopBreak(false, ..) => Ok(Dynamic::UNIT),
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
ERR::LoopBreak(true, ..) => Ok(()),
|
ERR::LoopBreak(true, value, ..) => Ok(value),
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.rewind(orig_scope_len);
|
scope.rewind(orig_scope_len);
|
||||||
|
|
||||||
loop_result.map(|_| Dynamic::UNIT)
|
loop_result
|
||||||
} else {
|
} else {
|
||||||
Err(ERR::ErrorFor(expr.start_position()).into())
|
Err(ERR::ErrorFor(expr.start_position()).into())
|
||||||
}
|
}
|
||||||
@ -673,8 +673,15 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Continue/Break statement
|
// Continue/Break statement
|
||||||
Stmt::BreakLoop(options, pos) => {
|
Stmt::BreakLoop(expr, options, pos) => {
|
||||||
Err(ERR::LoopBreak(options.contains(ASTFlags::BREAK), *pos).into())
|
let is_break = options.contains(ASTFlags::BREAK);
|
||||||
|
|
||||||
|
if let Some(ref expr) = expr {
|
||||||
|
self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)
|
||||||
|
.and_then(|v| ERR::LoopBreak(is_break, v, *pos).into())
|
||||||
|
} else {
|
||||||
|
Err(ERR::LoopBreak(is_break, Dynamic::UNIT, *pos).into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try/Catch statement
|
// Try/Catch statement
|
||||||
@ -712,8 +719,8 @@ impl Engine {
|
|||||||
|
|
||||||
err_map.insert("message".into(), err.to_string().into());
|
err_map.insert("message".into(), err.to_string().into());
|
||||||
|
|
||||||
if !global.source.is_empty() {
|
if let Some(ref source) = global.source {
|
||||||
err_map.insert("source".into(), global.source.clone().into());
|
err_map.insert("source".into(), source.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if !err_pos.is_none() {
|
if !err_pos.is_none() {
|
||||||
@ -970,23 +977,25 @@ impl Engine {
|
|||||||
|
|
||||||
// Share statement
|
// Share statement
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Stmt::Share(x, pos) => {
|
Stmt::Share(x) => {
|
||||||
let (name, index) = &**x;
|
x.iter()
|
||||||
|
.try_for_each(|(name, index, pos)| {
|
||||||
|
if let Some(index) = index
|
||||||
|
.map(|n| scope.len() - n.get())
|
||||||
|
.or_else(|| scope.search(name))
|
||||||
|
{
|
||||||
|
let val = scope.get_mut_by_index(index);
|
||||||
|
|
||||||
if let Some(index) = index
|
if !val.is_shared() {
|
||||||
.map(|n| scope.len() - n.get())
|
// Replace the variable with a shared value.
|
||||||
.or_else(|| scope.search(name))
|
*val = std::mem::take(val).into_shared();
|
||||||
{
|
}
|
||||||
let val = scope.get_mut_by_index(index);
|
Ok(())
|
||||||
|
} else {
|
||||||
if !val.is_shared() {
|
Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into())
|
||||||
// Replace the variable with a shared value.
|
}
|
||||||
*val = std::mem::take(val).into_shared();
|
})
|
||||||
}
|
.map(|_| Dynamic::UNIT)
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
} else {
|
|
||||||
Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => unreachable!("statement cannot be evaluated: {:?}", stmt),
|
_ => unreachable!("statement cannot be evaluated: {:?}", stmt),
|
||||||
|
@ -218,7 +218,7 @@ impl Engine {
|
|||||||
.iter()
|
.iter()
|
||||||
.copied()
|
.copied()
|
||||||
.chain(self.global_modules.iter().map(|m| m.as_ref()))
|
.chain(self.global_modules.iter().map(|m| m.as_ref()))
|
||||||
.find_map(|m| m.get_fn(hash).map(|f| (f, m.id())));
|
.find_map(|m| m.get_fn(hash).map(|f| (f, m.id_raw())));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let func = if args.is_none() {
|
let func = if args.is_none() {
|
||||||
@ -228,7 +228,7 @@ impl Engine {
|
|||||||
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
|
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
|
||||||
self.global_sub_modules
|
self.global_sub_modules
|
||||||
.values()
|
.values()
|
||||||
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id())))
|
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -236,7 +236,7 @@ impl Engine {
|
|||||||
// Specific version found
|
// Specific version found
|
||||||
let new_entry = Some(FnResolutionCacheEntry {
|
let new_entry = Some(FnResolutionCacheEntry {
|
||||||
func: f.clone(),
|
func: f.clone(),
|
||||||
source: s.map(|s| Box::new(s.into())),
|
source: s.cloned(),
|
||||||
});
|
});
|
||||||
return if cache.filter.is_absent_and_set(hash) {
|
return if cache.filter.is_absent_and_set(hash) {
|
||||||
// Do not cache "one-hit wonders"
|
// Do not cache "one-hit wonders"
|
||||||
@ -358,7 +358,6 @@ impl Engine {
|
|||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
self.track_operation(global, pos)?;
|
self.track_operation(global, pos)?;
|
||||||
|
|
||||||
let parent_source = global.source.clone();
|
|
||||||
let op_assign = if is_op_assign {
|
let op_assign = if is_op_assign {
|
||||||
Token::lookup_from_syntax(name)
|
Token::lookup_from_syntax(name)
|
||||||
} else {
|
} else {
|
||||||
@ -398,24 +397,19 @@ impl Engine {
|
|||||||
backup.change_first_arg_to_copy(args);
|
backup.change_first_arg_to_copy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
let source = match (source, parent_source.as_str()) {
|
|
||||||
(None, "") => None,
|
|
||||||
(None, s) => Some(s),
|
|
||||||
(Some(s), ..) => Some(s.as_str()),
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.debugger.is_some() {
|
||||||
global.debugger.push_call_stack_frame(
|
global.debugger.push_call_stack_frame(
|
||||||
name,
|
self.get_interned_string(name),
|
||||||
args.iter().map(|v| (*v).clone()).collect(),
|
args.iter().map(|v| (*v).clone()).collect(),
|
||||||
source.unwrap_or(""),
|
source.clone().or_else(|| global.source.clone()),
|
||||||
pos,
|
pos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run external function
|
// Run external function
|
||||||
let context = (self, name, source, &*global, lib, pos, level).into();
|
let src = source.as_ref().map(|s| s.as_str());
|
||||||
|
let context = (self, name, src, &*global, lib, pos, level).into();
|
||||||
|
|
||||||
let result = if func.is_plugin_fn() {
|
let result = if func.is_plugin_fn() {
|
||||||
let f = func.get_plugin_fn().unwrap();
|
let f = func.get_plugin_fn().unwrap();
|
||||||
@ -484,12 +478,7 @@ impl Engine {
|
|||||||
let t = self.map_type_name(type_name::<ImmutableString>()).into();
|
let t = self.map_type_name(type_name::<ImmutableString>()).into();
|
||||||
ERR::ErrorMismatchOutputType(t, typ.into(), pos)
|
ERR::ErrorMismatchOutputType(t, typ.into(), pos)
|
||||||
})?;
|
})?;
|
||||||
let source = if global.source.is_empty() {
|
((*self.debug)(&text, global.source(), pos).into(), false)
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(global.source.as_str())
|
|
||||||
};
|
|
||||||
((*self.debug)(&text, source, pos).into(), false)
|
|
||||||
}
|
}
|
||||||
_ => (result, is_method),
|
_ => (result, is_method),
|
||||||
});
|
});
|
||||||
@ -685,12 +674,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let orig_source = mem::replace(
|
let orig_source = mem::replace(&mut global.source, source.clone());
|
||||||
&mut global.source,
|
|
||||||
source
|
|
||||||
.as_ref()
|
|
||||||
.map_or(crate::Identifier::new_const(), |s| (**s).clone()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = if _is_method_call {
|
let result = if _is_method_call {
|
||||||
// Method call of script function - map first argument to `this`
|
// Method call of script function - map first argument to `this`
|
||||||
@ -1172,7 +1156,7 @@ impl Engine {
|
|||||||
return result.map_err(|err| {
|
return result.map_err(|err| {
|
||||||
ERR::ErrorInFunctionCall(
|
ERR::ErrorInFunctionCall(
|
||||||
KEYWORD_EVAL.to_string(),
|
KEYWORD_EVAL.to_string(),
|
||||||
global.source.to_string(),
|
global.source().unwrap_or("").to_string(),
|
||||||
err,
|
err,
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
@ -1416,14 +1400,13 @@ impl Engine {
|
|||||||
Some(f) if f.is_script() => {
|
Some(f) if f.is_script() => {
|
||||||
let fn_def = f.get_script_fn_def().expect("script-defined function");
|
let fn_def = f.get_script_fn_def().expect("script-defined function");
|
||||||
let new_scope = &mut Scope::new();
|
let new_scope = &mut Scope::new();
|
||||||
let mut source = module.id_raw().clone();
|
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
||||||
mem::swap(&mut global.source, &mut source);
|
|
||||||
|
|
||||||
let result = self.call_script_fn(
|
let result = self.call_script_fn(
|
||||||
new_scope, global, caches, lib, &mut None, fn_def, &mut args, true, pos, level,
|
new_scope, global, caches, lib, &mut None, fn_def, &mut args, true, pos, level,
|
||||||
);
|
);
|
||||||
|
|
||||||
global.source = source;
|
global.source = orig_source;
|
||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ impl Engine {
|
|||||||
|
|
||||||
Err(ERR::ErrorInFunctionCall(
|
Err(ERR::ErrorInFunctionCall(
|
||||||
name,
|
name,
|
||||||
source.unwrap_or_else(|| global.source.to_string()),
|
source.unwrap_or_else(|| global.source().unwrap_or("").to_string()),
|
||||||
err,
|
err,
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
|
@ -72,7 +72,7 @@ pub struct FuncInfo {
|
|||||||
/// Function access mode.
|
/// Function access mode.
|
||||||
pub access: FnAccess,
|
pub access: FnAccess,
|
||||||
/// Function name.
|
/// Function name.
|
||||||
pub name: Identifier,
|
pub name: ImmutableString,
|
||||||
/// Number of parameters.
|
/// Number of parameters.
|
||||||
pub num_params: usize,
|
pub num_params: usize,
|
||||||
/// Parameter types (if applicable).
|
/// Parameter types (if applicable).
|
||||||
@ -160,8 +160,7 @@ pub fn calc_native_fn_hash<'a>(
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
/// ID identifying the module.
|
/// ID identifying the module.
|
||||||
/// No ID if string is empty.
|
id: Option<ImmutableString>,
|
||||||
id: Identifier,
|
|
||||||
/// Module documentation.
|
/// Module documentation.
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
doc: crate::SmartString,
|
doc: crate::SmartString,
|
||||||
@ -292,7 +291,7 @@ impl Module {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_capacity(capacity: usize) -> Self {
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: Identifier::new_const(),
|
id: None,
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
doc: crate::SmartString::new_const(),
|
doc: crate::SmartString::new_const(),
|
||||||
internal: false,
|
internal: false,
|
||||||
@ -324,18 +323,14 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn id(&self) -> Option<&str> {
|
pub fn id(&self) -> Option<&str> {
|
||||||
if self.id_raw().is_empty() {
|
self.id.as_ref().map(|s| s.as_str())
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(self.id_raw())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the ID of the [`Module`] as an [`Identifier`], if any.
|
/// Get the ID of the [`Module`] as an [`Identifier`], if any.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) const fn id_raw(&self) -> &Identifier {
|
pub(crate) const fn id_raw(&self) -> Option<&ImmutableString> {
|
||||||
&self.id
|
self.id.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the ID of the [`Module`].
|
/// Set the ID of the [`Module`].
|
||||||
@ -351,8 +346,15 @@ impl Module {
|
|||||||
/// assert_eq!(module.id(), Some("hello"));
|
/// assert_eq!(module.id(), Some("hello"));
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_id(&mut self, id: impl Into<Identifier>) -> &mut Self {
|
pub fn set_id(&mut self, id: impl Into<ImmutableString>) -> &mut Self {
|
||||||
self.id = id.into();
|
let id = id.into();
|
||||||
|
|
||||||
|
if id.is_empty() {
|
||||||
|
self.id = None;
|
||||||
|
} else {
|
||||||
|
self.id = Some(id);
|
||||||
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,7 +372,7 @@ impl Module {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear_id(&mut self) -> &mut Self {
|
pub fn clear_id(&mut self) -> &mut Self {
|
||||||
self.id.clear();
|
self.id = None;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -434,7 +436,7 @@ impl Module {
|
|||||||
/// Clear the [`Module`].
|
/// Clear the [`Module`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.id.clear();
|
self.id = None;
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
self.doc.clear();
|
self.doc.clear();
|
||||||
self.internal = false;
|
self.internal = false;
|
||||||
@ -2078,7 +2080,7 @@ impl Module {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
module.set_id(ast.source_raw().clone());
|
module.id = ast.source_raw().cloned();
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
module.set_doc(ast.doc());
|
module.set_doc(ast.doc());
|
||||||
|
@ -8,7 +8,7 @@ use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT,
|
|||||||
use crate::eval::{Caches, GlobalRuntimeState};
|
use crate::eval::{Caches, GlobalRuntimeState};
|
||||||
use crate::func::builtin::get_builtin_binary_op_fn;
|
use crate::func::builtin::get_builtin_binary_op_fn;
|
||||||
use crate::func::hashing::get_hasher;
|
use crate::func::hashing::get_hasher;
|
||||||
use crate::tokenizer::{Span, Token};
|
use crate::tokenizer::Token;
|
||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier,
|
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Identifier,
|
||||||
@ -785,50 +785,6 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
*condition = Expr::Unit(*pos);
|
*condition = Expr::Unit(*pos);
|
||||||
}
|
}
|
||||||
**body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
|
**body = optimize_stmt_block(mem::take(&mut **body), state, false, true, false);
|
||||||
|
|
||||||
if body.len() == 1 {
|
|
||||||
match body[0] {
|
|
||||||
// while expr { break; } -> { expr; }
|
|
||||||
Stmt::BreakLoop(options, pos) if options.contains(ASTFlags::BREAK) => {
|
|
||||||
// Only a single break statement - turn into running the guard expression once
|
|
||||||
state.set_dirty();
|
|
||||||
if condition.is_unit() {
|
|
||||||
*stmt = Stmt::Noop(pos);
|
|
||||||
} else {
|
|
||||||
let mut statements = vec![Stmt::Expr(mem::take(condition).into())];
|
|
||||||
if preserve_result {
|
|
||||||
statements.push(Stmt::Noop(pos));
|
|
||||||
}
|
|
||||||
*stmt = (statements, Span::new(pos, Position::NONE)).into();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// do { block } until true -> { block }
|
|
||||||
Stmt::Do(x, options, ..)
|
|
||||||
if matches!(x.0, Expr::BoolConstant(true, ..))
|
|
||||||
&& options.contains(ASTFlags::NEGATED) =>
|
|
||||||
{
|
|
||||||
state.set_dirty();
|
|
||||||
*stmt = (
|
|
||||||
optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false),
|
|
||||||
x.1.span(),
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
// do { block } while false -> { block }
|
|
||||||
Stmt::Do(x, options, ..)
|
|
||||||
if matches!(x.0, Expr::BoolConstant(false, ..))
|
|
||||||
&& !options.contains(ASTFlags::NEGATED) =>
|
|
||||||
{
|
|
||||||
state.set_dirty();
|
|
||||||
*stmt = (
|
|
||||||
optimize_stmt_block(mem::take(&mut *x.1), state, false, true, false),
|
|
||||||
x.1.span(),
|
|
||||||
)
|
|
||||||
.into();
|
|
||||||
}
|
}
|
||||||
// do { block } while|until expr
|
// do { block } while|until expr
|
||||||
Stmt::Do(x, ..) => {
|
Stmt::Do(x, ..) => {
|
||||||
@ -916,6 +872,9 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// break expr;
|
||||||
|
Stmt::BreakLoop(Some(ref mut expr), ..) => optimize_expr(expr, state, false),
|
||||||
|
|
||||||
// return expr;
|
// return expr;
|
||||||
Stmt::Return(Some(ref mut expr), ..) => optimize_expr(expr, state, false),
|
Stmt::Return(Some(ref mut expr), ..) => optimize_expr(expr, state, false),
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ mod debugging_functions {
|
|||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| {
|
.filter(|crate::debugger::CallStackFrame { fn_name, args, .. }| {
|
||||||
fn_name != "back_trace" || !args.is_empty()
|
fn_name.as_str() != "back_trace" || !args.is_empty()
|
||||||
})
|
})
|
||||||
.map(
|
.map(
|
||||||
|frame @ crate::debugger::CallStackFrame {
|
|frame @ crate::debugger::CallStackFrame {
|
||||||
@ -62,8 +62,8 @@ mod debugging_functions {
|
|||||||
Dynamic::from_array(_args.clone().to_vec()),
|
Dynamic::from_array(_args.clone().to_vec()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if !_source.is_empty() {
|
if let Some(source) = _source {
|
||||||
map.insert("source".into(), _source.into());
|
map.insert("source".into(), source.into());
|
||||||
}
|
}
|
||||||
if !_pos.is_none() {
|
if !_pos.is_none() {
|
||||||
map.insert(
|
map.insert(
|
||||||
|
@ -27,7 +27,7 @@ mod fn_ptr_functions {
|
|||||||
/// ```
|
/// ```
|
||||||
#[rhai_fn(name = "name", get = "name", pure)]
|
#[rhai_fn(name = "name", get = "name", pure)]
|
||||||
pub fn name(fn_ptr: &mut FnPtr) -> ImmutableString {
|
pub fn name(fn_ptr: &mut FnPtr) -> ImmutableString {
|
||||||
fn_ptr.fn_name_raw().into()
|
fn_ptr.fn_name_raw().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return `true` if the function is an anonymous function.
|
/// Return `true` if the function is an anonymous function.
|
||||||
|
@ -122,7 +122,7 @@ impl<T: Debug + Copy + PartialOrd> FusedIterator for StepRange<T> {}
|
|||||||
|
|
||||||
// Bit-field iterator with step
|
// Bit-field iterator with step
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
pub struct BitRange(INT, INT, usize);
|
pub struct BitRange(INT, usize);
|
||||||
|
|
||||||
impl BitRange {
|
impl BitRange {
|
||||||
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
|
pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf<Self> {
|
||||||
@ -138,7 +138,7 @@ impl BitRange {
|
|||||||
len as usize
|
len as usize
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self(value, 1 << from, len))
|
Ok(Self(value >> from, len))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,19 +146,19 @@ impl Iterator for BitRange {
|
|||||||
type Item = bool;
|
type Item = bool;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.2 == 0 {
|
if self.1 == 0 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let r = (self.0 & self.1) != 0;
|
let r = (self.0 & 0x0001) != 0;
|
||||||
self.1 <<= 1;
|
self.0 >>= 1;
|
||||||
self.2 -= 1;
|
self.1 -= 1;
|
||||||
Some(r)
|
Some(r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
(self.2, Some(self.2))
|
(self.1, Some(self.1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ impl FusedIterator for BitRange {}
|
|||||||
impl ExactSizeIterator for BitRange {
|
impl ExactSizeIterator for BitRange {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.2
|
self.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use super::arithmetic::make_err as make_arithmetic_err;
|
use super::arithmetic::make_err as make_arithmetic_err;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::{def_package, Dynamic, EvalAltResult, RhaiResult, RhaiResultOf, INT};
|
use crate::{def_package, Dynamic, RhaiResult, RhaiResultOf, INT};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::FLOAT;
|
use crate::FLOAT;
|
||||||
|
@ -1380,6 +1380,23 @@ impl Engine {
|
|||||||
self.parse_if(input, state, lib, settings.level_up())?
|
self.parse_if(input, state, lib, settings.level_up())?
|
||||||
.into(),
|
.into(),
|
||||||
)),
|
)),
|
||||||
|
// Loops are allowed to act as expressions
|
||||||
|
Token::While | Token::Loop if settings.options.contains(LangOptions::LOOP_EXPR) => {
|
||||||
|
Expr::Stmt(Box::new(
|
||||||
|
self.parse_while_loop(input, state, lib, settings.level_up())?
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Token::Do if settings.options.contains(LangOptions::LOOP_EXPR) => Expr::Stmt(Box::new(
|
||||||
|
self.parse_do(input, state, lib, settings.level_up())?
|
||||||
|
.into(),
|
||||||
|
)),
|
||||||
|
Token::For if settings.options.contains(LangOptions::LOOP_EXPR) => {
|
||||||
|
Expr::Stmt(Box::new(
|
||||||
|
self.parse_for(input, state, lib, settings.level_up())?
|
||||||
|
.into(),
|
||||||
|
))
|
||||||
|
}
|
||||||
// Switch statement is allowed to act as expressions
|
// Switch statement is allowed to act as expressions
|
||||||
Token::Switch if settings.options.contains(LangOptions::SWITCH_EXPR) => {
|
Token::Switch if settings.options.contains(LangOptions::SWITCH_EXPR) => {
|
||||||
Expr::Stmt(Box::new(
|
Expr::Stmt(Box::new(
|
||||||
@ -3411,11 +3428,26 @@ impl Engine {
|
|||||||
|
|
||||||
Token::Continue if self.allow_looping() && settings.is_breakable => {
|
Token::Continue if self.allow_looping() && settings.is_breakable => {
|
||||||
let pos = eat_token(input, Token::Continue);
|
let pos = eat_token(input, Token::Continue);
|
||||||
Ok(Stmt::BreakLoop(ASTFlags::NONE, pos))
|
Ok(Stmt::BreakLoop(None, ASTFlags::NONE, pos))
|
||||||
}
|
}
|
||||||
Token::Break if self.allow_looping() && settings.is_breakable => {
|
Token::Break if self.allow_looping() && settings.is_breakable => {
|
||||||
let pos = eat_token(input, Token::Break);
|
let pos = eat_token(input, Token::Break);
|
||||||
Ok(Stmt::BreakLoop(ASTFlags::BREAK, pos))
|
|
||||||
|
let expr = match input.peek().expect(NEVER_ENDS) {
|
||||||
|
// `break` at <EOF>
|
||||||
|
(Token::EOF, ..) => None,
|
||||||
|
// `break` at end of block
|
||||||
|
(Token::RightBrace, ..) => None,
|
||||||
|
// `break;`
|
||||||
|
(Token::SemiColon, ..) => None,
|
||||||
|
// `break` with expression
|
||||||
|
_ => Some(
|
||||||
|
self.parse_expr(input, state, lib, settings.level_up())?
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Stmt::BreakLoop(expr, ASTFlags::BREAK, pos))
|
||||||
}
|
}
|
||||||
Token::Continue | Token::Break if self.allow_looping() => {
|
Token::Continue | Token::Break if self.allow_looping() => {
|
||||||
Err(PERR::LoopBreak.into_err(token_pos))
|
Err(PERR::LoopBreak.into_err(token_pos))
|
||||||
@ -3707,15 +3739,17 @@ impl Engine {
|
|||||||
|
|
||||||
// Convert the entire expression into a statement block, then insert the relevant
|
// Convert the entire expression into a statement block, then insert the relevant
|
||||||
// [`Share`][Stmt::Share] statements.
|
// [`Share`][Stmt::Share] statements.
|
||||||
let mut statements = StaticVec::with_capacity(externals.len() + 1);
|
let mut statements = StaticVec::with_capacity(2);
|
||||||
statements.extend(
|
statements.push(Stmt::Share(
|
||||||
externals
|
externals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|crate::ast::Ident { name, pos }| {
|
.map(|crate::ast::Ident { name, pos }| {
|
||||||
let (index, _) = parent.access_var(&name, lib, pos);
|
let (index, _) = parent.access_var(&name, lib, pos);
|
||||||
Stmt::Share((name, index).into(), pos)
|
(name, index, pos)
|
||||||
}),
|
})
|
||||||
);
|
.collect::<crate::FnArgsVec<_>>()
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
statements.push(Stmt::Expr(expr.into()));
|
statements.push(Stmt::Expr(expr.into()));
|
||||||
Expr::Stmt(crate::ast::StmtBlock::new(statements, pos, Position::NONE).into())
|
Expr::Stmt(crate::ast::StmtBlock::new(statements, pos, Position::NONE).into())
|
||||||
}
|
}
|
||||||
@ -3840,7 +3874,7 @@ impl Engine {
|
|||||||
let mut functions = StraightHashMap::default();
|
let mut functions = StraightHashMap::default();
|
||||||
|
|
||||||
let mut options = self.options;
|
let mut options = self.options;
|
||||||
options.remove(LangOptions::STMT_EXPR);
|
options.remove(LangOptions::STMT_EXPR | LangOptions::LOOP_EXPR);
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
options.remove(LangOptions::ANON_FN);
|
options.remove(LangOptions::ANON_FN);
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ fn check_struct_sizes() {
|
|||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
{
|
{
|
||||||
assert_eq!(size_of::<Scope>(), 536);
|
assert_eq!(size_of::<Scope>(), 536);
|
||||||
assert_eq!(size_of::<FnPtr>(), 80);
|
assert_eq!(size_of::<FnPtr>(), 64);
|
||||||
assert_eq!(size_of::<LexError>(), 56);
|
assert_eq!(size_of::<LexError>(), 56);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<ParseError>(),
|
size_of::<ParseError>(),
|
||||||
|
@ -117,7 +117,7 @@ pub enum EvalAltResult {
|
|||||||
/// Breaking out of loops - not an error if within a loop.
|
/// Breaking out of loops - not an error if within a loop.
|
||||||
/// The wrapped value, if true, means breaking clean out of the loop (i.e. a `break` statement).
|
/// The wrapped value, if true, means breaking clean out of the loop (i.e. a `break` statement).
|
||||||
/// The wrapped value, if false, means breaking the current context (i.e. a `continue` statement).
|
/// The wrapped value, if false, means breaking the current context (i.e. a `continue` statement).
|
||||||
LoopBreak(bool, Position),
|
LoopBreak(bool, Dynamic, Position),
|
||||||
/// Not an error: Value returned from a script via the `return` keyword.
|
/// Not an error: Value returned from a script via the `return` keyword.
|
||||||
/// Wrapped value is the result value.
|
/// Wrapped value is the result value.
|
||||||
Return(Dynamic, Position),
|
Return(Dynamic, Position),
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use crate::tokenizer::is_valid_identifier;
|
use crate::tokenizer::is_valid_identifier;
|
||||||
use crate::types::dynamic::Variant;
|
use crate::types::dynamic::Variant;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, Engine, FuncArgs, Identifier, Module, NativeCallContext, Position, RhaiError,
|
Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Position, RhaiError,
|
||||||
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -18,7 +18,7 @@ use std::{
|
|||||||
/// to be passed onto a function during a call.
|
/// to be passed onto a function during a call.
|
||||||
#[derive(Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct FnPtr {
|
pub struct FnPtr {
|
||||||
name: Identifier,
|
name: ImmutableString,
|
||||||
curry: StaticVec<Dynamic>,
|
curry: StaticVec<Dynamic>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,13 +42,16 @@ impl fmt::Debug for FnPtr {
|
|||||||
impl FnPtr {
|
impl FnPtr {
|
||||||
/// Create a new function pointer.
|
/// Create a new function pointer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(name: impl Into<Identifier>) -> RhaiResultOf<Self> {
|
pub fn new(name: impl Into<ImmutableString>) -> RhaiResultOf<Self> {
|
||||||
name.into().try_into()
|
name.into().try_into()
|
||||||
}
|
}
|
||||||
/// Create a new function pointer without checking its parameters.
|
/// Create a new function pointer without checking its parameters.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn new_unchecked(name: impl Into<Identifier>, curry: StaticVec<Dynamic>) -> Self {
|
pub(crate) fn new_unchecked(
|
||||||
|
name: impl Into<ImmutableString>,
|
||||||
|
curry: StaticVec<Dynamic>,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
curry,
|
curry,
|
||||||
@ -63,13 +66,13 @@ impl FnPtr {
|
|||||||
/// Get the name of the function.
|
/// Get the name of the function.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) const fn fn_name_raw(&self) -> &Identifier {
|
pub(crate) const fn fn_name_raw(&self) -> &ImmutableString {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
/// Get the underlying data of the function pointer.
|
/// Get the underlying data of the function pointer.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn take_data(self) -> (Identifier, StaticVec<Dynamic>) {
|
pub(crate) fn take_data(self) -> (ImmutableString, StaticVec<Dynamic>) {
|
||||||
(self.name, self.curry)
|
(self.name, self.curry)
|
||||||
}
|
}
|
||||||
/// Get the curried arguments.
|
/// Get the curried arguments.
|
||||||
@ -246,11 +249,11 @@ impl fmt::Display for FnPtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Identifier> for FnPtr {
|
impl TryFrom<ImmutableString> for FnPtr {
|
||||||
type Error = RhaiError;
|
type Error = RhaiError;
|
||||||
|
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
fn try_from(value: Identifier) -> RhaiResultOf<Self> {
|
fn try_from(value: ImmutableString) -> RhaiResultOf<Self> {
|
||||||
if is_valid_identifier(value.chars()) {
|
if is_valid_identifier(value.chars()) {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: value,
|
name: value,
|
||||||
@ -261,43 +264,3 @@ impl TryFrom<Identifier> for FnPtr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<crate::ImmutableString> for FnPtr {
|
|
||||||
type Error = RhaiError;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_from(value: crate::ImmutableString) -> RhaiResultOf<Self> {
|
|
||||||
let s: Identifier = value.into();
|
|
||||||
Self::try_from(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<String> for FnPtr {
|
|
||||||
type Error = RhaiError;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_from(value: String) -> RhaiResultOf<Self> {
|
|
||||||
let s: Identifier = value.into();
|
|
||||||
Self::try_from(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<Box<str>> for FnPtr {
|
|
||||||
type Error = RhaiError;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_from(value: Box<str>) -> RhaiResultOf<Self> {
|
|
||||||
let s: Identifier = value.into();
|
|
||||||
Self::try_from(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&str> for FnPtr {
|
|
||||||
type Error = RhaiError;
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_from(value: &str) -> RhaiResultOf<Self> {
|
|
||||||
let s: Identifier = value.into();
|
|
||||||
Self::try_from(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Module that defines the [`Scope`] type representing a function call-stack scope.
|
//! Module that defines the [`Scope`] type representing a function call-stack scope.
|
||||||
|
|
||||||
use super::dynamic::{AccessMode, Variant};
|
use super::dynamic::{AccessMode, Variant};
|
||||||
use crate::{Dynamic, Identifier};
|
use crate::{Dynamic, Identifier, ImmutableString};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -75,7 +75,7 @@ pub struct Scope<'a, const N: usize = SCOPE_ENTRIES_INLINED> {
|
|||||||
/// Name of the entry.
|
/// Name of the entry.
|
||||||
names: SmallVec<[Identifier; SCOPE_ENTRIES_INLINED]>,
|
names: SmallVec<[Identifier; SCOPE_ENTRIES_INLINED]>,
|
||||||
/// Aliases of the entry.
|
/// Aliases of the entry.
|
||||||
aliases: SmallVec<[Vec<Identifier>; SCOPE_ENTRIES_INLINED]>,
|
aliases: SmallVec<[Vec<ImmutableString>; SCOPE_ENTRIES_INLINED]>,
|
||||||
/// Phantom to keep the lifetime parameter in order not to break existing code.
|
/// Phantom to keep the lifetime parameter in order not to break existing code.
|
||||||
dummy: PhantomData<&'a ()>,
|
dummy: PhantomData<&'a ()>,
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ impl Clone for Scope<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IntoIterator for Scope<'_> {
|
impl IntoIterator for Scope<'_> {
|
||||||
type Item = (String, Dynamic, Vec<Identifier>);
|
type Item = (String, Dynamic, Vec<ImmutableString>);
|
||||||
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
type IntoIter = Box<dyn Iterator<Item = Self::Item>>;
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -140,7 +140,7 @@ impl IntoIterator for Scope<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a Scope<'_> {
|
impl<'a> IntoIterator for &'a Scope<'_> {
|
||||||
type Item = (&'a Identifier, &'a Dynamic, &'a Vec<Identifier>);
|
type Item = (&'a Identifier, &'a Dynamic, &'a Vec<ImmutableString>);
|
||||||
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -669,7 +669,7 @@ impl Scope<'_> {
|
|||||||
/// Panics if the index is out of bounds.
|
/// Panics if the index is out of bounds.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn add_alias_by_index(&mut self, index: usize, alias: Identifier) -> &mut Self {
|
pub(crate) fn add_alias_by_index(&mut self, index: usize, alias: ImmutableString) -> &mut Self {
|
||||||
let aliases = self.aliases.get_mut(index).unwrap();
|
let aliases = self.aliases.get_mut(index).unwrap();
|
||||||
if aliases.is_empty() || !aliases.contains(&alias) {
|
if aliases.is_empty() || !aliases.contains(&alias) {
|
||||||
aliases.push(alias);
|
aliases.push(alias);
|
||||||
@ -690,11 +690,11 @@ impl Scope<'_> {
|
|||||||
pub fn set_alias(
|
pub fn set_alias(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl AsRef<str> + Into<Identifier>,
|
name: impl AsRef<str> + Into<Identifier>,
|
||||||
alias: impl Into<Identifier>,
|
alias: impl Into<ImmutableString>,
|
||||||
) {
|
) {
|
||||||
if let Some(index) = self.search(name.as_ref()) {
|
if let Some(index) = self.search(name.as_ref()) {
|
||||||
let alias = match alias.into() {
|
let alias = match alias.into() {
|
||||||
x if x.is_empty() => name.into(),
|
x if x.is_empty() => name.into().into(),
|
||||||
x => x,
|
x => x,
|
||||||
};
|
};
|
||||||
self.add_alias_by_index(index, alias);
|
self.add_alias_by_index(index, alias);
|
||||||
@ -727,7 +727,9 @@ impl Scope<'_> {
|
|||||||
}
|
}
|
||||||
/// Get an iterator to entries in the [`Scope`].
|
/// Get an iterator to entries in the [`Scope`].
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Identifier, Dynamic, Vec<Identifier>)> {
|
pub(crate) fn into_iter(
|
||||||
|
self,
|
||||||
|
) -> impl Iterator<Item = (Identifier, Dynamic, Vec<ImmutableString>)> {
|
||||||
self.names
|
self.names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(self.values.into_iter().zip(self.aliases.into_iter()))
|
.zip(self.values.into_iter().zip(self.aliases.into_iter()))
|
||||||
|
@ -55,10 +55,13 @@ fn test_expressions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
)
|
)
|
||||||
.is_err());
|
.is_err());
|
||||||
|
|
||||||
assert!(engine.eval_expression::<()>("40 + 2;").is_err());
|
assert!(engine.compile_expression("40 + 2;").is_err());
|
||||||
assert!(engine.eval_expression::<()>("40 + { 2 }").is_err());
|
assert!(engine.compile_expression("40 + { 2 }").is_err());
|
||||||
assert!(engine.eval_expression::<()>("x = 42").is_err());
|
assert!(engine.compile_expression("x = 42").is_err());
|
||||||
assert!(engine.compile_expression("let x = 42").is_err());
|
assert!(engine.compile_expression("let x = 42").is_err());
|
||||||
|
assert!(engine
|
||||||
|
.compile_expression("do { break 42; } while true")
|
||||||
|
.is_err());
|
||||||
|
|
||||||
engine.compile("40 + { let x = 2; x }")?;
|
engine.compile("40 + { let x = 2; x }")?;
|
||||||
|
|
||||||
|
19
tests/for.rs
19
tests/for.rs
@ -231,6 +231,25 @@ fn test_for_loop() -> Result<(), Box<EvalAltResult>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
let a = [123, 999, 42, 0, true, "hello", "world!", 987.654];
|
||||||
|
|
||||||
|
for (item, count) in a {
|
||||||
|
switch item.type_of() {
|
||||||
|
"i64" | "i32" if item.is_even => break count,
|
||||||
|
"f64" | "f32" if item.to_int().is_even => break count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,7 +578,7 @@ fn test_module_context() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let new_context = NativeCallContext::new_with_all_fields(
|
let new_context = NativeCallContext::new_with_all_fields(
|
||||||
engine,
|
engine,
|
||||||
&fn_name,
|
&fn_name,
|
||||||
source.as_ref().map(|s| s.as_str()),
|
source.as_ref().map(String::as_str),
|
||||||
&global,
|
&global,
|
||||||
&lib,
|
&lib,
|
||||||
pos,
|
pos,
|
||||||
|
@ -89,21 +89,21 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{ast:?}"),
|
format!("{ast:?}"),
|
||||||
r#"AST { source: "", doc: "", resolver: None, body: [Expr(123 @ 1:53)] }"#
|
r#"AST { source: None, doc: "", resolver: None, body: [Expr(123 @ 1:53)] }"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
|
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{ast:?}"),
|
format!("{ast:?}"),
|
||||||
r#"AST { source: "", doc: "", resolver: None, body: [Var(("DECISION" @ 1:7, false @ 1:18, None), CONSTANT, 1:1), Expr(123 @ 1:51)] }"#
|
r#"AST { source: None, doc: "", resolver: None, body: [Var(("DECISION" @ 1:7, false @ 1:18, None), CONSTANT, 1:1), Expr(123 @ 1:51)] }"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{ast:?}"),
|
format!("{ast:?}"),
|
||||||
r#"AST { source: "", doc: "", resolver: None, body: [] }"#
|
r#"AST { source: None, doc: "", resolver: None, body: [] }"#
|
||||||
);
|
);
|
||||||
|
|
||||||
engine.set_optimization_level(OptimizationLevel::Full);
|
engine.set_optimization_level(OptimizationLevel::Full);
|
||||||
@ -112,14 +112,14 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{ast:?}"),
|
format!("{ast:?}"),
|
||||||
r#"AST { source: "", doc: "", resolver: None, body: [Expr(42 @ 1:1)] }"#
|
r#"AST { source: None, doc: "", resolver: None, body: [Expr(42 @ 1:1)] }"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let ast = engine.compile("NUMBER")?;
|
let ast = engine.compile("NUMBER")?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{ast:?}"),
|
format!("{ast:?}"),
|
||||||
r#"AST { source: "", doc: "", resolver: None, body: [Expr(Variable(NUMBER) @ 1:1)] }"#
|
r#"AST { source: None, doc: "", resolver: None, body: [Expr(Variable(NUMBER) @ 1:1)] }"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
@ -131,7 +131,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
format!("{ast:?}"),
|
format!("{ast:?}"),
|
||||||
r#"AST { source: "", doc: "", resolver: None, body: [Expr(42 @ 1:1)] }"#
|
r#"AST { source: None, doc: "", resolver: None, body: [Expr(42 @ 1:1)] }"#
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -22,6 +22,22 @@ fn test_while() -> Result<(), Box<EvalAltResult>> {
|
|||||||
6
|
6
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
|
while x < 10 {
|
||||||
|
x += 1;
|
||||||
|
if x > 5 { break x * 2; }
|
||||||
|
if x > 3 { continue; }
|
||||||
|
x += 3;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)?,
|
||||||
|
12
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +62,25 @@ fn test_do() -> Result<(), Box<EvalAltResult>> {
|
|||||||
)?,
|
)?,
|
||||||
6
|
6
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
x += 1;
|
||||||
|
if x > 5 { break x * 2; }
|
||||||
|
if x > 3 { continue; }
|
||||||
|
x += 3;
|
||||||
|
} while x < 10;
|
||||||
|
",
|
||||||
|
)?,
|
||||||
|
12
|
||||||
|
);
|
||||||
|
|
||||||
|
engine.run("do {} while false")?;
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>("do { break 42; } while false")?, 42);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user