Implement function pointers.

This commit is contained in:
Stephen Chung
2020-06-25 18:07:57 +08:00
parent aeb47efce8
commit fe640e0e13
18 changed files with 269 additions and 31 deletions

View File

@@ -3,7 +3,8 @@
use crate::any::{Dynamic, Union, Variant};
use crate::calc_fn_hash;
use crate::error::ParseErrorType;
use crate::fn_native::{CallableFunction, Callback, FnCallArgs};
use crate::fn_native::{CallableFunction, Callback, FnCallArgs, FnPtr};
use crate::fn_register::RegisterResultFn;
use crate::module::{resolvers, Module, ModuleResolver};
use crate::optimize::OptimizationLevel;
use crate::packages::{Package, PackageLibrary, PackagesCollection, StandardPackage};
@@ -11,7 +12,7 @@ use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, St
use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
use crate::result::EvalAltResult;
use crate::scope::{EntryType as ScopeEntryType, Scope};
use crate::token::Position;
use crate::token::{Position, Token};
use crate::utils::StaticVec;
#[cfg(not(feature = "no_float"))]
@@ -71,11 +72,13 @@ pub const KEYWORD_PRINT: &str = "print";
pub const KEYWORD_DEBUG: &str = "debug";
pub const KEYWORD_TYPE_OF: &str = "type_of";
pub const KEYWORD_EVAL: &str = "eval";
pub const KEYWORD_FN_PTR_CALL: &str = "call";
pub const FN_TO_STRING: &str = "to_string";
pub const FN_GET: &str = "get$";
pub const FN_SET: &str = "set$";
pub const FN_IDX_GET: &str = "$index$get$";
pub const FN_IDX_SET: &str = "$index$set$";
pub const FN_FN_PTR: &str = "Fn";
/// A type specifying the method of chaining.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
@@ -954,6 +957,14 @@ impl Engine {
false,
)),
// Fn
FN_FN_PTR if args.len() == 1 && !self.has_override(lib, hashes) => {
Err(Box::new(EvalAltResult::ErrorRuntime(
"'Fn' should not be called in method style. Try Fn(...);".into(),
Position::none(),
)))
}
// eval - reaching this point it must be a method-style call
KEYWORD_EVAL if args.len() == 1 && !self.has_override(lib, hashes) => {
Err(Box::new(EvalAltResult::ErrorRuntime(
@@ -1124,18 +1135,31 @@ impl Engine {
// Get a reference to the mutation target Dynamic
let (result, updated) = {
let obj = target.as_mut();
let mut arg_values: StaticVec<_> = once(obj)
.chain(
idx_val
.downcast_mut::<StaticVec<Dynamic>>()
.unwrap()
.iter_mut(),
)
.collect();
let idx = idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap();
// Check if it is a FnPtr call
let (fn_name, mut arg_values, hash_fn, is_native) =
if name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
// Redirect function name
let name = obj.as_fn_name().unwrap();
// Recalculate hash
let hash = calc_fn_hash(empty(), name, idx.len(), empty());
// Arguments are passed as-is
(name, idx.iter_mut().collect::<StaticVec<_>>(), hash, false)
} else {
// Attached object pointer in front of the arguments
(
name.as_ref(),
once(obj).chain(idx.iter_mut()).collect::<StaticVec<_>>(),
*hash,
*native,
)
};
let args = arg_values.as_mut();
self.exec_fn_call(
state, lib, name, *native, *hash, args, is_ref, def_val, 0,
state, lib, fn_name, is_native, hash_fn, args, is_ref, def_val, 0,
)
.map_err(|err| EvalAltResult::new_position(err, *pos))?
};
@@ -1697,6 +1721,27 @@ impl Engine {
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
let def_val = def_val.as_ref();
// Handle Fn
if name == FN_FN_PTR && args_expr.len() == 1 {
let hash_fn =
calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
if !self.has_override(lib, (hash_fn, *hash)) {
// Fn - only in function call style
let expr = args_expr.get(0);
let arg_value = self.eval_expr(scope, state, lib, expr, level)?;
return arg_value
.take_immutable_string()
.map(|s| FnPtr::from(s).into())
.map_err(|type_name| {
Box::new(EvalAltResult::ErrorMismatchOutputType(
type_name.into(),
Position::none(),
))
});
}
}
// Handle eval
if name == KEYWORD_EVAL && args_expr.len() == 1 {
let hash_fn =