commit
51c61b1bdb
@ -11,6 +11,7 @@ Bug fixes
|
|||||||
* `map` and `filter` for arrays are marked `pure`. Warnings are added to the documentation of pure array methods that take `this` closures.
|
* `map` and `filter` for arrays are marked `pure`. Warnings are added to the documentation of pure array methods that take `this` closures.
|
||||||
* Syntax such as `foo.bar::baz` no longer panics, but returns a proper parse error.
|
* Syntax such as `foo.bar::baz` no longer panics, but returns a proper parse error.
|
||||||
* `x += y` where `x` and `y` are `char` now works correctly.
|
* `x += y` where `x` and `y` are `char` now works correctly.
|
||||||
|
* Expressions such as `!inside` now parses correctly instead of as `!in` followed by `side`.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
@ -1133,7 +1133,7 @@ pub mod deprecated_array_functions {
|
|||||||
array: &mut Array,
|
array: &mut Array,
|
||||||
comparer: &str,
|
comparer: &str,
|
||||||
) -> RhaiResultOf<()> {
|
) -> RhaiResultOf<()> {
|
||||||
sort(ctx, array, FnPtr::new(comparer)?)
|
Ok(sort(ctx, array, FnPtr::new(comparer)?))
|
||||||
}
|
}
|
||||||
/// Remove all elements in the array that returns `true` when applied a function named by `filter`
|
/// Remove all elements in the array that returns `true` when applied a function named by `filter`
|
||||||
/// and return them as a new array.
|
/// and return them as a new array.
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
use super::{ASTFlags, ASTNode, Ident, Namespace, Stmt, StmtBlock};
|
use super::{ASTFlags, ASTNode, Ident, Namespace, Stmt, StmtBlock};
|
||||||
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE};
|
||||||
use crate::func::hashing::ALT_ZERO_HASH;
|
|
||||||
use crate::tokenizer::Token;
|
use crate::tokenizer::Token;
|
||||||
use crate::types::dynamic::Union;
|
use crate::types::dynamic::Union;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec,
|
calc_fn_hash, Dynamic, FnArgsVec, FnPtr, Identifier, ImmutableString, Position, SmartString,
|
||||||
INT,
|
StaticVec, INT,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
@ -17,7 +16,7 @@ use std::{
|
|||||||
fmt::Write,
|
fmt::Write,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
iter::once,
|
iter::once,
|
||||||
num::{NonZeroU64, NonZeroU8, NonZeroUsize},
|
num::{NonZeroU8, NonZeroUsize},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// _(internals)_ A binary expression.
|
/// _(internals)_ A binary expression.
|
||||||
@ -103,9 +102,9 @@ impl CustomExpr {
|
|||||||
pub struct FnCallHashes {
|
pub struct FnCallHashes {
|
||||||
/// Pre-calculated hash for a script-defined function ([`None`] if native functions only).
|
/// Pre-calculated hash for a script-defined function ([`None`] if native functions only).
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
script: Option<NonZeroU64>,
|
script: Option<u64>,
|
||||||
/// Pre-calculated hash for a native Rust function with no parameter types.
|
/// Pre-calculated hash for a native Rust function with no parameter types.
|
||||||
native: NonZeroU64,
|
native: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for FnCallHashes {
|
impl fmt::Debug for FnCallHashes {
|
||||||
@ -125,38 +124,37 @@ impl fmt::Debug for FnCallHashes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u64> for FnCallHashes {
|
impl FnCallHashes {
|
||||||
|
/// Create a [`FnCallHashes`] from a single hash.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(hash: u64) -> Self {
|
#[must_use]
|
||||||
let hash = NonZeroU64::new(if hash == 0 { ALT_ZERO_HASH } else { hash }).unwrap();
|
pub const fn from_hash(hash: u64) -> Self {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
script: Some(hash),
|
script: Some(hash),
|
||||||
native: hash,
|
native: hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl FnCallHashes {
|
|
||||||
/// Create a [`FnCallHashes`] with only the native Rust hash.
|
/// Create a [`FnCallHashes`] with only the native Rust hash.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_native(hash: u64) -> Self {
|
pub const fn from_native_only(hash: u64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
script: None,
|
script: None,
|
||||||
native: NonZeroU64::new(if hash == 0 { ALT_ZERO_HASH } else { hash }).unwrap(),
|
native: hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Create a [`FnCallHashes`] with both native Rust and script function hashes.
|
/// Create a [`FnCallHashes`] with both script function and native Rust hashes.
|
||||||
|
///
|
||||||
|
/// Not available under `no_function`.
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
|
pub const fn from_script_and_native(script: u64, native: u64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
#[cfg(not(feature = "no_function"))]
|
script: Some(script),
|
||||||
script: NonZeroU64::new(if script == 0 { ALT_ZERO_HASH } else { script }),
|
native,
|
||||||
native: NonZeroU64::new(if native == 0 { ALT_ZERO_HASH } else { native }).unwrap(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Is this [`FnCallHashes`] native-only?
|
/// Is this [`FnCallHashes`] native-only?
|
||||||
@ -174,7 +172,7 @@ impl FnCallHashes {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn native(&self) -> u64 {
|
pub const fn native(&self) -> u64 {
|
||||||
self.native.get()
|
self.native
|
||||||
}
|
}
|
||||||
/// Get the script hash.
|
/// Get the script hash.
|
||||||
///
|
///
|
||||||
@ -188,7 +186,7 @@ impl FnCallHashes {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn script(&self) -> u64 {
|
pub fn script(&self) -> u64 {
|
||||||
assert!(self.script.is_some());
|
assert!(self.script.is_some());
|
||||||
self.script.as_ref().unwrap().get()
|
self.script.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +201,7 @@ pub struct FnCallExpr {
|
|||||||
/// Pre-calculated hashes.
|
/// Pre-calculated hashes.
|
||||||
pub hashes: FnCallHashes,
|
pub hashes: FnCallHashes,
|
||||||
/// List of function call argument expressions.
|
/// List of function call argument expressions.
|
||||||
pub args: StaticVec<Expr>,
|
pub args: FnArgsVec<Expr>,
|
||||||
/// 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?
|
||||||
@ -580,7 +578,7 @@ impl Expr {
|
|||||||
FnCallExpr {
|
FnCallExpr {
|
||||||
namespace: Namespace::NONE,
|
namespace: Namespace::NONE,
|
||||||
name: KEYWORD_FN_PTR.into(),
|
name: KEYWORD_FN_PTR.into(),
|
||||||
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
hashes: FnCallHashes::from_hash(calc_fn_hash(None, f.fn_name(), 1)),
|
||||||
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,
|
||||||
op_token: None,
|
op_token: None,
|
||||||
|
@ -24,15 +24,15 @@ use std::{
|
|||||||
#[derive(Clone, 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,
|
hash_op_assign: u64,
|
||||||
/// Hash of the underlying operator call (for fallback).
|
/// Hash of the underlying operator call (for fallback).
|
||||||
pub hash_op: u64,
|
hash_op: u64,
|
||||||
/// Op-assignment operator.
|
/// Op-assignment operator.
|
||||||
pub op_assign: Token,
|
op_assign: Token,
|
||||||
/// Underlying operator.
|
/// Underlying operator.
|
||||||
pub op: Token,
|
op: Token,
|
||||||
/// [Position] of the op-assignment operator.
|
/// [Position] of the op-assignment operator.
|
||||||
pub pos: Position,
|
pos: Position,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OpAssignment {
|
impl OpAssignment {
|
||||||
@ -51,8 +51,31 @@ impl OpAssignment {
|
|||||||
/// Is this an op-assignment?
|
/// Is this an op-assignment?
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub const fn is_op_assignment(&self) -> bool {
|
pub fn is_op_assignment(&self) -> bool {
|
||||||
self.hash_op_assign != 0 || self.hash_op != 0
|
!matches!(self.op, Token::Equals)
|
||||||
|
}
|
||||||
|
/// Get information if this [`OpAssignment`] is an op-assignment.
|
||||||
|
///
|
||||||
|
/// Returns `( hash_op_assign, hash_op, op_assign, op )`:
|
||||||
|
///
|
||||||
|
/// * `hash_op_assign`: Hash of the op-assignment call.
|
||||||
|
/// * `hash_op`: Hash of the underlying operator call (for fallback).
|
||||||
|
/// * `op_assign`: Op-assignment operator.
|
||||||
|
/// * `op`: Underlying operator.
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub fn get_op_assignment_info(&self) -> Option<(u64, u64, &Token, &Token)> {
|
||||||
|
if self.is_op_assignment() {
|
||||||
|
Some((self.hash_op_assign, self.hash_op, &self.op_assign, &self.op))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Get the [position][Position] of this [`OpAssignment`].
|
||||||
|
#[must_use]
|
||||||
|
#[inline(always)]
|
||||||
|
pub const fn position(&self) -> Position {
|
||||||
|
self.pos
|
||||||
}
|
}
|
||||||
/// Create a new [`OpAssignment`].
|
/// Create a new [`OpAssignment`].
|
||||||
///
|
///
|
||||||
@ -93,7 +116,7 @@ impl OpAssignment {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
|
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
|
||||||
let op = Token::lookup_symbol_from_syntax(name).expect("operator");
|
let op = Token::lookup_symbol_from_syntax(name).expect("operator");
|
||||||
Self::new_op_assignment_from_base_token(op, pos)
|
Self::new_op_assignment_from_base_token(&op, pos)
|
||||||
}
|
}
|
||||||
/// Convert a [`Token`] into a new [`OpAssignment`].
|
/// Convert a [`Token`] into a new [`OpAssignment`].
|
||||||
///
|
///
|
||||||
@ -102,7 +125,7 @@ impl OpAssignment {
|
|||||||
/// Panics if the token is cannot be converted into an op-assignment operator.
|
/// Panics if the token is cannot be converted into an op-assignment operator.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_op_assignment_from_base_token(op: Token, pos: Position) -> Self {
|
pub fn new_op_assignment_from_base_token(op: &Token, pos: Position) -> Self {
|
||||||
Self::new_op_assignment_from_token(op.convert_to_op_assignment().expect("operator"), pos)
|
Self::new_op_assignment_from_token(op.convert_to_op_assignment().expect("operator"), pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,10 +166,7 @@ fn load_script(engine: &Engine) -> (rhai::AST, String) {
|
|||||||
|
|
||||||
let filename = match Path::new(&filename).canonicalize() {
|
let filename = match Path::new(&filename).canonicalize() {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
eprintln!(
|
eprintln!("\x1b[31mError script file path: {filename}\n{err}\x1b[39m");
|
||||||
"\x1b[31mError script file path: {}\n{}\x1b[39m",
|
|
||||||
filename, err
|
|
||||||
);
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
Ok(f) => {
|
Ok(f) => {
|
||||||
@ -315,7 +312,7 @@ fn debug_callback(
|
|||||||
}
|
}
|
||||||
["node"] => {
|
["node"] => {
|
||||||
if pos.is_none() {
|
if pos.is_none() {
|
||||||
println!("{:?}", node);
|
println!("{node:?}");
|
||||||
} else {
|
} else {
|
||||||
match source {
|
match source {
|
||||||
Some(source) => println!("{node:?} {source} @ {pos:?}"),
|
Some(source) => println!("{node:?} {source} @ {pos:?}"),
|
||||||
@ -400,7 +397,7 @@ fn debug_callback(
|
|||||||
#[cfg(not(feature = "no_position"))]
|
#[cfg(not(feature = "no_position"))]
|
||||||
rhai::debugger::BreakPoint::AtPosition { pos, .. } => {
|
rhai::debugger::BreakPoint::AtPosition { pos, .. } => {
|
||||||
let line_num = format!("[{}] line ", i + 1);
|
let line_num = format!("[{}] line ", i + 1);
|
||||||
print!("{}", line_num);
|
print!("{line_num}");
|
||||||
print_source(lines, *pos, line_num.len(), (0, 0));
|
print_source(lines, *pos, line_num.len(), (0, 0));
|
||||||
}
|
}
|
||||||
_ => println!("[{}] {bp}", i + 1),
|
_ => println!("[{}] {bp}", i + 1),
|
||||||
|
@ -621,7 +621,7 @@ impl Engine {
|
|||||||
self.eval_op_assignment(
|
self.eval_op_assignment(
|
||||||
global, caches, op_info, root, obj_ptr, new_val,
|
global, caches, op_info, root, obj_ptr, new_val,
|
||||||
)?;
|
)?;
|
||||||
self.check_data_size(obj_ptr.as_ref(), op_info.pos)?;
|
self.check_data_size(obj_ptr.as_ref(), op_info.position())?;
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
// Indexed value cannot be referenced - use indexer
|
// Indexed value cannot be referenced - use indexer
|
||||||
@ -647,7 +647,7 @@ impl Engine {
|
|||||||
)?;
|
)?;
|
||||||
// Replace new value
|
// Replace new value
|
||||||
new_val = val.take_or_clone();
|
new_val = val.take_or_clone();
|
||||||
self.check_data_size(&new_val, op_info.pos)?;
|
self.check_data_size(&new_val, op_info.position())?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,7 +729,7 @@ impl Engine {
|
|||||||
global, caches, op_info, root, val_target, new_val,
|
global, caches, op_info, root, val_target, new_val,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
self.check_data_size(target.source(), op_info.pos)?;
|
self.check_data_size(target.source(), op_info.position())?;
|
||||||
Ok((Dynamic::UNIT, true))
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
|
@ -495,13 +495,13 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// It is up to the [`Engine`] to reactivate the debugger.
|
/// It is up to the [`Engine`] to reactivate the debugger.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn run_debugger_raw<'a>(
|
pub(crate) fn run_debugger_raw(
|
||||||
&self,
|
&self,
|
||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
this_ptr: Option<&mut Dynamic>,
|
this_ptr: Option<&mut Dynamic>,
|
||||||
node: ASTNode<'a>,
|
node: ASTNode,
|
||||||
event: DebuggerEvent,
|
event: DebuggerEvent,
|
||||||
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
) -> Result<Option<DebuggerStatus>, Box<crate::EvalAltResult>> {
|
||||||
if let Some(ref x) = self.debugger_interface {
|
if let Some(ref x) = self.debugger_interface {
|
||||||
|
@ -125,23 +125,15 @@ impl Engine {
|
|||||||
return Err(ERR::ErrorAssignmentToConstant(name.to_string(), pos).into());
|
return Err(ERR::ErrorAssignmentToConstant(name.to_string(), pos).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if op_info.is_op_assignment() {
|
let pos = op_info.position();
|
||||||
let OpAssignment {
|
|
||||||
hash_op_assign,
|
|
||||||
hash_op,
|
|
||||||
op_assign,
|
|
||||||
op,
|
|
||||||
pos,
|
|
||||||
} = op_info;
|
|
||||||
|
|
||||||
|
if let Some((hash1, hash2, op_assign, op)) = op_info.get_op_assignment_info() {
|
||||||
let mut lock_guard = target.write_lock::<Dynamic>().unwrap();
|
let mut lock_guard = target.write_lock::<Dynamic>().unwrap();
|
||||||
|
|
||||||
let hash = *hash_op_assign;
|
|
||||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||||
|
|
||||||
if self.fast_operators() {
|
if self.fast_operators() {
|
||||||
if let Some((func, need_context)) =
|
if let Some((func, need_context)) =
|
||||||
get_builtin_op_assignment_fn(op_assign.clone(), args[0], args[1])
|
get_builtin_op_assignment_fn(op_assign, args[0], args[1])
|
||||||
{
|
{
|
||||||
// Built-in found
|
// Built-in found
|
||||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
@ -149,7 +141,7 @@ impl Engine {
|
|||||||
let context = if need_context {
|
let context = if need_context {
|
||||||
let op = op_assign.literal_syntax();
|
let op = op_assign.literal_syntax();
|
||||||
let source = global.source();
|
let source = global.source();
|
||||||
Some((self, op, source, &*global, *pos).into())
|
Some((self, op, source, &*global, pos).into())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@ -157,20 +149,20 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let token = Some(op_assign.clone());
|
let token = Some(op_assign);
|
||||||
let op_assign = op_assign.literal_syntax();
|
let op_assign = op_assign.literal_syntax();
|
||||||
|
|
||||||
match self.exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *pos)
|
match self.exec_native_fn_call(global, caches, op_assign, token, hash1, args, true, pos)
|
||||||
{
|
{
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
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 token = Some(op.clone());
|
let token = Some(op);
|
||||||
let op = op.literal_syntax();
|
let op = op.literal_syntax();
|
||||||
|
|
||||||
*args[0] = self
|
*args[0] = self
|
||||||
.exec_native_fn_call(global, caches, op, token, *hash_op, args, true, *pos)?
|
.exec_native_fn_call(global, caches, op, token, hash2, args, true, pos)?
|
||||||
.0;
|
.0;
|
||||||
}
|
}
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
@ -189,7 +181,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target.propagate_changed_value(op_info.pos)
|
target.propagate_changed_value(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a statement.
|
/// Evaluate a statement.
|
||||||
|
@ -86,7 +86,7 @@ fn const_false_fn(_: Option<NativeCallContext>, _: &mut [&mut Dynamic]) -> RhaiR
|
|||||||
///
|
///
|
||||||
/// 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: Token, 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();
|
||||||
|
|
||||||
@ -621,7 +621,7 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<F
|
|||||||
///
|
///
|
||||||
/// 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: Token, 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();
|
||||||
|
|
||||||
|
@ -164,15 +164,11 @@ impl Engine {
|
|||||||
_global: &GlobalRuntimeState,
|
_global: &GlobalRuntimeState,
|
||||||
caches: &'s mut Caches,
|
caches: &'s mut Caches,
|
||||||
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
||||||
op_token: Option<Token>,
|
op_token: Option<&Token>,
|
||||||
hash_base: u64,
|
hash_base: u64,
|
||||||
args: Option<&mut FnCallArgs>,
|
args: Option<&mut FnCallArgs>,
|
||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
) -> Option<&'s FnResolutionCacheEntry> {
|
) -> Option<&'s FnResolutionCacheEntry> {
|
||||||
if hash_base == 0 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut hash = args.as_deref().map_or(hash_base, |args| {
|
let mut hash = args.as_deref().map_or(hash_base, |args| {
|
||||||
calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id()))
|
calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id()))
|
||||||
});
|
});
|
||||||
@ -345,7 +341,7 @@ impl Engine {
|
|||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
name: &str,
|
name: &str,
|
||||||
op_token: Option<Token>,
|
op_token: Option<&Token>,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
@ -567,7 +563,7 @@ impl Engine {
|
|||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
_scope: Option<&mut Scope>,
|
_scope: Option<&mut Scope>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<Token>,
|
op_token: Option<&Token>,
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
mut _args: &mut FnCallArgs,
|
mut _args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
@ -789,9 +785,9 @@ impl Engine {
|
|||||||
let fn_name = fn_ptr.fn_name();
|
let fn_name = fn_ptr.fn_name();
|
||||||
// Recalculate hashes
|
// Recalculate hashes
|
||||||
let new_hash = if !is_anon && !is_valid_function_name(fn_name) {
|
let new_hash = if !is_anon && !is_valid_function_name(fn_name) {
|
||||||
FnCallHashes::from_native(calc_fn_hash(None, fn_name, args.len()))
|
FnCallHashes::from_native_only(calc_fn_hash(None, fn_name, args.len()))
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(None, fn_name, args.len()).into()
|
FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args.len()))
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
@ -871,14 +867,17 @@ impl Engine {
|
|||||||
args.insert(0, target.as_mut());
|
args.insert(0, target.as_mut());
|
||||||
|
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
let new_hash = if !is_anon && !is_valid_function_name(&fn_name) {
|
let new_hash = match is_anon {
|
||||||
FnCallHashes::from_native(calc_fn_hash(None, &fn_name, args.len()))
|
false if !is_valid_function_name(&fn_name) => {
|
||||||
} else {
|
FnCallHashes::from_native_only(calc_fn_hash(None, &fn_name, args.len()))
|
||||||
FnCallHashes::from_all(
|
}
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
_ => FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(None, &fn_name, args.len() - 1),
|
calc_fn_hash(None, &fn_name, args.len() - 1),
|
||||||
calc_fn_hash(None, &fn_name, args.len()),
|
calc_fn_hash(None, &fn_name, args.len()),
|
||||||
)
|
),
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
_ => FnCallHashes::from_native_only(calc_fn_hash(None, &fn_name, args.len())),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
@ -947,18 +946,22 @@ impl Engine {
|
|||||||
call_args = &mut _arg_values;
|
call_args = &mut _arg_values;
|
||||||
}
|
}
|
||||||
// Recalculate the hash based on the new function name and new arguments
|
// Recalculate the hash based on the new function name and new arguments
|
||||||
hash = if !is_anon && !is_valid_function_name(fn_name) {
|
let args_len = call_args.len() + 1;
|
||||||
FnCallHashes::from_native(calc_fn_hash(
|
hash = match is_anon {
|
||||||
None,
|
false if !is_valid_function_name(fn_name) => {
|
||||||
fn_name,
|
FnCallHashes::from_native_only(calc_fn_hash(
|
||||||
call_args.len() + 1,
|
None, fn_name, args_len,
|
||||||
))
|
))
|
||||||
} else {
|
}
|
||||||
FnCallHashes::from_all(
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
calc_fn_hash(None, fn_name, call_args.len()),
|
_ => FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(None, fn_name, call_args.len() + 1),
|
calc_fn_hash(None, fn_name, args_len - 1),
|
||||||
)
|
calc_fn_hash(None, fn_name, args_len),
|
||||||
|
),
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
_ => FnCallHashes::from_native_only(calc_fn_hash(
|
||||||
|
None, fn_name, args_len,
|
||||||
|
)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1000,7 +1003,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
mut this_ptr: Option<&mut Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<Token>,
|
op_token: Option<&Token>,
|
||||||
first_arg: Option<&Expr>,
|
first_arg: Option<&Expr>,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
@ -1084,9 +1087,9 @@ impl Engine {
|
|||||||
let args_len = total_args + curry.len();
|
let args_len = total_args + curry.len();
|
||||||
|
|
||||||
hashes = if !is_anon && !is_valid_function_name(name) {
|
hashes = if !is_anon && !is_valid_function_name(name) {
|
||||||
FnCallHashes::from_native(calc_fn_hash(None, name, args_len))
|
FnCallHashes::from_native_only(calc_fn_hash(None, name, args_len))
|
||||||
} else {
|
} else {
|
||||||
calc_fn_hash(None, name, args_len).into()
|
FnCallHashes::from_hash(calc_fn_hash(None, name, args_len))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Handle Fn()
|
// Handle Fn()
|
||||||
@ -1564,10 +1567,10 @@ impl Engine {
|
|||||||
..
|
..
|
||||||
} = expr;
|
} = expr;
|
||||||
|
|
||||||
let op_token = op_token.clone();
|
let op_token = op_token.as_ref();
|
||||||
|
|
||||||
// Short-circuit native unary operator call if under Fast Operators mode
|
// Short-circuit native unary operator call if under Fast Operators mode
|
||||||
if op_token == Some(Token::Bang) && self.fast_operators() && args.len() == 1 {
|
if op_token == Some(&Token::Bang) && self.fast_operators() && args.len() == 1 {
|
||||||
let mut value = self
|
let mut value = self
|
||||||
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])?
|
||||||
.0
|
.0
|
||||||
@ -1597,7 +1600,7 @@ impl Engine {
|
|||||||
let operands = &mut [&mut lhs, &mut rhs];
|
let operands = &mut [&mut lhs, &mut rhs];
|
||||||
|
|
||||||
if let Some((func, need_context)) =
|
if let Some((func, need_context)) =
|
||||||
get_builtin_binary_op_fn(op_token.clone().unwrap(), operands[0], operands[1])
|
get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1])
|
||||||
{
|
{
|
||||||
// Built-in found
|
// Built-in found
|
||||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
auto_restore! { let orig_level = global.level; global.level += 1 }
|
||||||
|
@ -13,25 +13,7 @@ pub type StraightHashMap<V> = hashbrown::HashMap<u64, V, StraightHasherBuilder>;
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
pub type StraightHashMap<V> = std::collections::HashMap<u64, V, StraightHasherBuilder>;
|
pub type StraightHashMap<V> = std::collections::HashMap<u64, V, StraightHasherBuilder>;
|
||||||
|
/// A hasher that only takes one single [`u64`] and returns it as a hash key.
|
||||||
/// Dummy hash value to map zeros to. This value can be anything.
|
|
||||||
///
|
|
||||||
/// # Notes
|
|
||||||
///
|
|
||||||
/// Hashes are `u64`, and they can be zero (although extremely unlikely).
|
|
||||||
/// It is possible to hijack the zero value to indicate non-existence,
|
|
||||||
/// like [`None`] in [`Option<u64>`].
|
|
||||||
///
|
|
||||||
/// When a hash is calculated to be zero, it gets mapped to this alternate hash value.
|
|
||||||
/// This has the effect of releasing the zero value at the expense of causing the probability of
|
|
||||||
/// this value to double, which has minor impacts.
|
|
||||||
pub const ALT_ZERO_HASH: u64 = 42;
|
|
||||||
|
|
||||||
/// A hasher that only takes one single [`u64`] and returns it as a non-zero hash key.
|
|
||||||
///
|
|
||||||
/// # Zeros
|
|
||||||
///
|
|
||||||
/// If the value is zero, it is mapped to `ALT_ZERO_HASH`.
|
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -81,15 +63,11 @@ pub fn get_hasher() -> ahash::AHasher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a non-zero [`u64`] hash key from a namespace-qualified variable name.
|
/// Calculate a [`u64`] hash key from a namespace-qualified variable name.
|
||||||
///
|
///
|
||||||
/// Module names are passed in via `&str` references from an iterator.
|
/// Module names are passed in via `&str` references from an iterator.
|
||||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
///
|
///
|
||||||
/// # Zeros
|
|
||||||
///
|
|
||||||
/// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`.
|
|
||||||
///
|
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
@ -110,13 +88,10 @@ pub fn calc_var_hash<'a>(namespace: impl IntoIterator<Item = &'a str>, var_name:
|
|||||||
count.hash(s);
|
count.hash(s);
|
||||||
var_name.hash(s);
|
var_name.hash(s);
|
||||||
|
|
||||||
match s.finish() {
|
s.finish()
|
||||||
0 => ALT_ZERO_HASH,
|
|
||||||
r => r,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a non-zero [`u64`] hash key from a namespace-qualified function name
|
/// Calculate a [`u64`] hash key from a namespace-qualified function name
|
||||||
/// and the number of parameters, but no parameter types.
|
/// and the number of parameters, but no parameter types.
|
||||||
///
|
///
|
||||||
/// Module names making up the namespace are passed in via `&str` references from an iterator.
|
/// Module names making up the namespace are passed in via `&str` references from an iterator.
|
||||||
@ -124,10 +99,6 @@ pub fn calc_var_hash<'a>(namespace: impl IntoIterator<Item = &'a str>, var_name:
|
|||||||
///
|
///
|
||||||
/// If the function is not namespace-qualified, pass [`None`] as the namespace.
|
/// If the function is not namespace-qualified, pass [`None`] as the namespace.
|
||||||
///
|
///
|
||||||
/// # Zeros
|
|
||||||
///
|
|
||||||
/// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`.
|
|
||||||
///
|
|
||||||
/// # Note
|
/// # Note
|
||||||
///
|
///
|
||||||
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
/// The first module name is skipped. Hashing starts from the _second_ module in the chain.
|
||||||
@ -152,19 +123,12 @@ pub fn calc_fn_hash<'a>(
|
|||||||
fn_name.hash(s);
|
fn_name.hash(s);
|
||||||
num.hash(s);
|
num.hash(s);
|
||||||
|
|
||||||
match s.finish() {
|
s.finish()
|
||||||
0 => ALT_ZERO_HASH,
|
|
||||||
r => r,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate a non-zero [`u64`] hash key from a base [`u64`] hash key and a list of parameter types.
|
/// Calculate a [`u64`] hash key from a base [`u64`] hash key and a list of parameter types.
|
||||||
///
|
///
|
||||||
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
/// Parameter types are passed in via [`TypeId`] values from an iterator.
|
||||||
///
|
|
||||||
/// # Zeros
|
|
||||||
///
|
|
||||||
/// If the hash happens to be zero, it is mapped to `ALT_ZERO_HASH`.
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn calc_fn_hash_full(base: u64, params: impl IntoIterator<Item = TypeId>) -> u64 {
|
pub fn calc_fn_hash_full(base: u64, params: impl IntoIterator<Item = TypeId>) -> u64 {
|
||||||
@ -177,8 +141,5 @@ pub fn calc_fn_hash_full(base: u64, params: impl IntoIterator<Item = TypeId>) ->
|
|||||||
});
|
});
|
||||||
count.hash(s);
|
count.hash(s);
|
||||||
|
|
||||||
match s.finish() {
|
s.finish()
|
||||||
0 => ALT_ZERO_HASH,
|
|
||||||
r => r,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -446,7 +446,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
fn_name,
|
fn_name,
|
||||||
op_token,
|
op_token.as_ref(),
|
||||||
calc_fn_hash(None, fn_name, args_len),
|
calc_fn_hash(None, fn_name, args_len),
|
||||||
args,
|
args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -457,14 +457,15 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
|
|
||||||
// Native or script
|
// Native or script
|
||||||
|
|
||||||
let hash = if is_method_call {
|
let hash = match is_method_call {
|
||||||
FnCallHashes::from_all(
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
true => FnCallHashes::from_script_and_native(
|
||||||
calc_fn_hash(None, fn_name, args_len - 1),
|
calc_fn_hash(None, fn_name, args_len - 1),
|
||||||
calc_fn_hash(None, fn_name, args_len),
|
calc_fn_hash(None, fn_name, args_len),
|
||||||
)
|
),
|
||||||
} else {
|
#[cfg(feature = "no_function")]
|
||||||
calc_fn_hash(None, fn_name, args_len).into()
|
true => FnCallHashes::from_native_only(calc_fn_hash(None, fn_name, args_len)),
|
||||||
|
_ => FnCallHashes::from_hash(calc_fn_hash(None, fn_name, args_len)),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.engine()
|
self.engine()
|
||||||
@ -473,7 +474,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
fn_name,
|
fn_name,
|
||||||
op_token,
|
op_token.as_ref(),
|
||||||
hash,
|
hash,
|
||||||
args,
|
args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
|
15
src/lib.rs
15
src/lib.rs
@ -80,11 +80,14 @@
|
|||||||
#![allow(clippy::upper_case_acronyms)]
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
#![allow(clippy::match_same_arms)]
|
#![allow(clippy::match_same_arms)]
|
||||||
// The lints below can be turned off to reduce signal/noise ratio
|
// The lints below can be turned off to reduce signal/noise ratio
|
||||||
// #![allow(clippy::too_many_lines)]
|
#![allow(clippy::too_many_lines)]
|
||||||
// #![allow(clippy::let_underscore_drop)]
|
#![allow(clippy::let_underscore_drop)]
|
||||||
#![allow(clippy::absurd_extreme_comparisons)]
|
#![allow(clippy::absurd_extreme_comparisons)]
|
||||||
#![allow(clippy::unnecessary_cast)]
|
#![allow(clippy::unnecessary_cast)]
|
||||||
// #![allow(clippy::wildcard_imports)]
|
#![allow(clippy::wildcard_imports)]
|
||||||
|
#![allow(clippy::no_effect_underscore_binding)]
|
||||||
|
#![allow(clippy::semicolon_if_nothing_returned)]
|
||||||
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
@ -166,7 +169,8 @@ const MAX_USIZE_INT: INT = INT::MAX;
|
|||||||
const MAX_USIZE_INT: INT = usize::MAX as INT;
|
const MAX_USIZE_INT: INT = usize::MAX as INT;
|
||||||
|
|
||||||
/// The maximum integer that can fit into a [`usize`].
|
/// The maximum integer that can fit into a [`usize`].
|
||||||
#[cfg(all(feature = "only_i32", target_pointer_width = "32"))]
|
#[cfg(feature = "only_i32")]
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
const MAX_USIZE_INT: INT = INT::MAX;
|
const MAX_USIZE_INT: INT = INT::MAX;
|
||||||
|
|
||||||
/// Number of bits in [`INT`].
|
/// Number of bits in [`INT`].
|
||||||
@ -315,7 +319,8 @@ pub type OptimizationLevel = ();
|
|||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant};
|
pub use types::dynamic::{AccessMode, DynamicReadLock, DynamicWriteLock, Variant};
|
||||||
|
|
||||||
#[cfg(all(feature = "internals", not(feature = "no_float")))]
|
#[cfg(feature = "internals")]
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
pub use types::FloatWrapper;
|
pub use types::FloatWrapper;
|
||||||
|
|
||||||
#[cfg(feature = "internals")]
|
#[cfg(feature = "internals")]
|
||||||
|
@ -145,7 +145,7 @@ impl FuncInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ Calculate a non-zero [`u64`] hash key from a namespace-qualified function name and parameter types.
|
/// _(internals)_ Calculate a [`u64`] hash key from a namespace-qualified function name and parameter types.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
///
|
///
|
||||||
/// Module names are passed in via `&str` references from an iterator.
|
/// Module names are passed in via `&str` references from an iterator.
|
||||||
@ -233,7 +233,7 @@ impl fmt::Debug for Module {
|
|||||||
.modules
|
.modules
|
||||||
.as_deref()
|
.as_deref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|m| m.keys())
|
.flat_map(BTreeMap::keys)
|
||||||
.map(SmartString::as_str)
|
.map(SmartString::as_str)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
@ -991,7 +991,7 @@ impl Module {
|
|||||||
type_id
|
type_id
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function into the [`Module`], returning a non-zero hash key.
|
/// Set a Rust function into the [`Module`], returning a [`u64`] hash key.
|
||||||
///
|
///
|
||||||
/// If there is an existing Rust function of the same hash, it is replaced.
|
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||||
///
|
///
|
||||||
@ -1089,7 +1089,7 @@ impl Module {
|
|||||||
hash_fn
|
hash_fn
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(metadata)_ Set a Rust function into the [`Module`], returning a non-zero hash key.
|
/// _(metadata)_ Set a Rust function into the [`Module`], returning a [`u64`] hash key.
|
||||||
/// Exported under the `metadata` feature only.
|
/// Exported under the `metadata` feature only.
|
||||||
///
|
///
|
||||||
/// If there is an existing Rust function of the same hash, it is replaced.
|
/// If there is an existing Rust function of the same hash, it is replaced.
|
||||||
@ -1144,7 +1144,7 @@ impl Module {
|
|||||||
|
|
||||||
/// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine],
|
/// Set a Rust function taking a reference to the scripting [`Engine`][crate::Engine],
|
||||||
/// the current set of functions, plus a list of mutable [`Dynamic`] references
|
/// the current set of functions, plus a list of mutable [`Dynamic`] references
|
||||||
/// into the [`Module`], returning a non-zero hash key.
|
/// into the [`Module`], returning a [`u64`] hash key.
|
||||||
///
|
///
|
||||||
/// Use this to register a built-in function which must reference settings on the scripting
|
/// Use this to register a built-in function which must reference settings on the scripting
|
||||||
/// [`Engine`][crate::Engine] (e.g. to prevent growing an array beyond the allowed maximum size),
|
/// [`Engine`][crate::Engine] (e.g. to prevent growing an array beyond the allowed maximum size),
|
||||||
@ -1234,7 +1234,7 @@ impl Module {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust function into the [`Module`], returning a non-zero hash key.
|
/// Set a Rust function into the [`Module`], returning a [`u64`] hash key.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing Rust function, it is replaced.
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
///
|
///
|
||||||
@ -1287,7 +1287,7 @@ impl Module {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust getter function taking one mutable parameter, returning a non-zero hash key.
|
/// Set a Rust getter function taking one mutable parameter, returning a [`u64`] hash key.
|
||||||
/// This function is automatically exposed to the global namespace.
|
/// This function is automatically exposed to the global namespace.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing Rust getter function, it is replaced.
|
/// If there is a similar existing Rust getter function, it is replaced.
|
||||||
@ -1327,7 +1327,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust setter function taking two parameters (the first one mutable) into the [`Module`],
|
/// Set a Rust setter function taking two parameters (the first one mutable) into the [`Module`],
|
||||||
/// returning a non-zero hash key.
|
/// returning a [`u64`] hash key.
|
||||||
/// This function is automatically exposed to the global namespace.
|
/// This function is automatically exposed to the global namespace.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing setter Rust function, it is replaced.
|
/// If there is a similar existing setter Rust function, it is replaced.
|
||||||
@ -1370,7 +1370,7 @@ impl Module {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a pair of Rust getter and setter functions into the [`Module`], returning both non-zero hash keys.
|
/// Set a pair of Rust getter and setter functions into the [`Module`], returning both [`u64`] hash keys.
|
||||||
/// This is a short-hand for [`set_getter_fn`][Module::set_getter_fn] and [`set_setter_fn`][Module::set_setter_fn].
|
/// This is a short-hand for [`set_getter_fn`][Module::set_getter_fn] and [`set_setter_fn`][Module::set_setter_fn].
|
||||||
///
|
///
|
||||||
/// These function are automatically exposed to the global namespace.
|
/// These function are automatically exposed to the global namespace.
|
||||||
@ -1418,7 +1418,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust index getter taking two parameters (the first one mutable) into the [`Module`],
|
/// Set a Rust index getter taking two parameters (the first one mutable) into the [`Module`],
|
||||||
/// returning a non-zero hash key.
|
/// returning a [`u64`] hash key.
|
||||||
/// This function is automatically exposed to the global namespace.
|
/// This function is automatically exposed to the global namespace.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing setter Rust function, it is replaced.
|
/// If there is a similar existing setter Rust function, it is replaced.
|
||||||
@ -1479,7 +1479,7 @@ impl Module {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set a Rust index setter taking three parameters (the first one mutable) into the [`Module`],
|
/// Set a Rust index setter taking three parameters (the first one mutable) into the [`Module`],
|
||||||
/// returning a non-zero hash key.
|
/// returning a [`u64`] hash key.
|
||||||
/// This function is automatically exposed to the global namespace.
|
/// This function is automatically exposed to the global namespace.
|
||||||
///
|
///
|
||||||
/// If there is a similar existing Rust function, it is replaced.
|
/// If there is a similar existing Rust function, it is replaced.
|
||||||
@ -1539,7 +1539,7 @@ impl Module {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a pair of Rust index getter and setter functions into the [`Module`], returning both non-zero hash keys.
|
/// Set a pair of Rust index getter and setter functions into the [`Module`], returning both [`u64`] hash keys.
|
||||||
/// This is a short-hand for [`set_indexer_get_fn`][Module::set_indexer_get_fn] and
|
/// This is a short-hand for [`set_indexer_get_fn`][Module::set_indexer_get_fn] and
|
||||||
/// [`set_indexer_set_fn`][Module::set_indexer_set_fn].
|
/// [`set_indexer_set_fn`][Module::set_indexer_set_fn].
|
||||||
///
|
///
|
||||||
@ -2202,7 +2202,7 @@ impl Module {
|
|||||||
environ: ref mut e, ..
|
environ: ref mut e, ..
|
||||||
} = f.func
|
} = f.func
|
||||||
{
|
{
|
||||||
*e = Some(environ.clone())
|
*e = Some(environ.clone());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ impl<'a> OptimizerState<'a> {
|
|||||||
pub fn call_fn_with_constant_arguments(
|
pub fn call_fn_with_constant_arguments(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<Token>,
|
op_token: Option<&Token>,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> Option<Dynamic> {
|
) -> Option<Dynamic> {
|
||||||
self.engine
|
self.engine
|
||||||
@ -1138,7 +1138,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
// Overloaded operators can override built-in.
|
// Overloaded operators can override built-in.
|
||||||
_ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => {
|
_ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => {
|
||||||
if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone().unwrap(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(x.op_token.as_ref().unwrap(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|(f, ctx)| {
|
.and_then(|(f, ctx)| {
|
||||||
let context = if ctx {
|
let context = if ctx {
|
||||||
Some((state.engine, x.name.as_str(), None, &state.global, *pos).into())
|
Some((state.engine, x.name.as_str(), None, &state.global, *pos).into())
|
||||||
@ -1193,7 +1193,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
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()),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE),
|
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Some(Dynamic::FALSE),
|
||||||
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.clone(), arg_values)
|
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(r) = result {
|
if let Some(r) = result {
|
||||||
|
@ -1468,10 +1468,9 @@ pub mod array_functions {
|
|||||||
///
|
///
|
||||||
/// print(x); // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
|
/// print(x); // prints "[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]"
|
||||||
/// ```
|
/// ```
|
||||||
#[rhai_fn(return_raw)]
|
pub fn sort(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) {
|
||||||
pub fn sort(ctx: NativeCallContext, array: &mut Array, comparer: FnPtr) -> RhaiResultOf<()> {
|
|
||||||
if array.len() <= 1 {
|
if array.len() <= 1 {
|
||||||
return Ok(());
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
array.sort_by(|x, y| {
|
array.sort_by(|x, y| {
|
||||||
@ -1489,8 +1488,6 @@ pub mod array_functions {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
/// Sort the array.
|
/// Sort the array.
|
||||||
///
|
///
|
||||||
|
@ -513,11 +513,17 @@ pub mod blob_functions {
|
|||||||
///
|
///
|
||||||
/// print(b); // prints "[030405]"
|
/// print(b); // prints "[030405]"
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(clippy::cast_sign_loss, clippy::needless_pass_by_value)]
|
#[allow(
|
||||||
|
clippy::cast_sign_loss,
|
||||||
|
clippy::needless_pass_by_value,
|
||||||
|
clippy::cast_possible_truncation
|
||||||
|
)]
|
||||||
pub fn chop(blob: &mut Blob, len: INT) {
|
pub fn chop(blob: &mut Blob, len: INT) {
|
||||||
if !blob.is_empty() {
|
if !blob.is_empty() {
|
||||||
if len <= 0 {
|
if len <= 0 {
|
||||||
blob.clear();
|
blob.clear();
|
||||||
|
} else if len > MAX_USIZE_INT {
|
||||||
|
// len > BLOB length
|
||||||
} else if (len as usize) < blob.len() {
|
} else if (len as usize) < blob.len() {
|
||||||
blob.drain(0..blob.len() - len as usize);
|
blob.drain(0..blob.len() - len as usize);
|
||||||
}
|
}
|
||||||
|
@ -109,11 +109,11 @@ mod core_functions {
|
|||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
pub fn sleep(seconds: INT) {
|
pub fn sleep(seconds: INT) {
|
||||||
if seconds <= 0 {
|
if seconds > 0 {
|
||||||
return;
|
#[allow(clippy::cast_sign_loss)]
|
||||||
}
|
|
||||||
std::thread::sleep(std::time::Duration::from_secs(seconds as u64));
|
std::thread::sleep(std::time::Duration::from_secs(seconds as u64));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a JSON string into a value.
|
/// Parse a JSON string into a value.
|
||||||
///
|
///
|
||||||
@ -142,23 +142,23 @@ mod reflection_functions {
|
|||||||
|
|
||||||
/// Return an array of object maps containing metadata of all script-defined functions.
|
/// Return an array of object maps containing metadata of all script-defined functions.
|
||||||
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> Array {
|
pub fn get_fn_metadata_list(ctx: NativeCallContext) -> Array {
|
||||||
collect_fn_metadata(ctx, |_, _, _, _, _| true)
|
collect_fn_metadata(&ctx, |_, _, _, _, _| true)
|
||||||
}
|
}
|
||||||
/// Return an array of object maps containing metadata of all script-defined functions
|
/// Return an array of object maps containing metadata of all script-defined functions
|
||||||
/// matching the specified name.
|
/// matching the specified name.
|
||||||
#[rhai_fn(name = "get_fn_metadata_list")]
|
#[rhai_fn(name = "get_fn_metadata_list")]
|
||||||
pub fn get_fn_metadata(ctx: NativeCallContext, name: &str) -> Array {
|
pub fn get_fn_metadata(ctx: NativeCallContext, name: &str) -> Array {
|
||||||
collect_fn_metadata(ctx, |_, _, n, _, _| n == name)
|
collect_fn_metadata(&ctx, |_, _, n, _, _| n == name)
|
||||||
}
|
}
|
||||||
/// Return an array of object maps containing metadata of all script-defined functions
|
/// Return an array of object maps containing metadata of all script-defined functions
|
||||||
/// matching the specified name and arity (number of parameters).
|
/// matching the specified name and arity (number of parameters).
|
||||||
#[rhai_fn(name = "get_fn_metadata_list")]
|
#[rhai_fn(name = "get_fn_metadata_list")]
|
||||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||||
pub fn get_fn_metadata2(ctx: NativeCallContext, name: &str, params: INT) -> Array {
|
pub fn get_fn_metadata2(ctx: NativeCallContext, name: &str, params: INT) -> Array {
|
||||||
if !(0..=crate::MAX_USIZE_INT).contains(¶ms) {
|
if (0..=crate::MAX_USIZE_INT).contains(¶ms) {
|
||||||
Array::new()
|
collect_fn_metadata(&ctx, |_, _, n, p, _| p == (params as usize) && n == name)
|
||||||
} else {
|
} else {
|
||||||
collect_fn_metadata(ctx, |_, _, n, p, _| p == (params as usize) && n == name)
|
Array::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ mod reflection_functions {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
fn collect_fn_metadata(
|
fn collect_fn_metadata(
|
||||||
ctx: NativeCallContext,
|
ctx: &NativeCallContext,
|
||||||
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFnDef>) -> bool
|
filter: impl Fn(FnNamespace, FnAccess, &str, usize, &crate::Shared<crate::ast::ScriptFnDef>) -> bool
|
||||||
+ Copy,
|
+ Copy,
|
||||||
) -> crate::Array {
|
) -> crate::Array {
|
||||||
|
@ -17,9 +17,9 @@ use crate::tokenizer::{
|
|||||||
use crate::types::dynamic::AccessMode;
|
use crate::types::dynamic::AccessMode;
|
||||||
use crate::types::StringsInterner;
|
use crate::types::StringsInterner;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ExclusiveRange, Identifier,
|
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ExclusiveRange, FnArgsVec,
|
||||||
ImmutableString, InclusiveRange, LexError, OptimizationLevel, ParseError, Position, Scope,
|
Identifier, ImmutableString, InclusiveRange, LexError, OptimizationLevel, ParseError, Position,
|
||||||
Shared, SmartString, StaticVec, AST, INT, PERR,
|
Scope, Shared, SmartString, StaticVec, AST, INT, PERR,
|
||||||
};
|
};
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -567,7 +567,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut _namespace = namespace;
|
let mut _namespace = namespace;
|
||||||
let mut args = StaticVec::new_const();
|
let mut args = FnArgsVec::new_const();
|
||||||
|
|
||||||
match token {
|
match token {
|
||||||
// id( <EOF>
|
// id( <EOF>
|
||||||
@ -625,9 +625,9 @@ impl Engine {
|
|||||||
let hash = calc_fn_hash(None, &id, 0);
|
let hash = calc_fn_hash(None, &id, 0);
|
||||||
|
|
||||||
let hashes = if is_valid_function_name(&id) {
|
let hashes = if is_valid_function_name(&id) {
|
||||||
hash.into()
|
FnCallHashes::from_hash(hash)
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(hash)
|
FnCallHashes::from_native_only(hash)
|
||||||
};
|
};
|
||||||
|
|
||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
@ -700,9 +700,9 @@ impl Engine {
|
|||||||
let hash = calc_fn_hash(None, &id, args.len());
|
let hash = calc_fn_hash(None, &id, args.len());
|
||||||
|
|
||||||
let hashes = if is_valid_function_name(&id) {
|
let hashes = if is_valid_function_name(&id) {
|
||||||
hash.into()
|
FnCallHashes::from_hash(hash)
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(hash)
|
FnCallHashes::from_native_only(hash)
|
||||||
};
|
};
|
||||||
|
|
||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
@ -1945,14 +1945,14 @@ impl Engine {
|
|||||||
|
|
||||||
// Call negative function
|
// Call negative function
|
||||||
expr => {
|
expr => {
|
||||||
let mut args = StaticVec::new_const();
|
let mut args = FnArgsVec::new_const();
|
||||||
args.push(expr);
|
args.push(expr);
|
||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
|
|
||||||
Ok(FnCallExpr {
|
Ok(FnCallExpr {
|
||||||
namespace: Namespace::NONE,
|
namespace: Namespace::NONE,
|
||||||
name: state.get_interned_string("-"),
|
name: state.get_interned_string("-"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
|
hashes: FnCallHashes::from_native_only(calc_fn_hash(None, "-", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: Some(token),
|
op_token: Some(token),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
@ -1973,14 +1973,14 @@ impl Engine {
|
|||||||
|
|
||||||
// Call plus function
|
// Call plus function
|
||||||
expr => {
|
expr => {
|
||||||
let mut args = StaticVec::new_const();
|
let mut args = FnArgsVec::new_const();
|
||||||
args.push(expr);
|
args.push(expr);
|
||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
|
|
||||||
Ok(FnCallExpr {
|
Ok(FnCallExpr {
|
||||||
namespace: Namespace::NONE,
|
namespace: Namespace::NONE,
|
||||||
name: state.get_interned_string("+"),
|
name: state.get_interned_string("+"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
|
hashes: FnCallHashes::from_native_only(calc_fn_hash(None, "+", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: Some(token),
|
op_token: Some(token),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
@ -1994,14 +1994,14 @@ impl Engine {
|
|||||||
let token = token.clone();
|
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 = FnArgsVec::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();
|
||||||
|
|
||||||
Ok(FnCallExpr {
|
Ok(FnCallExpr {
|
||||||
namespace: Namespace::NONE,
|
namespace: Namespace::NONE,
|
||||||
name: state.get_interned_string("!"),
|
name: state.get_interned_string("!"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
|
hashes: FnCallHashes::from_native_only(calc_fn_hash(None, "!", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: Some(token),
|
op_token: Some(token),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
@ -2180,14 +2180,21 @@ impl Engine {
|
|||||||
// lhs.func(...)
|
// lhs.func(...)
|
||||||
(lhs, Expr::FnCall(mut f, func_pos)) => {
|
(lhs, Expr::FnCall(mut f, func_pos)) => {
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
|
let args_len = f.args.len() + 1;
|
||||||
f.hashes = if is_valid_function_name(&f.name) {
|
f.hashes = if is_valid_function_name(&f.name) {
|
||||||
FnCallHashes::from_all(
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
calc_fn_hash(None, &f.name, f.args.len()),
|
{
|
||||||
calc_fn_hash(None, &f.name, f.args.len() + 1),
|
FnCallHashes::from_script_and_native(
|
||||||
|
calc_fn_hash(None, &f.name, args_len - 1),
|
||||||
|
calc_fn_hash(None, &f.name, args_len),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
{
|
||||||
|
FnCallHashes::from_native_only(calc_fn_hash(None, &f.name, args_len))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(calc_fn_hash(None, &f.name, f.args.len() + 1))
|
FnCallHashes::from_native_only(calc_fn_hash(None, &f.name, args_len))
|
||||||
};
|
};
|
||||||
|
|
||||||
let rhs = Expr::MethodCall(f, func_pos);
|
let rhs = Expr::MethodCall(f, func_pos);
|
||||||
@ -2228,14 +2235,23 @@ impl Engine {
|
|||||||
// lhs.func().dot_rhs or lhs.func()[idx_rhs]
|
// lhs.func().dot_rhs or lhs.func()[idx_rhs]
|
||||||
Expr::FnCall(mut f, func_pos) => {
|
Expr::FnCall(mut f, func_pos) => {
|
||||||
// Recalculate hash
|
// Recalculate hash
|
||||||
|
let args_len = f.args.len() + 1;
|
||||||
f.hashes = if is_valid_function_name(&f.name) {
|
f.hashes = if is_valid_function_name(&f.name) {
|
||||||
FnCallHashes::from_all(
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
calc_fn_hash(None, &f.name, f.args.len()),
|
{
|
||||||
calc_fn_hash(None, &f.name, f.args.len() + 1),
|
FnCallHashes::from_script_and_native(
|
||||||
|
calc_fn_hash(None, &f.name, args_len - 1),
|
||||||
|
calc_fn_hash(None, &f.name, args_len),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
#[cfg(feature = "no_function")]
|
||||||
|
{
|
||||||
|
FnCallHashes::from_native_only(calc_fn_hash(
|
||||||
|
None, &f.name, args_len,
|
||||||
|
))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(calc_fn_hash(None, &f.name, f.args.len() + 1))
|
FnCallHashes::from_native_only(calc_fn_hash(None, &f.name, args_len))
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_lhs = BinaryExpr {
|
let new_lhs = BinaryExpr {
|
||||||
@ -2343,7 +2359,7 @@ impl Engine {
|
|||||||
Some(op_token.clone())
|
Some(op_token.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut args = StaticVec::new_const();
|
let mut args = FnArgsVec::new_const();
|
||||||
args.push(root);
|
args.push(root);
|
||||||
args.push(rhs);
|
args.push(rhs);
|
||||||
args.shrink_to_fit();
|
args.shrink_to_fit();
|
||||||
@ -2351,7 +2367,7 @@ impl Engine {
|
|||||||
let mut op_base = FnCallExpr {
|
let mut op_base = FnCallExpr {
|
||||||
namespace: Namespace::NONE,
|
namespace: Namespace::NONE,
|
||||||
name: state.get_interned_string(&op),
|
name: state.get_interned_string(&op),
|
||||||
hashes: FnCallHashes::from_native(hash),
|
hashes: FnCallHashes::from_native_only(hash),
|
||||||
args,
|
args,
|
||||||
op_token: operator_token,
|
op_token: operator_token,
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
@ -2394,7 +2410,7 @@ impl Engine {
|
|||||||
op_base.args.shrink_to_fit();
|
op_base.args.shrink_to_fit();
|
||||||
|
|
||||||
// Convert into a call to `contains`
|
// Convert into a call to `contains`
|
||||||
op_base.hashes = calc_fn_hash(None, OP_CONTAINS, 2).into();
|
op_base.hashes = FnCallHashes::from_hash(calc_fn_hash(None, OP_CONTAINS, 2));
|
||||||
op_base.name = state.get_interned_string(OP_CONTAINS);
|
op_base.name = state.get_interned_string(OP_CONTAINS);
|
||||||
let fn_call = op_base.into_fn_call_expr(pos);
|
let fn_call = op_base.into_fn_call_expr(pos);
|
||||||
|
|
||||||
@ -2403,13 +2419,13 @@ impl Engine {
|
|||||||
} else {
|
} else {
|
||||||
// Put a `!` call in front
|
// Put a `!` call in front
|
||||||
let op = Token::Bang.literal_syntax();
|
let op = Token::Bang.literal_syntax();
|
||||||
let mut args = StaticVec::new_const();
|
let mut args = FnArgsVec::new_const();
|
||||||
args.push(fn_call);
|
args.push(fn_call);
|
||||||
|
|
||||||
let not_base = FnCallExpr {
|
let not_base = FnCallExpr {
|
||||||
namespace: Namespace::NONE,
|
namespace: Namespace::NONE,
|
||||||
name: state.get_interned_string(op),
|
name: state.get_interned_string(op),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, op, 1)),
|
hashes: FnCallHashes::from_native_only(calc_fn_hash(None, op, 1)),
|
||||||
args,
|
args,
|
||||||
op_token: Some(Token::Bang),
|
op_token: Some(Token::Bang),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
@ -2427,9 +2443,9 @@ impl Engine {
|
|||||||
.map_or(false, Option::is_some) =>
|
.map_or(false, Option::is_some) =>
|
||||||
{
|
{
|
||||||
op_base.hashes = if is_valid_script_function {
|
op_base.hashes = if is_valid_script_function {
|
||||||
calc_fn_hash(None, &s, 2).into()
|
FnCallHashes::from_hash(calc_fn_hash(None, &s, 2))
|
||||||
} else {
|
} else {
|
||||||
FnCallHashes::from_native(calc_fn_hash(None, &s, 2))
|
FnCallHashes::from_native_only(calc_fn_hash(None, &s, 2))
|
||||||
};
|
};
|
||||||
op_base.into_fn_call_expr(pos)
|
op_base.into_fn_call_expr(pos)
|
||||||
}
|
}
|
||||||
@ -3560,7 +3576,9 @@ impl Engine {
|
|||||||
// try { try_block } catch ( var ) { catch_block }
|
// try { try_block } catch ( var ) { catch_block }
|
||||||
let branch = self.parse_block(input, state, lib, settings)?.into();
|
let branch = self.parse_block(input, state, lib, settings)?.into();
|
||||||
|
|
||||||
let expr = if !catch_var.is_empty() {
|
let expr = if catch_var.is_empty() {
|
||||||
|
Expr::Unit(catch_var.pos)
|
||||||
|
} else {
|
||||||
// Remove the error variable from the stack
|
// Remove the error variable from the stack
|
||||||
state.stack.as_deref_mut().unwrap().pop();
|
state.stack.as_deref_mut().unwrap().pop();
|
||||||
|
|
||||||
@ -3569,12 +3587,10 @@ impl Engine {
|
|||||||
None,
|
None,
|
||||||
catch_var.pos,
|
catch_var.pos,
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
Expr::Unit(catch_var.pos)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Stmt::TryCatch(
|
Ok(Stmt::TryCatch(
|
||||||
FlowControl { body, expr, branch }.into(),
|
FlowControl { expr, body, branch }.into(),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -3692,7 +3708,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let num_externals = externals.len();
|
let num_externals = externals.len();
|
||||||
let mut args = StaticVec::with_capacity(externals.len() + 1);
|
let mut args = FnArgsVec::with_capacity(externals.len() + 1);
|
||||||
|
|
||||||
args.push(fn_expr);
|
args.push(fn_expr);
|
||||||
|
|
||||||
@ -3709,7 +3725,7 @@ impl Engine {
|
|||||||
let expr = FnCallExpr {
|
let expr = FnCallExpr {
|
||||||
namespace: Namespace::NONE,
|
namespace: Namespace::NONE,
|
||||||
name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY),
|
name: state.get_interned_string(crate::engine::KEYWORD_FN_PTR_CURRY),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(
|
hashes: FnCallHashes::from_native_only(calc_fn_hash(
|
||||||
None,
|
None,
|
||||||
crate::engine::KEYWORD_FN_PTR_CURRY,
|
crate::engine::KEYWORD_FN_PTR_CURRY,
|
||||||
num_externals + 1,
|
num_externals + 1,
|
||||||
|
163
src/tokenizer.rs
163
src/tokenizer.rs
@ -6,6 +6,7 @@ use crate::engine::{
|
|||||||
};
|
};
|
||||||
use crate::func::native::OnParseTokenCallback;
|
use crate::func::native::OnParseTokenCallback;
|
||||||
use crate::{Engine, Identifier, LexError, Position, SmartString, StaticVec, INT, UNSIGNED_INT};
|
use crate::{Engine, Identifier, LexError, Position, SmartString, StaticVec, INT, UNSIGNED_INT};
|
||||||
|
use smallvec::SmallVec;
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{
|
use std::{
|
||||||
@ -980,7 +981,7 @@ pub fn parse_string_literal(
|
|||||||
if termination_char == next_char && escape.is_empty() {
|
if termination_char == next_char && escape.is_empty() {
|
||||||
// Double wrapper
|
// Double wrapper
|
||||||
if stream.peek_next().map_or(false, |c| c == termination_char) {
|
if stream.peek_next().map_or(false, |c| c == termination_char) {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
if let Some(ref mut last) = state.last_token {
|
if let Some(ref mut last) = state.last_token {
|
||||||
last.push(termination_char);
|
last.push(termination_char);
|
||||||
}
|
}
|
||||||
@ -1122,7 +1123,7 @@ pub fn parse_string_literal(
|
|||||||
|
|
||||||
/// Consume the next character.
|
/// Consume the next character.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eat_next(stream: &mut impl InputStream, pos: &mut Position) -> Option<char> {
|
fn eat_next_and_advance(stream: &mut impl InputStream, pos: &mut Position) -> Option<char> {
|
||||||
pos.advance();
|
pos.advance();
|
||||||
stream.get_next()
|
stream.get_next()
|
||||||
}
|
}
|
||||||
@ -1147,7 +1148,7 @@ fn scan_block_comment(
|
|||||||
match c {
|
match c {
|
||||||
'/' => {
|
'/' => {
|
||||||
if let Some(c2) = stream.peek_next().filter(|&c2| c2 == '*') {
|
if let Some(c2) = stream.peek_next().filter(|&c2| c2 == '*') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
if let Some(comment) = comment.as_mut() {
|
if let Some(comment) = comment.as_mut() {
|
||||||
comment.push(c2);
|
comment.push(c2);
|
||||||
}
|
}
|
||||||
@ -1156,7 +1157,7 @@ fn scan_block_comment(
|
|||||||
}
|
}
|
||||||
'*' => {
|
'*' => {
|
||||||
if let Some(c2) = stream.peek_next().filter(|&c2| c2 == '/') {
|
if let Some(c2) = stream.peek_next().filter(|&c2| c2 == '/') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
if let Some(comment) = comment.as_mut() {
|
if let Some(comment) = comment.as_mut() {
|
||||||
comment.push(c2);
|
comment.push(c2);
|
||||||
}
|
}
|
||||||
@ -1287,11 +1288,11 @@ fn get_next_token_inner(
|
|||||||
while let Some(next_char) = stream.peek_next() {
|
while let Some(next_char) = stream.peek_next() {
|
||||||
match next_char {
|
match next_char {
|
||||||
NUMBER_SEPARATOR => {
|
NUMBER_SEPARATOR => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
}
|
}
|
||||||
ch if valid(ch) => {
|
ch if valid(ch) => {
|
||||||
result.push(next_char);
|
result.push(next_char);
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
}
|
}
|
||||||
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
|
#[cfg(any(not(feature = "no_float"), feature = "decimal"))]
|
||||||
'.' => {
|
'.' => {
|
||||||
@ -1357,7 +1358,7 @@ fn get_next_token_inner(
|
|||||||
if c == '0' && result.len() <= 1 =>
|
if c == '0' && result.len() <= 1 =>
|
||||||
{
|
{
|
||||||
result.push(next_char);
|
result.push(next_char);
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
valid = match ch {
|
valid = match ch {
|
||||||
'x' | 'X' => is_hex_digit,
|
'x' | 'X' => is_hex_digit,
|
||||||
@ -1461,16 +1462,16 @@ fn get_next_token_inner(
|
|||||||
match stream.peek_next() {
|
match stream.peek_next() {
|
||||||
// `\r - start from next line
|
// `\r - start from next line
|
||||||
Some('\r') => {
|
Some('\r') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
// `\r\n
|
// `\r\n
|
||||||
if stream.peek_next() == Some('\n') {
|
if stream.peek_next() == Some('\n') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
}
|
}
|
||||||
pos.new_line();
|
pos.new_line();
|
||||||
}
|
}
|
||||||
// `\n - start from next line
|
// `\n - start from next line
|
||||||
Some('\n') => {
|
Some('\n') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
pos.new_line();
|
pos.new_line();
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -1522,13 +1523,13 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
// Unit
|
// Unit
|
||||||
('(', ')') => {
|
('(', ')') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Unit, start_pos));
|
return Some((Token::Unit, start_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parentheses
|
// Parentheses
|
||||||
('(', '*') => {
|
('(', '*') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("(*".into())), start_pos));
|
return Some((Token::Reserved(Box::new("(*".into())), start_pos));
|
||||||
}
|
}
|
||||||
('(', ..) => return Some((Token::LeftParen, start_pos)),
|
('(', ..) => return Some((Token::LeftParen, start_pos)),
|
||||||
@ -1541,16 +1542,16 @@ fn get_next_token_inner(
|
|||||||
// Map literal
|
// Map literal
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
('#', '{') => {
|
('#', '{') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::MapStart, start_pos));
|
return Some((Token::MapStart, start_pos));
|
||||||
}
|
}
|
||||||
// Shebang
|
// Shebang
|
||||||
('#', '!') => return Some((Token::Reserved(Box::new("#!".into())), start_pos)),
|
('#', '!') => return Some((Token::Reserved(Box::new("#!".into())), start_pos)),
|
||||||
|
|
||||||
('#', ' ') => {
|
('#', ' ') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
let token = if stream.peek_next() == Some('{') {
|
let token = if stream.peek_next() == Some('{') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
"# {"
|
"# {"
|
||||||
} else {
|
} else {
|
||||||
"#"
|
"#"
|
||||||
@ -1562,11 +1563,11 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
('+', '=') => {
|
('+', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::PlusAssign, start_pos));
|
return Some((Token::PlusAssign, start_pos));
|
||||||
}
|
}
|
||||||
('+', '+') => {
|
('+', '+') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("++".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 => {
|
||||||
@ -1577,15 +1578,15 @@ fn get_next_token_inner(
|
|||||||
('-', '0'..='9') if !state.next_token_cannot_be_unary => negated = Some(start_pos),
|
('-', '0'..='9') if !state.next_token_cannot_be_unary => negated = Some(start_pos),
|
||||||
('-', '0'..='9') => return Some((Token::Minus, start_pos)),
|
('-', '0'..='9') => return Some((Token::Minus, start_pos)),
|
||||||
('-', '=') => {
|
('-', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::MinusAssign, start_pos));
|
return Some((Token::MinusAssign, start_pos));
|
||||||
}
|
}
|
||||||
('-', '>') => {
|
('-', '>') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("->".into())), start_pos));
|
return Some((Token::Reserved(Box::new("->".into())), start_pos));
|
||||||
}
|
}
|
||||||
('-', '-') => {
|
('-', '-') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("--".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 => {
|
||||||
@ -1594,19 +1595,19 @@ fn get_next_token_inner(
|
|||||||
('-', ..) => return Some((Token::Minus, start_pos)),
|
('-', ..) => return Some((Token::Minus, start_pos)),
|
||||||
|
|
||||||
('*', ')') => {
|
('*', ')') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("*)".into())), start_pos));
|
return Some((Token::Reserved(Box::new("*)".into())), start_pos));
|
||||||
}
|
}
|
||||||
('*', '=') => {
|
('*', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::MultiplyAssign, start_pos));
|
return Some((Token::MultiplyAssign, start_pos));
|
||||||
}
|
}
|
||||||
('*', '*') => {
|
('*', '*') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
return Some((
|
return Some((
|
||||||
if stream.peek_next() == Some('=') {
|
if stream.peek_next() == Some('=') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
Token::PowerOfAssign
|
Token::PowerOfAssign
|
||||||
} else {
|
} else {
|
||||||
Token::PowerOf
|
Token::PowerOf
|
||||||
@ -1618,13 +1619,13 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
('/', '/') => {
|
('/', '/') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
let mut comment: Option<String> = match stream.peek_next() {
|
let mut comment: Option<String> = match stream.peek_next() {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
Some('/') => {
|
Some('/') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
// Long streams of `///...` are not doc-comments
|
// Long streams of `///...` are not doc-comments
|
||||||
match stream.peek_next() {
|
match stream.peek_next() {
|
||||||
@ -1634,7 +1635,7 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
Some('!') => {
|
Some('!') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
Some("//!".into())
|
Some("//!".into())
|
||||||
}
|
}
|
||||||
_ if state.include_comments => Some("//".into()),
|
_ if state.include_comments => Some("//".into()),
|
||||||
@ -1645,7 +1646,7 @@ fn get_next_token_inner(
|
|||||||
if c == '\r' {
|
if c == '\r' {
|
||||||
// \r\n
|
// \r\n
|
||||||
if stream.peek_next() == Some('\n') {
|
if stream.peek_next() == Some('\n') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
}
|
}
|
||||||
pos.new_line();
|
pos.new_line();
|
||||||
break;
|
break;
|
||||||
@ -1676,13 +1677,13 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
('/', '*') => {
|
('/', '*') => {
|
||||||
state.comment_level = 1;
|
state.comment_level = 1;
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
let mut comment: Option<String> = match stream.peek_next() {
|
let mut comment: Option<String> = match stream.peek_next() {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
Some('*') => {
|
Some('*') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
// Long streams of `/****...` are not doc-comments
|
// Long streams of `/****...` are not doc-comments
|
||||||
match stream.peek_next() {
|
match stream.peek_next() {
|
||||||
@ -1703,7 +1704,7 @@ fn get_next_token_inner(
|
|||||||
}
|
}
|
||||||
|
|
||||||
('/', '=') => {
|
('/', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::DivideAssign, start_pos));
|
return Some((Token::DivideAssign, start_pos));
|
||||||
}
|
}
|
||||||
('/', ..) => return Some((Token::Divide, start_pos)),
|
('/', ..) => return Some((Token::Divide, start_pos)),
|
||||||
@ -1712,15 +1713,15 @@ fn get_next_token_inner(
|
|||||||
(',', ..) => return Some((Token::Comma, start_pos)),
|
(',', ..) => return Some((Token::Comma, start_pos)),
|
||||||
|
|
||||||
('.', '.') => {
|
('.', '.') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((
|
return Some((
|
||||||
match stream.peek_next() {
|
match stream.peek_next() {
|
||||||
Some('.') => {
|
Some('.') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
Token::Reserved(Box::new("...".into()))
|
Token::Reserved(Box::new("...".into()))
|
||||||
}
|
}
|
||||||
Some('=') => {
|
Some('=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
Token::InclusiveRange
|
Token::InclusiveRange
|
||||||
}
|
}
|
||||||
_ => Token::ExclusiveRange,
|
_ => Token::ExclusiveRange,
|
||||||
@ -1731,56 +1732,56 @@ fn get_next_token_inner(
|
|||||||
('.', ..) => return Some((Token::Period, start_pos)),
|
('.', ..) => return Some((Token::Period, start_pos)),
|
||||||
|
|
||||||
('=', '=') => {
|
('=', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
if stream.peek_next() == Some('=') {
|
if stream.peek_next() == Some('=') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("===".into())), start_pos));
|
return Some((Token::Reserved(Box::new("===".into())), start_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some((Token::EqualsTo, start_pos));
|
return Some((Token::EqualsTo, start_pos));
|
||||||
}
|
}
|
||||||
('=', '>') => {
|
('=', '>') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::DoubleArrow, start_pos));
|
return Some((Token::DoubleArrow, start_pos));
|
||||||
}
|
}
|
||||||
('=', ..) => return Some((Token::Equals, start_pos)),
|
('=', ..) => return Some((Token::Equals, start_pos)),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
(':', ':') => {
|
(':', ':') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
if stream.peek_next() == Some('<') {
|
if stream.peek_next() == Some('<') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("::<".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_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new(":=".into())), start_pos));
|
return Some((Token::Reserved(Box::new(":=".into())), start_pos));
|
||||||
}
|
}
|
||||||
(':', ';') => {
|
(':', ';') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new(":;".into())), start_pos));
|
return Some((Token::Reserved(Box::new(":;".into())), start_pos));
|
||||||
}
|
}
|
||||||
(':', ..) => return Some((Token::Colon, start_pos)),
|
(':', ..) => return Some((Token::Colon, start_pos)),
|
||||||
|
|
||||||
('<', '=') => {
|
('<', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::LessThanEqualsTo, start_pos));
|
return Some((Token::LessThanEqualsTo, start_pos));
|
||||||
}
|
}
|
||||||
('<', '-') => {
|
('<', '-') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("<-".into())), start_pos));
|
return Some((Token::Reserved(Box::new("<-".into())), start_pos));
|
||||||
}
|
}
|
||||||
('<', '<') => {
|
('<', '<') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
return Some((
|
return Some((
|
||||||
if stream.peek_next() == Some('=') {
|
if stream.peek_next() == Some('=') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
Token::LeftShiftAssign
|
Token::LeftShiftAssign
|
||||||
} else {
|
} else {
|
||||||
Token::LeftShift
|
Token::LeftShift
|
||||||
@ -1789,21 +1790,21 @@ fn get_next_token_inner(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
('<', '|') => {
|
('<', '|') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("<|".into())), start_pos));
|
return Some((Token::Reserved(Box::new("<|".into())), start_pos));
|
||||||
}
|
}
|
||||||
('<', ..) => return Some((Token::LessThan, start_pos)),
|
('<', ..) => return Some((Token::LessThan, start_pos)),
|
||||||
|
|
||||||
('>', '=') => {
|
('>', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::GreaterThanEqualsTo, start_pos));
|
return Some((Token::GreaterThanEqualsTo, start_pos));
|
||||||
}
|
}
|
||||||
('>', '>') => {
|
('>', '>') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
return Some((
|
return Some((
|
||||||
if stream.peek_next() == Some('=') {
|
if stream.peek_next() == Some('=') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
Token::RightShiftAssign
|
Token::RightShiftAssign
|
||||||
} else {
|
} else {
|
||||||
Token::RightShift
|
Token::RightShift
|
||||||
@ -1814,56 +1815,68 @@ fn get_next_token_inner(
|
|||||||
('>', ..) => return Some((Token::GreaterThan, start_pos)),
|
('>', ..) => return Some((Token::GreaterThan, start_pos)),
|
||||||
|
|
||||||
('!', 'i') => {
|
('!', 'i') => {
|
||||||
eat_next(stream, pos);
|
stream.get_next().unwrap();
|
||||||
if stream.peek_next() == Some('n') {
|
if stream.peek_next() == Some('n') {
|
||||||
eat_next(stream, pos);
|
stream.get_next().unwrap();
|
||||||
|
match stream.peek_next() {
|
||||||
|
Some(c) if is_id_continue(c) => {
|
||||||
|
stream.unget('n');
|
||||||
|
stream.unget('i');
|
||||||
|
return Some((Token::Bang, start_pos));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
pos.advance();
|
||||||
|
pos.advance();
|
||||||
return Some((Token::NotIn, start_pos));
|
return Some((Token::NotIn, start_pos));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stream.unget('i');
|
stream.unget('i');
|
||||||
return Some((Token::Bang, start_pos));
|
return Some((Token::Bang, start_pos));
|
||||||
}
|
}
|
||||||
('!', '=') => {
|
('!', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
|
|
||||||
if stream.peek_next() == Some('=') {
|
if stream.peek_next() == Some('=') {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("!==".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_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("!.".into())), start_pos));
|
return Some((Token::Reserved(Box::new("!.".into())), start_pos));
|
||||||
}
|
}
|
||||||
('!', ..) => return Some((Token::Bang, start_pos)),
|
('!', ..) => return Some((Token::Bang, start_pos)),
|
||||||
|
|
||||||
('|', '|') => {
|
('|', '|') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Or, start_pos));
|
return Some((Token::Or, start_pos));
|
||||||
}
|
}
|
||||||
('|', '=') => {
|
('|', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::OrAssign, start_pos));
|
return Some((Token::OrAssign, start_pos));
|
||||||
}
|
}
|
||||||
('|', '>') => {
|
('|', '>') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::Reserved(Box::new("|>".into())), start_pos));
|
return Some((Token::Reserved(Box::new("|>".into())), start_pos));
|
||||||
}
|
}
|
||||||
('|', ..) => return Some((Token::Pipe, start_pos)),
|
('|', ..) => return Some((Token::Pipe, start_pos)),
|
||||||
|
|
||||||
('&', '&') => {
|
('&', '&') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::And, start_pos));
|
return Some((Token::And, start_pos));
|
||||||
}
|
}
|
||||||
('&', '=') => {
|
('&', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::AndAssign, start_pos));
|
return Some((Token::AndAssign, start_pos));
|
||||||
}
|
}
|
||||||
('&', ..) => return Some((Token::Ampersand, start_pos)),
|
('&', ..) => return Some((Token::Ampersand, start_pos)),
|
||||||
|
|
||||||
('^', '=') => {
|
('^', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::XOrAssign, start_pos));
|
return Some((Token::XOrAssign, start_pos));
|
||||||
}
|
}
|
||||||
('^', ..) => return Some((Token::XOr, start_pos)),
|
('^', ..) => return Some((Token::XOr, start_pos)),
|
||||||
@ -1871,7 +1884,7 @@ fn get_next_token_inner(
|
|||||||
('~', ..) => return Some((Token::Reserved(Box::new("~".into())), start_pos)),
|
('~', ..) => return Some((Token::Reserved(Box::new("~".into())), start_pos)),
|
||||||
|
|
||||||
('%', '=') => {
|
('%', '=') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::ModuloAssign, start_pos));
|
return Some((Token::ModuloAssign, start_pos));
|
||||||
}
|
}
|
||||||
('%', ..) => return Some((Token::Modulo, start_pos)),
|
('%', ..) => return Some((Token::Modulo, start_pos)),
|
||||||
@ -1881,7 +1894,7 @@ fn get_next_token_inner(
|
|||||||
('$', ..) => return Some((Token::Reserved(Box::new("$".into())), start_pos)),
|
('$', ..) => return Some((Token::Reserved(Box::new("$".into())), start_pos)),
|
||||||
|
|
||||||
('?', '.') => {
|
('?', '.') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((
|
return Some((
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Token::Elvis,
|
Token::Elvis,
|
||||||
@ -1891,11 +1904,11 @@ fn get_next_token_inner(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
('?', '?') => {
|
('?', '?') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((Token::DoubleQuestion, start_pos));
|
return Some((Token::DoubleQuestion, start_pos));
|
||||||
}
|
}
|
||||||
('?', '[') => {
|
('?', '[') => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
return Some((
|
return Some((
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Token::QuestionBracket,
|
Token::QuestionBracket,
|
||||||
@ -1940,7 +1953,7 @@ fn parse_identifier_token(
|
|||||||
while let Some(next_char) = stream.peek_next() {
|
while let Some(next_char) = stream.peek_next() {
|
||||||
match next_char {
|
match next_char {
|
||||||
x if is_id_continue(x) => {
|
x if is_id_continue(x) => {
|
||||||
eat_next(stream, pos);
|
eat_next_and_advance(stream, pos);
|
||||||
identifier.push(x);
|
identifier.push(x);
|
||||||
if let Some(ref mut last) = state.last_token {
|
if let Some(ref mut last) = state.last_token {
|
||||||
last.push(x);
|
last.push(x);
|
||||||
@ -2091,8 +2104,8 @@ pub fn is_reserved_keyword_or_symbol(syntax: &str) -> bool {
|
|||||||
///
|
///
|
||||||
/// Multiple character streams are jointed together to form one single stream.
|
/// Multiple character streams are jointed together to form one single stream.
|
||||||
pub struct MultiInputsStream<'a> {
|
pub struct MultiInputsStream<'a> {
|
||||||
/// Buffered character, if any.
|
/// Buffered characters, if any.
|
||||||
pub buf: Option<char>,
|
pub buf: SmallVec<[char; 2]>,
|
||||||
/// The current stream index.
|
/// The current stream index.
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
/// The input character streams.
|
/// The input character streams.
|
||||||
@ -2102,15 +2115,11 @@ pub struct MultiInputsStream<'a> {
|
|||||||
impl InputStream for MultiInputsStream<'_> {
|
impl InputStream for MultiInputsStream<'_> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn unget(&mut self, ch: char) {
|
fn unget(&mut self, ch: char) {
|
||||||
if self.buf.is_some() {
|
self.buf.push(ch);
|
||||||
panic!("cannot unget two characters in a row");
|
|
||||||
}
|
|
||||||
|
|
||||||
self.buf = Some(ch);
|
|
||||||
}
|
}
|
||||||
fn get_next(&mut self) -> Option<char> {
|
fn get_next(&mut self) -> Option<char> {
|
||||||
if let Some(ch) = self.buf.take() {
|
if let ch @ Some(..) = self.buf.pop() {
|
||||||
return Some(ch);
|
return ch;
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -2127,8 +2136,8 @@ impl InputStream for MultiInputsStream<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn peek_next(&mut self) -> Option<char> {
|
fn peek_next(&mut self) -> Option<char> {
|
||||||
if let Some(ch) = self.buf {
|
if let ch @ Some(..) = self.buf.last() {
|
||||||
return Some(ch);
|
return ch.copied();
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -2368,7 +2377,7 @@ impl Engine {
|
|||||||
},
|
},
|
||||||
pos: Position::new(1, 0),
|
pos: Position::new(1, 0),
|
||||||
stream: MultiInputsStream {
|
stream: MultiInputsStream {
|
||||||
buf: None,
|
buf: SmallVec::new_const(),
|
||||||
streams: input
|
streams: input
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|s| s.as_ref().chars().peekable())
|
.map(|s| s.as_ref().chars().peekable())
|
||||||
|
Loading…
Reference in New Issue
Block a user