Add fast-ops feature.

This commit is contained in:
Stephen Chung 2022-09-02 23:45:25 +08:00
parent 7dbc605d0b
commit 0516e8088c
7 changed files with 71 additions and 13 deletions

View File

@ -3,6 +3,7 @@ on:
push: push:
branches: branches:
- master - master
- fast-ops
jobs: jobs:
benchmark: benchmark:

View File

@ -39,9 +39,10 @@ serde_bytes = "0.11"
serde_json = { version = "1.0", default-features = false, features = ["alloc"] } serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
[features] [features]
default = ["std"] default = ["std", "fast_ops"]
std = ["ahash/std", "ahash/runtime-rng", "num-traits/std", "smartstring/std"] std = ["ahash/std", "ahash/runtime-rng", "num-traits/std", "smartstring/std"]
unchecked = [] # unchecked arithmetic unchecked = [] # unchecked arithmetic
fast_ops = [] # ignore overloaded standard operators
sync = [] # restrict to only types that implement Send + Sync sync = [] # restrict to only types that implement Send + Sync
no_position = [] # do not track position in the parser no_position = [] # do not track position in the parser
no_optimize = [] # no script optimizer no_optimize = [] # no script optimizer

View File

@ -190,6 +190,8 @@ pub struct FnCallExpr {
pub args: StaticVec<Expr>, pub args: StaticVec<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 simple symbol-based operator?
pub is_standard_operator: bool,
/// [Position] of the function name. /// [Position] of the function name.
pub pos: Position, pub pos: Position,
} }
@ -204,6 +206,9 @@ impl fmt::Debug for FnCallExpr {
if self.capture_parent_scope { if self.capture_parent_scope {
ff.field("capture_parent_scope", &self.capture_parent_scope); ff.field("capture_parent_scope", &self.capture_parent_scope);
} }
if self.is_standard_operator {
ff.field("is_standard_operator", &self.is_standard_operator);
}
ff.field("hash", &self.hashes) ff.field("hash", &self.hashes)
.field("name", &self.name) .field("name", &self.name)
.field("args", &self.args); .field("args", &self.args);
@ -662,6 +667,7 @@ impl Expr {
hashes: calc_fn_hash(f.fn_name(), 1).into(), hashes: calc_fn_hash(f.fn_name(), 1).into(),
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
capture_parent_scope: false, capture_parent_scope: false,
is_standard_operator: false,
pos, pos,
} }
.into(), .into(),

View File

@ -222,11 +222,39 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
capture_parent_scope: capture, capture_parent_scope: capture,
is_standard_operator,
hashes, hashes,
args, args,
.. ..
} = expr; } = expr;
#[cfg(feature = "fast_ops")]
if *is_standard_operator {
let mut lhs = self
.get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)?
.0
.flatten();
let mut rhs = self
.get_arg_value(scope, global, caches, lib, this_ptr, &args[1], level)?
.0
.flatten();
let arg_values = &mut [&mut lhs, &mut rhs];
return if let Some(f) =
crate::func::get_builtin_binary_op_fn(&name, arg_values[0], arg_values[1])
{
let context = (self, name, None, &*global, lib, pos, level).into();
(f)(context, arg_values)
} else {
self.exec_fn_call(
None, global, caches, lib, name, *hashes, arg_values, false, false, pos, level,
)
.map(|(v, ..)| v)
};
}
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
if !namespace.is_empty() { if !namespace.is_empty() {
// Qualified function call // Qualified function call

View File

@ -614,8 +614,9 @@ impl Engine {
args.shrink_to_fit(); args.shrink_to_fit();
return Ok(FnCallExpr { return Ok(FnCallExpr {
name: id, name: state.get_interned_string(id),
capture_parent_scope, capture_parent_scope,
is_standard_operator: false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
hashes, hashes,
@ -687,6 +688,7 @@ impl Engine {
return Ok(FnCallExpr { return Ok(FnCallExpr {
name: state.get_interned_string(id), name: state.get_interned_string(id),
capture_parent_scope, capture_parent_scope,
is_standard_operator: false,
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
namespace, namespace,
hashes, hashes,
@ -2339,6 +2341,7 @@ impl Engine {
name: state.get_interned_string(op.as_ref()), name: state.get_interned_string(op.as_ref()),
hashes: FnCallHashes::from_native(hash), hashes: FnCallHashes::from_native(hash),
pos, pos,
is_standard_operator: op_token.is_standard_symbol(),
..Default::default() ..Default::default()
}; };

View File

@ -169,6 +169,22 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
" "
)?); )?);
let value = vec![
String::from("hello"),
String::from("world"),
String::from("foo"),
String::from("bar"),
];
let array: Dynamic = value.into();
assert_eq!(array.type_name(), "array");
let array = array.cast::<Array>();
assert_eq!(array[0].type_name(), "string");
assert_eq!(array.len(), 4);
Ok(()) Ok(())
} }

View File

@ -49,21 +49,24 @@ fn test_optimizer_run() -> Result<(), Box<EvalAltResult>> {
run_test(&mut engine)?; run_test(&mut engine)?;
// Override == operator // Override == operator
engine.register_fn("==", |_x: INT, _y: INT| false); #[cfg(not(feature = "fast_ops"))]
{
engine.register_fn("==", |_x: INT, _y: INT| false);
engine.set_optimization_level(OptimizationLevel::Simple); engine.set_optimization_level(OptimizationLevel::Simple);
assert_eq!( assert_eq!(
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
123 123
); );
engine.set_optimization_level(OptimizationLevel::Full); engine.set_optimization_level(OptimizationLevel::Full);
assert_eq!( assert_eq!(
engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?, engine.eval::<INT>("if 1 == 1 || 2 > 3 { 42 } else { 123 }")?,
123 123
); );
}
Ok(()) Ok(())
} }