commit
e590fe92fd
13
CHANGELOG.md
13
CHANGELOG.md
@ -14,6 +14,12 @@ Bug fixes
|
|||||||
* Capturing an unknown variable in a closure no longer panics.
|
* Capturing an unknown variable in a closure no longer panics.
|
||||||
* Fixes panic in interpolated strings with constant expressions.
|
* Fixes panic in interpolated strings with constant expressions.
|
||||||
* Using `call_fn_raw` on a function without evaluating the AST no longer panics on namespace-qualified function calls due to `import` statements not run.
|
* Using `call_fn_raw` on a function without evaluating the AST no longer panics on namespace-qualified function calls due to `import` statements not run.
|
||||||
|
* Some reserved tokens (such as "?", "++") cannot be used in custom syntax; this is now fixed.
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* The first closure passed to `Engine::register_debugger` now takes a single parameter which is a reference to the current `Engine`.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -44,6 +50,11 @@ New features
|
|||||||
|
|
||||||
* A new volatile API, `Engine::build_type`, enables registration of the entire API of a custom type in one go, provided that the custom type implements the `CustomType` trait (which uses `TypeBuilder` to register the API functions).
|
* A new volatile API, `Engine::build_type`, enables registration of the entire API of a custom type in one go, provided that the custom type implements the `CustomType` trait (which uses `TypeBuilder` to register the API functions).
|
||||||
|
|
||||||
|
### Simpler Package API
|
||||||
|
|
||||||
|
* It is now easier to register packages via the `Package::register_into_engine` and `Package::register_into_engine_as` API.
|
||||||
|
* Defining a custom package with base packages is also much easier with a new syntax - put the new base packages after a colon.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
@ -64,6 +75,8 @@ Enhancements
|
|||||||
* `Scope::remove` is added to remove a variable from a `Scope`, returning its value.
|
* `Scope::remove` is added to remove a variable from a `Scope`, returning its value.
|
||||||
* The code base is cleaner by running it through Clippy.
|
* The code base is cleaner by running it through Clippy.
|
||||||
* `ParseError::err_type` and `ParseError::position` are added for convenience.
|
* `ParseError::err_type` and `ParseError::position` are added for convenience.
|
||||||
|
* The source of an `AST` compiled from a script file is set to the file's path.
|
||||||
|
* `|>` and `<|` are now reserved symbols.
|
||||||
|
|
||||||
|
|
||||||
Version 1.8.0
|
Version 1.8.0
|
||||||
|
@ -60,7 +60,7 @@ unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for ident
|
|||||||
metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata
|
metadata = ["serde", "serde_json", "rhai_codegen/metadata", "smartstring/serde"] # enable exporting functions metadata
|
||||||
internals = [] # expose internal data structures
|
internals = [] # expose internal data structures
|
||||||
debugging = ["internals"] # enable debugging
|
debugging = ["internals"] # enable debugging
|
||||||
serde = ["dep:serde", "smartstring/serde"] # implement serde for rhai types
|
serde = ["dep:serde", "smartstring/serde", "smallvec/serde"] # implement serde for rhai types
|
||||||
|
|
||||||
# compiling for no-std
|
# compiling for no-std
|
||||||
no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
|
no_std = ["no-std-compat", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
|
||||||
|
@ -23,7 +23,7 @@ fn bench_engine_new_raw_core(bench: &mut Bencher) {
|
|||||||
|
|
||||||
bench.iter(|| {
|
bench.iter(|| {
|
||||||
let mut engine = Engine::new_raw();
|
let mut engine = Engine::new_raw();
|
||||||
engine.register_global_module(package.as_shared_module());
|
package.register_into_engine(&mut engine);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -9,7 +9,7 @@ use crate::{Engine, Module, Scope, INT};
|
|||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::type_name, borrow::Cow, cmp::Ordering, fmt, fmt::Write};
|
use std::{any::type_name, borrow::Cow, cmp::Ordering, fmt};
|
||||||
|
|
||||||
impl Engine {
|
impl Engine {
|
||||||
/// _(metadata, internals)_ Return [`Definitions`] that can be used to generate definition files
|
/// _(metadata, internals)_ Return [`Definitions`] that can be used to generate definition files
|
||||||
@ -186,6 +186,8 @@ impl Definitions<'_> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
for (module_name, module_def) in self.modules_impl(&config) {
|
for (module_name, module_def) in self.modules_impl(&config) {
|
||||||
write!(
|
write!(
|
||||||
&mut def_file,
|
&mut def_file,
|
||||||
@ -382,6 +384,7 @@ impl Definitions<'_> {
|
|||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
/// Return definitions for all items inside the [`Module`].
|
/// Return definitions for all items inside the [`Module`].
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
fn definition(&self, def: &Definitions) -> String {
|
fn definition(&self, def: &Definitions) -> String {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
self.write_definition(&mut s, def).unwrap();
|
self.write_definition(&mut s, def).unwrap();
|
||||||
|
@ -348,7 +348,7 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_debugger(
|
pub fn register_debugger(
|
||||||
&mut self,
|
&mut self,
|
||||||
init: impl Fn() -> Dynamic + SendSync + 'static,
|
init: impl Fn(&Engine) -> Dynamic + SendSync + 'static,
|
||||||
callback: impl Fn(
|
callback: impl Fn(
|
||||||
EvalContext,
|
EvalContext,
|
||||||
crate::eval::DebuggerEvent,
|
crate::eval::DebuggerEvent,
|
||||||
|
@ -106,7 +106,11 @@ impl Engine {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn compile_file_with_scope(&self, scope: &Scope, path: PathBuf) -> RhaiResultOf<AST> {
|
pub fn compile_file_with_scope(&self, scope: &Scope, path: PathBuf) -> RhaiResultOf<AST> {
|
||||||
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
|
Self::read_file(&path).and_then(|contents| {
|
||||||
|
let mut ast = self.compile_with_scope(scope, &contents)?;
|
||||||
|
ast.set_source(path.to_string_lossy());
|
||||||
|
Ok(ast)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
/// Evaluate a script file, returning the result value or an error.
|
/// Evaluate a script file, returning the result value or an error.
|
||||||
///
|
///
|
||||||
|
@ -213,6 +213,7 @@ impl AST {
|
|||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn doc_mut(&mut self) -> &mut crate::SmartString {
|
pub(crate) fn doc_mut(&mut self) -> &mut crate::SmartString {
|
||||||
&mut self.doc
|
&mut self.doc
|
||||||
}
|
}
|
||||||
|
@ -362,6 +362,8 @@ pub enum Expr {
|
|||||||
///
|
///
|
||||||
/// Used to hold complex constants such as [`Array`][crate::Array] or [`Map`][crate::Map] for quick cloning.
|
/// Used to hold complex constants such as [`Array`][crate::Array] or [`Map`][crate::Map] for quick cloning.
|
||||||
/// Primitive data types should use the appropriate variants to avoid an allocation.
|
/// Primitive data types should use the appropriate variants to avoid an allocation.
|
||||||
|
///
|
||||||
|
/// The [`Dynamic`] value is boxed in order to avoid bloating the size of [`Expr`].
|
||||||
DynamicConstant(Box<Dynamic>, Position),
|
DynamicConstant(Box<Dynamic>, Position),
|
||||||
/// Boolean constant.
|
/// Boolean constant.
|
||||||
BoolConstant(bool, Position),
|
BoolConstant(bool, Position),
|
||||||
|
@ -6,6 +6,8 @@ use std::prelude::v1::*;
|
|||||||
|
|
||||||
/// A type representing the access mode of a function.
|
/// A type representing the access mode of a function.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
#[cfg_attr(feature = "metadata", derive(serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "metadata", serde(rename_all = "camelCase"))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum FnAccess {
|
pub enum FnAccess {
|
||||||
/// Private function.
|
/// Private function.
|
||||||
|
@ -216,7 +216,7 @@ impl IntoIterator for RangeCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RangeCase {
|
impl RangeCase {
|
||||||
/// Is the range empty?
|
/// Returns `true` if the range contains no items.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
@ -356,7 +356,7 @@ impl StmtBlock {
|
|||||||
span: Span::new(pos, pos),
|
span: Span::new(pos, pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this statements block empty?
|
/// Returns `true` if this statements block contains no statements.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
|
@ -611,7 +611,7 @@ fn main() {
|
|||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
engine.register_debugger(
|
engine.register_debugger(
|
||||||
// Store the current source in the debugger state
|
// Store the current source in the debugger state
|
||||||
|| "".into(),
|
|_| "".into(),
|
||||||
// Main debugging interface
|
// Main debugging interface
|
||||||
move |context, event, node, source, pos| {
|
move |context, event, node, source, pos| {
|
||||||
debug_callback(context, event, node, source, pos, &lines)
|
debug_callback(context, event, node, source, pos, &lines)
|
||||||
|
@ -14,11 +14,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{collections::BTreeSet, fmt, num::NonZeroU8};
|
||||||
collections::{BTreeMap, BTreeSet},
|
|
||||||
fmt,
|
|
||||||
num::NonZeroU8,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub type Precedence = NonZeroU8;
|
pub type Precedence = NonZeroU8;
|
||||||
|
|
||||||
@ -99,7 +95,7 @@ pub struct Engine {
|
|||||||
pub(crate) global_modules: StaticVec<Shared<Module>>,
|
pub(crate) global_modules: StaticVec<Shared<Module>>,
|
||||||
/// A collection of all sub-modules directly loaded into the Engine.
|
/// A collection of all sub-modules directly loaded into the Engine.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub(crate) global_sub_modules: BTreeMap<Identifier, Shared<Module>>,
|
pub(crate) global_sub_modules: std::collections::BTreeMap<Identifier, Shared<Module>>,
|
||||||
|
|
||||||
/// A module resolution service.
|
/// A module resolution service.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -112,10 +108,11 @@ pub struct Engine {
|
|||||||
pub(crate) disabled_symbols: BTreeSet<Identifier>,
|
pub(crate) disabled_symbols: BTreeSet<Identifier>,
|
||||||
/// A map containing custom keywords and precedence to recognize.
|
/// A map containing custom keywords and precedence to recognize.
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
pub(crate) custom_keywords: BTreeMap<Identifier, Option<Precedence>>,
|
pub(crate) custom_keywords: std::collections::BTreeMap<Identifier, Option<Precedence>>,
|
||||||
/// Custom syntax.
|
/// Custom syntax.
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
pub(crate) custom_syntax: BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>,
|
pub(crate) custom_syntax:
|
||||||
|
std::collections::BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>,
|
||||||
/// Callback closure for filtering variable definition.
|
/// Callback closure for filtering variable definition.
|
||||||
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
|
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
|
||||||
/// Callback closure for resolving variable access.
|
/// Callback closure for resolving variable access.
|
||||||
@ -265,7 +262,7 @@ impl Engine {
|
|||||||
global_modules: StaticVec::new_const(),
|
global_modules: StaticVec::new_const(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
global_sub_modules: BTreeMap::new(),
|
global_sub_modules: std::collections::BTreeMap::new(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
|
module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
|
||||||
@ -273,9 +270,9 @@ impl Engine {
|
|||||||
interned_strings: StringsInterner::new().into(),
|
interned_strings: StringsInterner::new().into(),
|
||||||
disabled_symbols: BTreeSet::new(),
|
disabled_symbols: BTreeSet::new(),
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
custom_keywords: BTreeMap::new(),
|
custom_keywords: std::collections::BTreeMap::new(),
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
custom_syntax: BTreeMap::new(),
|
custom_syntax: std::collections::BTreeMap::new(),
|
||||||
|
|
||||||
def_var_filter: None,
|
def_var_filter: None,
|
||||||
resolve_var: None,
|
resolve_var: None,
|
||||||
@ -311,9 +308,10 @@ impl Engine {
|
|||||||
engine
|
engine
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an interned string.
|
/// Get an interned [string][ImmutableString].
|
||||||
#[must_use]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
pub(crate) fn get_interned_string(
|
pub(crate) fn get_interned_string(
|
||||||
&self,
|
&self,
|
||||||
string: impl AsRef<str> + Into<ImmutableString>,
|
string: impl AsRef<str> + Into<ImmutableString>,
|
||||||
@ -321,6 +319,28 @@ impl Engine {
|
|||||||
locked_write(&self.interned_strings).get(string).into()
|
locked_write(&self.interned_strings).get(string).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// _(internals)_ Get an interned [string][ImmutableString].
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// [`Engine`] keeps a cache of [`ImmutableString`] instances and tries to avoid new allocations
|
||||||
|
/// when an existing instance is found.
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_interned_string(
|
||||||
|
&self,
|
||||||
|
string: impl AsRef<str> + Into<ImmutableString>,
|
||||||
|
) -> ImmutableString {
|
||||||
|
locked_write(&self.interned_strings).get(string).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an empty [`ImmutableString`] which refers to a shared instance.
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub fn const_empty_string(&self) -> ImmutableString {
|
||||||
|
self.get_interned_string("")
|
||||||
|
}
|
||||||
|
|
||||||
/// Check a result to ensure that it is valid.
|
/// Check a result to ensure that it is valid.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult {
|
pub(crate) fn check_return_value(&self, result: RhaiResult, _pos: Position) -> RhaiResult {
|
||||||
|
@ -10,10 +10,10 @@ use std::{fmt, iter::repeat, mem};
|
|||||||
|
|
||||||
/// Callback function to initialize the debugger.
|
/// Callback function to initialize the debugger.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnDebuggingInit = dyn Fn() -> Dynamic;
|
pub type OnDebuggingInit = dyn Fn(&Engine) -> Dynamic;
|
||||||
/// Callback function to initialize the debugger.
|
/// Callback function to initialize the debugger.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnDebuggingInit = dyn Fn() -> Dynamic + Send + Sync;
|
pub type OnDebuggingInit = dyn Fn(&Engine) -> Dynamic + Send + Sync;
|
||||||
|
|
||||||
/// Callback function for debugging.
|
/// Callback function for debugging.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
|
@ -6,6 +6,7 @@ use crate::{Dynamic, Engine, Module, Scope};
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
/// Context of a script evaluation process.
|
/// Context of a script evaluation process.
|
||||||
|
#[derive(Debug)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct EvalContext<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> {
|
pub struct EvalContext<'a, 's, 'ps, 'g, 'pg, 'c, 'pc, 't, 'pt> {
|
||||||
/// The current [`Engine`].
|
/// The current [`Engine`].
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Global runtime state.
|
//! Global runtime state.
|
||||||
|
|
||||||
use crate::{Dynamic, Engine, Identifier, ImmutableString};
|
use crate::{Dynamic, Engine, Identifier};
|
||||||
#[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<ImmutableString, Dynamic>>>;
|
crate::Shared<crate::Locked<std::collections::BTreeMap<crate::ImmutableString, Dynamic>>>;
|
||||||
|
|
||||||
/// _(internals)_ Global runtime states.
|
/// _(internals)_ Global runtime states.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -25,7 +25,7 @@ pub type GlobalConstants =
|
|||||||
pub struct GlobalRuntimeState<'a> {
|
pub struct GlobalRuntimeState<'a> {
|
||||||
/// Stack of module names.
|
/// Stack of module names.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
keys: crate::StaticVec<ImmutableString>,
|
keys: crate::StaticVec<crate::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>>,
|
||||||
@ -106,7 +106,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
crate::eval::DebuggerStatus::CONTINUE
|
crate::eval::DebuggerStatus::CONTINUE
|
||||||
},
|
},
|
||||||
if let Some((ref init, ..)) = engine.debugger {
|
if let Some((ref init, ..)) = engine.debugger {
|
||||||
init()
|
init(engine)
|
||||||
} else {
|
} else {
|
||||||
Dynamic::UNIT
|
Dynamic::UNIT
|
||||||
},
|
},
|
||||||
@ -169,7 +169,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_import(
|
pub fn push_import(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<ImmutableString>,
|
name: impl Into<crate::ImmutableString>,
|
||||||
module: impl Into<crate::Shared<crate::Module>>,
|
module: impl Into<crate::Shared<crate::Module>>,
|
||||||
) {
|
) {
|
||||||
self.keys.push(name.into());
|
self.keys.push(name.into());
|
||||||
@ -205,7 +205,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn iter_imports_raw(
|
pub(crate) fn iter_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&ImmutableString, &crate::Shared<crate::Module>)> {
|
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::Shared<crate::Module>)> {
|
||||||
self.keys.iter().rev().zip(self.modules.iter().rev())
|
self.keys.iter().rev().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.
|
||||||
@ -216,7 +216,7 @@ impl GlobalRuntimeState<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn scan_imports_raw(
|
pub fn scan_imports_raw(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (&ImmutableString, &crate::Shared<crate::Module>)> {
|
) -> impl Iterator<Item = (&crate::ImmutableString, &crate::Shared<crate::Module>)> {
|
||||||
self.keys.iter().zip(self.modules.iter())
|
self.keys.iter().zip(self.modules.iter())
|
||||||
}
|
}
|
||||||
/// Does the specified function hash key exist in the stack of globally-imported
|
/// Does the specified function hash key exist in the stack of globally-imported
|
||||||
@ -310,9 +310,9 @@ impl GlobalRuntimeState<'_> {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
impl IntoIterator for GlobalRuntimeState<'_> {
|
impl IntoIterator for GlobalRuntimeState<'_> {
|
||||||
type Item = (ImmutableString, crate::Shared<crate::Module>);
|
type Item = (crate::ImmutableString, crate::Shared<crate::Module>);
|
||||||
type IntoIter = std::iter::Zip<
|
type IntoIter = std::iter::Zip<
|
||||||
std::iter::Rev<smallvec::IntoIter<[ImmutableString; 3]>>,
|
std::iter::Rev<smallvec::IntoIter<[crate::ImmutableString; 3]>>,
|
||||||
std::iter::Rev<smallvec::IntoIter<[crate::Shared<crate::Module>; 3]>>,
|
std::iter::Rev<smallvec::IntoIter<[crate::Shared<crate::Module>; 3]>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@ -327,9 +327,9 @@ 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 ImmutableString, &'a crate::Shared<crate::Module>);
|
type Item = (&'a crate::ImmutableString, &'a crate::Shared<crate::Module>);
|
||||||
type IntoIter = std::iter::Zip<
|
type IntoIter = std::iter::Zip<
|
||||||
std::iter::Rev<std::slice::Iter<'a, ImmutableString>>,
|
std::iter::Rev<std::slice::Iter<'a, crate::ImmutableString>>,
|
||||||
std::iter::Rev<std::slice::Iter<'a, crate::Shared<crate::Module>>>,
|
std::iter::Rev<std::slice::Iter<'a, crate::Shared<crate::Module>>>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@ -341,7 +341,7 @@ impl<'a> IntoIterator for &'a GlobalRuntimeState<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
impl<K: Into<ImmutableString>, M: Into<crate::Shared<crate::Module>>> Extend<(K, M)>
|
impl<K: Into<crate::ImmutableString>, M: Into<crate::Shared<crate::Module>>> Extend<(K, M)>
|
||||||
for GlobalRuntimeState<'_>
|
for GlobalRuntimeState<'_>
|
||||||
{
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -22,6 +22,8 @@ use std::{
|
|||||||
|
|
||||||
/// A type representing the namespace of a function.
|
/// A type representing the namespace of a function.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
|
#[cfg_attr(feature = "metadata", derive(serde::Serialize))]
|
||||||
|
#[cfg_attr(feature = "metadata", serde(rename_all = "camelCase"))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum FnNamespace {
|
pub enum FnNamespace {
|
||||||
/// Module namespace only.
|
/// Module namespace only.
|
||||||
@ -589,7 +591,7 @@ impl Module {
|
|||||||
self.custom_types.get(key)
|
self.custom_types.get(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the [`Module`] empty?
|
/// Returns `true` if this [`Module`] contains no items.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -86,7 +86,7 @@ impl ModuleResolversCollection {
|
|||||||
self.0.clear();
|
self.0.clear();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Is this [`ModuleResolversCollection`] empty?
|
/// Returns `true` if this [`ModuleResolversCollection`] contains no module resolvers.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
|
@ -98,7 +98,7 @@ impl StaticModuleResolver {
|
|||||||
self.0.clear();
|
self.0.clear();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Is this [`StaticModuleResolver`] empty?
|
/// Returns `true` if this [`StaticModuleResolver`] contains no module resolvers.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
|
@ -26,6 +26,7 @@ where
|
|||||||
x.checked_add(&y)
|
x.checked_add(&y)
|
||||||
}
|
}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
#[allow(dead_code)]
|
||||||
fn regular_add<T>(x: T, y: T) -> Option<T>
|
fn regular_add<T>(x: T, y: T) -> Option<T>
|
||||||
where
|
where
|
||||||
T: Debug + Copy + PartialOrd + std::ops::Add<Output = T>,
|
T: Debug + Copy + PartialOrd + std::ops::Add<Output = T>,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::types::{dynamic::Tag, StringsInterner};
|
use crate::types::dynamic::Tag;
|
||||||
use crate::{Dynamic, RhaiResultOf, ERR, INT};
|
use crate::{Dynamic, RhaiResultOf, ERR, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -129,12 +129,12 @@ fn collect_fn_metadata(
|
|||||||
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFnDef>) -> bool
|
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFnDef>) -> bool
|
||||||
+ Copy,
|
+ Copy,
|
||||||
) -> crate::Array {
|
) -> crate::Array {
|
||||||
use crate::{ast::ScriptFnDef, Array, Identifier, Map};
|
use crate::{ast::ScriptFnDef, Array, Map};
|
||||||
|
|
||||||
// Create a metadata record for a function.
|
// Create a metadata record for a function.
|
||||||
fn make_metadata(
|
fn make_metadata(
|
||||||
dict: &mut StringsInterner,
|
dict: &mut crate::types::StringsInterner,
|
||||||
#[cfg(not(feature = "no_module"))] namespace: Identifier,
|
#[cfg(not(feature = "no_module"))] namespace: crate::Identifier,
|
||||||
func: &ScriptFnDef,
|
func: &ScriptFnDef,
|
||||||
) -> Map {
|
) -> Map {
|
||||||
let mut map = Map::new();
|
let mut map = Map::new();
|
||||||
@ -179,7 +179,7 @@ fn collect_fn_metadata(
|
|||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
let dict = &mut StringsInterner::new();
|
let dict = &mut crate::types::StringsInterner::new();
|
||||||
let mut list = Array::new();
|
let mut list = Array::new();
|
||||||
|
|
||||||
ctx.iter_namespaces()
|
ctx.iter_namespaces()
|
||||||
@ -190,7 +190,7 @@ fn collect_fn_metadata(
|
|||||||
make_metadata(
|
make_metadata(
|
||||||
dict,
|
dict,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Identifier::new_const(),
|
crate::Identifier::new_const(),
|
||||||
f,
|
f,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@ -207,7 +207,7 @@ fn collect_fn_metadata(
|
|||||||
make_metadata(
|
make_metadata(
|
||||||
dict,
|
dict,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Identifier::new_const(),
|
crate::Identifier::new_const(),
|
||||||
f,
|
f,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@ -225,7 +225,7 @@ fn collect_fn_metadata(
|
|||||||
make_metadata(
|
make_metadata(
|
||||||
dict,
|
dict,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Identifier::new_const(),
|
crate::Identifier::new_const(),
|
||||||
f,
|
f,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
@ -236,7 +236,7 @@ fn collect_fn_metadata(
|
|||||||
{
|
{
|
||||||
// Recursively scan modules for script-defined functions.
|
// Recursively scan modules for script-defined functions.
|
||||||
fn scan_module(
|
fn scan_module(
|
||||||
dict: &mut StringsInterner,
|
dict: &mut crate::types::StringsInterner,
|
||||||
list: &mut Array,
|
list: &mut Array,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages.
|
//! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages.
|
||||||
|
|
||||||
use crate::{Module, Shared};
|
use crate::{Engine, Module, Shared};
|
||||||
|
|
||||||
pub(crate) mod arithmetic;
|
pub(crate) mod arithmetic;
|
||||||
pub(crate) mod array_basic;
|
pub(crate) mod array_basic;
|
||||||
@ -47,6 +47,49 @@ pub trait Package {
|
|||||||
/// Functions should be registered into `module` here.
|
/// Functions should be registered into `module` here.
|
||||||
fn init(module: &mut Module);
|
fn init(module: &mut Module);
|
||||||
|
|
||||||
|
/// Initialize the package with an [`Engine`].
|
||||||
|
///
|
||||||
|
/// Perform tasks such as registering custom operators/syntax.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn init_engine(engine: &mut Engine) {}
|
||||||
|
|
||||||
|
/// Register the package with an [`Engine`].
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use rhai::Engine;
|
||||||
|
/// # use rhai::packages::{Package, CorePackage};
|
||||||
|
/// let mut engine = Engine::new_raw();
|
||||||
|
/// let package = CorePackage::new();
|
||||||
|
///
|
||||||
|
/// package.register_into_engine(&mut engine);
|
||||||
|
/// ```
|
||||||
|
fn register_into_engine(&self, engine: &mut Engine) -> &Self {
|
||||||
|
Self::init_engine(engine);
|
||||||
|
engine.register_global_module(self.as_shared_module());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register the package with an [`Engine`] under a static namespace.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use rhai::Engine;
|
||||||
|
/// # use rhai::packages::{Package, CorePackage};
|
||||||
|
/// let mut engine = Engine::new_raw();
|
||||||
|
/// let package = CorePackage::new();
|
||||||
|
///
|
||||||
|
/// package.register_into_engine_as(&mut engine, "core");
|
||||||
|
/// ```
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
fn register_into_engine_as(&self, engine: &mut Engine, name: &str) -> &Self {
|
||||||
|
Self::init_engine(engine);
|
||||||
|
engine.register_static_module(name, self.as_shared_module());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a reference to a shared module from this package.
|
/// Get a reference to a shared module from this package.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn as_shared_module(&self) -> Shared<Module>;
|
fn as_shared_module(&self) -> Shared<Module>;
|
||||||
@ -70,27 +113,49 @@ pub trait Package {
|
|||||||
/// def_package! {
|
/// def_package! {
|
||||||
/// /// My super-duper package.
|
/// /// My super-duper package.
|
||||||
/// pub MyPackage(module) {
|
/// pub MyPackage(module) {
|
||||||
/// // Load a binary function with all value parameters.
|
/// // Load a native Rust function.
|
||||||
/// module.set_native_fn("my_add", add);
|
/// module.set_native_fn("my_add", add);
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! def_package {
|
macro_rules! def_package {
|
||||||
($($(#[$outer:meta])* $mod:vis $package:ident($lib:ident) $block:block)+) => { $(
|
($($(#[$outer:meta])* $mod:vis $package:ident($lib:ident)
|
||||||
|
$( : $($(#[$base_meta:meta])* $base_pkg:ty),+ )?
|
||||||
|
$block:block
|
||||||
|
$( |> | $engine:ident | $init_engine:block )?
|
||||||
|
)+) => { $(
|
||||||
$(#[$outer])*
|
$(#[$outer])*
|
||||||
$mod struct $package($crate::Shared<$crate::Module>);
|
$mod struct $package($crate::Shared<$crate::Module>);
|
||||||
|
|
||||||
impl $crate::packages::Package for $package {
|
impl $crate::packages::Package for $package {
|
||||||
|
#[inline(always)]
|
||||||
fn as_shared_module(&self) -> $crate::Shared<$crate::Module> {
|
fn as_shared_module(&self) -> $crate::Shared<$crate::Module> {
|
||||||
self.0.clone()
|
self.0.clone()
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
fn init($lib: &mut $crate::Module) {
|
fn init($lib: &mut $crate::Module) {
|
||||||
|
$($(
|
||||||
|
$(#[$base_meta])* { <$base_pkg>::init($lib); }
|
||||||
|
)*)*
|
||||||
|
|
||||||
$block
|
$block
|
||||||
}
|
}
|
||||||
|
#[inline]
|
||||||
|
fn init_engine(_engine: &mut $crate::Engine) {
|
||||||
|
$($(
|
||||||
|
$(#[$base_meta])* { <$base_pkg>::init_engine(_engine); }
|
||||||
|
)*)*
|
||||||
|
|
||||||
|
$(
|
||||||
|
let $engine = _engine;
|
||||||
|
$init_engine
|
||||||
|
)*
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for $package {
|
impl Default for $package {
|
||||||
|
#[inline(always)]
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
@ -98,6 +163,7 @@ macro_rules! def_package {
|
|||||||
|
|
||||||
impl $package {
|
impl $package {
|
||||||
#[doc=concat!("Create a new `", stringify!($package), "`")]
|
#[doc=concat!("Create a new `", stringify!($package), "`")]
|
||||||
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut module = $crate::Module::new();
|
let mut module = $crate::Module::new();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
|
|
||||||
def_package! {
|
def_package! {
|
||||||
@ -14,15 +15,14 @@ def_package! {
|
|||||||
/// * [`BasicIteratorPackage`][super::BasicIteratorPackage]
|
/// * [`BasicIteratorPackage`][super::BasicIteratorPackage]
|
||||||
/// * [`BasicFnPackage`][super::BasicFnPackage]
|
/// * [`BasicFnPackage`][super::BasicFnPackage]
|
||||||
/// * [`DebuggingPackage`][super::DebuggingPackage]
|
/// * [`DebuggingPackage`][super::DebuggingPackage]
|
||||||
pub CorePackage(lib) {
|
pub CorePackage(lib) :
|
||||||
|
LanguageCorePackage,
|
||||||
|
ArithmeticPackage,
|
||||||
|
BasicStringPackage,
|
||||||
|
BasicIteratorPackage,
|
||||||
|
BasicFnPackage,
|
||||||
|
#[cfg(feature = "debugging")] DebuggingPackage
|
||||||
|
{
|
||||||
lib.standard = true;
|
lib.standard = true;
|
||||||
|
|
||||||
super::LanguageCorePackage::init(lib);
|
|
||||||
super::ArithmeticPackage::init(lib);
|
|
||||||
super::BasicStringPackage::init(lib);
|
|
||||||
super::BasicIteratorPackage::init(lib);
|
|
||||||
super::BasicFnPackage::init(lib);
|
|
||||||
#[cfg(feature = "debugging")]
|
|
||||||
super::DebuggingPackage::init(lib);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
|
|
||||||
def_package! {
|
def_package! {
|
||||||
@ -17,21 +18,17 @@ def_package! {
|
|||||||
/// * [`BasicMapPackage`][super::BasicMapPackage]
|
/// * [`BasicMapPackage`][super::BasicMapPackage]
|
||||||
/// * [`BasicTimePackage`][super::BasicTimePackage]
|
/// * [`BasicTimePackage`][super::BasicTimePackage]
|
||||||
/// * [`MoreStringPackage`][super::MoreStringPackage]
|
/// * [`MoreStringPackage`][super::MoreStringPackage]
|
||||||
pub StandardPackage(lib) {
|
pub StandardPackage(lib) :
|
||||||
|
CorePackage,
|
||||||
|
BitFieldPackage,
|
||||||
|
LogicPackage,
|
||||||
|
BasicMathPackage,
|
||||||
|
#[cfg(not(feature = "no_index"))] BasicArrayPackage,
|
||||||
|
#[cfg(not(feature = "no_index"))] BasicBlobPackage,
|
||||||
|
#[cfg(not(feature = "no_object"))] BasicMapPackage,
|
||||||
|
#[cfg(not(feature = "no_std"))] BasicTimePackage,
|
||||||
|
MoreStringPackage
|
||||||
|
{
|
||||||
lib.standard = true;
|
lib.standard = true;
|
||||||
|
|
||||||
super::CorePackage::init(lib);
|
|
||||||
super::BitFieldPackage::init(lib);
|
|
||||||
super::LogicPackage::init(lib);
|
|
||||||
super::BasicMathPackage::init(lib);
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
super::BasicArrayPackage::init(lib);
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
super::BasicBlobPackage::init(lib);
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
super::BasicMapPackage::init(lib);
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
|
||||||
super::BasicTimePackage::init(lib);
|
|
||||||
super::MoreStringPackage::init(lib);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,22 +43,17 @@ const NEVER_ENDS: &str = "`Token`";
|
|||||||
/// Unroll `switch` ranges no larger than this.
|
/// Unroll `switch` ranges no larger than this.
|
||||||
const SMALL_SWITCH_RANGE: usize = 16;
|
const SMALL_SWITCH_RANGE: usize = 16;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
const NUM_INTERNERS: usize = if cfg!(feature = "no_object") { 1 } else { 3 };
|
||||||
pub struct InternedStrings<'e> {
|
|
||||||
pub main: StringsInterner<'e>,
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
pub getters: StringsInterner<'e>,
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
pub setters: StringsInterner<'e>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// _(internals)_ A type that encapsulates the current state of the parser.
|
/// _(internals)_ A type that encapsulates the current state of the parser.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
pub struct ParseState<'e> {
|
pub struct ParseState<'e> {
|
||||||
/// Input stream buffer containing the next character to read.
|
/// Input stream buffer containing the next character to read.
|
||||||
pub tokenizer_control: TokenizerControl,
|
pub tokenizer_control: TokenizerControl,
|
||||||
|
/// Controls whether parsing of an expression should stop given the next token.
|
||||||
|
pub expr_filter: fn(&Token) -> bool,
|
||||||
/// String interners.
|
/// String interners.
|
||||||
interned_strings: InternedStrings<'e>,
|
interned_strings: [StringsInterner<'e>; NUM_INTERNERS],
|
||||||
/// External [scope][Scope] with constants.
|
/// External [scope][Scope] with constants.
|
||||||
pub scope: &'e Scope<'e>,
|
pub scope: &'e Scope<'e>,
|
||||||
/// Global runtime state.
|
/// Global runtime state.
|
||||||
@ -67,8 +62,6 @@ pub struct ParseState<'e> {
|
|||||||
pub stack: Scope<'e>,
|
pub stack: Scope<'e>,
|
||||||
/// Size of the local variables stack upon entry of the current block scope.
|
/// Size of the local variables stack upon entry of the current block scope.
|
||||||
pub block_stack_len: usize,
|
pub block_stack_len: usize,
|
||||||
/// Controls whether parsing of an expression should stop given the next token.
|
|
||||||
pub expr_filter: fn(&Token) -> bool,
|
|
||||||
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
pub external_vars: Vec<crate::ast::Ident>,
|
pub external_vars: Vec<crate::ast::Ident>,
|
||||||
@ -118,7 +111,7 @@ impl<'e> ParseState<'e> {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &Engine,
|
engine: &Engine,
|
||||||
scope: &'e Scope,
|
scope: &'e Scope,
|
||||||
interners: InternedStrings<'e>,
|
interned_strings: [StringsInterner<'e>; NUM_INTERNERS],
|
||||||
tokenizer_control: TokenizerControl,
|
tokenizer_control: TokenizerControl,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -128,7 +121,7 @@ impl<'e> ParseState<'e> {
|
|||||||
external_vars: Vec::new(),
|
external_vars: Vec::new(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
allow_capture: true,
|
allow_capture: true,
|
||||||
interned_strings: interners,
|
interned_strings,
|
||||||
scope,
|
scope,
|
||||||
global: GlobalRuntimeState::new(engine),
|
global: GlobalRuntimeState::new(engine),
|
||||||
stack: Scope::new(),
|
stack: Scope::new(),
|
||||||
@ -194,12 +187,13 @@ impl<'e> ParseState<'e> {
|
|||||||
lib: &FnLib,
|
lib: &FnLib,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> (Option<NonZeroUsize>, bool) {
|
) -> (Option<NonZeroUsize>, bool) {
|
||||||
|
let _lib = lib;
|
||||||
let _pos = pos;
|
let _pos = pos;
|
||||||
|
|
||||||
let (index, hit_barrier) = self.find_var(name);
|
let (index, hit_barrier) = self.find_var(name);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
let is_func_name = lib.values().any(|f| f.name == name);
|
let is_func_name = _lib.values().any(|f| f.name == name);
|
||||||
|
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
let is_func_name = false;
|
let is_func_name = false;
|
||||||
@ -250,40 +244,35 @@ impl<'e> ParseState<'e> {
|
|||||||
|
|
||||||
/// Get an interned string, creating one if it is not yet interned.
|
/// Get an interned string, creating one if it is not yet interned.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_interned_string(
|
pub fn get_interned_string(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
self.interned_strings.main.get(text)
|
self.interned_strings[0].get(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an interned property getter, creating one if it is not yet interned.
|
/// Get an interned property getter, creating one if it is not yet interned.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_interned_getter(
|
pub fn get_interned_getter(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
self.interned_strings
|
self.interned_strings[1]
|
||||||
.getters
|
|
||||||
.get_with_mapper(|s| crate::engine::make_getter(s.as_ref()).into(), text)
|
.get_with_mapper(|s| crate::engine::make_getter(s.as_ref()).into(), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an interned property setter, creating one if it is not yet interned.
|
/// Get an interned property setter, creating one if it is not yet interned.
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[allow(dead_code)]
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_interned_setter(
|
pub fn get_interned_setter(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
self.interned_strings
|
self.interned_strings[2]
|
||||||
.setters
|
|
||||||
.get_with_mapper(|s| crate::engine::make_setter(s.as_ref()).into(), text)
|
.get_with_mapper(|s| crate::engine::make_setter(s.as_ref()).into(), text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1384,12 +1373,13 @@ impl Engine {
|
|||||||
// | ...
|
// | ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Token::Pipe | Token::Or if settings.options.contains(LangOptions::ANON_FN) => {
|
Token::Pipe | Token::Or if settings.options.contains(LangOptions::ANON_FN) => {
|
||||||
let interners = std::mem::take(&mut state.interned_strings);
|
// Build new parse state
|
||||||
|
let interned_strings = std::mem::take(&mut state.interned_strings);
|
||||||
|
|
||||||
let mut new_state = ParseState::new(
|
let mut new_state = ParseState::new(
|
||||||
self,
|
self,
|
||||||
state.scope,
|
state.scope,
|
||||||
interners,
|
interned_strings,
|
||||||
state.tokenizer_control.clone(),
|
state.tokenizer_control.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1435,6 +1425,7 @@ impl Engine {
|
|||||||
|
|
||||||
let result = self.parse_anon_fn(input, &mut new_state, lib, new_settings);
|
let result = self.parse_anon_fn(input, &mut new_state, lib, new_settings);
|
||||||
|
|
||||||
|
// Restore parse state
|
||||||
state.interned_strings = new_state.interned_strings;
|
state.interned_strings = new_state.interned_strings;
|
||||||
|
|
||||||
let (expr, func) = result?;
|
let (expr, func) = result?;
|
||||||
@ -3259,12 +3250,13 @@ impl Engine {
|
|||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::Fn, pos) => {
|
(Token::Fn, pos) => {
|
||||||
let interners = std::mem::take(&mut state.interned_strings);
|
// Build new parse state
|
||||||
|
let interned_strings = std::mem::take(&mut state.interned_strings);
|
||||||
|
|
||||||
let mut new_state = ParseState::new(
|
let mut new_state = ParseState::new(
|
||||||
self,
|
self,
|
||||||
state.scope,
|
state.scope,
|
||||||
interners,
|
interned_strings,
|
||||||
state.tokenizer_control.clone(),
|
state.tokenizer_control.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -3314,6 +3306,7 @@ impl Engine {
|
|||||||
comments,
|
comments,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Restore parse state
|
||||||
state.interned_strings = new_state.interned_strings;
|
state.interned_strings = new_state.interned_strings;
|
||||||
|
|
||||||
let func = func?;
|
let func = func?;
|
||||||
|
@ -2,54 +2,20 @@
|
|||||||
#![cfg(feature = "metadata")]
|
#![cfg(feature = "metadata")]
|
||||||
|
|
||||||
use crate::module::{calc_native_fn_hash, FuncInfo};
|
use crate::module::{calc_native_fn_hash, FuncInfo};
|
||||||
use crate::{calc_fn_hash, Engine, SmartString, AST};
|
use crate::{calc_fn_hash, Engine, FnAccess, SmartString, StaticVec, AST};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Serialize;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{borrow::Cow, cmp::Ordering, collections::BTreeMap};
|
use std::{borrow::Cow, cmp::Ordering, collections::BTreeMap};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
enum FnType {
|
enum FnType {
|
||||||
Script,
|
Script,
|
||||||
Native,
|
Native,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize)]
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
enum FnNamespace {
|
|
||||||
Global,
|
|
||||||
Internal,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
impl From<crate::FnNamespace> for FnNamespace {
|
|
||||||
fn from(value: crate::FnNamespace) -> Self {
|
|
||||||
match value {
|
|
||||||
crate::FnNamespace::Global => Self::Global,
|
|
||||||
crate::FnNamespace::Internal => Self::Internal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
enum FnAccess {
|
|
||||||
Public,
|
|
||||||
Private,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<crate::FnAccess> for FnAccess {
|
|
||||||
fn from(value: crate::FnAccess) -> Self {
|
|
||||||
match value {
|
|
||||||
crate::FnAccess::Public => Self::Public,
|
|
||||||
crate::FnAccess::Private => Self::Private,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct FnParam<'a> {
|
struct FnParam<'a> {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@ -58,28 +24,28 @@ struct FnParam<'a> {
|
|||||||
pub typ: Option<Cow<'a, str>>,
|
pub typ: Option<Cow<'a, str>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct FnMetadata<'a> {
|
struct FnMetadata<'a> {
|
||||||
pub base_hash: u64,
|
pub base_hash: u64,
|
||||||
pub full_hash: u64,
|
pub full_hash: u64,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub namespace: FnNamespace,
|
pub namespace: crate::FnNamespace,
|
||||||
pub access: FnAccess,
|
pub access: FnAccess,
|
||||||
pub name: String,
|
pub name: &'a str,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub typ: FnType,
|
pub typ: FnType,
|
||||||
pub num_params: usize,
|
pub num_params: usize,
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "StaticVec::is_empty")]
|
||||||
pub params: Vec<FnParam<'a>>,
|
pub params: StaticVec<FnParam<'a>>,
|
||||||
// No idea why the following is needed otherwise serde comes back with a lifetime error
|
// No idea why the following is needed otherwise serde comes back with a lifetime error
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub _dummy: Option<&'a str>,
|
pub _dummy: Option<&'a str>,
|
||||||
#[serde(default, skip_serializing_if = "String::is_empty")]
|
#[serde(default, skip_serializing_if = "str::is_empty")]
|
||||||
pub return_type: String,
|
pub return_type: Cow<'a, str>,
|
||||||
pub signature: String,
|
pub signature: SmartString,
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "StaticVec::is_empty")]
|
||||||
pub doc_comments: Vec<&'a str>,
|
pub doc_comments: StaticVec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for FnMetadata<'_> {
|
impl PartialOrd for FnMetadata<'_> {
|
||||||
@ -115,7 +81,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace: info.metadata.namespace.into(),
|
namespace: info.metadata.namespace.into(),
|
||||||
access: info.metadata.access.into(),
|
access: info.metadata.access.into(),
|
||||||
name: info.metadata.name.to_string(),
|
name: &info.metadata.name,
|
||||||
typ,
|
typ,
|
||||||
num_params: info.metadata.params,
|
num_params: info.metadata.params,
|
||||||
params: info
|
params: info
|
||||||
@ -133,8 +99,8 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
_dummy: None,
|
_dummy: None,
|
||||||
return_type: FuncInfo::format_type(&info.metadata.return_type, true).into_owned(),
|
return_type: FuncInfo::format_type(&info.metadata.return_type, true),
|
||||||
signature: info.gen_signature(),
|
signature: info.gen_signature().into(),
|
||||||
doc_comments: if info.func.is_script() {
|
doc_comments: if info.func.is_script() {
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
unreachable!("script-defined functions should not exist under no_function");
|
unreachable!("script-defined functions should not exist under no_function");
|
||||||
@ -158,12 +124,12 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct ModuleMetadata<'a> {
|
struct ModuleMetadata<'a> {
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
#[serde(skip_serializing_if = "SmartString::is_empty")]
|
#[serde(skip_serializing_if = "str::is_empty")]
|
||||||
pub doc: SmartString,
|
pub doc: &'a str,
|
||||||
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
|
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
|
||||||
pub modules: BTreeMap<&'a str, Self>,
|
pub modules: BTreeMap<&'a str, Self>,
|
||||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
#[serde(skip_serializing_if = "StaticVec::is_empty")]
|
||||||
pub functions: Vec<FnMetadata<'a>>,
|
pub functions: StaticVec<FnMetadata<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleMetadata<'_> {
|
impl ModuleMetadata<'_> {
|
||||||
@ -171,16 +137,16 @@ impl ModuleMetadata<'_> {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
doc: SmartString::new_const(),
|
doc: "",
|
||||||
modules: BTreeMap::new(),
|
modules: BTreeMap::new(),
|
||||||
functions: Vec::new(),
|
functions: StaticVec::new_const(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> {
|
impl<'a> From<&'a crate::Module> for ModuleMetadata<'a> {
|
||||||
fn from(module: &'a crate::Module) -> Self {
|
fn from(module: &'a crate::Module) -> Self {
|
||||||
let mut functions: Vec<_> = module.iter_fn().map(Into::into).collect();
|
let mut functions: StaticVec<_> = module.iter_fn().map(Into::into).collect();
|
||||||
functions.sort();
|
functions.sort();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
@ -218,7 +184,7 @@ pub fn gen_metadata_to_json(
|
|||||||
let mut meta: FnMetadata = f.into();
|
let mut meta: FnMetadata = f.into();
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
meta.namespace = FnNamespace::Global;
|
meta.namespace = crate::FnNamespace::Global;
|
||||||
}
|
}
|
||||||
global.functions.push(meta);
|
global.functions.push(meta);
|
||||||
});
|
});
|
||||||
@ -230,7 +196,7 @@ pub fn gen_metadata_to_json(
|
|||||||
let mut meta: FnMetadata = f.into();
|
let mut meta: FnMetadata = f.into();
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
meta.namespace = FnNamespace::Global;
|
meta.namespace = crate::FnNamespace::Global;
|
||||||
}
|
}
|
||||||
global.functions.push(meta);
|
global.functions.push(meta);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// _(internals)_ A type containing commands to control the tokenizer.
|
/// _(internals)_ A type containing commands to control the tokenizer.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
|
||||||
pub struct TokenizerControlBlock {
|
pub struct TokenizerControlBlock {
|
||||||
/// Is the current tokenizer position within an interpolated text string?
|
/// Is the current tokenizer position within an interpolated text string?
|
||||||
/// This flag allows switching the tokenizer back to _text_ parsing after an interpolation stream.
|
/// This flag allows switching the tokenizer back to _text_ parsing after an interpolation stream.
|
||||||
@ -333,22 +333,29 @@ impl Span {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Span {
|
impl fmt::Display for Span {
|
||||||
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let _f = f;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_position"))]
|
||||||
match (self.start(), self.end()) {
|
match (self.start(), self.end()) {
|
||||||
(Position::NONE, Position::NONE) => write!(f, "{:?}", Position::NONE),
|
(Position::NONE, Position::NONE) => write!(_f, "{:?}", Position::NONE),
|
||||||
(Position::NONE, end) => write!(f, "..{:?}", end),
|
(Position::NONE, end) => write!(_f, "..{:?}", end),
|
||||||
(start, Position::NONE) => write!(f, "{:?}", start),
|
(start, Position::NONE) => write!(_f, "{:?}", start),
|
||||||
(start, end) if start.line() != end.line() => {
|
(start, end) if start.line() != end.line() => {
|
||||||
write!(f, "{:?}-{:?}", start, end)
|
write!(_f, "{:?}-{:?}", start, end)
|
||||||
}
|
}
|
||||||
(start, end) => write!(
|
(start, end) => write!(
|
||||||
f,
|
_f,
|
||||||
"{}:{}-{}",
|
"{}:{}-{}",
|
||||||
start.line().unwrap(),
|
start.line().unwrap(),
|
||||||
start.position().unwrap_or(0),
|
start.position().unwrap_or(0),
|
||||||
end.position().unwrap_or(0)
|
end.position().unwrap_or(0)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_position")]
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,6 +873,11 @@ impl Token {
|
|||||||
"**" => PowerOf,
|
"**" => PowerOf,
|
||||||
"**=" => PowerOfAssign,
|
"**=" => PowerOfAssign,
|
||||||
|
|
||||||
|
#[cfg(feature = "no_object")]
|
||||||
|
"?." => Reserved(syntax.into()),
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
"?[" => Reserved(syntax.into()),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
"fn" => Fn,
|
"fn" => Fn,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -885,9 +897,8 @@ impl Token {
|
|||||||
"import" | "export" | "as" => Reserved(syntax.into()),
|
"import" | "export" | "as" => Reserved(syntax.into()),
|
||||||
|
|
||||||
// List of reserved operators
|
// List of reserved operators
|
||||||
"===" | "!==" | "->" | "<-" | ":=" | "~" | "::<" | "(*" | "*)" | "#" | "#!" => {
|
"===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)"
|
||||||
Reserved(syntax.into())
|
| "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => Reserved(syntax.into()),
|
||||||
}
|
|
||||||
|
|
||||||
// List of reserved keywords
|
// List of reserved keywords
|
||||||
"public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var"
|
"public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var"
|
||||||
@ -1110,7 +1121,7 @@ impl From<Token> for String {
|
|||||||
|
|
||||||
/// _(internals)_ State of the tokenizer.
|
/// _(internals)_ State of the tokenizer.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq, Default)]
|
||||||
pub struct TokenizeState {
|
pub struct TokenizeState {
|
||||||
/// Maximum length of a string.
|
/// Maximum length of a string.
|
||||||
pub max_string_size: Option<NonZeroUsize>,
|
pub max_string_size: Option<NonZeroUsize>,
|
||||||
@ -2036,6 +2047,10 @@ fn get_next_token_inner(
|
|||||||
start_pos,
|
start_pos,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
('<', '|') => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
return Some((Token::Reserved("<|".into()), start_pos));
|
||||||
|
}
|
||||||
('<', ..) => return Some((Token::LessThan, start_pos)),
|
('<', ..) => return Some((Token::LessThan, start_pos)),
|
||||||
|
|
||||||
('>', '=') => {
|
('>', '=') => {
|
||||||
@ -2067,7 +2082,10 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
return Some((Token::NotEqualsTo, start_pos));
|
return Some((Token::NotEqualsTo, start_pos));
|
||||||
}
|
}
|
||||||
('!', '.') => return Some((Token::Reserved("!.".into()), start_pos)),
|
('!', '.') => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
return Some((Token::Reserved("!.".into()), start_pos));
|
||||||
|
}
|
||||||
('!', ..) => return Some((Token::Bang, start_pos)),
|
('!', ..) => return Some((Token::Bang, start_pos)),
|
||||||
|
|
||||||
('|', '|') => {
|
('|', '|') => {
|
||||||
@ -2078,6 +2096,10 @@ fn get_next_token_inner(
|
|||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
return Some((Token::OrAssign, start_pos));
|
return Some((Token::OrAssign, start_pos));
|
||||||
}
|
}
|
||||||
|
('|', '>') => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
return Some((Token::Reserved("|>".into()), start_pos));
|
||||||
|
}
|
||||||
('|', ..) => return Some((Token::Pipe, start_pos)),
|
('|', ..) => return Some((Token::Pipe, start_pos)),
|
||||||
|
|
||||||
('&', '&') => {
|
('&', '&') => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Helper module which defines the [`Dynamic`] data type and the
|
//! Helper module which defines the [`Dynamic`] data type and the
|
||||||
//! [`Any`] trait to to allow custom type handling.
|
//! [`Any`] trait to to allow custom type handling.
|
||||||
|
|
||||||
use crate::func::{locked_read, SendSync};
|
use crate::func::SendSync;
|
||||||
use crate::{reify, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT};
|
use crate::{reify, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -23,6 +23,7 @@ pub use std::time::Instant;
|
|||||||
pub use instant::Instant;
|
pub use instant::Instant;
|
||||||
|
|
||||||
/// The message: data type was checked
|
/// The message: data type was checked
|
||||||
|
#[allow(dead_code)]
|
||||||
const CHECKED: &str = "data type was checked";
|
const CHECKED: &str = "data type was checked";
|
||||||
|
|
||||||
mod private {
|
mod private {
|
||||||
@ -186,6 +187,9 @@ pub enum Union {
|
|||||||
TimeStamp(Box<Instant>, Tag, AccessMode),
|
TimeStamp(Box<Instant>, Tag, AccessMode),
|
||||||
|
|
||||||
/// Any type as a trait object.
|
/// Any type as a trait object.
|
||||||
|
///
|
||||||
|
/// An extra level of redirection is used in order to avoid bloating the size of [`Dynamic`]
|
||||||
|
/// because `Box<dyn Variant>` is a fat pointer.
|
||||||
Variant(Box<Box<dyn Variant>>, Tag, AccessMode),
|
Variant(Box<Box<dyn Variant>>, Tag, AccessMode),
|
||||||
|
|
||||||
/// A _shared_ value of any type.
|
/// A _shared_ value of any type.
|
||||||
@ -424,7 +428,7 @@ impl Dynamic {
|
|||||||
Union::Variant(ref v, ..) => (***v).type_id(),
|
Union::Variant(ref v, ..) => (***v).type_id(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => (*locked_read(cell)).type_id(),
|
Union::Shared(ref cell, ..) => (*crate::func::locked_read(cell)).type_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get the name of the type of the value held by this [`Dynamic`].
|
/// Get the name of the type of the value held by this [`Dynamic`].
|
||||||
@ -498,7 +502,7 @@ impl Hash for Dynamic {
|
|||||||
Union::FnPtr(ref f, ..) => f.hash(state),
|
Union::FnPtr(ref f, ..) => f.hash(state),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => (*locked_read(cell)).hash(state),
|
Union::Shared(ref cell, ..) => (*crate::func::locked_read(cell)).hash(state),
|
||||||
|
|
||||||
Union::Variant(..) => unimplemented!("{} cannot be hashed", self.type_name()),
|
Union::Variant(..) => unimplemented!("{} cannot be hashed", self.type_name()),
|
||||||
|
|
||||||
@ -1070,7 +1074,7 @@ impl Dynamic {
|
|||||||
pub fn is_read_only(&self) -> bool {
|
pub fn is_read_only(&self) -> bool {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
if let Union::Shared(ref cell, ..) = self.0 {
|
if let Union::Shared(ref cell, ..) = self.0 {
|
||||||
return match locked_read(cell).access_mode() {
|
return match crate::func::locked_read(cell).access_mode() {
|
||||||
ReadWrite => false,
|
ReadWrite => false,
|
||||||
ReadOnly => true,
|
ReadOnly => true,
|
||||||
};
|
};
|
||||||
@ -1099,7 +1103,7 @@ impl Dynamic {
|
|||||||
Union::Map(..) => true,
|
Union::Map(..) => true,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => locked_read(cell).is_hashable(),
|
Union::Shared(ref cell, ..) => crate::func::locked_read(cell).is_hashable(),
|
||||||
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
@ -1350,7 +1354,7 @@ impl Dynamic {
|
|||||||
pub fn flatten_clone(&self) -> Self {
|
pub fn flatten_clone(&self) -> Self {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => locked_read(cell).clone(),
|
Union::Shared(ref cell, ..) => crate::func::locked_read(cell).clone(),
|
||||||
_ => self.clone(),
|
_ => self.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1366,7 +1370,7 @@ impl Dynamic {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell, ..) => crate::func::shared_try_take(cell).map_or_else(
|
Union::Shared(cell, ..) => crate::func::shared_try_take(cell).map_or_else(
|
||||||
|ref cell| locked_read(cell).clone(),
|
|ref cell| crate::func::locked_read(cell).clone(),
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
|value| value.into_inner(),
|
|value| value.into_inner(),
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
@ -1388,7 +1392,7 @@ impl Dynamic {
|
|||||||
Union::Shared(ref mut cell, ..) => {
|
Union::Shared(ref mut cell, ..) => {
|
||||||
let cell = mem::take(cell);
|
let cell = mem::take(cell);
|
||||||
*self = crate::func::shared_try_take(cell).map_or_else(
|
*self = crate::func::shared_try_take(cell).map_or_else(
|
||||||
|ref cell| locked_read(cell).clone(),
|
|ref cell| crate::func::locked_read(cell).clone(),
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
|value| value.into_inner(),
|
|value| value.into_inner(),
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
@ -1437,7 +1441,7 @@ impl Dynamic {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => {
|
Union::Shared(ref cell, ..) => {
|
||||||
let value = locked_read(cell);
|
let value = crate::func::locked_read(cell);
|
||||||
|
|
||||||
return if (*value).type_id() != TypeId::of::<T>()
|
return if (*value).type_id() != TypeId::of::<T>()
|
||||||
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
|
&& TypeId::of::<Dynamic>() != TypeId::of::<T>()
|
||||||
@ -1785,7 +1789,7 @@ impl Dynamic {
|
|||||||
Union::Str(s, ..) => Ok(s),
|
Union::Str(s, ..) => Ok(s),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => {
|
Union::Shared(ref cell, ..) => {
|
||||||
let value = locked_read(cell);
|
let value = crate::func::locked_read(cell);
|
||||||
|
|
||||||
match value.0 {
|
match value.0 {
|
||||||
Union::Str(ref s, ..) => Ok(s.clone()),
|
Union::Str(ref s, ..) => Ok(s.clone()),
|
||||||
@ -1804,7 +1808,7 @@ impl Dynamic {
|
|||||||
Union::Array(a, ..) => Ok(*a),
|
Union::Array(a, ..) => Ok(*a),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => {
|
Union::Shared(ref cell, ..) => {
|
||||||
let value = locked_read(cell);
|
let value = crate::func::locked_read(cell);
|
||||||
|
|
||||||
match value.0 {
|
match value.0 {
|
||||||
Union::Array(ref a, ..) => Ok(a.as_ref().clone()),
|
Union::Array(ref a, ..) => Ok(a.as_ref().clone()),
|
||||||
@ -1839,7 +1843,7 @@ impl Dynamic {
|
|||||||
Union::Blob(..) if TypeId::of::<T>() == TypeId::of::<u8>() => Ok(self.cast::<Vec<T>>()),
|
Union::Blob(..) if TypeId::of::<T>() == TypeId::of::<u8>() => Ok(self.cast::<Vec<T>>()),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => {
|
Union::Shared(ref cell, ..) => {
|
||||||
let value = locked_read(cell);
|
let value = crate::func::locked_read(cell);
|
||||||
|
|
||||||
match value.0 {
|
match value.0 {
|
||||||
Union::Array(ref a, ..) => {
|
Union::Array(ref a, ..) => {
|
||||||
@ -1877,7 +1881,7 @@ impl Dynamic {
|
|||||||
Union::Blob(a, ..) => Ok(*a),
|
Union::Blob(a, ..) => Ok(*a),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell, ..) => {
|
Union::Shared(ref cell, ..) => {
|
||||||
let value = locked_read(cell);
|
let value = crate::func::locked_read(cell);
|
||||||
|
|
||||||
match value.0 {
|
match value.0 {
|
||||||
Union::Blob(ref a, ..) => Ok(a.as_ref().clone()),
|
Union::Blob(ref a, ..) => Ok(a.as_ref().clone()),
|
||||||
|
@ -5,6 +5,7 @@ use crate::ImmutableString;
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::AddAssign,
|
ops::AddAssign,
|
||||||
@ -20,10 +21,12 @@ pub const MAX_STRING_LEN: usize = 24;
|
|||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// Normal identifiers, property getters and setters are interned separately.
|
/// Normal identifiers, property getters and setters are interned separately.
|
||||||
#[derive(Debug, Clone, Hash)]
|
#[derive(Clone, Hash)]
|
||||||
pub struct StringsInterner<'a> {
|
pub struct StringsInterner<'a> {
|
||||||
/// Maximum capacity.
|
/// Maximum number of strings interned.
|
||||||
max: usize,
|
pub capacity: usize,
|
||||||
|
/// Maximum string length.
|
||||||
|
pub max_string_len: usize,
|
||||||
/// Normal strings.
|
/// Normal strings.
|
||||||
strings: BTreeMap<u64, ImmutableString>,
|
strings: BTreeMap<u64, ImmutableString>,
|
||||||
/// Take care of the lifetime parameter.
|
/// Take care of the lifetime parameter.
|
||||||
@ -37,46 +40,43 @@ impl Default for StringsInterner<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for StringsInterner<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_list().entries(self.strings.values()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl StringsInterner<'_> {
|
impl StringsInterner<'_> {
|
||||||
/// Create a new [`StringsInterner`].
|
/// Create a new [`StringsInterner`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::new_with_capacity(MAX_INTERNED_STRINGS)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new [`StringsInterner`] with maximum capacity.
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new_with_capacity(capacity: usize) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
max: capacity,
|
capacity: MAX_INTERNED_STRINGS,
|
||||||
|
max_string_len: MAX_STRING_LEN,
|
||||||
strings: BTreeMap::new(),
|
strings: BTreeMap::new(),
|
||||||
dummy: PhantomData,
|
dummy: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an identifier from a text string, adding it to the interner if necessary.
|
/// Get an identifier from a text string, adding it to the interner if necessary.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get<T: AsRef<str> + Into<ImmutableString>>(&mut self, text: T) -> ImmutableString {
|
pub fn get<S: AsRef<str> + Into<ImmutableString>>(&mut self, text: S) -> ImmutableString {
|
||||||
self.get_with_mapper(|s| s.into(), text)
|
self.get_with_mapper(|s| s.into(), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an identifier from a text string, adding it to the interner if necessary.
|
/// Get an identifier from a text string, adding it to the interner if necessary.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_with_mapper<T: AsRef<str> + Into<ImmutableString>>(
|
pub fn get_with_mapper<S: AsRef<str>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
mapper: fn(T) -> ImmutableString,
|
mapper: fn(S) -> ImmutableString,
|
||||||
text: T,
|
text: S,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
let key = text.as_ref();
|
let key = text.as_ref();
|
||||||
|
|
||||||
// Do not intern numbers
|
|
||||||
if key.bytes().all(|c| c == b'.' || (c >= b'0' && c <= b'9')) {
|
|
||||||
return text.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
if key.len() > MAX_STRING_LEN {
|
if key.len() > MAX_STRING_LEN {
|
||||||
return mapper(text);
|
return mapper(text);
|
||||||
}
|
}
|
||||||
@ -98,11 +98,15 @@ impl StringsInterner<'_> {
|
|||||||
self.strings.insert(key, value.clone());
|
self.strings.insert(key, value.clone());
|
||||||
|
|
||||||
// If the interner is over capacity, remove the longest entry
|
// If the interner is over capacity, remove the longest entry
|
||||||
if self.strings.len() > self.max {
|
if self.strings.len() > self.capacity {
|
||||||
// Leave some buffer to grow when shrinking the cache.
|
// Leave some buffer to grow when shrinking the cache.
|
||||||
// We leave at least two entries, one for the empty string, and one for the string
|
// We leave at least two entries, one for the empty string, and one for the string
|
||||||
// that has just been inserted.
|
// that has just been inserted.
|
||||||
let max = if self.max < 5 { 2 } else { self.max - 3 };
|
let max = if self.capacity < 5 {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
self.capacity - 3
|
||||||
|
};
|
||||||
|
|
||||||
while self.strings.len() > max {
|
while self.strings.len() > max {
|
||||||
let (_, n) = self.strings.iter().fold((0, 0), |(x, n), (&k, v)| {
|
let (_, n) = self.strings.iter().fold((0, 0), |(x, n), (&k, v)| {
|
||||||
@ -123,19 +127,22 @@ impl StringsInterner<'_> {
|
|||||||
/// Number of strings interned.
|
/// Number of strings interned.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.strings.len()
|
self.strings.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number of strings interned.
|
/// Returns `true` if there are no interned strings.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.strings.is_empty()
|
self.strings.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear all interned strings.
|
/// Clear all interned strings.
|
||||||
#[inline]
|
#[inline(always)]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.strings.clear();
|
self.strings.clear();
|
||||||
}
|
}
|
||||||
|
@ -214,7 +214,7 @@ impl Scope<'_> {
|
|||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.values.len()
|
self.values.len()
|
||||||
}
|
}
|
||||||
/// Is the [`Scope`] empty?
|
/// Returns `true` if this [`Scope`] contains no variables.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
@ -21,8 +21,46 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
ParseErrorType::Reserved(err) if err == "while"
|
ParseErrorType::Reserved(err) if err == "while"
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// Implement ternary operator
|
||||||
engine.register_custom_syntax(
|
engine.register_custom_syntax(
|
||||||
&[
|
["iff", "$expr$", "?", "$expr$", ":", "$expr$"],
|
||||||
|
false,
|
||||||
|
|context, inputs| match context.eval_expression_tree(&inputs[0])?.as_bool() {
|
||||||
|
Ok(true) => context.eval_expression_tree(&inputs[1]),
|
||||||
|
Ok(false) => context.eval_expression_tree(&inputs[2]),
|
||||||
|
Err(typ) => Err(Box::new(EvalAltResult::ErrorMismatchDataType(
|
||||||
|
"bool".to_string(),
|
||||||
|
typ.to_string(),
|
||||||
|
inputs[0].position(),
|
||||||
|
))),
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = 42;
|
||||||
|
let y = iff x > 40 ? 0 : 123;
|
||||||
|
y
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
"
|
||||||
|
let x = 42;
|
||||||
|
let y = iff x == 0 ? 0 : 123;
|
||||||
|
y
|
||||||
|
"
|
||||||
|
)?,
|
||||||
|
123
|
||||||
|
);
|
||||||
|
|
||||||
|
// Custom syntax
|
||||||
|
engine.register_custom_syntax(
|
||||||
|
[
|
||||||
"exec", "[", "$ident$", "$symbol$", "$int$", "]", "->", "$block$", "while", "$expr$",
|
"exec", "[", "$ident$", "$symbol$", "$int$", "]", "->", "$block$", "while", "$expr$",
|
||||||
],
|
],
|
||||||
true,
|
true,
|
||||||
@ -155,7 +193,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
// The first symbol must be an identifier
|
// The first symbol must be an identifier
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*engine
|
*engine
|
||||||
.register_custom_syntax(&["!"], false, |_, _| Ok(Dynamic::UNIT))
|
.register_custom_syntax(["!"], false, |_, _| Ok(Dynamic::UNIT))
|
||||||
.expect_err("should error")
|
.expect_err("should error")
|
||||||
.err_type(),
|
.err_type(),
|
||||||
ParseErrorType::BadInput(LexError::ImproperSymbol(
|
ParseErrorType::BadInput(LexError::ImproperSymbol(
|
||||||
@ -166,9 +204,9 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
// Check self-termination
|
// Check self-termination
|
||||||
engine
|
engine
|
||||||
.register_custom_syntax(&["test1", "$block$"], true, |_, _| Ok(Dynamic::UNIT))?
|
.register_custom_syntax(["test1", "$block$"], true, |_, _| Ok(Dynamic::UNIT))?
|
||||||
.register_custom_syntax(&["test2", "}"], true, |_, _| Ok(Dynamic::UNIT))?
|
.register_custom_syntax(["test2", "}"], true, |_, _| Ok(Dynamic::UNIT))?
|
||||||
.register_custom_syntax(&["test3", ";"], true, |_, _| Ok(Dynamic::UNIT))?;
|
.register_custom_syntax(["test3", ";"], true, |_, _| Ok(Dynamic::UNIT))?;
|
||||||
|
|
||||||
assert_eq!(engine.eval::<INT>("test1 { x = y + z; } 42")?, 42);
|
assert_eq!(engine.eval::<INT>("test1 { x = y + z; } 42")?, 42);
|
||||||
assert_eq!(engine.eval::<INT>("test2 } 42")?, 42);
|
assert_eq!(engine.eval::<INT>("test2 } 42")?, 42);
|
||||||
@ -176,7 +214,7 @@ fn test_custom_syntax() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
// Register the custom syntax: var x = ???
|
// Register the custom syntax: var x = ???
|
||||||
engine.register_custom_syntax(
|
engine.register_custom_syntax(
|
||||||
&["var", "$ident$", "=", "$expr$"],
|
["var", "$ident$", "=", "$expr$"],
|
||||||
true,
|
true,
|
||||||
|context, inputs| {
|
|context, inputs| {
|
||||||
let var_name = inputs[0].get_string_value().unwrap();
|
let var_name = inputs[0].get_string_value().unwrap();
|
||||||
|
@ -12,7 +12,7 @@ fn test_debugging() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_debugger(
|
engine.register_debugger(
|
||||||
|| Dynamic::UNIT,
|
|_| Dynamic::UNIT,
|
||||||
|_, _, _, _, _| Ok(rhai::debugger::DebuggerCommand::Continue),
|
|_, _, _, _, _| Ok(rhai::debugger::DebuggerCommand::Continue),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ fn test_debugger_state() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.register_debugger(
|
engine.register_debugger(
|
||||||
|| {
|
|_| {
|
||||||
// Say, use an object map for the debugger state
|
// Say, use an object map for the debugger state
|
||||||
let mut state = Map::new();
|
let mut state = Map::new();
|
||||||
// Initialize properties
|
// Initialize properties
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
use rhai::packages::{Package, StandardPackage};
|
use rhai::packages::{Package, StandardPackage as SSS};
|
||||||
use rhai::{Engine, EvalAltResult, Module, Scope, INT};
|
use rhai::{def_package, Engine, EvalAltResult, Module, Scope, INT};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_packages() -> Result<(), Box<EvalAltResult>> {
|
fn test_packages() -> Result<(), Box<EvalAltResult>> {
|
||||||
let engine = Engine::new();
|
def_package! {
|
||||||
let ast = engine.compile("x")?;
|
/// My custom package.
|
||||||
let std_pkg = StandardPackage::new();
|
MyPackage(m) : SSS {
|
||||||
|
m.set_native_fn("hello", |x: INT| Ok(x + 1));
|
||||||
|
m.set_native_fn("@", |x: INT, y: INT| Ok(x * x + y * y));
|
||||||
|
} |> |engine| {
|
||||||
|
engine.register_custom_operator("@", 160).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let pkg = MyPackage::new();
|
||||||
|
|
||||||
let make_call = |x: INT| -> Result<INT, Box<EvalAltResult>> {
|
let make_call = |x: INT| -> Result<INT, Box<EvalAltResult>> {
|
||||||
// Create a raw Engine - extremely cheap.
|
// Create a raw Engine - extremely cheap.
|
||||||
let mut engine = Engine::new_raw();
|
let mut engine = Engine::new_raw();
|
||||||
|
|
||||||
// Register packages - cheap.
|
// Register packages - cheap.
|
||||||
engine.register_global_module(std_pkg.as_shared_module());
|
pkg.register_into_engine(&mut engine);
|
||||||
|
pkg.register_into_engine_as(&mut engine, "foo");
|
||||||
|
|
||||||
// Create custom scope - cheap.
|
// Create custom scope - cheap.
|
||||||
let mut scope = Scope::new();
|
let mut scope = Scope::new();
|
||||||
@ -21,10 +32,11 @@ fn test_packages() -> Result<(), Box<EvalAltResult>> {
|
|||||||
scope.push("x", x);
|
scope.push("x", x);
|
||||||
|
|
||||||
// Evaluate script.
|
// Evaluate script.
|
||||||
engine.eval_ast_with_scope::<INT>(&mut scope, &ast)
|
|
||||||
|
engine.eval_with_scope::<INT>(&mut scope, "hello(x) @ foo::hello(x)")
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(make_call(42)?, 42);
|
assert_eq!(make_call(42)?, 3698);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user