Refine native/script code splits.

This commit is contained in:
Stephen Chung 2022-10-30 15:45:25 +08:00
parent d97f3f7ec4
commit c9184db4d2
10 changed files with 163 additions and 158 deletions

View File

@ -273,7 +273,7 @@ impl Engine {
.into_err(Position::NONE)); .into_err(Position::NONE));
} }
// Identifier in first position // Identifier in first position
_ if segments.is_empty() && is_valid_identifier(s.chars()) => { _ if segments.is_empty() && is_valid_identifier(s) => {
// Make it a custom keyword/symbol if it is disabled or reserved // Make it a custom keyword/symbol if it is disabled or reserved
if (!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s)) if (!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s))
|| token.map_or(false, |v| v.is_reserved()) || token.map_or(false, |v| v.is_reserved())

View File

@ -111,9 +111,11 @@ impl CustomExpr {
pub struct FnCallHashes { pub struct FnCallHashes {
/// Pre-calculated hash for a script-defined function (zero if native functions only). /// Pre-calculated hash for a script-defined function (zero if native functions only).
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
pub script: u64, script: 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.
pub native: u64, ///
/// This hash can never be zero.
native: u64,
} }
impl fmt::Debug for FnCallHashes { impl fmt::Debug for FnCallHashes {
@ -148,7 +150,7 @@ impl From<u64> for FnCallHashes {
impl FnCallHashes { impl FnCallHashes {
/// Create a [`FnCallHashes`] with only the native Rust hash. /// Create a [`FnCallHashes`] with only the native Rust hash.
#[inline] #[inline(always)]
#[must_use] #[must_use]
pub const fn from_native(hash: u64) -> Self { pub const fn from_native(hash: u64) -> Self {
Self { Self {
@ -158,7 +160,7 @@ impl FnCallHashes {
} }
} }
/// Create a [`FnCallHashes`] with both native Rust and script function hashes. /// Create a [`FnCallHashes`] with both native Rust and script function hashes.
#[inline] #[inline(always)]
#[must_use] #[must_use]
pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self { pub const fn from_all(#[cfg(not(feature = "no_function"))] script: u64, native: u64) -> Self {
Self { Self {
@ -167,16 +169,29 @@ impl FnCallHashes {
native: if native == 0 { ALT_ZERO_HASH } else { native }, native: if native == 0 { ALT_ZERO_HASH } else { native },
} }
} }
/// Is this [`FnCallHashes`] native Rust only? /// Is this [`FnCallHashes`] native-only?
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub const fn is_native_only(&self) -> bool { pub const fn is_native_only(&self) -> bool {
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
return self.script == 0; return self.script == 0;
#[cfg(feature = "no_function")] #[cfg(feature = "no_function")]
return true; return true;
} }
/// Get the native hash.
#[inline(always)]
#[must_use]
pub const fn native(&self) -> u64 {
self.native
}
/// Get the script hash.
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub const fn script(&self) -> u64 {
assert!(self.script != 0);
self.script
}
} }
/// _(internals)_ A function call. /// _(internals)_ A function call.
@ -196,9 +211,6 @@ pub struct FnCallExpr {
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 operator_token: Option<Token>, pub operator_token: Option<Token>,
/// Can this function call be a scripted function?
#[cfg(not(feature = "no_function"))]
pub can_be_script: bool,
/// [Position] of the function name. /// [Position] of the function name.
pub pos: Position, pub pos: Position,
} }
@ -221,10 +233,6 @@ 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);
} }
#[cfg(not(feature = "no_function"))]
if self.can_be_script {
ff.field("can_be_script", &self.can_be_script);
}
ff.field("pos", &self.pos); ff.field("pos", &self.pos);
ff.finish() ff.finish()
} }
@ -691,8 +699,6 @@ impl Expr {
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,
operator_token: None, operator_token: None,
#[cfg(not(feature = "no_function"))]
can_be_script: true,
pos, pos,
} }
.into(), .into(),

View File

@ -223,16 +223,9 @@ impl Engine {
hashes, hashes,
args, args,
operator_token, operator_token,
#[cfg(not(feature = "no_function"))]
can_be_script,
.. ..
} = expr; } = expr;
#[cfg(not(feature = "no_function"))]
let native = !can_be_script;
#[cfg(feature = "no_function")]
let native = true;
// Short-circuit native binary operator call if under Fast Operators mode // Short-circuit native binary operator call if under Fast Operators mode
if operator_token.is_some() && self.fast_operators() && args.len() == 2 { if operator_token.is_some() && self.fast_operators() && args.len() == 2 {
let mut lhs = self let mut lhs = self
@ -257,8 +250,7 @@ impl Engine {
return self return self
.exec_fn_call( .exec_fn_call(
None, global, caches, lib, name, native, *hashes, operands, false, false, pos, None, global, caches, lib, name, *hashes, operands, false, false, pos, level,
level,
) )
.map(|(v, ..)| v); .map(|(v, ..)| v);
} }
@ -266,7 +258,7 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if !expr.namespace.is_empty() { if !expr.namespace.is_empty() {
// Qualified function call // Qualified function call
let hash = hashes.native; let hash = hashes.native();
let namespace = &expr.namespace; let namespace = &expr.namespace;
return self.make_qualified_function_call( return self.make_qualified_function_call(
@ -287,7 +279,6 @@ impl Engine {
lib, lib,
this_ptr, this_ptr,
name, name,
native,
first_arg, first_arg,
args, args,
*hashes, *hashes,

View File

@ -11,9 +11,9 @@ use crate::engine::{
use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState};
use crate::tokenizer::Token; 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, is_valid_function_name, Dynamic, Engine,
ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, FnArgsVec, FnPtr, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult,
Scope, ERR, RhaiResultOf, Scope, ERR,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use hashbrown::hash_map::Entry; use hashbrown::hash_map::Entry;
@ -570,7 +570,6 @@ impl Engine {
caches: &mut Caches, caches: &mut Caches,
lib: &[&Module], lib: &[&Module],
fn_name: &str, fn_name: &str,
_native_only: bool,
hashes: FnCallHashes, hashes: FnCallHashes,
args: &mut FnCallArgs, args: &mut FnCallArgs,
is_ref_mut: bool, is_ref_mut: bool,
@ -590,7 +589,10 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
ensure_no_data_race(fn_name, args, is_ref_mut)?; ensure_no_data_race(fn_name, args, is_ref_mut)?;
let level = level + 1;
// These may be redirected from method style calls. // These may be redirected from method style calls.
if hashes.is_native_only() {
match fn_name { match fn_name {
// Handle type_of() // Handle type_of()
KEYWORD_TYPE_OF if args.len() == 1 => { KEYWORD_TYPE_OF if args.len() == 1 => {
@ -610,7 +612,8 @@ impl Engine {
if num_params < 0 || num_params > crate::MAX_USIZE_INT { if num_params < 0 || num_params > crate::MAX_USIZE_INT {
false false
} else { } else {
let hash_script = calc_fn_hash(None, fn_name.as_str(), num_params as usize); let hash_script =
calc_fn_hash(None, fn_name.as_str(), num_params as usize);
self.has_script_fn(Some(global), caches, lib, hash_script) self.has_script_fn(Some(global), caches, lib, hash_script)
} }
.into(), .into(),
@ -634,11 +637,10 @@ impl Engine {
_ => (), _ => (),
} }
}
let level = level + 1;
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
if !_native_only { if !hashes.is_native_only() {
// Script-defined function call? // Script-defined function call?
let local_entry = &mut None; let local_entry = &mut None;
@ -649,7 +651,7 @@ impl Engine {
local_entry, local_entry,
lib, lib,
fn_name, fn_name,
hashes.script, hashes.script(),
None, None,
false, false,
None, None,
@ -719,7 +721,7 @@ impl Engine {
} }
// Native function call // Native function call
let hash = hashes.native; let hash = hashes.native();
self.call_native_fn( self.call_native_fn(
global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level, global, caches, lib, fn_name, hash, args, is_ref_mut, false, pos, level,
) )
@ -808,9 +810,13 @@ impl Engine {
let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`"); let fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`");
// Redirect function name // Redirect function name
let fn_name = fn_ptr.fn_name(); let fn_name = fn_ptr.fn_name();
let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hashes // Recalculate hashes
let new_hash = calc_fn_hash(None, fn_name, args_len).into(); let args_len = call_args.len() + fn_ptr.curry().len();
let new_hash = if !fn_ptr.is_anonymous() && !is_valid_function_name(fn_name) {
FnCallHashes::from_native(calc_fn_hash(None, fn_name, args_len))
} else {
calc_fn_hash(None, fn_name, args_len).into()
};
// Arguments are passed as-is, adding the curried arguments // Arguments are passed as-is, adding the curried arguments
let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len()); let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len());
curry.extend(fn_ptr.curry().iter().cloned()); curry.extend(fn_ptr.curry().iter().cloned());
@ -825,7 +831,6 @@ impl Engine {
caches, caches,
lib, lib,
fn_name, fn_name,
false,
new_hash, new_hash,
&mut args, &mut args,
false, false,
@ -845,20 +850,25 @@ impl Engine {
// FnPtr call on object // FnPtr call on object
let fn_ptr = mem::take(&mut call_args[0]).cast::<FnPtr>(); let fn_ptr = mem::take(&mut call_args[0]).cast::<FnPtr>();
let is_anon = fn_ptr.is_anonymous();
call_args = &mut call_args[1..]; call_args = &mut call_args[1..];
// Redirect function name // Redirect function name
let fn_name = fn_ptr.fn_name(); let (fn_name, fn_curry) = fn_ptr.take_data();
let args_len = call_args.len() + fn_ptr.curry().len();
// Recalculate hash // Recalculate hash
let new_hash = FnCallHashes::from_all( let args_len = call_args.len() + fn_curry.len();
let new_hash = if !is_anon && !is_valid_function_name(&fn_name) {
FnCallHashes::from_native(calc_fn_hash(None, &fn_name, args_len + 1))
} else {
FnCallHashes::from_all(
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
calc_fn_hash(None, fn_name, args_len), calc_fn_hash(None, &fn_name, args_len),
calc_fn_hash(None, fn_name, args_len + 1), calc_fn_hash(None, &fn_name, args_len + 1),
); )
};
// Replace the first argument with the object pointer, adding the curried arguments // Replace the first argument with the object pointer, adding the curried arguments
let mut curry = FnArgsVec::with_capacity(fn_ptr.curry().len()); let mut curry = FnArgsVec::with_capacity(fn_curry.len());
curry.extend(fn_ptr.curry().iter().cloned()); curry.extend(fn_curry.into_iter());
let mut args = FnArgsVec::with_capacity(curry.len() + call_args.len() + 1); let mut args = FnArgsVec::with_capacity(curry.len() + call_args.len() + 1);
args.push(target.as_mut()); args.push(target.as_mut());
args.extend(curry.iter_mut()); args.extend(curry.iter_mut());
@ -870,8 +880,7 @@ impl Engine {
global, global,
caches, caches,
lib, lib,
fn_name, &fn_name,
false,
new_hash, new_hash,
&mut args, &mut args,
is_ref_mut, is_ref_mut,
@ -939,11 +948,19 @@ 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 = FnCallHashes::from_all( hash = if !fn_ptr.is_anonymous() && !is_valid_function_name(&fn_name) {
FnCallHashes::from_native(calc_fn_hash(
None,
fn_name,
call_args.len() + 1,
))
} else {
FnCallHashes::from_all(
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
calc_fn_hash(None, fn_name, call_args.len()), calc_fn_hash(None, fn_name, call_args.len()),
calc_fn_hash(None, fn_name, call_args.len() + 1), calc_fn_hash(None, fn_name, call_args.len() + 1),
); )
};
} }
} }
}; };
@ -959,7 +976,6 @@ impl Engine {
caches, caches,
lib, lib,
fn_name, fn_name,
false,
hash, hash,
&mut args, &mut args,
is_ref_mut, is_ref_mut,
@ -987,7 +1003,6 @@ impl Engine {
lib: &[&Module], lib: &[&Module],
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
fn_name: &str, fn_name: &str,
native_only: bool,
first_arg: Option<&Expr>, first_arg: Option<&Expr>,
args_expr: &[Expr], args_expr: &[Expr],
hashes: FnCallHashes, hashes: FnCallHashes,
@ -996,7 +1011,6 @@ impl Engine {
pos: Position, pos: Position,
level: usize, level: usize,
) -> RhaiResult { ) -> RhaiResult {
let native = native_only;
let mut first_arg = first_arg; let mut first_arg = first_arg;
let mut a_expr = args_expr; let mut a_expr = args_expr;
let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len(); let mut total_args = if first_arg.is_some() { 1 } else { 0 } + a_expr.len();
@ -1020,10 +1034,12 @@ impl Engine {
} }
let fn_ptr = arg_value.cast::<FnPtr>(); let fn_ptr = arg_value.cast::<FnPtr>();
curry.extend(fn_ptr.curry().iter().cloned()); let is_anon = fn_ptr.is_anonymous();
let (fn_name, fn_curry) = fn_ptr.take_data();
curry.extend(fn_curry.into_iter());
// Redirect function name // Redirect function name
redirected = fn_ptr.take_data().0; redirected = fn_name;
name = &redirected; name = &redirected;
// Shift the arguments // Shift the arguments
@ -1035,7 +1051,8 @@ impl Engine {
// Recalculate hash // Recalculate hash
let args_len = total_args + curry.len(); let args_len = total_args + curry.len();
hashes = if hashes.is_native_only() {
hashes = if !is_anon && !is_valid_function_name(name) {
FnCallHashes::from_native(calc_fn_hash(None, name, args_len)) FnCallHashes::from_native(calc_fn_hash(None, name, args_len))
} else { } else {
calc_fn_hash(None, name, args_len).into() calc_fn_hash(None, name, args_len).into()
@ -1193,8 +1210,8 @@ impl Engine {
return self return self
.exec_fn_call( .exec_fn_call(
scope, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false, scope, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos,
pos, level, level,
) )
.map(|(v, ..)| v); .map(|(v, ..)| v);
} }
@ -1256,8 +1273,7 @@ impl Engine {
} }
self.exec_fn_call( self.exec_fn_call(
None, global, caches, lib, name, native, hashes, &mut args, is_ref_mut, false, pos, None, global, caches, lib, name, hashes, &mut args, is_ref_mut, false, pos, level,
level,
) )
.map(|(v, ..)| v) .map(|(v, ..)| v)
} }

View File

@ -416,7 +416,6 @@ impl<'a> NativeCallContext<'a> {
caches, caches,
self.lib, self.lib,
fn_name, fn_name,
false,
hash, hash,
args, args,
is_ref_mut, is_ref_mut,

View File

@ -1142,7 +1142,7 @@ 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 && x.operator_token.is_some() && (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.operator_token.as_ref().unwrap(), &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"))]

View File

@ -469,7 +469,7 @@ fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position
// 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.as_str()) => {
Err(PERR::Reserved(s.to_string()).into_err(pos)) Err(PERR::Reserved(s.to_string()).into_err(pos))
} }
// Bad identifier // Bad identifier
@ -486,7 +486,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.as_str()) => 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
@ -599,9 +599,7 @@ impl Engine {
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
let hash = calc_fn_hash(None, &id, 0); let hash = calc_fn_hash(None, &id, 0);
let is_valid_function_name = is_valid_function_name(&id); let hashes = if is_valid_function_name(&id) {
let hashes = if is_valid_function_name {
hash.into() hash.into()
} else { } else {
FnCallHashes::from_native(hash) FnCallHashes::from_native(hash)
@ -613,8 +611,6 @@ impl Engine {
name: state.get_interned_string(id), name: state.get_interned_string(id),
capture_parent_scope, capture_parent_scope,
operator_token: None, operator_token: None,
#[cfg(not(feature = "no_function"))]
can_be_script: is_valid_function_name,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
hashes, hashes,
@ -671,9 +667,7 @@ impl Engine {
#[cfg(feature = "no_module")] #[cfg(feature = "no_module")]
let hash = calc_fn_hash(None, &id, args.len()); let hash = calc_fn_hash(None, &id, args.len());
let is_valid_function_name = is_valid_function_name(&id); let hashes = if is_valid_function_name(&id) {
let hashes = if is_valid_function_name {
hash.into() hash.into()
} else { } else {
FnCallHashes::from_native(hash) FnCallHashes::from_native(hash)
@ -685,8 +679,6 @@ impl Engine {
name: state.get_interned_string(id), name: state.get_interned_string(id),
capture_parent_scope, capture_parent_scope,
operator_token: None, operator_token: None,
#[cfg(not(feature = "no_function"))]
can_be_script: is_valid_function_name,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
hashes, hashes,
@ -1012,7 +1004,7 @@ impl Engine {
(Token::InterpolatedString(..), pos) => { (Token::InterpolatedString(..), pos) => {
return Err(PERR::PropertyExpected.into_err(pos)) return Err(PERR::PropertyExpected.into_err(pos))
} }
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { (Token::Reserved(s), pos) if is_valid_identifier(s.as_str()) => {
return Err(PERR::Reserved(s.to_string()).into_err(pos)); return Err(PERR::Reserved(s.to_string()).into_err(pos));
} }
(Token::LexError(err), pos) => return Err(err.into_err(pos)), (Token::LexError(err), pos) => return Err(err.into_err(pos)),
@ -1944,8 +1936,6 @@ impl Engine {
pos, pos,
operator_token: Some(token), operator_token: Some(token),
capture_parent_scope: false, capture_parent_scope: false,
#[cfg(not(feature = "no_function"))]
can_be_script: false,
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
} }
@ -1976,8 +1966,6 @@ impl Engine {
pos, pos,
operator_token: Some(token), operator_token: Some(token),
capture_parent_scope: false, capture_parent_scope: false,
#[cfg(not(feature = "no_function"))]
can_be_script: false,
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
} }
@ -2001,8 +1989,6 @@ impl Engine {
pos, pos,
operator_token: Some(token), operator_token: Some(token),
capture_parent_scope: false, capture_parent_scope: false,
#[cfg(not(feature = "no_function"))]
can_be_script: false,
} }
.into_fn_call_expr(pos)) .into_fn_call_expr(pos))
} }
@ -2217,11 +2203,15 @@ impl Engine {
// lhs.func(...) // lhs.func(...)
(lhs, Expr::FnCall(mut func, func_pos)) => { (lhs, Expr::FnCall(mut func, func_pos)) => {
// Recalculate hash // Recalculate hash
func.hashes = FnCallHashes::from_all( func.hashes = if is_valid_function_name(&func.name) {
FnCallHashes::from_all(
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
calc_fn_hash(None, &func.name, func.args.len()), calc_fn_hash(None, &func.name, func.args.len()),
calc_fn_hash(None, &func.name, func.args.len() + 1), calc_fn_hash(None, &func.name, func.args.len() + 1),
); )
} else {
FnCallHashes::from_native(calc_fn_hash(None, &func.name, func.args.len() + 1))
};
let rhs = Expr::MethodCall(func, func_pos); let rhs = Expr::MethodCall(func, func_pos);
Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_flags, op_pos)) Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_flags, op_pos))
@ -2263,11 +2253,19 @@ impl Engine {
// lhs.func().dot_rhs or lhs.func()[idx_rhs] // lhs.func().dot_rhs or lhs.func()[idx_rhs]
Expr::FnCall(mut func, func_pos) => { Expr::FnCall(mut func, func_pos) => {
// Recalculate hash // Recalculate hash
func.hashes = FnCallHashes::from_all( func.hashes = if is_valid_function_name(&func.name) {
FnCallHashes::from_all(
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
calc_fn_hash(None, &func.name, func.args.len()), calc_fn_hash(None, &func.name, func.args.len()),
calc_fn_hash(None, &func.name, func.args.len() + 1), calc_fn_hash(None, &func.name, func.args.len() + 1),
); )
} else {
FnCallHashes::from_native(calc_fn_hash(
None,
&func.name,
func.args.len() + 1,
))
};
let new_lhs = BinaryExpr { let new_lhs = BinaryExpr {
lhs: Expr::MethodCall(func, func_pos), lhs: Expr::MethodCall(func, func_pos),
@ -2322,7 +2320,7 @@ impl Engine {
.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) => {
return Err(PERR::UnknownOperator(c.to_string()).into_err(*current_pos)) return Err(PERR::UnknownOperator(c.to_string()).into_err(*current_pos))
} }
_ => current_op.precedence(), _ => current_op.precedence(),
@ -2347,7 +2345,7 @@ impl Engine {
.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) => {
return Err(PERR::UnknownOperator(c.to_string()).into_err(*next_pos)) return Err(PERR::UnknownOperator(c.to_string()).into_err(*next_pos))
} }
_ => next_op.precedence(), _ => next_op.precedence(),
@ -2371,8 +2369,8 @@ 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 is_function = is_valid_function_name(&op); let is_valid_script_function = is_valid_function_name(&op);
let operator_token = if is_function { let operator_token = if is_valid_script_function {
None None
} else { } else {
Some(op_token.clone()) Some(op_token.clone())
@ -2387,8 +2385,6 @@ impl Engine {
pos, pos,
operator_token, operator_token,
capture_parent_scope: false, capture_parent_scope: false,
#[cfg(not(feature = "no_function"))]
can_be_script: is_function,
}; };
let mut args = StaticVec::new_const(); let mut args = StaticVec::new_const();
@ -2474,7 +2470,7 @@ impl Engine {
let pos = args[0].start_position(); let pos = args[0].start_position();
FnCallExpr { FnCallExpr {
hashes: if is_function { hashes: if is_valid_script_function {
hash.into() hash.into()
} else { } else {
FnCallHashes::from_native(hash) FnCallHashes::from_native(hash)
@ -3732,8 +3728,6 @@ impl Engine {
pos, pos,
operator_token: None, operator_token: None,
capture_parent_scope: false, capture_parent_scope: false,
#[cfg(not(feature = "no_function"))]
can_be_script: false,
} }
.into_fn_call_expr(pos); .into_fn_call_expr(pos);

View File

@ -2199,7 +2199,7 @@ fn get_identifier(
} }
} }
let is_valid_identifier = is_valid_identifier(identifier.chars()); let is_valid_identifier = is_valid_identifier(&identifier);
if let Some(token) = Token::lookup_from_syntax(&identifier) { if let Some(token) = Token::lookup_from_syntax(&identifier) {
return (token, start_pos); return (token, start_pos);
@ -2233,10 +2233,10 @@ pub fn is_keyword_function(name: &str) -> bool {
/// _(internals)_ Is a text string a valid identifier? /// _(internals)_ Is a text string a valid identifier?
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
#[must_use] #[must_use]
pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool { pub fn is_valid_identifier(name: &str) -> bool {
let mut first_alphabetic = false; let mut first_alphabetic = false;
for ch in name { for ch in name.chars() {
match ch { match ch {
'_' => (), '_' => (),
_ if is_id_first_alphabetic(ch) => first_alphabetic = true, _ if is_id_first_alphabetic(ch) => first_alphabetic = true,
@ -2254,7 +2254,7 @@ pub fn is_valid_identifier(name: impl Iterator<Item = char>) -> bool {
#[inline(always)] #[inline(always)]
#[must_use] #[must_use]
pub fn is_valid_function_name(name: &str) -> bool { pub fn is_valid_function_name(name: &str) -> bool {
is_valid_identifier(name.chars()) is_valid_identifier(name) && !is_keyword_function(name)
} }
/// Is a character valid to start an identifier? /// Is a character valid to start an identifier?
@ -2433,7 +2433,7 @@ impl<'a> Iterator for TokenIterator<'a> {
(.., true) => unreachable!("no custom operators"), (.., true) => unreachable!("no custom operators"),
// Reserved keyword that is not custom and disabled. // Reserved keyword that is not custom and disabled.
(token, false) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token) => { (token, false) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token) => {
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token.chars()) { "keyword"} else {"symbol"}); let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"});
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into()) Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
}, },
// Reserved keyword/operator that is not custom. // Reserved keyword/operator that is not custom.

View File

@ -1,10 +1,9 @@
//! The `FnPtr` type. //! The `FnPtr` type.
use crate::tokenizer::is_valid_identifier;
use crate::types::dynamic::Variant; use crate::types::dynamic::Variant;
use crate::{ use crate::{
Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Position, RhaiError, is_valid_function_name, Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext,
RhaiResult, RhaiResultOf, StaticVec, AST, ERR, Position, RhaiError, RhaiResult, RhaiResultOf, StaticVec, AST, ERR,
}; };
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
@ -254,7 +253,7 @@ impl TryFrom<ImmutableString> for FnPtr {
#[inline(always)] #[inline(always)]
fn try_from(value: ImmutableString) -> RhaiResultOf<Self> { fn try_from(value: ImmutableString) -> RhaiResultOf<Self> {
if is_valid_identifier(value.chars()) { if is_valid_function_name(&value) {
Ok(Self { Ok(Self {
name: value, name: value,
curry: StaticVec::new_const(), curry: StaticVec::new_const(),

View File

@ -238,7 +238,7 @@ impl fmt::Display for ParseErrorType {
Self::AssignmentToInvalidLHS(s) => f.write_str(s), Self::AssignmentToInvalidLHS(s) => f.write_str(s),
Self::LiteralTooLarge(typ, max) => write!(f, "{typ} exceeds the maximum limit ({max})"), Self::LiteralTooLarge(typ, max) => write!(f, "{typ} exceeds the maximum limit ({max})"),
Self::Reserved(s) if is_valid_identifier(s.chars()) => write!(f, "'{s}' is a reserved keyword"), Self::Reserved(s) if is_valid_identifier(s.as_str()) => write!(f, "'{s}' is a reserved keyword"),
Self::Reserved(s) => write!(f, "'{s}' is a reserved symbol"), Self::Reserved(s) => write!(f, "'{s}' is a reserved symbol"),
Self::UnexpectedEOF => f.write_str("Script is incomplete"), Self::UnexpectedEOF => f.write_str("Script is incomplete"),
Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"), Self::WrongSwitchIntegerCase => f.write_str("Integer switch case cannot follow a range case"),