Merge pull request #649 from schungx/master

Speed improvements.
This commit is contained in:
Stephen Chung 2022-09-26 21:43:39 +08:00 committed by GitHub
commit 884e4ff011
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1012 additions and 854 deletions

View File

@ -1,6 +1,18 @@
Rhai Release Notes Rhai Release Notes
================== ==================
Version 1.11.0
==============
Enhancements
------------
* The look-ahead symbol for custom syntax now renders a string literal in quotes (instead of the generic term `string`). This facilitates more accurate parsing by separating strings and identifiers.
* Due to a code refactor, built-in operators for standard types now run even faster, in certain cases by 20-30%.
* `Scope` is now serializable and deserializable via `serde`.
* `Scope` now contains a const generic parameter that allows specifying how many entries to be kept inline.
Version 1.10.1 Version 1.10.1
============== ==============
@ -8,7 +20,7 @@ Bug fixes
--------- ---------
* Compiling on 32-bit architectures no longer cause a compilation error. * Compiling on 32-bit architectures no longer cause a compilation error.
* Fix type-size test fo 32-bit architectures without the `decimal` feature. * Fix type-size test for 32-bit architectures without the `decimal` feature.
Custom syntax with state Custom syntax with state
------------------------ ------------------------

View File

@ -147,6 +147,7 @@ impl Expression<'_> {
impl AsRef<Expr> for Expression<'_> { impl AsRef<Expr> for Expression<'_> {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &Expr { fn as_ref(&self) -> &Expr {
self.0 self.0
} }
@ -156,6 +157,7 @@ impl Deref for Expression<'_> {
type Target = Expr; type Target = Expr;
#[inline(always)] #[inline(always)]
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.0 self.0
} }

View File

@ -169,10 +169,11 @@ impl Engine {
/// let mut engine = Engine::new(); /// let mut engine = Engine::new();
/// ///
/// // Register a token mapper. /// // Register a token mapper.
/// # #[allow(deprecated)]
/// engine.on_parse_token(|token, _, _| { /// engine.on_parse_token(|token, _, _| {
/// match token { /// match token {
/// // Convert all integer literals to strings /// // Convert all integer literals to strings
/// Token::IntegerConstant(n) => Token::StringConstant(n.to_string().into()), /// Token::IntegerConstant(n) => Token::StringConstant(Box::new(n.to_string().into())),
/// // Convert 'begin' .. 'end' to '{' .. '}' /// // Convert 'begin' .. 'end' to '{' .. '}'
/// Token::Identifier(s) if &*s == "begin" => Token::LeftBrace, /// Token::Identifier(s) if &*s == "begin" => Token::LeftBrace,
/// Token::Identifier(s) if &*s == "end" => Token::RightBrace, /// Token::Identifier(s) if &*s == "end" => Token::RightBrace,

View File

@ -108,7 +108,7 @@ impl Engine {
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| { Self::read_file(&path).and_then(|contents| {
let mut ast = self.compile_with_scope(scope, &contents)?; let mut ast = self.compile_with_scope(scope, &contents)?;
ast.set_source(path.to_string_lossy()); ast.set_source(path.to_string_lossy().as_ref());
Ok(ast) Ok(ast)
}) })
} }

View File

@ -75,6 +75,7 @@ impl Engine {
/// Not available under `no_module`. /// Not available under `no_module`.
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn module_resolver(&self) -> &dyn crate::ModuleResolver { pub fn module_resolver(&self) -> &dyn crate::ModuleResolver {
&*self.module_resolver &*self.module_resolver
} }

View File

@ -16,12 +16,14 @@ impl Engine {
/// Get the global namespace module (which is the fist module in `global_modules`). /// Get the global namespace module (which is the fist module in `global_modules`).
#[inline(always)] #[inline(always)]
#[allow(dead_code)] #[allow(dead_code)]
#[must_use]
pub(crate) fn global_namespace(&self) -> &Module { pub(crate) fn global_namespace(&self) -> &Module {
self.global_modules.first().unwrap() self.global_modules.first().unwrap()
} }
/// Get a mutable reference to the global namespace module /// Get a mutable reference to the global namespace module
/// (which is the first module in `global_modules`). /// (which is the first module in `global_modules`).
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn global_namespace_mut(&mut self) -> &mut Module { pub(crate) fn global_namespace_mut(&mut self) -> &mut Module {
let module = self.global_modules.first_mut().unwrap(); let module = self.global_modules.first_mut().unwrap();
Shared::get_mut(module).expect("not shared") Shared::get_mut(module).expect("not shared")

View File

@ -919,6 +919,7 @@ impl<A: Into<Self>> AddAssign<A> for AST {
impl AsRef<[Stmt]> for AST { impl AsRef<[Stmt]> for AST {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &[Stmt] { fn as_ref(&self) -> &[Stmt] {
self.statements() self.statements()
} }
@ -927,6 +928,7 @@ impl AsRef<[Stmt]> for AST {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
impl AsRef<crate::Module> for AST { impl AsRef<crate::Module> for AST {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &crate::Module { fn as_ref(&self) -> &crate::Module {
self.shared_lib().as_ref() self.shared_lib().as_ref()
} }
@ -935,6 +937,7 @@ impl AsRef<crate::Module> for AST {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
impl AsRef<crate::Shared<crate::Module>> for AST { impl AsRef<crate::Shared<crate::Module>> for AST {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &crate::Shared<crate::Module> { fn as_ref(&self) -> &crate::Shared<crate::Module> {
self.shared_lib() self.shared_lib()
} }

View File

@ -193,7 +193,7 @@ pub struct FnCallExpr {
/// Does this function call capture the parent scope? /// Does this function call capture the parent scope?
pub capture_parent_scope: bool, pub capture_parent_scope: bool,
/// Is this function call a native operator? /// Is this function call a native operator?
pub is_native_operator: bool, pub operator_token: Option<Token>,
/// [Position] of the function name. /// [Position] of the function name.
pub pos: Position, pub pos: Position,
} }
@ -208,8 +208,8 @@ impl fmt::Debug for FnCallExpr {
if self.capture_parent_scope { if self.capture_parent_scope {
ff.field("capture_parent_scope", &self.capture_parent_scope); ff.field("capture_parent_scope", &self.capture_parent_scope);
} }
if self.is_native_operator { if let Some(ref token) = self.operator_token {
ff.field("is_native_operator", &self.is_native_operator); ff.field("operator_token", token);
} }
ff.field("hash", &self.hashes) ff.field("hash", &self.hashes)
.field("name", &self.name) .field("name", &self.name)
@ -257,6 +257,7 @@ impl Hash for FloatWrapper<crate::FLOAT> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
impl<F: Float> AsRef<F> for FloatWrapper<F> { impl<F: Float> AsRef<F> for FloatWrapper<F> {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &F { fn as_ref(&self) -> &F {
&self.0 &self.0
} }
@ -265,6 +266,7 @@ impl<F: Float> AsRef<F> for FloatWrapper<F> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
impl<F: Float> AsMut<F> for FloatWrapper<F> { impl<F: Float> AsMut<F> for FloatWrapper<F> {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_mut(&mut self) -> &mut F { fn as_mut(&mut self) -> &mut F {
&mut self.0 &mut self.0
} }
@ -275,6 +277,7 @@ impl<F: Float> Deref for FloatWrapper<F> {
type Target = F; type Target = F;
#[inline(always)] #[inline(always)]
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
@ -283,6 +286,7 @@ impl<F: Float> Deref for FloatWrapper<F> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
impl<F: Float> DerefMut for FloatWrapper<F> { impl<F: Float> DerefMut for FloatWrapper<F> {
#[inline(always)] #[inline(always)]
#[must_use]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0 &mut self.0
} }
@ -669,7 +673,7 @@ impl Expr {
hashes: calc_fn_hash(None, f.fn_name(), 1).into(), hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
capture_parent_scope: false, capture_parent_scope: false,
is_native_operator: false, operator_token: None,
pos, pos,
} }
.into(), .into(),

View File

@ -28,6 +28,7 @@ impl fmt::Debug for Ident {
impl AsRef<str> for Ident { impl AsRef<str> for Ident {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &str { fn as_ref(&self) -> &str {
self.name.as_ref() self.name.as_ref()
} }
@ -37,6 +38,7 @@ impl Deref for Ident {
type Target = ImmutableString; type Target = ImmutableString;
#[inline(always)] #[inline(always)]
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.name &self.name
} }
@ -44,6 +46,7 @@ impl Deref for Ident {
impl DerefMut for Ident { impl DerefMut for Ident {
#[inline(always)] #[inline(always)]
#[must_use]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.name &mut self.name
} }

View File

@ -70,6 +70,7 @@ impl Deref for Namespace {
type Target = StaticVec<Ident>; type Target = StaticVec<Ident>;
#[inline(always)] #[inline(always)]
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.path &self.path
} }
@ -77,6 +78,7 @@ impl Deref for Namespace {
impl DerefMut for Namespace { impl DerefMut for Namespace {
#[inline(always)] #[inline(always)]
#[must_use]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.path &mut self.path
} }

View File

@ -19,16 +19,16 @@ use std::{
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
/// This type may hold a straight assignment (i.e. not an op-assignment). /// This type may hold a straight assignment (i.e. not an op-assignment).
#[derive(Clone, Copy, Eq, PartialEq, Hash)] #[derive(Clone, PartialEq, Hash)]
pub struct OpAssignment { pub struct OpAssignment {
/// Hash of the op-assignment call. /// Hash of the op-assignment call.
pub hash_op_assign: u64, pub hash_op_assign: u64,
/// Hash of the underlying operator call (for fallback). /// Hash of the underlying operator call (for fallback).
pub hash_op: u64, pub hash_op: u64,
/// Op-assignment operator. /// Op-assignment operator.
pub op_assign: &'static str, pub op_assign: Token,
/// Underlying operator. /// Underlying operator.
pub op: &'static str, pub op: Token,
/// [Position] of the op-assignment operator. /// [Position] of the op-assignment operator.
pub pos: Position, pub pos: Position,
} }
@ -41,8 +41,8 @@ impl OpAssignment {
Self { Self {
hash_op_assign: 0, hash_op_assign: 0,
hash_op: 0, hash_op: 0,
op_assign: "=", op_assign: Token::Equals,
op: "=", op: Token::Equals,
pos, pos,
} }
} }
@ -71,12 +71,11 @@ impl OpAssignment {
pub fn new_op_assignment_from_token(op: &Token, pos: Position) -> Self { pub fn new_op_assignment_from_token(op: &Token, pos: Position) -> Self {
let op_raw = op let op_raw = op
.get_base_op_from_assignment() .get_base_op_from_assignment()
.expect("op-assignment operator") .expect("op-assignment operator");
.literal_syntax();
Self { Self {
hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2), hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2),
hash_op: calc_fn_hash(None, op_raw, 2), hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2),
op_assign: op.literal_syntax(), op_assign: op.clone(),
op: op_raw, op: op_raw,
pos, pos,
} }
@ -424,6 +423,7 @@ impl Deref for StmtBlock {
type Target = StmtBlockContainer; type Target = StmtBlockContainer;
#[inline(always)] #[inline(always)]
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.block &self.block
} }
@ -431,6 +431,7 @@ impl Deref for StmtBlock {
impl DerefMut for StmtBlock { impl DerefMut for StmtBlock {
#[inline(always)] #[inline(always)]
#[must_use]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.block &mut self.block
} }
@ -438,6 +439,7 @@ impl DerefMut for StmtBlock {
impl AsRef<[Stmt]> for StmtBlock { impl AsRef<[Stmt]> for StmtBlock {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &[Stmt] { fn as_ref(&self) -> &[Stmt] {
&self.block &self.block
} }
@ -445,6 +447,7 @@ impl AsRef<[Stmt]> for StmtBlock {
impl AsMut<[Stmt]> for StmtBlock { impl AsMut<[Stmt]> for StmtBlock {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_mut(&mut self) -> &mut [Stmt] { fn as_mut(&mut self) -> &mut [Stmt] {
&mut self.block &mut self.block
} }

View File

@ -240,12 +240,12 @@ impl Engine {
source.map_or_else( source.map_or_else(
|| { || {
if pos.is_none() { if pos.is_none() {
println!("{}", s); println!("{s}");
} else { } else {
println!("{:?} | {}", pos, s); println!("{pos:?} | {s}");
} }
}, },
|source| println!("{} @ {:?} | {}", source, pos, s), |source| println!("{source} @ {pos:?} | {s}"),
) )
}); });
} }

View File

@ -50,7 +50,7 @@ impl Engine {
idx_values: &mut FnArgsVec<Dynamic>, idx_values: &mut FnArgsVec<Dynamic>,
chain_type: ChainType, chain_type: ChainType,
level: usize, level: usize,
new_val: Option<(Dynamic, OpAssignment)>, new_val: Option<(Dynamic, &OpAssignment)>,
) -> RhaiResultOf<(Dynamic, bool)> { ) -> RhaiResultOf<(Dynamic, bool)> {
let is_ref_mut = target.is_ref(); let is_ref_mut = target.is_ref();
@ -558,7 +558,7 @@ impl Engine {
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
expr: &Expr, expr: &Expr,
level: usize, level: usize,
new_val: Option<(Dynamic, OpAssignment)>, new_val: Option<(Dynamic, &OpAssignment)>,
) -> RhaiResult { ) -> RhaiResult {
let chain_type = ChainType::from(expr); let chain_type = ChainType::from(expr);
let (crate::ast::BinaryExpr { lhs, rhs }, options, op_pos) = match expr { let (crate::ast::BinaryExpr { lhs, rhs }, options, op_pos) = match expr {

View File

@ -3,17 +3,9 @@
use super::{Caches, EvalContext, GlobalRuntimeState, Target}; use super::{Caches, EvalContext, GlobalRuntimeState, Target};
use crate::ast::{Expr, FnCallExpr, OpAssignment}; use crate::ast::{Expr, FnCallExpr, OpAssignment};
use crate::engine::{KEYWORD_THIS, OP_CONCAT}; use crate::engine::{KEYWORD_THIS, OP_CONCAT};
use crate::eval::FnResolutionCacheEntry; use crate::func::get_builtin_binary_op_fn;
use crate::func::{
calc_fn_params_hash, combine_hashes, gen_fn_call_signature, get_builtin_binary_op_fn,
CallableFunction,
};
use crate::types::dynamic::AccessMode; use crate::types::dynamic::AccessMode;
use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR}; use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR};
#[cfg(feature = "no_std")]
use hashbrown::hash_map::Entry;
#[cfg(not(feature = "no_std"))]
use std::collections::hash_map::Entry;
use std::num::NonZeroUsize; use std::num::NonZeroUsize;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -227,87 +219,41 @@ impl Engine {
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
let FnCallExpr { let FnCallExpr {
name, hashes, args, .. name,
hashes,
args,
operator_token,
..
} = expr; } = expr;
// Short-circuit native binary operator call if under Fast Operators mode // Short-circuit native binary operator call if under Fast Operators mode
if expr.is_native_operator && self.fast_operators() && (args.len() == 1 || args.len() == 2) if operator_token.is_some() && self.fast_operators() && args.len() == 2 {
{
let mut lhs = self let mut lhs = self
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
.0 .0
.flatten(); .flatten();
let mut rhs = if args.len() == 2 { let mut rhs = self
self.get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)? .get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)?
.0 .0
.flatten() .flatten();
} else {
Dynamic::UNIT
};
let mut operands = [&mut lhs, &mut rhs]; let operands = &mut [&mut lhs, &mut rhs];
let operands = if args.len() == 2 {
&mut operands[..]
} else {
&mut operands[0..1]
};
let hash = calc_fn_params_hash(operands.iter().map(|a| a.type_id())); if let Some(func) =
let hash = combine_hashes(hashes.native, hash); get_builtin_binary_op_fn(operator_token.as_ref().unwrap(), operands[0], operands[1])
{
// Built-in found
let context = (self, name, None, &*global, lib, pos, level + 1).into();
let result = func(context, operands);
return self.check_return_value(result, pos);
}
let cache = caches.fn_resolution_cache_mut(); return self
let local_entry: CallableFunction; .exec_fn_call(
None, global, caches, lib, name, *hashes, operands, false, false, pos, level,
let func = match cache.map.entry(hash) { )
Entry::Vacant(entry) => { .map(|(v, ..)| v);
let func = if args.len() == 2 {
get_builtin_binary_op_fn(name, operands[0], operands[1])
} else {
None
};
if let Some(f) = func {
if cache.filter.is_absent_and_set(hash) {
// Do not cache "one-hit wonders"
local_entry = CallableFunction::from_fn_builtin(f);
&local_entry
} else {
// Cache repeated calls
&entry
.insert(Some(FnResolutionCacheEntry {
func: CallableFunction::from_fn_builtin(f),
source: None,
}))
.as_ref()
.unwrap()
.func
}
} else {
let result = self.exec_fn_call(
None, global, caches, lib, name, *hashes, operands, false, false, pos,
level,
);
return result.map(|(v, ..)| v);
}
}
Entry::Occupied(entry) => {
if let Some(entry) = entry.into_mut() {
&entry.func
} else {
let sig = gen_fn_call_signature(self, name, operands);
return Err(ERR::ErrorFunctionNotFound(sig, pos).into());
}
}
};
let context = (self, name, None, &*global, lib, pos, level).into();
let result = if func.is_plugin_fn() {
func.get_plugin_fn().unwrap().call(context, operands)
} else {
func.get_native_fn().unwrap()(context, operands)
};
return self.check_return_value(result, pos);
} }
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -338,7 +284,7 @@ impl Engine {
args, args,
*hashes, *hashes,
expr.capture_parent_scope, expr.capture_parent_scope,
expr.is_native_operator, expr.operator_token.as_ref(),
pos, pos,
level, level,
) )
@ -444,9 +390,9 @@ impl Engine {
op_info.pos = expr.start_position(); op_info.pos = expr.start_position();
if let Err(err) = self if let Err(err) = self.eval_op_assignment(
.eval_op_assignment(global, caches, lib, op_info, target, root, item, level) global, caches, lib, &op_info, target, root, item, level,
{ ) {
result = Err(err); result = Err(err);
break; break;
} }

View File

@ -5,7 +5,7 @@ use crate::api::events::VarDefInfo;
use crate::ast::{ use crate::ast::{
ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock, ASTFlags, BinaryExpr, Expr, Ident, OpAssignment, Stmt, SwitchCasesCollection, TryCatchBlock,
}; };
use crate::func::get_hasher; use crate::func::{get_builtin_op_assignment_fn, get_hasher};
use crate::types::dynamic::{AccessMode, Union}; use crate::types::dynamic::{AccessMode, Union};
use crate::{ use crate::{
Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT, Dynamic, Engine, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, INT,
@ -117,7 +117,7 @@ impl Engine {
global: &mut GlobalRuntimeState, global: &mut GlobalRuntimeState,
caches: &mut Caches, caches: &mut Caches,
lib: &[&Module], lib: &[&Module],
op_info: OpAssignment, op_info: &OpAssignment,
target: &mut Target, target: &mut Target,
root: (&str, Position), root: (&str, Position),
new_val: Dynamic, new_val: Dynamic,
@ -141,12 +141,29 @@ impl Engine {
let mut lock_guard = target.write_lock::<Dynamic>().unwrap(); let mut lock_guard = target.write_lock::<Dynamic>().unwrap();
let hash = hash_op_assign; let hash = *hash_op_assign;
let args = &mut [&mut *lock_guard, &mut new_val]; let args = &mut [&mut *lock_guard, &mut new_val];
let level = level + 1; let level = level + 1;
if self.fast_operators() {
if let Some(func) = get_builtin_op_assignment_fn(op_assign, args[0], args[1]) {
// Built-in found
let op = op_assign.literal_syntax();
let context = (self, op, None, &*global, lib, *op_pos, level).into();
let result = func(context, args).map(|_| ());
#[cfg(not(feature = "unchecked"))]
self.check_data_size(args[0], root.1)?;
return result;
}
}
let op_assign = op_assign.literal_syntax();
let op = op.literal_syntax();
match self.call_native_fn( match self.call_native_fn(
global, caches, lib, op_assign, hash, args, true, true, op_pos, level, global, caches, lib, op_assign, hash, args, true, true, *op_pos, level,
) { ) {
Ok(_) => { Ok(_) => {
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
@ -155,16 +172,13 @@ impl Engine {
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
{ {
// Expand to `var = var op rhs` // Expand to `var = var op rhs`
let (value, ..) = self *args[0] = self
.call_native_fn( .call_native_fn(
global, caches, lib, op, hash_op, args, true, false, op_pos, level, global, caches, lib, op, *hash_op, args, true, false, *op_pos, level,
) )
.map_err(|err| err.fill_position(op_info.pos))?; .map_err(|err| err.fill_position(op_info.pos))?
.0
#[cfg(not(feature = "unchecked"))] .flatten();
self.check_data_size(&value, root.1)?;
*args[0] = value.flatten();
} }
Err(err) => return Err(err), Err(err) => return Err(err),
} }
@ -269,7 +283,7 @@ impl Engine {
let lhs_ptr = &mut lhs_ptr; let lhs_ptr = &mut lhs_ptr;
self.eval_op_assignment( self.eval_op_assignment(
global, caches, lib, *op_info, lhs_ptr, root, rhs_val, level, global, caches, lib, op_info, lhs_ptr, root, rhs_val, level,
) )
.map(|_| Dynamic::UNIT) .map(|_| Dynamic::UNIT)
} else { } else {
@ -293,7 +307,7 @@ impl Engine {
rhs_val rhs_val
}; };
let _new_val = Some((rhs_val, *op_info)); let _new_val = Some((rhs_val, op_info));
// Must be either `var[index] op= val` or `var.prop op= val` // Must be either `var[index] op= val` or `var.prop op= val`
match lhs { match lhs {

View File

@ -399,6 +399,7 @@ impl Deref for Target<'_> {
type Target = Dynamic; type Target = Dynamic;
#[inline] #[inline]
#[must_use]
fn deref(&self) -> &Dynamic { fn deref(&self) -> &Dynamic {
match self { match self {
Self::RefMut(r) => r, Self::RefMut(r) => r,
@ -416,6 +417,7 @@ impl Deref for Target<'_> {
impl AsRef<Dynamic> for Target<'_> { impl AsRef<Dynamic> for Target<'_> {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &Dynamic { fn as_ref(&self) -> &Dynamic {
self self
} }
@ -423,6 +425,7 @@ impl AsRef<Dynamic> for Target<'_> {
impl DerefMut for Target<'_> { impl DerefMut for Target<'_> {
#[inline] #[inline]
#[must_use]
fn deref_mut(&mut self) -> &mut Dynamic { fn deref_mut(&mut self) -> &mut Dynamic {
match self { match self {
Self::RefMut(r) => r, Self::RefMut(r) => r,
@ -440,6 +443,7 @@ impl DerefMut for Target<'_> {
impl AsMut<Dynamic> for Target<'_> { impl AsMut<Dynamic> for Target<'_> {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_mut(&mut self) -> &mut Dynamic { fn as_mut(&mut self) -> &mut Dynamic {
self self
} }

View File

@ -2,7 +2,7 @@
use super::call::FnCallArgs; use super::call::FnCallArgs;
use super::native::FnBuiltin; use super::native::FnBuiltin;
use crate::engine::OP_CONTAINS; use crate::tokenizer::Token;
use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT}; use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT};
use std::any::TypeId; use std::any::TypeId;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
@ -67,7 +67,7 @@ fn is_numeric(type_id: TypeId) -> bool {
/// ///
/// The return function will be registered as a _method_, so the first parameter cannot be consumed. /// The return function will be registered as a _method_, so the first parameter cannot be consumed.
#[must_use] #[must_use]
pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> { pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
let type1 = x.type_id(); let type1 = x.type_id();
let type2 = y.type_id(); let type2 = y.type_id();
@ -131,46 +131,46 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
match op { match op {
"+" => return Some(impl_op!(INT => add(as_int, as_int))), Token::Plus => return Some(impl_op!(INT => add(as_int, as_int))),
"-" => return Some(impl_op!(INT => subtract(as_int, as_int))), Token::Minus => return Some(impl_op!(INT => subtract(as_int, as_int))),
"*" => return Some(impl_op!(INT => multiply(as_int, as_int))), Token::Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))),
"/" => return Some(impl_op!(INT => divide(as_int, as_int))), Token::Divide => return Some(impl_op!(INT => divide(as_int, as_int))),
"%" => return Some(impl_op!(INT => modulo(as_int, as_int))), Token::Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))),
"**" => return Some(impl_op!(INT => power(as_int, as_int))), Token::PowerOf => return Some(impl_op!(INT => power(as_int, as_int))),
">>" => return Some(impl_op!(INT => shift_right(as_int, as_int))), Token::RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))),
"<<" => return Some(impl_op!(INT => shift_left(as_int, as_int))), Token::LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))),
_ => (), _ => (),
} }
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
match op { match op {
"+" => return Some(impl_op!(INT => as_int + as_int)), Token::Plus => return Some(impl_op!(INT => as_int + as_int)),
"-" => return Some(impl_op!(INT => as_int - as_int)), Token::Minus => return Some(impl_op!(INT => as_int - as_int)),
"*" => return Some(impl_op!(INT => as_int * as_int)), Token::Multiply => return Some(impl_op!(INT => as_int * as_int)),
"/" => return Some(impl_op!(INT => as_int / as_int)), Token::Divide => return Some(impl_op!(INT => as_int / as_int)),
"%" => return Some(impl_op!(INT => as_int % as_int)), Token::Modulo => return Some(impl_op!(INT => as_int % as_int)),
"**" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), Token::PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
">>" => return Some(impl_op!(INT => as_int >> as_int)), Token::RightShift => return Some(impl_op!(INT => as_int >> as_int)),
"<<" => return Some(impl_op!(INT => as_int << as_int)), Token::LeftShift => return Some(impl_op!(INT => as_int << as_int)),
_ => (), _ => (),
} }
return match op { return match op {
"==" => Some(impl_op!(INT => as_int == as_int)), Token::EqualsTo => Some(impl_op!(INT => as_int == as_int)),
"!=" => Some(impl_op!(INT => as_int != as_int)), Token::NotEqualsTo => Some(impl_op!(INT => as_int != as_int)),
">" => Some(impl_op!(INT => as_int > as_int)), Token::GreaterThan => Some(impl_op!(INT => as_int > as_int)),
">=" => Some(impl_op!(INT => as_int >= as_int)), Token::GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)),
"<" => Some(impl_op!(INT => as_int < as_int)), Token::LessThan => Some(impl_op!(INT => as_int < as_int)),
"<=" => Some(impl_op!(INT => as_int <= as_int)), Token::LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)),
"&" => Some(impl_op!(INT => as_int & as_int)), Token::Ampersand => Some(impl_op!(INT => as_int & as_int)),
"|" => Some(impl_op!(INT => as_int | as_int)), Token::Pipe => Some(impl_op!(INT => as_int | as_int)),
"^" => Some(impl_op!(INT => as_int ^ as_int)), Token::XOr => Some(impl_op!(INT => as_int ^ as_int)),
".." => Some(|_, args| { Token::ExclusiveRange => Some(|_, args| {
let x = args[0].as_int().expect(BUILTIN); let x = args[0].as_int().expect(BUILTIN);
let y = args[1].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN);
Ok((x..y).into()) Ok((x..y).into())
}), }),
"..=" => Some(|_, args| { Token::InclusiveRange => Some(|_, args| {
let x = args[0].as_int().expect(BUILTIN); let x = args[0].as_int().expect(BUILTIN);
let y = args[1].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN);
Ok((x..=y).into()) Ok((x..=y).into())
@ -181,47 +181,46 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
if type1 == TypeId::of::<bool>() { if type1 == TypeId::of::<bool>() {
return match op { return match op {
"==" => Some(impl_op!(bool => as_bool == as_bool)), Token::EqualsTo => Some(impl_op!(bool => as_bool == as_bool)),
"!=" => Some(impl_op!(bool => as_bool != as_bool)), Token::NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)),
">" => Some(impl_op!(bool => as_bool > as_bool)), Token::GreaterThan => Some(impl_op!(bool => as_bool > as_bool)),
">=" => Some(impl_op!(bool => as_bool >= as_bool)), Token::GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)),
"<" => Some(impl_op!(bool => as_bool < as_bool)), Token::LessThan => Some(impl_op!(bool => as_bool < as_bool)),
"<=" => Some(impl_op!(bool => as_bool <= as_bool)), Token::LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)),
"&" => Some(impl_op!(bool => as_bool & as_bool)), Token::Ampersand => Some(impl_op!(bool => as_bool & as_bool)),
"|" => Some(impl_op!(bool => as_bool | as_bool)), Token::Pipe => Some(impl_op!(bool => as_bool | as_bool)),
"^" => Some(impl_op!(bool => as_bool ^ as_bool)), Token::XOr => Some(impl_op!(bool => as_bool ^ as_bool)),
_ => None, _ => None,
}; };
} }
if type1 == TypeId::of::<ImmutableString>() { if type1 == TypeId::of::<ImmutableString>() {
return match op { return match op {
"+" => Some(impl_op!(ImmutableString + ImmutableString)), Token::Plus => Some(impl_op!(ImmutableString + ImmutableString)),
"-" => Some(impl_op!(ImmutableString - ImmutableString)), Token::Minus => Some(impl_op!(ImmutableString - ImmutableString)),
"==" => Some(impl_op!(ImmutableString == ImmutableString)), Token::EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)),
"!=" => Some(impl_op!(ImmutableString != ImmutableString)), Token::NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)),
">" => Some(impl_op!(ImmutableString > ImmutableString)), Token::GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)),
">=" => Some(impl_op!(ImmutableString >= ImmutableString)), Token::GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)),
"<" => Some(impl_op!(ImmutableString < ImmutableString)), Token::LessThan => Some(impl_op!(ImmutableString < ImmutableString)),
"<=" => Some(impl_op!(ImmutableString <= ImmutableString)), Token::LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)),
OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))),
_ => None, _ => None,
}; };
} }
if type1 == TypeId::of::<char>() { if type1 == TypeId::of::<char>() {
return match op { return match op {
"+" => Some(|_, args| { Token::Plus => Some(|_, args| {
let x = args[0].as_char().expect(BUILTIN); let x = args[0].as_char().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
Ok(format!("{x}{y}").into()) Ok(format!("{x}{y}").into())
}), }),
"==" => Some(impl_op!(char => as_char == as_char)), Token::EqualsTo => Some(impl_op!(char => as_char == as_char)),
"!=" => Some(impl_op!(char => as_char != as_char)), Token::NotEqualsTo => Some(impl_op!(char => as_char != as_char)),
">" => Some(impl_op!(char => as_char > as_char)), Token::GreaterThan => Some(impl_op!(char => as_char > as_char)),
">=" => Some(impl_op!(char => as_char >= as_char)), Token::GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)),
"<" => Some(impl_op!(char => as_char < as_char)), Token::LessThan => Some(impl_op!(char => as_char < as_char)),
"<=" => Some(impl_op!(char => as_char <= as_char)), Token::LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)),
_ => None, _ => None,
}; };
} }
@ -231,7 +230,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
use crate::Blob; use crate::Blob;
return match op { return match op {
"+" => Some(|_, args| { Token::Plus => Some(|_, args| {
let blob1 = &*args[0].read_lock::<Blob>().expect(BUILTIN); let blob1 = &*args[0].read_lock::<Blob>().expect(BUILTIN);
let blob2 = &*args[1].read_lock::<Blob>().expect(BUILTIN); let blob2 = &*args[1].read_lock::<Blob>().expect(BUILTIN);
@ -245,16 +244,20 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
blob blob
})) }))
}), }),
"==" => Some(impl_op!(Blob == Blob)), Token::EqualsTo => Some(impl_op!(Blob == Blob)),
"!=" => Some(impl_op!(Blob != Blob)), Token::NotEqualsTo => Some(impl_op!(Blob != Blob)),
_ => None, _ => None,
}; };
} }
if type1 == TypeId::of::<()>() { if type1 == TypeId::of::<()>() {
return match op { return match op {
"==" => Some(|_, _| Ok(Dynamic::TRUE)), Token::EqualsTo => Some(|_, _| Ok(Dynamic::TRUE)),
"!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), Token::NotEqualsTo
| Token::GreaterThan
| Token::GreaterThanEqualsTo
| Token::LessThan
| Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)),
_ => None, _ => None,
}; };
} }
@ -265,18 +268,18 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
($x:ty, $xx:ident, $y:ty, $yy:ident) => { ($x:ty, $xx:ident, $y:ty, $yy:ident) => {
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
return match op { return match op {
"+" => Some(impl_op!(FLOAT => $xx + $yy)), Token::Plus => Some(impl_op!(FLOAT => $xx + $yy)),
"-" => Some(impl_op!(FLOAT => $xx - $yy)), Token::Minus => Some(impl_op!(FLOAT => $xx - $yy)),
"*" => Some(impl_op!(FLOAT => $xx * $yy)), Token::Multiply => Some(impl_op!(FLOAT => $xx * $yy)),
"/" => Some(impl_op!(FLOAT => $xx / $yy)), Token::Divide => Some(impl_op!(FLOAT => $xx / $yy)),
"%" => Some(impl_op!(FLOAT => $xx % $yy)), Token::Modulo => Some(impl_op!(FLOAT => $xx % $yy)),
"**" => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), Token::PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))),
"==" => Some(impl_op!(FLOAT => $xx == $yy)), Token::EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)),
"!=" => Some(impl_op!(FLOAT => $xx != $yy)), Token::NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)),
">" => Some(impl_op!(FLOAT => $xx > $yy)), Token::GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)),
">=" => Some(impl_op!(FLOAT => $xx >= $yy)), Token::GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)),
"<" => Some(impl_op!(FLOAT => $xx < $yy)), Token::LessThan => Some(impl_op!(FLOAT => $xx < $yy)),
"<=" => Some(impl_op!(FLOAT => $xx <= $yy)), Token::LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)),
_ => None, _ => None,
}; };
} }
@ -299,12 +302,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
match op { match op {
"+" => return Some(impl_op!(from Decimal => add($xx, $yy))), Token::Plus => return Some(impl_op!(from Decimal => add($xx, $yy))),
"-" => return Some(impl_op!(from Decimal => subtract($xx, $yy))), Token::Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))),
"*" => return Some(impl_op!(from Decimal => multiply($xx, $yy))), Token::Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))),
"/" => return Some(impl_op!(from Decimal => divide($xx, $yy))), Token::Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))),
"%" => return Some(impl_op!(from Decimal => modulo($xx, $yy))), Token::Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))),
"**" => return Some(impl_op!(from Decimal => power($xx, $yy))), Token::PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))),
_ => () _ => ()
} }
@ -313,22 +316,22 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
match op { match op {
"+" => return Some(impl_op!(from Decimal => $xx + $yy)), Token::Plus => return Some(impl_op!(from Decimal => $xx + $yy)),
"-" => return Some(impl_op!(from Decimal => $xx - $yy)), Token::Minus => return Some(impl_op!(from Decimal => $xx - $yy)),
"*" => return Some(impl_op!(from Decimal => $xx * $yy)), Token::Multiply => return Some(impl_op!(from Decimal => $xx * $yy)),
"/" => return Some(impl_op!(from Decimal => $xx / $yy)), Token::Divide => return Some(impl_op!(from Decimal => $xx / $yy)),
"%" => return Some(impl_op!(from Decimal => $xx % $yy)), Token::Modulo => return Some(impl_op!(from Decimal => $xx % $yy)),
"**" => return Some(impl_op!(from Decimal => $xx.powd($yy))), Token::PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))),
_ => () _ => ()
} }
return match op { return match op {
"==" => Some(impl_op!(from Decimal => $xx == $yy)), Token::EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)),
"!=" => Some(impl_op!(from Decimal => $xx != $yy)), Token::NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)),
">" => Some(impl_op!(from Decimal => $xx > $yy)), Token::GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)),
">=" => Some(impl_op!(from Decimal => $xx >= $yy)), Token::GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)),
"<" => Some(impl_op!(from Decimal => $xx < $yy)), Token::LessThan => Some(impl_op!(from Decimal => $xx < $yy)),
"<=" => Some(impl_op!(from Decimal => $xx <= $yy)), Token::LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)),
_ => None _ => None
}; };
} }
@ -354,17 +357,17 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
} }
return match op { return match op {
"+" => Some(|_, args| { Token::Plus => Some(|_, args| {
let x = args[0].as_char().expect(BUILTIN); let x = args[0].as_char().expect(BUILTIN);
let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN); let y = &*args[1].read_lock::<ImmutableString>().expect(BUILTIN);
Ok(format!("{x}{y}").into()) Ok(format!("{x}{y}").into())
}), }),
"==" => Some(impl_op!(get_s1s2(==))), Token::EqualsTo => Some(impl_op!(get_s1s2(==))),
"!=" => Some(impl_op!(get_s1s2(!=))), Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
">" => Some(impl_op!(get_s1s2(>))), Token::GreaterThan => Some(impl_op!(get_s1s2(>))),
">=" => Some(impl_op!(get_s1s2(>=))), Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))),
"<" => Some(impl_op!(get_s1s2(<))), Token::LessThan => Some(impl_op!(get_s1s2(<))),
"<=" => Some(impl_op!(get_s1s2(<=))), Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))),
_ => None, _ => None,
}; };
} }
@ -380,45 +383,48 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
} }
return match op { return match op {
"+" => Some(|_, args| { Token::Plus => Some(|_, args| {
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN); let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
Ok((x + y).into()) Ok((x + y).into())
}), }),
"-" => Some(|_, args| { Token::Minus => Some(|_, args| {
let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN); let x = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
Ok((x - y).into()) Ok((x - y).into())
}), }),
"==" => Some(impl_op!(get_s1s2(==))), Token::EqualsTo => Some(impl_op!(get_s1s2(==))),
"!=" => Some(impl_op!(get_s1s2(!=))), Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))),
">" => Some(impl_op!(get_s1s2(>))), Token::GreaterThan => Some(impl_op!(get_s1s2(>))),
">=" => Some(impl_op!(get_s1s2(>=))), Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))),
"<" => Some(impl_op!(get_s1s2(<))), Token::LessThan => Some(impl_op!(get_s1s2(<))),
"<=" => Some(impl_op!(get_s1s2(<=))), Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))),
OP_CONTAINS => Some(|_, args| {
let s = &*args[0].read_lock::<ImmutableString>().expect(BUILTIN);
let c = args[1].as_char().expect(BUILTIN);
Ok(s.contains(c).into())
}),
_ => None, _ => None,
}; };
} }
// () op string // () op string
if (type1, type2) == (TypeId::of::<()>(), TypeId::of::<ImmutableString>()) { if (type1, type2) == (TypeId::of::<()>(), TypeId::of::<ImmutableString>()) {
return match op { return match op {
"+" => Some(|_, args| Ok(args[1].clone())), Token::Plus => Some(|_, args| Ok(args[1].clone())),
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), Token::EqualsTo
"!=" => Some(|_, _| Ok(Dynamic::TRUE)), | Token::GreaterThan
| Token::GreaterThanEqualsTo
| Token::LessThan
| Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)),
Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)),
_ => None, _ => None,
}; };
} }
// string op () // string op ()
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<()>()) { if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<()>()) {
return match op { return match op {
"+" => Some(|_, args| Ok(args[0].clone())), Token::Plus => Some(|_, args| Ok(args[0].clone())),
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), Token::EqualsTo
"!=" => Some(|_, _| Ok(Dynamic::TRUE)), | Token::GreaterThan
| Token::GreaterThanEqualsTo
| Token::LessThan
| Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)),
Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)),
_ => None, _ => None,
}; };
} }
@ -428,19 +434,9 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
if type1 == TypeId::of::<crate::Blob>() { if type1 == TypeId::of::<crate::Blob>() {
use crate::Blob; use crate::Blob;
if type2 == TypeId::of::<INT>() {
return match op {
OP_CONTAINS => Some(|_, args| {
let blob = &*args[0].read_lock::<Blob>().expect(BUILTIN);
let x = (args[1].as_int().expect("`INT`") & 0x0000_00ff) as u8;
Ok((!blob.is_empty() && blob.contains(&x)).into())
}),
_ => None,
};
}
if type2 == TypeId::of::<char>() { if type2 == TypeId::of::<char>() {
return match op { return match op {
"+" => Some(|_, args| { Token::Plus => Some(|_, args| {
let mut buf = [0_u8; 4]; let mut buf = [0_u8; 4];
let mut blob = args[0].read_lock::<Blob>().expect(BUILTIN).clone(); let mut blob = args[0].read_lock::<Blob>().expect(BUILTIN).clone();
let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf); let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf);
@ -452,17 +448,6 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
} }
} }
// map op string
#[cfg(not(feature = "no_object"))]
if (type1, type2) == (TypeId::of::<crate::Map>(), TypeId::of::<ImmutableString>()) {
use crate::Map;
return match op {
OP_CONTAINS => Some(impl_op!(Map.contains_key(ImmutableString.as_str()))),
_ => None,
};
}
// Non-compatible ranges // Non-compatible ranges
if (type1, type2) if (type1, type2)
== ( == (
@ -476,48 +461,28 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
) )
{ {
return match op { return match op {
"!=" => Some(|_, _| Ok(Dynamic::TRUE)), Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)),
"==" => Some(|_, _| Ok(Dynamic::FALSE)), Token::Equals => Some(|_, _| Ok(Dynamic::FALSE)),
_ => None, _ => None,
}; };
} }
// Handle ranges here because ranges are implemented as custom type // Handle ranges here because ranges are implemented as custom type
if type1 == TypeId::of::<ExclusiveRange>() { if type1 == TypeId::of::<ExclusiveRange>() {
if type2 == TypeId::of::<INT>() {
return match op {
OP_CONTAINS => Some(|_, args| {
let range = &*args[0].read_lock::<ExclusiveRange>().expect(BUILTIN);
let x = args[1].as_int().expect("`INT`");
Ok(range.contains(&x).into())
}),
_ => None,
};
}
if type1 == type2 { if type1 == type2 {
return match op { return match op {
"==" => Some(impl_op!(ExclusiveRange == ExclusiveRange)), Token::EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)),
"!=" => Some(impl_op!(ExclusiveRange != ExclusiveRange)), Token::NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)),
_ => None, _ => None,
}; };
} }
} }
if type1 == TypeId::of::<InclusiveRange>() { if type1 == TypeId::of::<InclusiveRange>() {
if type2 == TypeId::of::<INT>() {
return match op {
OP_CONTAINS => Some(|_, args| {
let range = &*args[0].read_lock::<InclusiveRange>().expect(BUILTIN);
let x = args[1].as_int().expect("`INT`");
Ok(range.contains(&x).into())
}),
_ => None,
};
}
if type1 == type2 { if type1 == type2 {
return match op { return match op {
"==" => Some(impl_op!(InclusiveRange == InclusiveRange)), Token::EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)),
"!=" => Some(impl_op!(InclusiveRange != InclusiveRange)), Token::NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)),
_ => None, _ => None,
}; };
} }
@ -531,8 +496,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
} else if type1 != type2 { } else if type1 != type2 {
// If the types are not the same, default to not compare // If the types are not the same, default to not compare
match op { match op {
"!=" => Some(|_, _| Ok(Dynamic::TRUE)), Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)),
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), Token::EqualsTo
| Token::GreaterThan
| Token::GreaterThanEqualsTo
| Token::LessThan
| Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)),
_ => None, _ => None,
} }
} else { } else {
@ -544,8 +513,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
// Default comparison operators for different types // Default comparison operators for different types
if type2 != type1 { if type2 != type1 {
return match op { return match op {
"!=" => Some(|_, _| Ok(Dynamic::TRUE)), Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)),
"==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), Token::EqualsTo
| Token::GreaterThan
| Token::GreaterThanEqualsTo
| Token::LessThan
| Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)),
_ => None, _ => None,
}; };
} }
@ -558,7 +531,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
/// ///
/// The return function is registered as a _method_, so the first parameter cannot be consumed. /// The return function is registered as a _method_, so the first parameter cannot be consumed.
#[must_use] #[must_use]
pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> { pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
let type1 = x.type_id(); let type1 = x.type_id();
let type2 = y.type_id(); let type2 = y.type_id();
@ -610,49 +583,51 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
match op { match op {
"+=" => return Some(impl_op!(INT => add(as_int, as_int))), Token::PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))),
"-=" => return Some(impl_op!(INT => subtract(as_int, as_int))), Token::MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))),
"*=" => return Some(impl_op!(INT => multiply(as_int, as_int))), Token::MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))),
"/=" => return Some(impl_op!(INT => divide(as_int, as_int))), Token::DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))),
"%=" => return Some(impl_op!(INT => modulo(as_int, as_int))), Token::ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))),
"**=" => return Some(impl_op!(INT => power(as_int, as_int))), Token::PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))),
">>=" => return Some(impl_op!(INT => shift_right(as_int, as_int))), Token::RightShiftAssign => {
"<<=" => return Some(impl_op!(INT => shift_left(as_int, as_int))), return Some(impl_op!(INT => shift_right(as_int, as_int)))
}
Token::LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))),
_ => (), _ => (),
} }
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
match op { match op {
"+=" => return Some(impl_op!(INT += as_int)), Token::PlusAssign => return Some(impl_op!(INT += as_int)),
"-=" => return Some(impl_op!(INT -= as_int)), Token::MinusAssign => return Some(impl_op!(INT -= as_int)),
"*=" => return Some(impl_op!(INT *= as_int)), Token::MultiplyAssign => return Some(impl_op!(INT *= as_int)),
"/=" => return Some(impl_op!(INT /= as_int)), Token::DivideAssign => return Some(impl_op!(INT /= as_int)),
"%=" => return Some(impl_op!(INT %= as_int)), Token::ModuloAssign => return Some(impl_op!(INT %= as_int)),
"**=" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), Token::PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))),
">>=" => return Some(impl_op!(INT >>= as_int)), Token::RightShiftAssign => return Some(impl_op!(INT >>= as_int)),
"<<=" => return Some(impl_op!(INT <<= as_int)), Token::LeftShiftAssign => return Some(impl_op!(INT <<= as_int)),
_ => (), _ => (),
} }
return match op { return match op {
"&=" => Some(impl_op!(INT &= as_int)), Token::AndAssign => Some(impl_op!(INT &= as_int)),
"|=" => Some(impl_op!(INT |= as_int)), Token::OrAssign => Some(impl_op!(INT |= as_int)),
"^=" => Some(impl_op!(INT ^= as_int)), Token::XOrAssign => Some(impl_op!(INT ^= as_int)),
_ => None, _ => None,
}; };
} }
if type1 == TypeId::of::<bool>() { if type1 == TypeId::of::<bool>() {
return match op { return match op {
"&=" => Some(impl_op!(bool = x && as_bool)), Token::AndAssign => Some(impl_op!(bool = x && as_bool)),
"|=" => Some(impl_op!(bool = x || as_bool)), Token::OrAssign => Some(impl_op!(bool = x || as_bool)),
_ => None, _ => None,
}; };
} }
if type1 == TypeId::of::<char>() { if type1 == TypeId::of::<char>() {
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let y = args[1].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN);
let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN); let x = &mut *args[0].write_lock::<Dynamic>().expect(BUILTIN);
Ok((*x = format!("{x}{y}").into()).into()) Ok((*x = format!("{x}{y}").into()).into())
@ -663,13 +638,13 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
if type1 == TypeId::of::<ImmutableString>() { if type1 == TypeId::of::<ImmutableString>() {
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let (first, second) = args.split_first_mut().expect(BUILTIN); let (first, second) = args.split_first_mut().expect(BUILTIN);
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN); let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
let y = std::mem::take(second[0]).cast::<ImmutableString>(); let y = std::mem::take(second[0]).cast::<ImmutableString>();
Ok((*x += y).into()) Ok((*x += y).into())
}), }),
"-=" => Some(|_, args| { Token::MinusAssign => Some(|_, args| {
let (first, second) = args.split_first_mut().expect(BUILTIN); let (first, second) = args.split_first_mut().expect(BUILTIN);
let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN); let x = &mut *first.write_lock::<ImmutableString>().expect(BUILTIN);
let y = std::mem::take(second[0]).cast::<ImmutableString>(); let y = std::mem::take(second[0]).cast::<ImmutableString>();
@ -684,7 +659,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
use crate::Blob; use crate::Blob;
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let blob2 = std::mem::take(args[1]).cast::<Blob>(); let blob2 = std::mem::take(args[1]).cast::<Blob>();
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN); let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into())
@ -699,12 +674,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
($x:ident, $xx:ident, $y:ty, $yy:ident) => { ($x:ident, $xx:ident, $y:ty, $yy:ident) => {
if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) {
return match op { return match op {
"+=" => Some(impl_op!($x += $yy)), Token::PlusAssign => Some(impl_op!($x += $yy)),
"-=" => Some(impl_op!($x -= $yy)), Token::MinusAssign => Some(impl_op!($x -= $yy)),
"*=" => Some(impl_op!($x *= $yy)), Token::MultiplyAssign => Some(impl_op!($x *= $yy)),
"/=" => Some(impl_op!($x /= $yy)), Token::DivideAssign => Some(impl_op!($x /= $yy)),
"%=" => Some(impl_op!($x %= $yy)), Token::ModuloAssign => Some(impl_op!($x %= $yy)),
"**=" => Some(impl_op!($x => $xx.powf($yy as $x))), Token::PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))),
_ => None, _ => None,
}; };
} }
@ -726,12 +701,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
#[cfg(not(feature = "unchecked"))] #[cfg(not(feature = "unchecked"))]
return match op { return match op {
"+=" => Some(impl_op!(from $x => add($xx, $yy))), Token::PlusAssign => Some(impl_op!(from $x => add($xx, $yy))),
"-=" => Some(impl_op!(from $x => subtract($xx, $yy))), Token::MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))),
"*=" => Some(impl_op!(from $x => multiply($xx, $yy))), Token::MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))),
"/=" => Some(impl_op!(from $x => divide($xx, $yy))), Token::DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))),
"%=" => Some(impl_op!(from $x => modulo($xx, $yy))), Token::ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))),
"**=" => Some(impl_op!(from $x => power($xx, $yy))), Token::PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))),
_ => None, _ => None,
}; };
@ -740,12 +715,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
#[cfg(feature = "unchecked")] #[cfg(feature = "unchecked")]
return match op { return match op {
"+=" => Some(impl_op!(from $x += $yy)), Token::PlusAssign => Some(impl_op!(from $x += $yy)),
"-=" => Some(impl_op!(from $x -= $yy)), Token::MinusAssign => Some(impl_op!(from $x -= $yy)),
"*=" => Some(impl_op!(from $x *= $yy)), Token::MultiplyAssign => Some(impl_op!(from $x *= $yy)),
"/=" => Some(impl_op!(from $x /= $yy)), Token::DivideAssign => Some(impl_op!(from $x /= $yy)),
"%=" => Some(impl_op!(from $x %= $yy)), Token::ModuloAssign => Some(impl_op!(from $x %= $yy)),
"**=" => Some(impl_op!(from $x => $xx.powd($yy))), Token::PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))),
_ => None, _ => None,
}; };
} }
@ -761,15 +736,15 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
// string op= char // string op= char
if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) { if (type1, type2) == (TypeId::of::<ImmutableString>(), TypeId::of::<char>()) {
return match op { return match op {
"+=" => Some(impl_op!(ImmutableString += as_char as char)), Token::PlusAssign => Some(impl_op!(ImmutableString += as_char as char)),
"-=" => Some(impl_op!(ImmutableString -= as_char as char)), Token::MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)),
_ => None, _ => None,
}; };
} }
// char op= string // char op= string
if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) { if (type1, type2) == (TypeId::of::<char>(), TypeId::of::<ImmutableString>()) {
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let mut ch = args[0].as_char().expect(BUILTIN).to_string(); let mut ch = args[0].as_char().expect(BUILTIN).to_string();
ch.push_str( ch.push_str(
args[1] args[1]
@ -793,7 +768,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
if type2 == TypeId::of::<crate::Array>() { if type2 == TypeId::of::<crate::Array>() {
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let array2 = std::mem::take(args[1]).cast::<Array>(); let array2 = std::mem::take(args[1]).cast::<Array>();
let array1 = &mut *args[0].write_lock::<Array>().expect(BUILTIN); let array1 = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
Ok(append(array1, array2).into()) Ok(append(array1, array2).into())
@ -802,7 +777,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
}; };
} }
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let x = std::mem::take(args[1]); let x = std::mem::take(args[1]);
let array = &mut *args[0].write_lock::<Array>().expect(BUILTIN); let array = &mut *args[0].write_lock::<Array>().expect(BUILTIN);
Ok(push(array, x).into()) Ok(push(array, x).into())
@ -818,7 +793,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
// blob op= int // blob op= int
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<INT>()) { if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<INT>()) {
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let x = args[1].as_int().expect("`INT`"); let x = args[1].as_int().expect("`INT`");
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN); let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into()) Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into())
@ -830,7 +805,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
// blob op= char // blob op= char
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<char>()) { if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<char>()) {
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let x = args[1].as_char().expect("`char`"); let x = args[1].as_char().expect("`char`");
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN); let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into()) Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into())
@ -842,7 +817,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
// blob op= string // blob op= string
if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) { if (type1, type2) == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) {
return match op { return match op {
"+=" => Some(|_, args| { Token::PlusAssign => Some(|_, args| {
let s = std::mem::take(args[1]).cast::<ImmutableString>(); let s = std::mem::take(args[1]).cast::<ImmutableString>();
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN); let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into()) Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into())

View File

@ -9,6 +9,7 @@ use crate::engine::{
KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
}; };
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
use crate::tokenizer::Token;
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr,
ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf,
@ -191,7 +192,7 @@ impl Engine {
hash_base: u64, hash_base: u64,
args: Option<&mut FnCallArgs>, args: Option<&mut FnCallArgs>,
allow_dynamic: bool, allow_dynamic: bool,
is_op_assignment: bool, op_assignment_token: Option<&Token>,
) -> Option<&'s FnResolutionCacheEntry> { ) -> Option<&'s FnResolutionCacheEntry> {
if hash_base == 0 { if hash_base == 0 {
return None; return None;
@ -279,22 +280,23 @@ impl Engine {
// Try to find a built-in version // Try to find a built-in version
let builtin = args.and_then(|args| { let builtin = args.and_then(|args| {
if is_op_assignment { if let Some(op_assign) = op_assignment_token {
let (first_arg, rest_args) = args.split_first().unwrap(); let (first_arg, rest_args) = args.split_first().unwrap();
get_builtin_op_assignment_fn(fn_name, *first_arg, rest_args[0]).map( get_builtin_op_assignment_fn(op_assign, *first_arg, rest_args[0])
|f| FnResolutionCacheEntry { .map(|f| FnResolutionCacheEntry {
func: CallableFunction::from_fn_builtin(f), func: CallableFunction::from_fn_builtin(f),
source: None, source: None,
}, })
) } else if let Some(ref operator) = Token::lookup_from_syntax(fn_name) {
} else { get_builtin_binary_op_fn(operator, args[0], args[1]).map(|f| {
get_builtin_binary_op_fn(fn_name, args[0], args[1]).map(|f| {
FnResolutionCacheEntry { FnResolutionCacheEntry {
func: CallableFunction::from_fn_builtin(f), func: CallableFunction::from_fn_builtin(f),
source: None, source: None,
} }
}) })
} else {
None
} }
}); });
@ -360,6 +362,11 @@ impl Engine {
self.inc_operations(&mut global.num_operations, pos)?; self.inc_operations(&mut global.num_operations, pos)?;
let parent_source = global.source.clone(); let parent_source = global.source.clone();
let op_assign = if is_op_assign {
Token::lookup_from_syntax(name)
} else {
None
};
// Check if function access already in the cache // Check if function access already in the cache
let local_entry = &mut None; let local_entry = &mut None;
@ -373,7 +380,7 @@ impl Engine {
hash, hash,
Some(args), Some(args),
true, true,
is_op_assign, op_assign.as_ref(),
); );
if func.is_some() { if func.is_some() {
@ -653,7 +660,7 @@ impl Engine {
hashes.script, hashes.script,
None, None,
false, false,
false, None,
) )
.cloned() .cloned()
{ {
@ -993,7 +1000,7 @@ impl Engine {
args_expr: &[Expr], args_expr: &[Expr],
hashes: FnCallHashes, hashes: FnCallHashes,
capture_scope: bool, capture_scope: bool,
is_operator: bool, operator_token: Option<&Token>,
pos: Position, pos: Position,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
@ -1006,7 +1013,7 @@ impl Engine {
let redirected; // Handle call() - Redirect function call let redirected; // Handle call() - Redirect function call
match name { match name {
_ if is_operator => (), _ if operator_token.is_some() => (),
// Handle call() // Handle call()
KEYWORD_FN_PTR_CALL if total_args >= 1 => { KEYWORD_FN_PTR_CALL if total_args >= 1 => {

View File

@ -197,7 +197,7 @@ impl CallableFunction {
Self::Script(..) => None, Self::Script(..) => None,
} }
} }
/// Create a new [`CallableFunction::Method`] from `FnBuiltin`. /// Create a new [`CallableFunction::Method`] from a [built-in function][`FnBuiltin`].
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn from_fn_builtin(func: FnBuiltin) -> Self { pub fn from_fn_builtin(func: FnBuiltin) -> Self {

View File

@ -194,8 +194,8 @@ impl FileModuleResolver {
/// Get a reference to the file module resolver's [scope][Scope]. /// Get a reference to the file module resolver's [scope][Scope].
/// ///
/// The [scope][Scope] is used for compiling module scripts. /// The [scope][Scope] is used for compiling module scripts.
#[must_use]
#[inline(always)] #[inline(always)]
#[must_use]
pub const fn scope(&self) -> &Scope { pub const fn scope(&self) -> &Scope {
&self.scope &self.scope
} }
@ -211,8 +211,8 @@ impl FileModuleResolver {
/// Get a mutable reference to the file module resolver's [scope][Scope]. /// Get a mutable reference to the file module resolver's [scope][Scope].
/// ///
/// The [scope][Scope] is used for compiling module scripts. /// The [scope][Scope] is used for compiling module scripts.
#[must_use]
#[inline(always)] #[inline(always)]
#[must_use]
pub fn scope_mut(&mut self) -> &mut Scope<'static> { pub fn scope_mut(&mut self) -> &mut Scope<'static> {
&mut self.scope &mut self.scope
} }

View File

@ -1181,8 +1181,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
return; return;
} }
// Overloaded operators can override built-in. // Overloaded operators can override built-in.
_ if x.args.len() == 2 && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { _ if x.args.len() == 2 && x.operator_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => {
if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1]) if let Some(result) = get_builtin_binary_op_fn(x.operator_token.as_ref().unwrap(), &arg_values[0], &arg_values[1])
.and_then(|f| { .and_then(|f| {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let lib = state.lib; let lib = state.lib;
@ -1229,12 +1229,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
=> { => {
// First search for script-defined functions (can override built-in) // First search for script-defined functions (can override built-in)
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
let has_script_fn = state.lib.iter().copied().any(|m| m.get_script_fn(&x.name, x.args.len()).is_some()); let has_script_fn = state.lib.iter().find_map(|&m| m.get_script_fn(&x.name, x.args.len())).is_some();
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
let has_script_fn = false; let has_script_fn = false;
if !has_script_fn { if !has_script_fn {
let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::<StaticVec<_>>(); let arg_values = &mut x.args.iter().map(Expr::get_literal_value).collect::<Option<StaticVec<_>>>().unwrap();
let result = match x.name.as_str() { let result = match x.name.as_str() {
KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()),

View File

@ -146,6 +146,21 @@ pub mod blob_functions {
pub fn is_empty(blob: &mut Blob) -> bool { pub fn is_empty(blob: &mut Blob) -> bool {
blob.len() == 0 blob.len() == 0
} }
/// Return `true` if the BLOB contains a specified byte value.
///
/// # Example
///
/// ```rhai
/// let text = "hello, world!";
///
/// print(text.contains('h')); // prints true
///
/// print(text.contains('x')); // prints false
/// ```
#[rhai_fn(name = "contains")]
pub fn contains(blob: &mut Blob, value: INT) -> bool {
blob.contains(&((value & 0x0000_00ff) as u8))
}
/// Get the byte value at the `index` position in the BLOB. /// Get the byte value at the `index` position in the BLOB.
/// ///
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element). /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element).

View File

@ -668,6 +668,12 @@ mod range_functions {
pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool { pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool {
range.is_empty() range.is_empty()
} }
/// Return `true` if the range contains a specified value.
#[rhai_fn(name = "contains")]
pub fn contains_exclusive(range: &mut ExclusiveRange, value: INT) -> bool {
range.contains(&value)
}
/// Return the start of the inclusive range. /// Return the start of the inclusive range.
#[rhai_fn(get = "start", name = "start", pure)] #[rhai_fn(get = "start", name = "start", pure)]
pub fn start_inclusive(range: &mut InclusiveRange) -> INT { pub fn start_inclusive(range: &mut InclusiveRange) -> INT {
@ -695,4 +701,9 @@ mod range_functions {
pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool { pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool {
range.is_empty() range.is_empty()
} }
/// Return `true` if the range contains a specified value.
#[rhai_fn(name = "contains")]
pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool {
range.contains(&value)
}
} }

View File

@ -30,6 +30,20 @@ mod map_functions {
pub fn is_empty(map: &mut Map) -> bool { pub fn is_empty(map: &mut Map) -> bool {
map.len() == 0 map.len() == 0
} }
/// Returns `true` if the object map contains a specified property.
///
/// # Example
///
/// ```rhai
/// let m = #{a: 1, b: 2, c: 3};
///
/// print(m.contains("b")); // prints true
///
/// print(m.contains("x")); // prints false
/// ```
pub fn contains(map: &mut Map, property: &str) -> bool {
map.contains_key(property)
}
/// Get the value of the `property` in the object map and return a copy. /// Get the value of the `property` in the object map and return a copy.
/// ///
/// If `property` does not exist in the object map, `()` is returned. /// If `property` does not exist in the object map, `()` is returned.

View File

@ -506,6 +506,37 @@ mod string_functions {
*character = to_lower_char(*character); *character = to_lower_char(*character);
} }
/// Return `true` if the string contains a specified string.
///
/// # Example
///
/// ```rhai
/// let text = "hello, world!";
///
/// print(text.contains("hello")); // prints true
///
/// print(text.contains("hey")); // prints false
/// ```
pub fn contains(string: &str, match_string: &str) -> bool {
string.contains(match_string)
}
/// Return `true` if the string contains a specified character.
///
/// # Example
///
/// ```rhai
/// let text = "hello, world!";
///
/// print(text.contains('h')); // prints true
///
/// print(text.contains('x')); // prints false
/// ```
#[rhai_fn(name = "contains")]
pub fn contains_char(string: &str, character: char) -> bool {
string.contains(character).into()
}
/// Return `true` if the string starts with a specified string. /// Return `true` if the string starts with a specified string.
/// ///
/// # Example /// # Example

View File

@ -472,7 +472,7 @@ fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) {
fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> { fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> {
match input.next().expect(NEVER_ENDS) { match input.next().expect(NEVER_ENDS) {
// Variable name // Variable name
(Token::Identifier(s), pos) => Ok((s, pos)), (Token::Identifier(s), pos) => Ok((*s, pos)),
// Reserved keyword // Reserved keyword
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
Err(PERR::Reserved(s.to_string()).into_err(pos)) Err(PERR::Reserved(s.to_string()).into_err(pos))
@ -492,7 +492,7 @@ fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)>
// Symbol // Symbol
(token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)), (token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)),
// Reserved symbol // Reserved symbol
(Token::Reserved(s), pos) if !is_valid_identifier(s.chars()) => Ok((s, pos)), (Token::Reserved(s), pos) if !is_valid_identifier(s.chars()) => Ok((*s, pos)),
// Bad symbol // Bad symbol
(Token::LexError(err), pos) => Err(err.into_err(pos)), (Token::LexError(err), pos) => Err(err.into_err(pos)),
// Not a symbol // Not a symbol
@ -616,7 +616,7 @@ impl Engine {
return Ok(FnCallExpr { return Ok(FnCallExpr {
name: state.get_interned_string(id), name: state.get_interned_string(id),
capture_parent_scope, capture_parent_scope,
is_native_operator: false, operator_token: None,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
hashes, hashes,
@ -684,7 +684,7 @@ impl Engine {
return Ok(FnCallExpr { return Ok(FnCallExpr {
name: state.get_interned_string(id), name: state.get_interned_string(id),
capture_parent_scope, capture_parent_scope,
is_native_operator: false, operator_token: None,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
hashes, hashes,
@ -1000,10 +1000,10 @@ impl Engine {
let (name, pos) = match input.next().expect(NEVER_ENDS) { let (name, pos) = match input.next().expect(NEVER_ENDS) {
(Token::Identifier(s) | Token::StringConstant(s), pos) => { (Token::Identifier(s) | Token::StringConstant(s), pos) => {
if map.iter().any(|(p, ..)| **p == s) { if map.iter().any(|(p, ..)| **p == *s) {
return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos)); return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos));
} }
(s, pos) (*s, pos)
} }
(Token::InterpolatedString(..), pos) => { (Token::InterpolatedString(..), pos) => {
return Err(PERR::PropertyExpected.into_err(pos)) return Err(PERR::PropertyExpected.into_err(pos))
@ -1342,7 +1342,7 @@ impl Engine {
Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos), Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos),
Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos),
Token::StringConstant(s) => { Token::StringConstant(s) => {
Expr::StringConstant(state.get_interned_string(s), settings.pos) Expr::StringConstant(state.get_interned_string(*s), settings.pos)
} }
Token::True => Expr::BoolConstant(true, settings.pos), Token::True => Expr::BoolConstant(true, settings.pos),
Token::False => Expr::BoolConstant(false, settings.pos), Token::False => Expr::BoolConstant(false, settings.pos),
@ -1356,7 +1356,7 @@ impl Engine {
} }
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
Token::DecimalConstant(x) => { Token::DecimalConstant(x) => {
let x = (*x).into(); let x = (**x).into();
input.next(); input.next();
Expr::DynamicConstant(Box::new(x), settings.pos) Expr::DynamicConstant(Box::new(x), settings.pos)
} }
@ -1479,7 +1479,7 @@ impl Engine {
match input.next().expect(NEVER_ENDS) { match input.next().expect(NEVER_ENDS) {
(Token::InterpolatedString(s), ..) if s.is_empty() => (), (Token::InterpolatedString(s), ..) if s.is_empty() => (),
(Token::InterpolatedString(s), pos) => { (Token::InterpolatedString(s), pos) => {
segments.push(Expr::StringConstant(s.into(), pos)) segments.push(Expr::StringConstant(state.get_interned_string(*s), pos))
} }
token => { token => {
unreachable!("Token::InterpolatedString expected but gets {:?}", token) unreachable!("Token::InterpolatedString expected but gets {:?}", token)
@ -1502,14 +1502,16 @@ impl Engine {
match input.next().expect(NEVER_ENDS) { match input.next().expect(NEVER_ENDS) {
(Token::StringConstant(s), pos) => { (Token::StringConstant(s), pos) => {
if !s.is_empty() { if !s.is_empty() {
segments.push(Expr::StringConstant(s.into(), pos)); segments
.push(Expr::StringConstant(state.get_interned_string(*s), pos));
} }
// End the interpolated string if it is terminated by a back-tick. // End the interpolated string if it is terminated by a back-tick.
break; break;
} }
(Token::InterpolatedString(s), pos) => { (Token::InterpolatedString(s), pos) => {
if !s.is_empty() { if !s.is_empty() {
segments.push(Expr::StringConstant(s.into(), pos)); segments
.push(Expr::StringConstant(state.get_interned_string(*s), pos));
} }
} }
(Token::LexError(err), pos) (Token::LexError(err), pos)
@ -1574,7 +1576,7 @@ impl Engine {
state.allow_capture = true; state.allow_capture = true;
} }
Expr::Variable( Expr::Variable(
(None, ns, 0, state.get_interned_string(s)).into(), (None, ns, 0, state.get_interned_string(*s)).into(),
None, None,
settings.pos, settings.pos,
) )
@ -1587,7 +1589,7 @@ impl Engine {
// Once the identifier consumed we must enable next variables capturing // Once the identifier consumed we must enable next variables capturing
state.allow_capture = true; state.allow_capture = true;
} }
let name = state.get_interned_string(s); let name = state.get_interned_string(*s);
Expr::Variable((None, ns, 0, name).into(), None, settings.pos) Expr::Variable((None, ns, 0, name).into(), None, settings.pos)
} }
// Normal variable access // Normal variable access
@ -1612,7 +1614,7 @@ impl Engine {
None None
} }
}); });
let name = state.get_interned_string(s); let name = state.get_interned_string(*s);
Expr::Variable((index, ns, 0, name).into(), short_index, settings.pos) Expr::Variable((index, ns, 0, name).into(), short_index, settings.pos)
} }
} }
@ -1634,7 +1636,7 @@ impl Engine {
// Function call is allowed to have reserved keyword // Function call is allowed to have reserved keyword
Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s) => { Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s) => {
Expr::Variable( Expr::Variable(
(None, ns, 0, state.get_interned_string(s)).into(), (None, ns, 0, state.get_interned_string(*s)).into(),
None, None,
settings.pos, settings.pos,
) )
@ -1642,7 +1644,7 @@ impl Engine {
// Access to `this` as a variable is OK within a function scope // Access to `this` as a variable is OK within a function scope
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
_ if &*s == KEYWORD_THIS && settings.in_fn_scope => Expr::Variable( _ if &*s == KEYWORD_THIS && settings.in_fn_scope => Expr::Variable(
(None, ns, 0, state.get_interned_string(s)).into(), (None, ns, 0, state.get_interned_string(*s)).into(),
None, None,
settings.pos, settings.pos,
), ),
@ -1888,7 +1890,7 @@ impl Engine {
// -expr // -expr
Token::Minus | Token::UnaryMinus => { Token::Minus | Token::UnaryMinus => {
let token = token.clone(); let token = token.clone();
let pos = eat_token(input, token); let pos = eat_token(input, token.clone());
match self.parse_unary(input, state, lib, settings.level_up())? { match self.parse_unary(input, state, lib, settings.level_up())? {
// Negative integer // Negative integer
@ -1918,7 +1920,7 @@ impl Engine {
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
args, args,
pos, pos,
is_native_operator: true, operator_token: Some(token),
..Default::default() ..Default::default()
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
@ -1928,7 +1930,7 @@ impl Engine {
// +expr // +expr
Token::Plus | Token::UnaryPlus => { Token::Plus | Token::UnaryPlus => {
let token = token.clone(); let token = token.clone();
let pos = eat_token(input, token); let pos = eat_token(input, token.clone());
match self.parse_unary(input, state, lib, settings.level_up())? { match self.parse_unary(input, state, lib, settings.level_up())? {
expr @ Expr::IntegerConstant(..) => Ok(expr), expr @ Expr::IntegerConstant(..) => Ok(expr),
@ -1946,7 +1948,7 @@ impl Engine {
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
args, args,
pos, pos,
is_native_operator: true, operator_token: Some(token),
..Default::default() ..Default::default()
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
@ -1955,7 +1957,9 @@ impl Engine {
} }
// !expr // !expr
Token::Bang => { Token::Bang => {
let token = token.clone();
let pos = eat_token(input, Token::Bang); let pos = eat_token(input, Token::Bang);
let mut args = StaticVec::new_const(); let mut args = StaticVec::new_const();
args.push(self.parse_unary(input, state, lib, settings.level_up())?); args.push(self.parse_unary(input, state, lib, settings.level_up())?);
args.shrink_to_fit(); args.shrink_to_fit();
@ -1965,7 +1969,7 @@ impl Engine {
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
args, args,
pos, pos,
is_native_operator: true, operator_token: Some(token),
..Default::default() ..Default::default()
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
@ -2283,7 +2287,7 @@ impl Engine {
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Token::Custom(c) => self Token::Custom(c) => self
.custom_keywords .custom_keywords
.get(c) .get(&**c)
.copied() .copied()
.ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*current_pos))?, .ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*current_pos))?,
Token::Reserved(c) if !is_valid_identifier(c.chars()) => { Token::Reserved(c) if !is_valid_identifier(c.chars()) => {
@ -2308,7 +2312,7 @@ impl Engine {
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Token::Custom(c) => self Token::Custom(c) => self
.custom_keywords .custom_keywords
.get(c) .get(&**c)
.copied() .copied()
.ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*next_pos))?, .ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*next_pos))?,
Token::Reserved(c) if !is_valid_identifier(c.chars()) => { Token::Reserved(c) if !is_valid_identifier(c.chars()) => {
@ -2335,12 +2339,17 @@ impl Engine {
let op = op_token.syntax(); let op = op_token.syntax();
let hash = calc_fn_hash(None, &op, 2); let hash = calc_fn_hash(None, &op, 2);
let operator_token = if is_valid_function_name(&op) {
None
} else {
Some(op_token.clone())
};
let op_base = FnCallExpr { let op_base = FnCallExpr {
name: state.get_interned_string(op.as_ref()), name: state.get_interned_string(op.as_ref()),
hashes: FnCallHashes::from_native(hash), hashes: FnCallHashes::from_native(hash),
pos, pos,
is_native_operator: !is_valid_function_name(&op), operator_token,
..Default::default() ..Default::default()
}; };
@ -2461,7 +2470,7 @@ impl Engine {
use crate::api::custom_syntax::markers::*; use crate::api::custom_syntax::markers::*;
let mut settings = settings; let mut settings = settings;
let mut inputs = StaticVec::<Expr>::new(); let mut inputs = StaticVec::new_const();
let mut segments = StaticVec::new_const(); let mut segments = StaticVec::new_const();
let mut tokens = StaticVec::new_const(); let mut tokens = StaticVec::new_const();
@ -2578,7 +2587,7 @@ impl Engine {
}, },
CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) {
(Token::StringConstant(s), pos) => { (Token::StringConstant(s), pos) => {
let s = state.get_interned_string(s); let s = state.get_interned_string(*s);
inputs.push(Expr::StringConstant(s.clone(), pos)); inputs.push(Expr::StringConstant(s.clone(), pos));
segments.push(s); segments.push(s);
tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_STRING)); tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_STRING));
@ -3228,7 +3237,7 @@ impl Engine {
match input.next().expect(NEVER_ENDS).0 { match input.next().expect(NEVER_ENDS).0 {
Token::Comment(comment) => { Token::Comment(comment) => {
comments.push(comment); comments.push(*comment);
match input.peek().expect(NEVER_ENDS) { match input.peek().expect(NEVER_ENDS) {
(Token::Fn | Token::Private, ..) => break, (Token::Fn | Token::Private, ..) => break,
@ -3562,7 +3571,7 @@ impl Engine {
return Err(PERR::FnDuplicatedParam(name.to_string(), s.to_string()) return Err(PERR::FnDuplicatedParam(name.to_string(), s.to_string())
.into_err(pos)); .into_err(pos));
} }
let s = state.get_interned_string(s); let s = state.get_interned_string(*s);
state.stack.push(s.clone(), ()); state.stack.push(s.clone(), ());
params.push((s, pos)); params.push((s, pos));
} }
@ -3700,7 +3709,7 @@ impl Engine {
PERR::FnDuplicatedParam(String::new(), s.to_string()).into_err(pos) PERR::FnDuplicatedParam(String::new(), s.to_string()).into_err(pos)
); );
} }
let s = state.get_interned_string(s); let s = state.get_interned_string(*s);
state.stack.push(s.clone(), ()); state.stack.push(s.clone(), ());
params_list.push(s); params_list.push(s);
} }

View File

@ -8,12 +8,17 @@ use serde::{Deserialize, Deserializer};
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{any::type_name, fmt}; use std::{any::type_name, fmt};
/// Deserializer for [`Dynamic`][crate::Dynamic] which is kept as a reference. /// Deserializer for [`Dynamic`][crate::Dynamic].
/// pub struct DynamicDeserializer<'de>(&'de Dynamic);
/// The reference is necessary because the deserialized type may hold references
/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic]. impl<'de> IntoDeserializer<'de, RhaiError> for &'de Dynamic {
struct DynamicDeserializer<'a> { type Deserializer = DynamicDeserializer<'de>;
value: &'a Dynamic,
#[inline(always)]
#[must_use]
fn into_deserializer(self) -> Self::Deserializer {
DynamicDeserializer(self)
}
} }
impl<'de> DynamicDeserializer<'de> { impl<'de> DynamicDeserializer<'de> {
@ -21,28 +26,28 @@ impl<'de> DynamicDeserializer<'de> {
/// ///
/// The reference is necessary because the deserialized type may hold references /// The reference is necessary because the deserialized type may hold references
/// (especially `&str`) to the source [`Dynamic`][crate::Dynamic]. /// (especially `&str`) to the source [`Dynamic`][crate::Dynamic].
#[inline(always)]
#[must_use] #[must_use]
pub const fn from_dynamic(value: &'de Dynamic) -> Self { pub const fn new(value: &'de Dynamic) -> Self {
Self { value } Self(value)
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
#[cold]
#[inline(always)]
fn type_error<T>(&self) -> RhaiResultOf<T> { fn type_error<T>(&self) -> RhaiResultOf<T> {
self.type_error_str(type_name::<T>()) self.type_error_str(type_name::<T>())
} }
/// Shortcut for a type conversion error. /// Shortcut for a type conversion error.
#[cold]
#[inline(never)]
fn type_error_str<T>(&self, error: &str) -> RhaiResultOf<T> { fn type_error_str<T>(&self, error: &str) -> RhaiResultOf<T> {
Err(ERR::ErrorMismatchOutputType( Err(
error.into(), ERR::ErrorMismatchOutputType(error.into(), self.0.type_name().into(), Position::NONE)
self.value.type_name().into(), .into(),
Position::NONE,
) )
.into())
} }
fn deserialize_int<V: Visitor<'de>>( #[inline(always)]
&mut self, fn deserialize_int<V: Visitor<'de>>(self, v: crate::INT, visitor: V) -> RhaiResultOf<V::Value> {
v: crate::INT,
visitor: V,
) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return visitor.visit_i64(v); return visitor.visit_i64(v);
#[cfg(feature = "only_i32")] #[cfg(feature = "only_i32")]
@ -102,10 +107,12 @@ impl<'de> DynamicDeserializer<'de> {
/// # } /// # }
/// ``` /// ```
pub fn from_dynamic<'de, T: Deserialize<'de>>(value: &'de Dynamic) -> RhaiResultOf<T> { pub fn from_dynamic<'de, T: Deserialize<'de>>(value: &'de Dynamic) -> RhaiResultOf<T> {
T::deserialize(&mut DynamicDeserializer::from_dynamic(value)) T::deserialize(DynamicDeserializer::new(value))
} }
impl Error for RhaiError { impl Error for RhaiError {
#[cold]
#[inline(never)]
fn custom<T: fmt::Display>(err: T) -> Self { fn custom<T: fmt::Display>(err: T) -> Self {
LexError::ImproperSymbol(String::new(), err.to_string()) LexError::ImproperSymbol(String::new(), err.to_string())
.into_err(Position::NONE) .into_err(Position::NONE)
@ -113,11 +120,11 @@ impl Error for RhaiError {
} }
} }
impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> { impl<'de> Deserializer<'de> for DynamicDeserializer<'de> {
type Error = RhaiError; type Error = RhaiError;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
match self.value.0 { match self.0 .0 {
Union::Unit(..) => self.deserialize_unit(visitor), Union::Unit(..) => self.deserialize_unit(visitor),
Union::Bool(..) => self.deserialize_bool(visitor), Union::Bool(..) => self.deserialize_bool(visitor),
Union::Str(..) => self.deserialize_str(visitor), Union::Str(..) => self.deserialize_str(visitor),
@ -171,110 +178,110 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
visitor.visit_bool(self.value.as_bool().or_else(|_| self.type_error())?) visitor.visit_bool(self.0.as_bool().or_else(|_| self.type_error())?)
} }
fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<i8>() .downcast_ref::<i8>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i8(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i8(x))
} }
} }
fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<i16>() .downcast_ref::<i16>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i16(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i16(x))
} }
} }
fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(feature = "only_i32") { } else if cfg!(feature = "only_i32") {
self.type_error() self.type_error()
} else { } else {
self.value self.0
.downcast_ref::<i32>() .downcast_ref::<i32>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i32(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i32(x))
} }
} }
fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) { } else if cfg!(not(feature = "only_i32")) {
self.type_error() self.type_error()
} else { } else {
self.value self.0
.downcast_ref::<i64>() .downcast_ref::<i64>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i64(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i64(x))
} }
} }
fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_i128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else if cfg!(not(feature = "only_i32")) { } else if cfg!(not(feature = "only_i32")) {
self.type_error() self.type_error()
} else { } else {
self.value self.0
.downcast_ref::<i128>() .downcast_ref::<i128>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_i128(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_i128(x))
} }
} }
fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u8<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u8>() .downcast_ref::<u8>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u8(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u8(x))
} }
} }
fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u16<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u16>() .downcast_ref::<u16>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u16(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u16(x))
} }
} }
fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u32<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u32>() .downcast_ref::<u32>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u32(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u32(x))
} }
} }
fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u64>() .downcast_ref::<u64>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u64(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u64(x))
} }
} }
fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_u128<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if let Ok(v) = self.value.as_int() { if let Ok(v) = self.0.as_int() {
self.deserialize_int(v, visitor) self.deserialize_int(v, visitor)
} else { } else {
self.value self.0
.downcast_ref::<u128>() .downcast_ref::<u128>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_u128(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_u128(x))
} }
@ -283,7 +290,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return self return self
.value .0
.downcast_ref::<f32>() .downcast_ref::<f32>()
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x)); .map_or_else(|| self.type_error(), |&x| _visitor.visit_f32(x));
@ -293,7 +300,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
return self return self
.value .0
.downcast_ref::<rust_decimal::Decimal>() .downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f32()) .and_then(|&x| x.to_f32())
.map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v)); .map_or_else(|| self.type_error(), |v| _visitor.visit_f32(v));
@ -307,7 +314,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
return self return self
.value .0
.downcast_ref::<f64>() .downcast_ref::<f64>()
.map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x)); .map_or_else(|| self.type_error(), |&x| _visitor.visit_f64(x));
@ -317,7 +324,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
return self return self
.value .0
.downcast_ref::<rust_decimal::Decimal>() .downcast_ref::<rust_decimal::Decimal>()
.and_then(|&x| x.to_f64()) .and_then(|&x| x.to_f64())
.map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v)); .map_or_else(|| self.type_error(), |v| _visitor.visit_f64(v));
@ -329,13 +336,13 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value self.0
.downcast_ref::<char>() .downcast_ref::<char>()
.map_or_else(|| self.type_error(), |&x| visitor.visit_char(x)) .map_or_else(|| self.type_error(), |&x| visitor.visit_char(x))
} }
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value.downcast_ref::<ImmutableString>().map_or_else( self.0.downcast_ref::<ImmutableString>().map_or_else(
|| self.type_error(), || self.type_error(),
|x| visitor.visit_borrowed_str(x.as_str()), |x| visitor.visit_borrowed_str(x.as_str()),
) )
@ -348,7 +355,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
fn deserialize_bytes<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_bytes<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return self return self
.value .0
.downcast_ref::<crate::Blob>() .downcast_ref::<crate::Blob>()
.map_or_else(|| self.type_error(), |x| _visitor.visit_bytes(x)); .map_or_else(|| self.type_error(), |x| _visitor.visit_bytes(x));
@ -361,7 +368,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
if self.value.is::<()>() { if self.0.is::<()>() {
visitor.visit_none() visitor.visit_none()
} else { } else {
visitor.visit_some(self) visitor.visit_some(self)
@ -369,7 +376,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.value self.0
.downcast_ref::<()>() .downcast_ref::<()>()
.map_or_else(|| self.type_error(), |_| visitor.visit_unit()) .map_or_else(|| self.type_error(), |_| visitor.visit_unit())
} }
@ -392,7 +399,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
fn deserialize_seq<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_seq<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return self.value.downcast_ref::<crate::Array>().map_or_else( return self.0.downcast_ref::<crate::Array>().map_or_else(
|| self.type_error(), || self.type_error(),
|arr| _visitor.visit_seq(IterateDynamicArray::new(arr.iter())), |arr| _visitor.visit_seq(IterateDynamicArray::new(arr.iter())),
); );
@ -416,7 +423,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_map<V: Visitor<'de>>(self, _visitor: V) -> RhaiResultOf<V::Value> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return self.value.downcast_ref::<crate::Map>().map_or_else( return self.0.downcast_ref::<crate::Map>().map_or_else(
|| self.type_error(), || self.type_error(),
|map| { |map| {
_visitor.visit_map(IterateMap::new( _visitor.visit_map(IterateMap::new(
@ -445,11 +452,11 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
_variants: &'static [&'static str], _variants: &'static [&'static str],
visitor: V, visitor: V,
) -> RhaiResultOf<V::Value> { ) -> RhaiResultOf<V::Value> {
if let Some(s) = self.value.read_lock::<ImmutableString>() { if let Some(s) = self.0.read_lock::<ImmutableString>() {
visitor.visit_enum(s.as_str().into_deserializer()) visitor.visit_enum(s.as_str().into_deserializer())
} else { } else {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return self.value.downcast_ref::<crate::Map>().map_or_else( return self.0.downcast_ref::<crate::Map>().map_or_else(
|| self.type_error(), || self.type_error(),
|map| { |map| {
let mut iter = map.iter(); let mut iter = map.iter();
@ -458,7 +465,7 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
if let (Some((key, value)), None) = (first, second) { if let (Some((key, value)), None) = (first, second) {
visitor.visit_enum(EnumDeserializer { visitor.visit_enum(EnumDeserializer {
tag: key, tag: key,
content: DynamicDeserializer::from_dynamic(value), content: DynamicDeserializer::new(value),
}) })
} else { } else {
self.type_error() self.type_error()
@ -470,10 +477,12 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
} }
} }
#[inline(always)]
fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.deserialize_str(visitor) self.deserialize_str(visitor)
} }
#[inline(always)]
fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> { fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.deserialize_any(visitor) self.deserialize_any(visitor)
} }
@ -481,13 +490,14 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
/// `SeqAccess` implementation for arrays. /// `SeqAccess` implementation for arrays.
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
struct IterateDynamicArray<'a, ITER: Iterator<Item = &'a Dynamic>> { struct IterateDynamicArray<'de, ITER: Iterator<Item = &'de Dynamic>> {
/// Iterator for a stream of [`Dynamic`][crate::Dynamic] values. /// Iterator for a stream of [`Dynamic`][crate::Dynamic] values.
iter: ITER, iter: ITER,
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateDynamicArray<'a, ITER> { impl<'de, ITER: Iterator<Item = &'de Dynamic>> IterateDynamicArray<'de, ITER> {
#[inline(always)]
#[must_use] #[must_use]
pub const fn new(iter: ITER) -> Self { pub const fn new(iter: ITER) -> Self {
Self { iter } Self { iter }
@ -495,8 +505,8 @@ impl<'a, ITER: Iterator<Item = &'a Dynamic>> IterateDynamicArray<'a, ITER> {
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
impl<'a: 'de, 'de, ITER: Iterator<Item = &'a Dynamic>> serde::de::SeqAccess<'de> impl<'de, ITER: Iterator<Item = &'de Dynamic>> serde::de::SeqAccess<'de>
for IterateDynamicArray<'a, ITER> for IterateDynamicArray<'de, ITER>
{ {
type Error = RhaiError; type Error = RhaiError;
@ -506,17 +516,15 @@ impl<'a: 'de, 'de, ITER: Iterator<Item = &'a Dynamic>> serde::de::SeqAccess<'de>
) -> RhaiResultOf<Option<T::Value>> { ) -> RhaiResultOf<Option<T::Value>> {
// Deserialize each item coming out of the iterator. // Deserialize each item coming out of the iterator.
match self.iter.next() { match self.iter.next() {
Some(item) => seed.deserialize(item.into_deserializer()).map(Some),
None => Ok(None), None => Ok(None),
Some(item) => seed
.deserialize(&mut DynamicDeserializer::from_dynamic(item))
.map(Some),
} }
} }
} }
/// `MapAccess` implementation for maps. /// `MapAccess` implementation for maps.
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
struct IterateMap<'a, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>> { struct IterateMap<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> {
// Iterator for a stream of [`Dynamic`][crate::Dynamic] keys. // Iterator for a stream of [`Dynamic`][crate::Dynamic] keys.
keys: K, keys: K,
// Iterator for a stream of [`Dynamic`][crate::Dynamic] values. // Iterator for a stream of [`Dynamic`][crate::Dynamic] values.
@ -524,7 +532,8 @@ struct IterateMap<'a, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynami
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
impl<'a, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>> IterateMap<'a, K, V> { impl<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> IterateMap<'de, K, V> {
#[inline(always)]
#[must_use] #[must_use]
pub const fn new(keys: K, values: V) -> Self { pub const fn new(keys: K, values: V) -> Self {
Self { keys, values } Self { keys, values }
@ -532,8 +541,8 @@ impl<'a, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>> IterateMa
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
impl<'a: 'de, 'de, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>> impl<'de, K: Iterator<Item = &'de str>, V: Iterator<Item = &'de Dynamic>> serde::de::MapAccess<'de>
serde::de::MapAccess<'de> for IterateMap<'a, K, V> for IterateMap<'de, K, V>
{ {
type Error = RhaiError; type Error = RhaiError;
@ -542,11 +551,9 @@ impl<'a: 'de, 'de, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>>
seed: S, seed: S,
) -> RhaiResultOf<Option<S::Value>> { ) -> RhaiResultOf<Option<S::Value>> {
// Deserialize each `Identifier` key coming out of the keys iterator. // Deserialize each `Identifier` key coming out of the keys iterator.
match self.keys.next() { match self.keys.next().map(<_>::into_deserializer) {
Some(d) => seed.deserialize(d).map(Some),
None => Ok(None), None => Ok(None),
Some(item) => seed
.deserialize(&mut super::str::StringSliceDeserializer::from_str(item))
.map(Some),
} }
} }
@ -555,20 +562,18 @@ impl<'a: 'de, 'de, K: Iterator<Item = &'a str>, V: Iterator<Item = &'a Dynamic>>
seed: S, seed: S,
) -> RhaiResultOf<S::Value> { ) -> RhaiResultOf<S::Value> {
// Deserialize each value item coming out of the iterator. // Deserialize each value item coming out of the iterator.
seed.deserialize(&mut DynamicDeserializer::from_dynamic( seed.deserialize(self.values.next().unwrap().into_deserializer())
self.values.next().unwrap(),
))
} }
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
struct EnumDeserializer<'t, 'de: 't> { struct EnumDeserializer<'de> {
tag: &'t str, tag: &'de str,
content: DynamicDeserializer<'de>, content: DynamicDeserializer<'de>,
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> { impl<'de> serde::de::EnumAccess<'de> for EnumDeserializer<'de> {
type Error = RhaiError; type Error = RhaiError;
type Variant = Self; type Variant = Self;
@ -582,26 +587,30 @@ impl<'t, 'de> serde::de::EnumAccess<'de> for EnumDeserializer<'t, 'de> {
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
impl<'t, 'de> serde::de::VariantAccess<'de> for EnumDeserializer<'t, 'de> { impl<'de> serde::de::VariantAccess<'de> for EnumDeserializer<'de> {
type Error = RhaiError; type Error = RhaiError;
fn unit_variant(mut self) -> RhaiResultOf<()> { #[inline(always)]
Deserialize::deserialize(&mut self.content) fn unit_variant(self) -> RhaiResultOf<()> {
Deserialize::deserialize(self.content)
} }
#[inline(always)]
fn newtype_variant_seed<T: serde::de::DeserializeSeed<'de>>( fn newtype_variant_seed<T: serde::de::DeserializeSeed<'de>>(
mut self, self,
seed: T, seed: T,
) -> RhaiResultOf<T::Value> { ) -> RhaiResultOf<T::Value> {
seed.deserialize(&mut self.content) seed.deserialize(self.content)
} }
fn tuple_variant<V: Visitor<'de>>(mut self, len: usize, visitor: V) -> RhaiResultOf<V::Value> { #[inline(always)]
fn tuple_variant<V: Visitor<'de>>(self, len: usize, visitor: V) -> RhaiResultOf<V::Value> {
self.content.deserialize_tuple(len, visitor) self.content.deserialize_tuple(len, visitor)
} }
#[inline(always)]
fn struct_variant<V: Visitor<'de>>( fn struct_variant<V: Visitor<'de>>(
mut self, self,
fields: &'static [&'static str], fields: &'static [&'static str],
visitor: V, visitor: V,
) -> RhaiResultOf<V::Value> { ) -> RhaiResultOf<V::Value> {

View File

@ -1,31 +1,41 @@
//! Implementations of [`serde::Deserialize`]. //! Implementations of [`serde::Deserialize`].
use crate::{Dynamic, ImmutableString, INT}; use crate::{Dynamic, Identifier, ImmutableString, Scope, INT};
use serde::de::{Deserialize, Deserializer, Error, Visitor}; use serde::{
de::{Error, SeqAccess, Visitor},
Deserialize, Deserializer,
};
use std::fmt; use std::fmt;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
struct DynamicVisitor; struct DynamicVisitor;
impl<'d> Visitor<'d> for DynamicVisitor { impl<'de> Visitor<'de> for DynamicVisitor {
type Value = Dynamic; type Value = Dynamic;
#[cold]
#[inline(never)]
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("any type that can be converted into a Dynamic") f.write_str("any type that can be converted into a Dynamic")
} }
#[inline(always)]
fn visit_bool<E: Error>(self, v: bool) -> Result<Self::Value, E> { fn visit_bool<E: Error>(self, v: bool) -> Result<Self::Value, E> {
Ok(v.into()) Ok(v.into())
} }
#[inline(always)]
fn visit_i8<E: Error>(self, v: i8) -> Result<Self::Value, E> { fn visit_i8<E: Error>(self, v: i8) -> Result<Self::Value, E> {
Ok(INT::from(v).into()) Ok(INT::from(v).into())
} }
#[inline(always)]
fn visit_i16<E: Error>(self, v: i16) -> Result<Self::Value, E> { fn visit_i16<E: Error>(self, v: i16) -> Result<Self::Value, E> {
Ok(INT::from(v).into()) Ok(INT::from(v).into())
} }
#[inline(always)]
fn visit_i32<E: Error>(self, v: i32) -> Result<Self::Value, E> { fn visit_i32<E: Error>(self, v: i32) -> Result<Self::Value, E> {
Ok(INT::from(v).into()) Ok(INT::from(v).into())
} }
#[inline]
fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> { fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ {
@ -38,12 +48,15 @@ impl<'d> Visitor<'d> for DynamicVisitor {
self.visit_i32(v as i32) self.visit_i32(v as i32)
} }
} }
#[inline(always)]
fn visit_u8<E: Error>(self, v: u8) -> Result<Self::Value, E> { fn visit_u8<E: Error>(self, v: u8) -> Result<Self::Value, E> {
Ok(INT::from(v).into()) Ok(INT::from(v).into())
} }
#[inline(always)]
fn visit_u16<E: Error>(self, v: u16) -> Result<Self::Value, E> { fn visit_u16<E: Error>(self, v: u16) -> Result<Self::Value, E> {
Ok(INT::from(v).into()) Ok(INT::from(v).into())
} }
#[inline]
fn visit_u32<E: Error>(self, v: u32) -> Result<Self::Value, E> { fn visit_u32<E: Error>(self, v: u32) -> Result<Self::Value, E> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ {
@ -56,6 +69,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
self.visit_i32(v as i32) self.visit_i32(v as i32)
} }
} }
#[inline]
fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> { fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
if v > i64::MAX as u64 { if v > i64::MAX as u64 {
@ -72,6 +86,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[inline(always)]
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> { fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
return self.visit_f64(v as f64); return self.visit_f64(v as f64);
@ -79,6 +94,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
return Ok(v.into()); return Ok(v.into());
} }
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
#[inline(always)]
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> { fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
#[cfg(not(feature = "f32_float"))] #[cfg(not(feature = "f32_float"))]
return Ok(v.into()); return Ok(v.into());
@ -88,6 +104,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[inline]
fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> { fn visit_f32<E: Error>(self, v: f32) -> Result<Self::Value, E> {
use rust_decimal::Decimal; use rust_decimal::Decimal;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -98,6 +115,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
} }
#[cfg(feature = "no_float")] #[cfg(feature = "no_float")]
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
#[inline]
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> { fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
use rust_decimal::Decimal; use rust_decimal::Decimal;
use std::convert::TryFrom; use std::convert::TryFrom;
@ -107,29 +125,35 @@ impl<'d> Visitor<'d> for DynamicVisitor {
.map_err(Error::custom) .map_err(Error::custom)
} }
#[inline(always)]
fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> { fn visit_char<E: Error>(self, v: char) -> Result<Self::Value, E> {
self.visit_string(v.to_string()) self.visit_string(v.to_string())
} }
#[inline(always)]
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> { fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
Ok(v.into()) Ok(v.into())
} }
#[inline(always)]
fn visit_borrowed_str<E: Error>(self, v: &str) -> Result<Self::Value, E> { fn visit_borrowed_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
self.visit_str(v) self.visit_str(v)
} }
#[inline(always)]
fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> { fn visit_string<E: Error>(self, v: String) -> Result<Self::Value, E> {
Ok(v.into()) Ok(v.into())
} }
#[inline(always)]
fn visit_unit<E: Error>(self) -> Result<Self::Value, E> { fn visit_unit<E: Error>(self) -> Result<Self::Value, E> {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
fn visit_newtype_struct<D: Deserializer<'d>>(self, de: D) -> Result<Self::Value, D::Error> { #[inline(always)]
fn visit_newtype_struct<D: Deserializer<'de>>(self, de: D) -> Result<Self::Value, D::Error> {
Deserialize::deserialize(de) Deserialize::deserialize(de)
} }
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
fn visit_seq<A: serde::de::SeqAccess<'d>>(self, mut seq: A) -> Result<Self::Value, A::Error> { fn visit_seq<A: serde::de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut arr = crate::Array::new(); let mut arr = crate::Array::new();
while let Some(v) = seq.next_element()? { while let Some(v) = seq.next_element()? {
@ -140,7 +164,7 @@ impl<'d> Visitor<'d> for DynamicVisitor {
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn visit_map<M: serde::de::MapAccess<'d>>(self, mut map: M) -> Result<Self::Value, M::Error> { fn visit_map<M: serde::de::MapAccess<'de>>(self, mut map: M) -> Result<Self::Value, M::Error> {
let mut m = crate::Map::new(); let mut m = crate::Map::new();
while let Some((k, v)) = map.next_entry()? { while let Some((k, v)) = map.next_entry()? {
@ -151,15 +175,69 @@ impl<'d> Visitor<'d> for DynamicVisitor {
} }
} }
impl<'d> Deserialize<'d> for Dynamic { impl<'de> Deserialize<'de> for Dynamic {
fn deserialize<D: Deserializer<'d>>(de: D) -> Result<Self, D::Error> { #[inline(always)]
de.deserialize_any(DynamicVisitor) fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(DynamicVisitor)
} }
} }
impl<'d> Deserialize<'d> for ImmutableString { impl<'de> Deserialize<'de> for ImmutableString {
fn deserialize<D: Deserializer<'d>>(de: D) -> Result<Self, D::Error> { #[inline(always)]
let s: String = Deserialize::deserialize(de)?; fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let s: String = Deserialize::deserialize(deserializer)?;
Ok(s.into()) Ok(s.into())
} }
} }
impl<'de> Deserialize<'de> for Scope<'de> {
#[inline(always)]
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
#[derive(Debug, Clone, Hash, Deserialize)]
struct ScopeEntry {
pub name: Identifier,
pub value: Dynamic,
#[serde(default)]
pub is_constant: bool,
}
struct VecVisitor;
impl<'de> Visitor<'de> for VecVisitor {
type Value = Scope<'static>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a sequence")
}
#[inline]
fn visit_seq<A>(self, mut access: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut scope = if let Some(size) = access.size_hint() {
Scope::with_capacity(size)
} else {
Scope::new()
};
while let Some(ScopeEntry {
name,
value,
is_constant,
}) = access.next_element()?
{
if is_constant {
scope.push_constant_dynamic(name, value);
} else {
scope.push_dynamic(name, value);
}
}
Ok(scope)
}
}
deserializer.deserialize_seq(VecVisitor)
}
}

View File

@ -6,7 +6,6 @@ mod deserialize;
mod metadata; mod metadata;
mod ser; mod ser;
mod serialize; mod serialize;
mod str;
pub use de::from_dynamic; pub use de::{from_dynamic, DynamicDeserializer};
pub use ser::to_dynamic; pub use ser::{to_dynamic, DynamicSerializer};

View File

@ -1,6 +1,6 @@
//! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`]. //! Implement serialization support of [`Dynamic`][crate::Dynamic] for [`serde`].
use crate::{Dynamic, Position, RhaiError, RhaiResult, RhaiResultOf, ERR}; use crate::{Dynamic, Identifier, Position, RhaiError, RhaiResult, RhaiResultOf, ERR};
use serde::ser::{ use serde::ser::{
Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct, Error, SerializeMap, SerializeSeq, SerializeStruct, SerializeTuple, SerializeTupleStruct,
}; };
@ -9,10 +9,10 @@ use std::fmt;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
/// Serializer for [`Dynamic`][crate::Dynamic] which is kept as a reference. /// Serializer for [`Dynamic`][crate::Dynamic].
struct DynamicSerializer { pub struct DynamicSerializer {
/// Buffer to hold a temporary key. /// Buffer to hold a temporary key.
_key: Dynamic, _key: Identifier,
/// Buffer to hold a temporary value. /// Buffer to hold a temporary value.
_value: Dynamic, _value: Dynamic,
} }
@ -20,10 +20,10 @@ struct DynamicSerializer {
impl DynamicSerializer { impl DynamicSerializer {
/// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value. /// Create a [`DynamicSerializer`] from a [`Dynamic`][crate::Dynamic] value.
#[must_use] #[must_use]
pub const fn new(_value: Dynamic) -> Self { pub const fn new(value: Dynamic) -> Self {
Self { Self {
_key: Dynamic::UNIT, _key: Identifier::new_const(),
_value, _value: value,
} }
} }
} }
@ -105,10 +105,12 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
type SerializeStructVariant = serde::ser::Impossible<Dynamic, RhaiError>; type SerializeStructVariant = serde::ser::Impossible<Dynamic, RhaiError>;
#[inline(always)]
fn serialize_bool(self, v: bool) -> RhaiResultOf<Self::Ok> { fn serialize_bool(self, v: bool) -> RhaiResultOf<Self::Ok> {
Ok(v.into()) Ok(v.into())
} }
#[inline(always)]
fn serialize_i8(self, v: i8) -> RhaiResultOf<Self::Ok> { fn serialize_i8(self, v: i8) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
@ -116,6 +118,7 @@ impl Serializer for &mut DynamicSerializer {
return self.serialize_i32(i32::from(v)); return self.serialize_i32(i32::from(v));
} }
#[inline(always)]
fn serialize_i16(self, v: i16) -> RhaiResultOf<Self::Ok> { fn serialize_i16(self, v: i16) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
@ -123,6 +126,7 @@ impl Serializer for &mut DynamicSerializer {
return self.serialize_i32(i32::from(v)); return self.serialize_i32(i32::from(v));
} }
#[inline(always)]
fn serialize_i32(self, v: i32) -> RhaiResultOf<Self::Ok> { fn serialize_i32(self, v: i32) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
@ -130,6 +134,7 @@ impl Serializer for &mut DynamicSerializer {
return Ok(v.into()); return Ok(v.into());
} }
#[inline]
fn serialize_i64(self, v: i64) -> RhaiResultOf<Self::Ok> { fn serialize_i64(self, v: i64) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ {
@ -143,6 +148,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
#[inline]
fn serialize_i128(self, v: i128) -> RhaiResultOf<Self::Ok> { fn serialize_i128(self, v: i128) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
if v > i64::MAX as i128 { if v > i64::MAX as i128 {
@ -158,6 +164,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
#[inline(always)]
fn serialize_u8(self, v: u8) -> RhaiResultOf<Self::Ok> { fn serialize_u8(self, v: u8) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
@ -165,6 +172,7 @@ impl Serializer for &mut DynamicSerializer {
return self.serialize_i32(i32::from(v)); return self.serialize_i32(i32::from(v));
} }
#[inline(always)]
fn serialize_u16(self, v: u16) -> RhaiResultOf<Self::Ok> { fn serialize_u16(self, v: u16) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
return self.serialize_i64(i64::from(v)); return self.serialize_i64(i64::from(v));
@ -172,6 +180,7 @@ impl Serializer for &mut DynamicSerializer {
return self.serialize_i32(i32::from(v)); return self.serialize_i32(i32::from(v));
} }
#[inline]
fn serialize_u32(self, v: u32) -> RhaiResultOf<Self::Ok> { fn serialize_u32(self, v: u32) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
{ {
@ -185,6 +194,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
#[inline]
fn serialize_u64(self, v: u64) -> RhaiResultOf<Self::Ok> { fn serialize_u64(self, v: u64) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
if v > i64::MAX as u64 { if v > i64::MAX as u64 {
@ -200,6 +210,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
#[inline]
fn serialize_u128(self, v: u128) -> RhaiResultOf<Self::Ok> { fn serialize_u128(self, v: u128) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "only_i32"))] #[cfg(not(feature = "only_i32"))]
if v > i64::MAX as u128 { if v > i64::MAX as u128 {
@ -215,6 +226,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
#[inline]
fn serialize_f32(self, v: f32) -> RhaiResultOf<Self::Ok> { fn serialize_f32(self, v: f32) -> RhaiResultOf<Self::Ok> {
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
return Ok(Dynamic::from(v)); return Ok(Dynamic::from(v));
@ -231,6 +243,7 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
#[inline]
fn serialize_f64(self, v: f64) -> RhaiResultOf<Self::Ok> { fn serialize_f64(self, v: f64) -> RhaiResultOf<Self::Ok> {
#[cfg(any(not(feature = "no_float"), not(feature = "decimal")))] #[cfg(any(not(feature = "no_float"), not(feature = "decimal")))]
return Ok(Dynamic::from(v)); return Ok(Dynamic::from(v));
@ -247,14 +260,17 @@ impl Serializer for &mut DynamicSerializer {
} }
} }
#[inline(always)]
fn serialize_char(self, v: char) -> RhaiResultOf<Self::Ok> { fn serialize_char(self, v: char) -> RhaiResultOf<Self::Ok> {
Ok(v.into()) Ok(v.into())
} }
#[inline(always)]
fn serialize_str(self, v: &str) -> RhaiResultOf<Self::Ok> { fn serialize_str(self, v: &str) -> RhaiResultOf<Self::Ok> {
Ok(v.into()) Ok(v.into())
} }
#[inline]
fn serialize_bytes(self, _v: &[u8]) -> RhaiResultOf<Self::Ok> { fn serialize_bytes(self, _v: &[u8]) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(Dynamic::from_blob(_v.to_vec())); return Ok(Dynamic::from_blob(_v.to_vec()));
@ -262,28 +278,33 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"BLOB's are not supported with 'no_index'".into(), "BLOB's are not supported under 'no_index'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline(always)]
fn serialize_none(self) -> RhaiResultOf<Self::Ok> { fn serialize_none(self) -> RhaiResultOf<Self::Ok> {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
#[inline(always)]
fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> RhaiResultOf<Self::Ok> { fn serialize_some<T: ?Sized + Serialize>(self, value: &T) -> RhaiResultOf<Self::Ok> {
value.serialize(&mut *self) value.serialize(&mut *self)
} }
#[inline(always)]
fn serialize_unit(self) -> RhaiResultOf<Self::Ok> { fn serialize_unit(self) -> RhaiResultOf<Self::Ok> {
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
} }
#[inline(always)]
fn serialize_unit_struct(self, _name: &'static str) -> RhaiResultOf<Self::Ok> { fn serialize_unit_struct(self, _name: &'static str) -> RhaiResultOf<Self::Ok> {
self.serialize_unit() self.serialize_unit()
} }
#[inline(always)]
fn serialize_unit_variant( fn serialize_unit_variant(
self, self,
_name: &'static str, _name: &'static str,
@ -293,6 +314,7 @@ impl Serializer for &mut DynamicSerializer {
self.serialize_str(variant) self.serialize_str(variant)
} }
#[inline(always)]
fn serialize_newtype_struct<T: ?Sized + Serialize>( fn serialize_newtype_struct<T: ?Sized + Serialize>(
self, self,
_name: &'static str, _name: &'static str,
@ -301,6 +323,7 @@ impl Serializer for &mut DynamicSerializer {
value.serialize(&mut *self) value.serialize(&mut *self)
} }
#[inline]
fn serialize_newtype_variant<T: ?Sized + Serialize>( fn serialize_newtype_variant<T: ?Sized + Serialize>(
self, self,
_name: &'static str, _name: &'static str,
@ -316,28 +339,31 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline]
fn serialize_seq(self, _len: Option<usize>) -> RhaiResultOf<Self::SerializeSeq> { fn serialize_seq(self, _len: Option<usize>) -> RhaiResultOf<Self::SerializeSeq> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(DynamicSerializer::new(crate::Array::new().into())); return Ok(DynamicSerializer::new(crate::Array::new().into()));
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"arrays are not supported with 'no_index'".into(), "arrays are not supported under 'no_index'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline(always)]
fn serialize_tuple(self, len: usize) -> RhaiResultOf<Self::SerializeTuple> { fn serialize_tuple(self, len: usize) -> RhaiResultOf<Self::SerializeTuple> {
self.serialize_seq(Some(len)) self.serialize_seq(Some(len))
} }
#[inline(always)]
fn serialize_tuple_struct( fn serialize_tuple_struct(
self, self,
_name: &'static str, _name: &'static str,
@ -346,6 +372,7 @@ impl Serializer for &mut DynamicSerializer {
self.serialize_seq(Some(len)) self.serialize_seq(Some(len))
} }
#[inline]
fn serialize_tuple_variant( fn serialize_tuple_variant(
self, self,
_name: &'static str, _name: &'static str,
@ -362,24 +389,26 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(any(feature = "no_object", feature = "no_index"))] #[cfg(any(feature = "no_object", feature = "no_index"))]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index' or 'no_object'".into(), "tuples are not supported under 'no_index' or 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline]
fn serialize_map(self, _len: Option<usize>) -> RhaiResultOf<Self::SerializeMap> { fn serialize_map(self, _len: Option<usize>) -> RhaiResultOf<Self::SerializeMap> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(DynamicSerializer::new(crate::Map::new().into())); return Ok(DynamicSerializer::new(crate::Map::new().into()));
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline(always)]
fn serialize_struct( fn serialize_struct(
self, self,
_name: &'static str, _name: &'static str,
@ -388,6 +417,7 @@ impl Serializer for &mut DynamicSerializer {
self.serialize_map(Some(len)) self.serialize_map(Some(len))
} }
#[inline]
fn serialize_struct_variant( fn serialize_struct_variant(
self, self,
_name: &'static str, _name: &'static str,
@ -403,7 +433,7 @@ impl Serializer for &mut DynamicSerializer {
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -425,20 +455,21 @@ impl SerializeSeq for DynamicSerializer {
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"arrays are not supported with 'no_index'".into(), "arrays are not supported under 'no_index'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
// Close the sequence. // Close the sequence.
#[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"arrays are not supported with 'no_index'".into(), "arrays are not supported under 'no_index'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -460,19 +491,20 @@ impl SerializeTuple for DynamicSerializer {
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index'".into(), "tuples are not supported under 'no_index'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index'".into(), "tuples are not supported under 'no_index'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -494,19 +526,20 @@ impl SerializeTupleStruct for DynamicSerializer {
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index'".into(), "tuples are not supported under 'no_index'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"tuples are not supported with 'no_index'".into(), "tuples are not supported under 'no_index'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -520,13 +553,19 @@ impl SerializeMap for DynamicSerializer {
fn serialize_key<T: ?Sized + Serialize>(&mut self, _key: &T) -> RhaiResultOf<()> { fn serialize_key<T: ?Sized + Serialize>(&mut self, _key: &T) -> RhaiResultOf<()> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
self._key = _key.serialize(&mut *self)?; let key = _key.serialize(&mut *self)?;
self._key = key
.into_immutable_string()
.map_err(|typ| {
ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE)
})?
.into();
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -535,20 +574,16 @@ impl SerializeMap for DynamicSerializer {
fn serialize_value<T: ?Sized + Serialize>(&mut self, _value: &T) -> RhaiResultOf<()> { fn serialize_value<T: ?Sized + Serialize>(&mut self, _value: &T) -> RhaiResultOf<()> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
{ {
let key = std::mem::take(&mut self._key) let key = std::mem::take(&mut self._key);
.into_immutable_string()
.map_err(|typ| {
ERR::ErrorMismatchDataType("string".into(), typ.into(), Position::NONE)
})?;
let value = _value.serialize(&mut *self)?; let value = _value.serialize(&mut *self)?;
let map = self._value.downcast_mut::<crate::Map>().unwrap(); let map = self._value.downcast_mut::<crate::Map>().unwrap();
map.insert(key.into(), value); map.insert(key, value);
Ok(()) Ok(())
} }
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -573,19 +608,20 @@ impl SerializeMap for DynamicSerializer {
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -611,19 +647,20 @@ impl SerializeStruct for DynamicSerializer {
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
} }
#[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
return Ok(self._value); return Ok(self._value);
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
return Err(ERR::ErrorMismatchDataType( return Err(ERR::ErrorMismatchDataType(
"".into(), "".into(),
"object maps are not supported with 'no_object'".into(), "object maps are not supported under 'no_object'".into(),
Position::NONE, Position::NONE,
) )
.into()); .into());
@ -632,7 +669,7 @@ impl SerializeStruct for DynamicSerializer {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
struct TupleVariantSerializer { pub struct TupleVariantSerializer {
variant: &'static str, variant: &'static str,
array: crate::Array, array: crate::Array,
} }
@ -649,13 +686,14 @@ impl serde::ser::SerializeTupleVariant for TupleVariantSerializer {
Ok(()) Ok(())
} }
#[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
make_variant(self.variant, self.array.into()) make_variant(self.variant, self.array.into())
} }
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
struct StructVariantSerializer { pub struct StructVariantSerializer {
variant: &'static str, variant: &'static str,
map: crate::Map, map: crate::Map,
} }
@ -665,6 +703,7 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer {
type Ok = Dynamic; type Ok = Dynamic;
type Error = RhaiError; type Error = RhaiError;
#[inline]
fn serialize_field<T: ?Sized + Serialize>( fn serialize_field<T: ?Sized + Serialize>(
&mut self, &mut self,
key: &'static str, key: &'static str,
@ -675,12 +714,14 @@ impl serde::ser::SerializeStructVariant for StructVariantSerializer {
Ok(()) Ok(())
} }
#[inline]
fn end(self) -> RhaiResultOf<Self::Ok> { fn end(self) -> RhaiResultOf<Self::Ok> {
make_variant(self.variant, self.map.into()) make_variant(self.variant, self.map.into())
} }
} }
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
#[inline]
fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult { fn make_variant(variant: &'static str, value: Dynamic) -> RhaiResult {
let mut map = crate::Map::new(); let mut map = crate::Map::new();
map.insert(variant.into(), value); map.insert(variant.into(), value);

View File

@ -1,8 +1,8 @@
//! Implementations of [`serde::Serialize`]. //! Implementations of [`serde::Serialize`].
use crate::types::dynamic::Union; use crate::types::dynamic::Union;
use crate::{Dynamic, ImmutableString}; use crate::{Dynamic, ImmutableString, Scope};
use serde::ser::{Serialize, Serializer}; use serde::{ser::SerializeSeq, Serialize, Serializer};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -83,7 +83,38 @@ impl Serialize for Dynamic {
} }
impl Serialize for ImmutableString { impl Serialize for ImmutableString {
#[inline(always)]
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> { fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
ser.serialize_str(self.as_str()) ser.serialize_str(self.as_str())
} }
} }
impl Serialize for Scope<'_> {
#[inline(always)]
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
#[derive(Debug, Clone, Hash, Serialize)]
struct ScopeEntry<'a> {
pub name: &'a str,
pub value: &'a Dynamic,
#[serde(default, skip_serializing_if = "is_false")]
pub is_constant: bool,
}
fn is_false(value: &bool) -> bool {
!value
}
let mut ser = ser.serialize_seq(Some(self.len()))?;
for (name, is_constant, value) in self.iter_raw() {
let entry = ScopeEntry {
name,
value,
is_constant,
};
ser.serialize_element(&entry)?;
}
ser.end()
}
}

View File

@ -1,143 +0,0 @@
//! Implement deserialization support of [`ImmutableString`][crate::ImmutableString] for [`serde`].
use crate::{Position, RhaiError, RhaiResultOf, ERR};
use serde::de::{Deserializer, Visitor};
use std::any::type_name;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
/// Deserializer for `ImmutableString`.
pub struct StringSliceDeserializer<'a> {
value: &'a str,
}
impl<'a> StringSliceDeserializer<'a> {
/// Create an `ImmutableStringDeserializer` from an `&str` reference.
#[must_use]
pub const fn from_str(value: &'a str) -> Self {
Self { value }
}
/// Shortcut for a type conversion error.
fn type_error<T>(&self) -> RhaiResultOf<T> {
Err(
ERR::ErrorMismatchOutputType(type_name::<T>().into(), "string".into(), Position::NONE)
.into(),
)
}
}
impl<'de> Deserializer<'de> for &mut StringSliceDeserializer<'de> {
type Error = RhaiError;
fn deserialize_any<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
self.deserialize_str(v)
}
fn deserialize_bool<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_i8<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_i16<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_i32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_i64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_u8<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_u16<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_u32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_u64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_f32<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_f64<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_char<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_str<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
// Only allow deserialization into a string.
v.visit_borrowed_str(self.value)
}
fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> RhaiResultOf<V::Value> {
self.deserialize_str(visitor)
}
fn deserialize_bytes<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_byte_buf<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_option<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_unit<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_unit_struct<V: Visitor<'de>>(
self,
_name: &'static str,
v: V,
) -> RhaiResultOf<V::Value> {
self.deserialize_unit(v)
}
fn deserialize_newtype_struct<V: Visitor<'de>>(
self,
_name: &'static str,
v: V,
) -> RhaiResultOf<V::Value> {
v.visit_newtype_struct(self)
}
fn deserialize_seq<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_tuple<V: Visitor<'de>>(self, _len: usize, v: V) -> RhaiResultOf<V::Value> {
self.deserialize_seq(v)
}
fn deserialize_tuple_struct<V: Visitor<'de>>(
self,
_name: &'static str,
_len: usize,
v: V,
) -> RhaiResultOf<V::Value> {
self.deserialize_seq(v)
}
fn deserialize_map<V: Visitor<'de>>(self, _: V) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_struct<V: Visitor<'de>>(
self,
_name: &'static str,
_fields: &'static [&'static str],
v: V,
) -> RhaiResultOf<V::Value> {
self.deserialize_map(v)
}
fn deserialize_enum<V: Visitor<'de>>(
self,
_name: &'static str,
_variants: &'static [&'static str],
_: V,
) -> RhaiResultOf<V::Value> {
self.type_error()
}
fn deserialize_identifier<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
self.deserialize_str(v)
}
fn deserialize_ignored_any<V: Visitor<'de>>(self, v: V) -> RhaiResultOf<V::Value> {
self.deserialize_any(v)
}
}

View File

@ -22,15 +22,7 @@ fn check_struct_sizes() {
); );
assert_eq!( assert_eq!(
size_of::<tokenizer::Token>(), size_of::<tokenizer::Token>(),
if IS_32_BIT { if IS_32_BIT { 8 } else { 16 }
if cfg!(feature = "decimal") {
24
} else {
16
}
} else {
32
}
); );
assert_eq!(size_of::<ast::Expr>(), if PACKED { 12 } else { 16 }); assert_eq!(size_of::<ast::Expr>(), if PACKED { 12 } else { 16 });
assert_eq!(size_of::<Option<ast::Expr>>(), if PACKED { 12 } else { 16 }); assert_eq!(size_of::<Option<ast::Expr>>(), if PACKED { 12 } else { 16 });

View File

@ -381,15 +381,15 @@ pub enum Token {
/// ///
/// Requires the `decimal` feature. /// Requires the `decimal` feature.
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
DecimalConstant(rust_decimal::Decimal), DecimalConstant(Box<rust_decimal::Decimal>),
/// An identifier. /// An identifier.
Identifier(Identifier), Identifier(Box<Identifier>),
/// A character constant. /// A character constant.
CharConstant(char), CharConstant(char),
/// A string constant. /// A string constant.
StringConstant(SmartString), StringConstant(Box<SmartString>),
/// An interpolated string. /// An interpolated string.
InterpolatedString(SmartString), InterpolatedString(Box<SmartString>),
/// `{` /// `{`
LeftBrace, LeftBrace,
/// `}` /// `}`
@ -570,14 +570,14 @@ pub enum Token {
/// A lexer error. /// A lexer error.
LexError(Box<LexError>), LexError(Box<LexError>),
/// A comment block. /// A comment block.
Comment(SmartString), Comment(Box<SmartString>),
/// A reserved symbol. /// A reserved symbol.
Reserved(SmartString), Reserved(Box<SmartString>),
/// A custom keyword. /// A custom keyword.
/// ///
/// Not available under `no_custom_syntax`. /// Not available under `no_custom_syntax`.
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Custom(SmartString), Custom(Box<SmartString>),
/// End of the input stream. /// End of the input stream.
EOF, EOF,
} }
@ -699,7 +699,7 @@ impl Token {
FloatConstant(f) => f.to_string().into(), FloatConstant(f) => f.to_string().into(),
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
DecimalConstant(d) => d.to_string().into(), DecimalConstant(d) => d.to_string().into(),
StringConstant(..) => "string".into(), StringConstant(s) => format!("\"{s}\"").into(),
InterpolatedString(..) => "string".into(), InterpolatedString(..) => "string".into(),
CharConstant(c) => c.to_string().into(), CharConstant(c) => c.to_string().into(),
Identifier(s) => s.to_string().into(), Identifier(s) => s.to_string().into(),
@ -874,9 +874,9 @@ impl Token {
"**=" => PowerOfAssign, "**=" => PowerOfAssign,
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
"?." => Reserved(syntax.into()), "?." => Reserved(Box::new(syntax.into())),
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
"?[" => Reserved(syntax.into()), "?[" => Reserved(Box::new(syntax.into())),
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
"fn" => Fn, "fn" => Fn,
@ -884,7 +884,7 @@ impl Token {
"private" => Private, "private" => Private,
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
"fn" | "private" => Reserved(syntax.into()), "fn" | "private" => Reserved(Box::new(syntax.into())),
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
"import" => Import, "import" => Import,
@ -894,25 +894,27 @@ impl Token {
"as" => As, "as" => As,
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
"import" | "export" | "as" => Reserved(syntax.into()), "import" | "export" | "as" => Reserved(Box::new(syntax.into())),
// List of reserved operators // List of reserved operators
"===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)" "===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)"
| "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => Reserved(syntax.into()), | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => {
Reserved(Box::new(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"
| "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case" | "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case"
| "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync"
| "async" | "await" | "yield" => Reserved(syntax.into()), | "async" | "await" | "yield" => Reserved(Box::new(syntax.into())),
KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR
| KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => { | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => {
Reserved(syntax.into()) Reserved(Box::new(syntax.into()))
} }
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
crate::engine::KEYWORD_IS_DEF_FN => Reserved(syntax.into()), crate::engine::KEYWORD_IS_DEF_FN => Reserved(Box::new(syntax.into())),
_ => return None, _ => return None,
}) })
@ -1097,8 +1099,8 @@ impl Token {
pub(crate) fn into_function_name_for_override(self) -> Result<SmartString, Self> { pub(crate) fn into_function_name_for_override(self) -> Result<SmartString, Self> {
match self { match self {
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Self::Custom(s) if is_valid_function_name(&s) => Ok(s), Self::Custom(s) if is_valid_function_name(&s) => Ok(*s),
Self::Identifier(s) if is_valid_function_name(&s) => Ok(s), Self::Identifier(s) if is_valid_function_name(&s) => Ok(*s),
_ => Err(self), _ => Err(self),
} }
} }
@ -1510,7 +1512,7 @@ fn get_next_token_inner(
let return_comment = return_comment || is_doc_comment(comment.as_ref().expect("`Some`")); let return_comment = return_comment || is_doc_comment(comment.as_ref().expect("`Some`"));
if return_comment { if return_comment {
return Some((Token::Comment(comment.expect("`Some`")), start_pos)); return Some((Token::Comment(comment.expect("`Some`").into()), start_pos));
} }
if state.comment_level > 0 { if state.comment_level > 0 {
// Reached EOF without ending comment block // Reached EOF without ending comment block
@ -1524,9 +1526,9 @@ fn get_next_token_inner(
|(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)),
|(result, interpolated, start_pos)| { |(result, interpolated, start_pos)| {
if interpolated { if interpolated {
Some((Token::InterpolatedString(result), start_pos)) Some((Token::InterpolatedString(result.into()), start_pos))
} else { } else {
Some((Token::StringConstant(result), start_pos)) Some((Token::StringConstant(result.into()), start_pos))
} }
}, },
); );
@ -1676,13 +1678,16 @@ fn get_next_token_inner(
// Then try decimal // Then try decimal
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
let num = num.or_else(|_| { let num = num.or_else(|_| {
rust_decimal::Decimal::from_str(&result).map(Token::DecimalConstant) rust_decimal::Decimal::from_str(&result)
.map(Box::new)
.map(Token::DecimalConstant)
}); });
// Then try decimal in scientific notation // Then try decimal in scientific notation
#[cfg(feature = "decimal")] #[cfg(feature = "decimal")]
let num = num.or_else(|_| { let num = num.or_else(|_| {
rust_decimal::Decimal::from_scientific(&result) rust_decimal::Decimal::from_scientific(&result)
.map(Box::new)
.map(Token::DecimalConstant) .map(Token::DecimalConstant)
}); });
@ -1709,7 +1714,7 @@ fn get_next_token_inner(
return parse_string_literal(stream, state, pos, c, false, true, false) return parse_string_literal(stream, state, pos, c, false, true, false)
.map_or_else( .map_or_else(
|(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)),
|(result, ..)| Some((Token::StringConstant(result), start_pos)), |(result, ..)| Some((Token::StringConstant(result.into()), start_pos)),
); );
} }
// ` - string literal // ` - string literal
@ -1737,9 +1742,9 @@ fn get_next_token_inner(
|(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)),
|(result, interpolated, ..)| { |(result, interpolated, ..)| {
if interpolated { if interpolated {
Some((Token::InterpolatedString(result), start_pos)) Some((Token::InterpolatedString(result.into()), start_pos))
} else { } else {
Some((Token::StringConstant(result), start_pos)) Some((Token::StringConstant(result.into()), start_pos))
} }
}, },
); );
@ -1786,7 +1791,7 @@ fn get_next_token_inner(
// Parentheses // Parentheses
('(', '*') => { ('(', '*') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("(*".into()), start_pos)); return Some((Token::Reserved(Box::new("(*".into())), start_pos));
} }
('(', ..) => return Some((Token::LeftParen, start_pos)), ('(', ..) => return Some((Token::LeftParen, start_pos)),
(')', ..) => return Some((Token::RightParen, start_pos)), (')', ..) => return Some((Token::RightParen, start_pos)),
@ -1802,7 +1807,7 @@ fn get_next_token_inner(
return Some((Token::MapStart, start_pos)); return Some((Token::MapStart, start_pos));
} }
// Shebang // Shebang
('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)), ('#', '!') => return Some((Token::Reserved(Box::new("#!".into())), start_pos)),
('#', ' ') => { ('#', ' ') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1812,10 +1817,10 @@ fn get_next_token_inner(
} else { } else {
"#" "#"
}; };
return Some((Token::Reserved(token.into()), start_pos)); return Some((Token::Reserved(Box::new(token.into())), start_pos));
} }
('#', ..) => return Some((Token::Reserved("#".into()), start_pos)), ('#', ..) => return Some((Token::Reserved(Box::new("#".into())), start_pos)),
// Operators // Operators
('+', '=') => { ('+', '=') => {
@ -1824,7 +1829,7 @@ fn get_next_token_inner(
} }
('+', '+') => { ('+', '+') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("++".into()), start_pos)); return Some((Token::Reserved(Box::new("++".into())), start_pos));
} }
('+', ..) if !state.next_token_cannot_be_unary => { ('+', ..) if !state.next_token_cannot_be_unary => {
return Some((Token::UnaryPlus, start_pos)) return Some((Token::UnaryPlus, start_pos))
@ -1839,11 +1844,11 @@ fn get_next_token_inner(
} }
('-', '>') => { ('-', '>') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("->".into()), start_pos)); return Some((Token::Reserved(Box::new("->".into())), start_pos));
} }
('-', '-') => { ('-', '-') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("--".into()), start_pos)); return Some((Token::Reserved(Box::new("--".into())), start_pos));
} }
('-', ..) if !state.next_token_cannot_be_unary => { ('-', ..) if !state.next_token_cannot_be_unary => {
return Some((Token::UnaryMinus, start_pos)) return Some((Token::UnaryMinus, start_pos))
@ -1852,7 +1857,7 @@ fn get_next_token_inner(
('*', ')') => { ('*', ')') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("*)".into()), start_pos)); return Some((Token::Reserved(Box::new("*)".into())), start_pos));
} }
('*', '=') => { ('*', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1925,7 +1930,7 @@ fn get_next_token_inner(
.borrow_mut() .borrow_mut()
.global_comments .global_comments
.push(comment), .push(comment),
_ => return Some((Token::Comment(comment), start_pos)), _ => return Some((Token::Comment(comment.into()), start_pos)),
} }
} }
} }
@ -1953,7 +1958,7 @@ fn get_next_token_inner(
scan_block_comment(stream, state.comment_level, pos, comment.as_mut()); scan_block_comment(stream, state.comment_level, pos, comment.as_mut());
if let Some(comment) = comment { if let Some(comment) = comment {
return Some((Token::Comment(comment), start_pos)); return Some((Token::Comment(comment.into()), start_pos));
} }
} }
@ -1972,7 +1977,7 @@ fn get_next_token_inner(
match stream.peek_next() { match stream.peek_next() {
Some('.') => { Some('.') => {
eat_next(stream, pos); eat_next(stream, pos);
Token::Reserved("...".into()) Token::Reserved(Box::new("...".into()))
} }
Some('=') => { Some('=') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -1990,7 +1995,7 @@ fn get_next_token_inner(
if stream.peek_next() == Some('=') { if stream.peek_next() == Some('=') {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("===".into()), start_pos)); return Some((Token::Reserved(Box::new("===".into())), start_pos));
} }
return Some((Token::EqualsTo, start_pos)); return Some((Token::EqualsTo, start_pos));
@ -2007,18 +2012,18 @@ fn get_next_token_inner(
if stream.peek_next() == Some('<') { if stream.peek_next() == Some('<') {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("::<".into()), start_pos)); return Some((Token::Reserved(Box::new("::<".into())), start_pos));
} }
return Some((Token::DoubleColon, start_pos)); return Some((Token::DoubleColon, start_pos));
} }
(':', '=') => { (':', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved(":=".into()), start_pos)); return Some((Token::Reserved(Box::new(":=".into())), start_pos));
} }
(':', ';') => { (':', ';') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved(":;".into()), start_pos)); return Some((Token::Reserved(Box::new(":;".into())), start_pos));
} }
(':', ..) => return Some((Token::Colon, start_pos)), (':', ..) => return Some((Token::Colon, start_pos)),
@ -2028,7 +2033,7 @@ fn get_next_token_inner(
} }
('<', '-') => { ('<', '-') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("<-".into()), start_pos)); return Some((Token::Reserved(Box::new("<-".into())), start_pos));
} }
('<', '<') => { ('<', '<') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -2045,7 +2050,7 @@ fn get_next_token_inner(
} }
('<', '|') => { ('<', '|') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("<|".into()), start_pos)); return Some((Token::Reserved(Box::new("<|".into())), start_pos));
} }
('<', ..) => return Some((Token::LessThan, start_pos)), ('<', ..) => return Some((Token::LessThan, start_pos)),
@ -2073,14 +2078,14 @@ fn get_next_token_inner(
if stream.peek_next() == Some('=') { if stream.peek_next() == Some('=') {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("!==".into()), start_pos)); return Some((Token::Reserved(Box::new("!==".into())), start_pos));
} }
return Some((Token::NotEqualsTo, start_pos)); return Some((Token::NotEqualsTo, start_pos));
} }
('!', '.') => { ('!', '.') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("!.".into()), start_pos)); return Some((Token::Reserved(Box::new("!.".into())), start_pos));
} }
('!', ..) => return Some((Token::Bang, start_pos)), ('!', ..) => return Some((Token::Bang, start_pos)),
@ -2094,7 +2099,7 @@ fn get_next_token_inner(
} }
('|', '>') => { ('|', '>') => {
eat_next(stream, pos); eat_next(stream, pos);
return Some((Token::Reserved("|>".into()), start_pos)); return Some((Token::Reserved(Box::new("|>".into())), start_pos));
} }
('|', ..) => return Some((Token::Pipe, start_pos)), ('|', ..) => return Some((Token::Pipe, start_pos)),
@ -2114,7 +2119,7 @@ fn get_next_token_inner(
} }
('^', ..) => return Some((Token::XOr, start_pos)), ('^', ..) => return Some((Token::XOr, start_pos)),
('~', ..) => return Some((Token::Reserved("~".into()), start_pos)), ('~', ..) => return Some((Token::Reserved(Box::new("~".into())), start_pos)),
('%', '=') => { ('%', '=') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -2122,9 +2127,9 @@ fn get_next_token_inner(
} }
('%', ..) => return Some((Token::Modulo, start_pos)), ('%', ..) => return Some((Token::Modulo, start_pos)),
('@', ..) => return Some((Token::Reserved("@".into()), start_pos)), ('@', ..) => return Some((Token::Reserved(Box::new("@".into())), start_pos)),
('$', ..) => return Some((Token::Reserved("$".into()), start_pos)), ('$', ..) => return Some((Token::Reserved(Box::new("$".into())), start_pos)),
('?', '.') => { ('?', '.') => {
eat_next(stream, pos); eat_next(stream, pos);
@ -2132,7 +2137,7 @@ fn get_next_token_inner(
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
Token::Elvis, Token::Elvis,
#[cfg(feature = "no_object")] #[cfg(feature = "no_object")]
Token::Reserved("?.".into()), Token::Reserved(Box::new("?.".into())),
start_pos, start_pos,
)); ));
} }
@ -2146,11 +2151,11 @@ fn get_next_token_inner(
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
Token::QuestionBracket, Token::QuestionBracket,
#[cfg(feature = "no_index")] #[cfg(feature = "no_index")]
Token::Reserved("?[".into()), Token::Reserved(Box::new("?[".into())),
start_pos, start_pos,
)); ));
} }
('?', ..) => return Some((Token::Reserved("?".into()), start_pos)), ('?', ..) => return Some((Token::Reserved(Box::new("?".into())), start_pos)),
(ch, ..) if ch.is_whitespace() => (), (ch, ..) if ch.is_whitespace() => (),
@ -2201,7 +2206,7 @@ fn get_identifier(
); );
} }
(Token::Identifier(identifier), start_pos) (Token::Identifier(identifier.into()), start_pos)
} }
/// Is a keyword allowed as a function? /// Is a keyword allowed as a function?
@ -2382,7 +2387,7 @@ impl<'a> Iterator for TokenIterator<'a> {
} }
// Reserved keyword/symbol // Reserved keyword/symbol
Some((Token::Reserved(s), pos)) => (match Some((Token::Reserved(s), pos)) => (match
(&*s, (s.as_str(),
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
(!self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s)), (!self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s)),
#[cfg(feature = "no_custom_syntax")] #[cfg(feature = "no_custom_syntax")]
@ -2438,7 +2443,7 @@ impl<'a> Iterator for TokenIterator<'a> {
Some((token, pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => { Some((token, pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => {
if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) { if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) {
// Disabled standard keyword/symbol // Disabled standard keyword/symbol
(Token::Custom(token.literal_syntax().into()), pos) (Token::Custom(Box::new(token.literal_syntax().into())), pos)
} else { } else {
// Active standard keyword - should never be a custom keyword! // Active standard keyword - should never be a custom keyword!
unreachable!("{:?} is an active keyword", token) unreachable!("{:?} is an active keyword", token)
@ -2446,7 +2451,7 @@ impl<'a> Iterator for TokenIterator<'a> {
} }
// Disabled symbol // Disabled symbol
Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => { Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => {
(Token::Reserved(token.literal_syntax().into()), pos) (Token::Reserved(Box::new(token.literal_syntax().into())), pos)
} }
// Normal symbol // Normal symbol
Some(r) => r, Some(r) => r,

View File

@ -1,7 +1,5 @@
//! Helper module which defines the [`Dynamic`] data type and the //! Helper module which defines the [`Dynamic`] data type.
//! [`Any`] trait to to allow custom type handling.
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::*;
@ -14,6 +12,8 @@ use std::{
str::FromStr, str::FromStr,
}; };
pub use super::Variant;
#[cfg(not(feature = "no_std"))] #[cfg(not(feature = "no_std"))]
#[cfg(not(target_family = "wasm"))] #[cfg(not(target_family = "wasm"))]
pub use std::time::Instant; pub use std::time::Instant;
@ -26,105 +26,6 @@ pub use instant::Instant;
#[allow(dead_code)] #[allow(dead_code)]
const CHECKED: &str = "data type was checked"; const CHECKED: &str = "data type was checked";
mod private {
use crate::func::SendSync;
use std::any::Any;
/// A sealed trait that prevents other crates from implementing [`Variant`][super::Variant].
pub trait Sealed {}
impl<T: Any + Clone + SendSync> Sealed for T {}
}
/// _(internals)_ Trait to represent any type.
/// Exported under the `internals` feature only.
///
/// This trait is sealed and cannot be implemented.
///
/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type.
/// Turn on the `sync` feature to restrict it to only types that implement [`Send`] `+` [`Sync`].
#[cfg(not(feature = "sync"))]
pub trait Variant: Any + private::Sealed {
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
#[must_use]
fn as_any(&self) -> &dyn Any;
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
#[must_use]
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Convert this [`Variant`] trait object to [`Box<dyn Any>`][Any].
#[must_use]
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
/// Get the name of this type.
#[must_use]
fn type_name(&self) -> &'static str;
/// Clone this [`Variant`] trait object.
#[must_use]
fn clone_object(&self) -> Box<dyn Variant>;
}
/// _(internals)_ Trait to represent any type.
/// Exported under the `internals` feature only.
///
/// This trait is sealed and cannot be implemented.
#[cfg(feature = "sync")]
pub trait Variant: Any + Send + Sync + private::Sealed {
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
#[must_use]
fn as_any(&self) -> &dyn Any;
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
#[must_use]
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Convert this [`Variant`] trait object to [`Box<dyn Any>`][Any].
#[must_use]
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
/// Get the name of this type.
#[must_use]
fn type_name(&self) -> &'static str;
/// Clone this [`Variant`] trait object.
#[must_use]
fn clone_object(&self) -> Box<dyn Variant>;
}
impl<T: Any + Clone + SendSync> Variant for T {
#[inline(always)]
fn as_any(&self) -> &dyn Any {
self
}
#[inline(always)]
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
#[inline(always)]
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any> {
self
}
#[inline(always)]
fn type_name(&self) -> &'static str {
type_name::<T>()
}
#[inline(always)]
fn clone_object(&self) -> Box<dyn Variant> {
Box::new(self.clone()) as Box<dyn Variant>
}
}
impl dyn Variant {
/// Is this [`Variant`] a specific type?
#[inline(always)]
#[must_use]
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id()
}
}
/// _(internals)_ Modes of access. /// _(internals)_ Modes of access.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
@ -252,6 +153,7 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
type Target = T; type Target = T;
#[inline] #[inline]
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
match self.0 { match self.0 {
DynamicWriteLockInner::Reference(ref reference) => *reference, DynamicWriteLockInner::Reference(ref reference) => *reference,
@ -263,6 +165,7 @@ impl<'d, T: Any + Clone> Deref for DynamicWriteLock<'d, T> {
impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> { impl<'d, T: Any + Clone> DerefMut for DynamicWriteLock<'d, T> {
#[inline] #[inline]
#[must_use]
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
match self.0 { match self.0 {
DynamicWriteLockInner::Reference(ref mut reference) => *reference, DynamicWriteLockInner::Reference(ref mut reference) => *reference,

View File

@ -23,6 +23,7 @@ use std::prelude::v1::*;
/// Turn on the `sync` feature to make it [`Send`] `+` [`Sync`]. /// Turn on the `sync` feature to make it [`Send`] `+` [`Sync`].
#[derive(Debug)] #[derive(Debug)]
#[non_exhaustive] #[non_exhaustive]
#[must_use]
pub enum EvalAltResult { pub enum EvalAltResult {
/// System error. Wrapped values are the error message and the internal error. /// System error. Wrapped values are the error message and the internal error.
#[cfg(not(feature = "sync"))] #[cfg(not(feature = "sync"))]
@ -494,6 +495,7 @@ impl EvalAltResult {
/// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards. /// The [position][Position] of this error is set to [`NONE`][Position::NONE] afterwards.
#[cold] #[cold]
#[inline(never)] #[inline(never)]
#[must_use]
pub fn take_position(&mut self) -> Position { pub fn take_position(&mut self) -> Position {
let pos = self.position(); let pos = self.position();
self.set_position(Position::NONE); self.set_position(Position::NONE);

View File

@ -53,6 +53,7 @@ impl Deref for ImmutableString {
type Target = SmartString; type Target = SmartString;
#[inline(always)] #[inline(always)]
#[must_use]
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
@ -60,6 +61,7 @@ impl Deref for ImmutableString {
impl AsRef<SmartString> for ImmutableString { impl AsRef<SmartString> for ImmutableString {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &SmartString { fn as_ref(&self) -> &SmartString {
&self.0 &self.0
} }
@ -67,6 +69,7 @@ impl AsRef<SmartString> for ImmutableString {
impl AsRef<str> for ImmutableString { impl AsRef<str> for ImmutableString {
#[inline(always)] #[inline(always)]
#[must_use]
fn as_ref(&self) -> &str { fn as_ref(&self) -> &str {
&self.0 &self.0
} }
@ -74,6 +77,7 @@ impl AsRef<str> for ImmutableString {
impl Borrow<SmartString> for ImmutableString { impl Borrow<SmartString> for ImmutableString {
#[inline(always)] #[inline(always)]
#[must_use]
fn borrow(&self) -> &SmartString { fn borrow(&self) -> &SmartString {
&self.0 &self.0
} }
@ -81,6 +85,7 @@ impl Borrow<SmartString> for ImmutableString {
impl Borrow<str> for ImmutableString { impl Borrow<str> for ImmutableString {
#[inline(always)] #[inline(always)]
#[must_use]
fn borrow(&self) -> &str { fn borrow(&self) -> &str {
self.as_str() self.as_str()
} }
@ -623,7 +628,7 @@ impl ImmutableString {
#[inline] #[inline]
#[must_use] #[must_use]
pub fn into_owned(mut self) -> String { pub fn into_owned(mut self) -> String {
self.make_mut(); // Make sure it is unique reference let _ = self.make_mut(); // Make sure it is unique reference
shared_take(self.0).into() // Should succeed shared_take(self.0).into() // Should succeed
} }
/// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references). /// Make sure that the [`ImmutableString`] is unique (i.e. no other outstanding references).
@ -631,6 +636,7 @@ impl ImmutableString {
/// ///
/// If there are other references to the same string, a cloned copy is used. /// If there are other references to the same string, a cloned copy is used.
#[inline(always)] #[inline(always)]
#[must_use]
pub(crate) fn make_mut(&mut self) -> &mut SmartString { pub(crate) fn make_mut(&mut self) -> &mut SmartString {
shared_make_mut(&mut self.0) shared_make_mut(&mut self.0)
} }

View File

@ -9,6 +9,7 @@ pub mod immutable_string;
pub mod interner; pub mod interner;
pub mod parse_error; pub mod parse_error;
pub mod scope; pub mod scope;
pub mod variant;
pub use bloom_filter::BloomFilterU64; pub use bloom_filter::BloomFilterU64;
pub use custom_types::{CustomTypeInfo, CustomTypesCollection}; pub use custom_types::{CustomTypeInfo, CustomTypesCollection};
@ -21,3 +22,4 @@ pub use immutable_string::ImmutableString;
pub use interner::StringsInterner; pub use interner::StringsInterner;
pub use parse_error::{LexError, ParseError, ParseErrorType}; pub use parse_error::{LexError, ParseError, ParseErrorType};
pub use scope::Scope; pub use scope::Scope;
pub use variant::Variant;

View File

@ -13,6 +13,7 @@ use std::prelude::v1::*;
/// Error encountered when tokenizing the script text. /// Error encountered when tokenizing the script text.
#[derive(Debug, Eq, PartialEq, Clone, Hash)] #[derive(Debug, Eq, PartialEq, Clone, Hash)]
#[non_exhaustive] #[non_exhaustive]
#[must_use]
pub enum LexError { pub enum LexError {
/// An unexpected symbol is encountered when tokenizing the script text. /// An unexpected symbol is encountered when tokenizing the script text.
UnexpectedInput(String), UnexpectedInput(String),
@ -58,8 +59,8 @@ impl fmt::Display for LexError {
impl LexError { impl LexError {
/// Convert a [`LexError`] into a [`ParseError`]. /// Convert a [`LexError`] into a [`ParseError`].
#[inline(always)] #[cold]
#[must_use] #[inline(never)]
pub fn into_err(self, pos: Position) -> ParseError { pub fn into_err(self, pos: Position) -> ParseError {
ParseError(Box::new(self.into()), pos) ParseError(Box::new(self.into()), pos)
} }
@ -72,6 +73,7 @@ impl LexError {
/// massive code changes to remove/add back enum variants in match statements. /// massive code changes to remove/add back enum variants in match statements.
#[derive(Debug, Eq, PartialEq, Clone, Hash)] #[derive(Debug, Eq, PartialEq, Clone, Hash)]
#[non_exhaustive] #[non_exhaustive]
#[must_use]
pub enum ParseErrorType { pub enum ParseErrorType {
/// The script ends prematurely. /// The script ends prematurely.
UnexpectedEOF, UnexpectedEOF,
@ -171,7 +173,8 @@ pub enum ParseErrorType {
impl ParseErrorType { impl ParseErrorType {
/// Make a [`ParseError`] using the current type and position. /// Make a [`ParseError`] using the current type and position.
#[inline(always)] #[cold]
#[inline(never)]
#[must_use] #[must_use]
pub(crate) fn into_err(self, pos: Position) -> ParseError { pub(crate) fn into_err(self, pos: Position) -> ParseError {
ParseError(self.into(), pos) ParseError(self.into(), pos)
@ -277,6 +280,7 @@ impl From<LexError> for ParseErrorType {
/// Error when parsing a script. /// Error when parsing a script.
#[derive(Debug, Eq, PartialEq, Clone, Hash)] #[derive(Debug, Eq, PartialEq, Clone, Hash)]
#[must_use]
pub struct ParseError( pub struct ParseError(
/// Parse error type. /// Parse error type.
pub Box<ParseErrorType>, pub Box<ParseErrorType>,

View File

@ -22,6 +22,14 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
/// Currently the lifetime parameter is not used, but it is not guaranteed to remain unused for /// Currently the lifetime parameter is not used, but it is not guaranteed to remain unused for
/// future versions. Until then, `'static` can be used. /// future versions. Until then, `'static` can be used.
/// ///
/// # Constant Generic Parameter
///
/// There is a constant generic parameter that indicates how many entries to keep inline.
/// As long as the number of entries does not exceed this limit, no allocations occur.
/// The default is 8.
///
/// A larger value makes [`Scope`] larger, but reduces the chance of allocations.
///
/// # Thread Safety /// # Thread Safety
/// ///
/// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. Turn on the `sync` feature to make it /// Currently, [`Scope`] is neither [`Send`] nor [`Sync`]. Turn on the `sync` feature to make it
@ -61,7 +69,7 @@ const SCOPE_ENTRIES_INLINED: usize = 8;
// //
// [`Dynamic`] is reasonably small so packing it tightly improves cache performance. // [`Dynamic`] is reasonably small so packing it tightly improves cache performance.
#[derive(Debug, Hash, Default)] #[derive(Debug, Hash, Default)]
pub struct Scope<'a> { pub struct Scope<'a, const N: usize = SCOPE_ENTRIES_INLINED> {
/// Current value of the entry. /// Current value of the entry.
values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>, values: SmallVec<[Dynamic; SCOPE_ENTRIES_INLINED]>,
/// Name of the entry. /// Name of the entry.
@ -170,6 +178,28 @@ impl Scope<'_> {
dummy: PhantomData, dummy: PhantomData,
} }
} }
/// Create a new [`Scope`] with a particular capacity.
///
/// # Example
///
/// ```
/// use rhai::Scope;
///
/// let mut my_scope = Scope::with_capacity(10);
///
/// my_scope.push("x", 42_i64);
/// assert_eq!(my_scope.get_value::<i64>("x").expect("x should exist"), 42);
/// ```
#[inline(always)]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
values: SmallVec::with_capacity(capacity),
names: SmallVec::with_capacity(capacity),
aliases: SmallVec::with_capacity(capacity),
dummy: PhantomData,
}
}
/// Empty the [`Scope`]. /// Empty the [`Scope`].
/// ///
/// # Example /// # Example

105
src/types/variant.rs Normal file
View File

@ -0,0 +1,105 @@
//! [`Variant`] trait to to allow custom type handling.
use crate::func::SendSync;
use std::any::{type_name, Any, TypeId};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
mod private {
use crate::func::SendSync;
use std::any::Any;
/// A sealed trait that prevents other crates from implementing [`Variant`][super::Variant].
pub trait Sealed {}
impl<T: Any + Clone + SendSync> Sealed for T {}
}
/// _(internals)_ Trait to represent any type.
/// Exported under the `internals` feature only.
///
/// This trait is sealed and cannot be implemented.
///
/// Currently, [`Variant`] is not [`Send`] nor [`Sync`], so it can practically be any type.
/// Turn on the `sync` feature to restrict it to only types that implement [`Send`] `+` [`Sync`].
#[cfg(not(feature = "sync"))]
pub trait Variant: Any + private::Sealed {
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
#[must_use]
fn as_any(&self) -> &dyn Any;
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
#[must_use]
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Convert this [`Variant`] trait object to [`Box<dyn Any>`][Any].
#[must_use]
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
/// Get the name of this type.
#[must_use]
fn type_name(&self) -> &'static str;
/// Clone this [`Variant`] trait object.
#[must_use]
fn clone_object(&self) -> Box<dyn Variant>;
}
/// _(internals)_ Trait to represent any type.
/// Exported under the `internals` feature only.
///
/// This trait is sealed and cannot be implemented.
#[cfg(feature = "sync")]
pub trait Variant: Any + Send + Sync + private::Sealed {
/// Convert this [`Variant`] trait object to [`&dyn Any`][Any].
#[must_use]
fn as_any(&self) -> &dyn Any;
/// Convert this [`Variant`] trait object to [`&mut dyn Any`][Any].
#[must_use]
fn as_any_mut(&mut self) -> &mut dyn Any;
/// Convert this [`Variant`] trait object to [`Box<dyn Any>`][Any].
#[must_use]
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any>;
/// Get the name of this type.
#[must_use]
fn type_name(&self) -> &'static str;
/// Clone this [`Variant`] trait object.
#[must_use]
fn clone_object(&self) -> Box<dyn Variant>;
}
impl<T: Any + Clone + SendSync> Variant for T {
#[inline(always)]
fn as_any(&self) -> &dyn Any {
self
}
#[inline(always)]
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
#[inline(always)]
fn as_boxed_any(self: Box<Self>) -> Box<dyn Any> {
self
}
#[inline(always)]
fn type_name(&self) -> &'static str {
type_name::<T>()
}
#[inline(always)]
fn clone_object(&self) -> Box<dyn Variant> {
Box::new(self.clone()) as Box<dyn Variant>
}
}
impl dyn Variant {
/// Is this [`Variant`] a specific type?
#[inline(always)]
#[must_use]
pub fn is<T: Any>(&self) -> bool {
TypeId::of::<T>() == self.type_id()
}
}

View File

@ -256,10 +256,18 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
engine.register_custom_syntax_with_state_raw( engine.register_custom_syntax_with_state_raw(
"hello", "hello",
|stream, _, state| match stream.len() { |stream, look_ahead, state| match stream.len() {
0 => unreachable!(), 0 => unreachable!(),
1 => Ok(Some("$ident$".into())), 1 if look_ahead == "\"world\"" => {
*state = Dynamic::TRUE;
Ok(Some("$string$".into()))
}
1 => {
*state = Dynamic::FALSE;
Ok(Some("$ident$".into()))
}
2 => match stream[1].as_str() { 2 => match stream[1].as_str() {
"world" if state.as_bool().unwrap_or(false) => Ok(Some("$$world".into())),
"world" => Ok(Some("$$hello".into())), "world" => Ok(Some("$$hello".into())),
"kitty" => { "kitty" => {
*state = (42 as INT).into(); *state = (42 as INT).into();
@ -276,16 +284,11 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
context.scope_mut().push("foo", 999 as INT); context.scope_mut().push("foo", 999 as INT);
Ok(match inputs[0].get_string_value().unwrap() { Ok(match inputs[0].get_string_value().unwrap() {
"world" "world" => match inputs.last().unwrap().get_string_value().unwrap_or("") {
if inputs "$$hello" => 0 as INT,
.last() "$$world" => 123456 as INT,
.unwrap() _ => 123 as INT,
.get_string_value() },
.map_or(false, |s| s == "$$hello") =>
{
0 as INT
}
"world" => 123 as INT,
"kitty" if inputs.len() > 1 => 999 as INT, "kitty" if inputs.len() > 1 => 999 as INT,
"kitty" => state.as_int().unwrap(), "kitty" => state.as_int().unwrap(),
_ => unreachable!(), _ => unreachable!(),
@ -294,6 +297,7 @@ fn test_custom_syntax_raw() -> Result<(), Box<EvalAltResult>> {
}, },
); );
assert_eq!(engine.eval::<INT>(r#"hello "world""#)?, 123456);
assert_eq!(engine.eval::<INT>("hello world")?, 0); assert_eq!(engine.eval::<INT>("hello world")?, 0);
assert_eq!(engine.eval::<INT>("hello kitty")?, 42); assert_eq!(engine.eval::<INT>("hello kitty")?, 42);
assert_eq!( assert_eq!(

View File

@ -10,7 +10,7 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
engine.on_progress(|count| { engine.on_progress(|count| {
if count % 100 == 0 { if count % 100 == 0 {
println!("{}", count); println!("{count}");
} }
None None
}); });
@ -68,7 +68,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
engine.on_progress(|count| { engine.on_progress(|count| {
if count % 100 == 0 { if count % 100 == 0 {
println!("{}", count); println!("{count}");
} }
None None
}); });
@ -124,7 +124,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
engine.on_progress(|count| { engine.on_progress(|count| {
if count % 100 == 0 { if count % 100 == 0 {
println!("{}", count); println!("{count}");
} }
None None
}); });

View File

@ -42,7 +42,7 @@ mod test {
#[rhai_fn(name = "no_effect", set = "no_effect", pure)] #[rhai_fn(name = "no_effect", set = "no_effect", pure)]
pub fn no_effect(array: &mut Array, value: INT) { pub fn no_effect(array: &mut Array, value: INT) {
// array is not modified // array is not modified
println!("Array = {:?}, Value = {}", array, value); println!("Array = {array:?}, Value = {value}");
} }
} }
} }

View File

@ -66,7 +66,7 @@ fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
); );
for entry in logbook.read().unwrap().iter() { for entry in logbook.read().unwrap().iter() {
println!("{}", entry); println!("{entry}");
} }
Ok(()) Ok(())

View File

@ -2,7 +2,7 @@
use rhai::{ use rhai::{
serde::{from_dynamic, to_dynamic}, serde::{from_dynamic, to_dynamic},
Dynamic, Engine, EvalAltResult, ImmutableString, INT, Dynamic, Engine, EvalAltResult, ImmutableString, Scope, INT,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::json; use serde_json::json;
@ -820,6 +820,37 @@ fn test_serde_blob() -> Result<(), Box<EvalAltResult>> {
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
fn test_serde_json_borrowed_string() { fn test_serde_json_borrowed_string() {
let value = json!({ "a": "b" }); let value = json!({ "a": "b" });
println!("value: {:?}", value); println!("value: {value:?}");
let _: Dynamic = serde_json::from_value(value).unwrap(); let _: Dynamic = serde_json::from_value(value).unwrap();
} }
#[test]
#[cfg(not(feature = "no_object"))]
fn test_serde_scope() {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
struct TestStruct {
foo: Option<char>,
}
let mut scope = Scope::new();
scope.push("x", 42 as INT);
scope.push_constant("y", true);
scope.push("z", TestStruct { foo: None });
let json = serde_json::to_string(&scope).unwrap();
assert_eq!(
json,
r#"[{"name":"x","value":42},{"name":"y","value":true,"is_constant":true},{"name":"z","value":"serde::test_serde_scope::TestStruct"}]"#
);
scope = serde_json::from_str(&json).unwrap();
assert_eq!(scope.len(), 3);
assert_eq!(scope.get_value::<INT>("x").unwrap(), 42);
assert_eq!(scope.get_value::<bool>("y").unwrap(), true);
assert_eq!(
scope.get_value::<String>("z").unwrap(),
"serde::test_serde_scope::TestStruct"
);
}

View File

@ -140,7 +140,7 @@ fn test_scope_eval() -> Result<(), Box<EvalAltResult>> {
// Second invocation using the same state // Second invocation using the same state
let result = engine.eval_with_scope::<INT>(&mut scope, "x")?; let result = engine.eval_with_scope::<INT>(&mut scope, "x")?;
println!("result: {}", result); // should print 966 println!("result: {result}"); // should print 966
// Variable y is changed in the script // Variable y is changed in the script
assert_eq!( assert_eq!(